mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-14 21:04:41 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a99cc426d6 | |||
| 4d49341918 | |||
| 2f0f8a979c | |||
| 413c7543ba | |||
| 975aa4e2f2 | |||
| a1f9e68f46 | |||
| 02dee4a20b | |||
| bc9b9480fb | |||
| d1ceeeca22 | |||
| ee188168c1 | |||
| 1f558ce9b3 | |||
| 28a2ff1b94 | |||
| d8070c74c3 | |||
| b89cd6903c | |||
| 7e84f9ef59 | |||
| 609756db30 |
Vendored
+96
-78
@@ -119,12 +119,12 @@ li.checked::marker { content: "\2612"; }
|
||||
<message>
|
||||
<location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="327"/>
|
||||
<source>%1 has been banned</source>
|
||||
<translation>%1 foi banido(a)</translation>
|
||||
<translation>%1 foi banido</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="330"/>
|
||||
<source>%1 has been unbanned</source>
|
||||
<translation>%1 foi desbanido(a)</translation>
|
||||
<translation>%1 foi desbanido</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="446"/>
|
||||
@@ -782,13 +782,14 @@ Desabiltar essa opção só serve para propósitos de depuração.</translation>
|
||||
<message>
|
||||
<location filename="../../src/qt_common/config/shared_translation.cpp" line="168"/>
|
||||
<source>Use asynchronous GPU emulation</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Usar emulação assíncrona de GPU</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/qt_common/config/shared_translation.cpp" line="169"/>
|
||||
<source>Uses an extra CPU thread for rendering.
|
||||
This option should always remain enabled.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Usa uma thread de CPU extra para renderização.
|
||||
Esta opção deve estar sempre habilitada.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/qt_common/config/shared_translation.cpp" line="170"/>
|
||||
@@ -2801,32 +2802,32 @@ Quando um programa tenta abrir o applet, ele é imediatamente fechado.</translat
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_debug.ui" line="526"/>
|
||||
<source>Bitmask for quick development toggles</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Bitmask para rápida alternativa de desenvolvimento</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_debug.ui" line="529"/>
|
||||
<source>Set debug knobs (bitmask)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Definir knobs de depuração (bitmask)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_debug.ui" line="532"/>
|
||||
<source>16-bit debug knob set for quick development toggles</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Knob de depuração 16-bit definido para rápida alternativa de desenvolvimento</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_debug.ui" line="535"/>
|
||||
<source> (bitmask)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>(bitmask)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_debug.ui" line="538"/>
|
||||
<source>Debug Knobs: </source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Knobs de depuração:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_debug.ui" line="572"/>
|
||||
<source>Unit Serial:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Número de série:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_debug.ui" line="601"/>
|
||||
@@ -2841,7 +2842,7 @@ Quando um programa tenta abrir o applet, ele é imediatamente fechado.</translat
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_debug.ui" line="611"/>
|
||||
<source>Flush log output on each line</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Liberar o log a cada linha</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_debug.ui" line="618"/>
|
||||
@@ -2856,7 +2857,7 @@ Quando um programa tenta abrir o applet, ele é imediatamente fechado.</translat
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_debug.ui" line="632"/>
|
||||
<source>Censor username in logs</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Censurar nome de usuário nos logs</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_debug.ui" line="668"/>
|
||||
@@ -2960,7 +2961,7 @@ Quando um programa tenta abrir o applet, ele é imediatamente fechado.</translat
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="77"/>
|
||||
<source>GraphicsExtra</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Gráficos Extra</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="78"/>
|
||||
@@ -3040,7 +3041,7 @@ Quando um programa tenta abrir o applet, ele é imediatamente fechado.</translat
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="65"/>
|
||||
<source>Save Data</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Salvar dados</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="101"/>
|
||||
@@ -3116,7 +3117,7 @@ Quando um programa tenta abrir o applet, ele é imediatamente fechado.</translat
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="111"/>
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="159"/>
|
||||
<source>Select Save Data Directory...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Selecionar o diretório de salvamento</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="114"/>
|
||||
@@ -3136,22 +3137,22 @@ Quando um programa tenta abrir o applet, ele é imediatamente fechado.</translat
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="148"/>
|
||||
<source>Save Data Directory</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Salvar diretório de salvamento</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="149"/>
|
||||
<source>Choose an action for the save data directory:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Escolha uma ação para o diretório de salvamento:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="151"/>
|
||||
<source>Set Custom Path</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Definir caminho personalizado</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="152"/>
|
||||
<source>Reset to NAND</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Resetar a NAND</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="208"/>
|
||||
@@ -3162,7 +3163,13 @@ New: %2
|
||||
|
||||
Would you like to migrate saves from the old location?
|
||||
WARNING: This will overwrite any conflicting saves in the new location!</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Salvamento existe tanto no antigo quanto nas novas locações.
|
||||
|
||||
Antigo: %1
|
||||
Novo: %2
|
||||
|
||||
Você gostaria de migrar seu salvamento do antigo local?
|
||||
ATENÇÃO: Isso irá sobrescrever qualquer salvamento conflitante no novo local!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="216"/>
|
||||
@@ -3170,51 +3177,57 @@ WARNING: This will overwrite any conflicting saves in the new location!</source>
|
||||
|
||||
From: %1
|
||||
To: %2</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Você gostaria de migrar seus salvamentos para o novo local?
|
||||
|
||||
De: %1
|
||||
Para: %2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="224"/>
|
||||
<source>Migrate Save Data</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Migrar salvamento</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="231"/>
|
||||
<source>Migrating save data...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Migrando salvamento...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="231"/>
|
||||
<source>Cancel</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Cancelar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="239"/>
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="252"/>
|
||||
<source>Migration Failed</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Migração Falhou</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="240"/>
|
||||
<source>Failed to create destination directory.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Falha ao criar diretório de destino.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="253"/>
|
||||
<source>Failed to migrate save data:
|
||||
%1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Falha ao migrar salvamento:
|
||||
1%</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="258"/>
|
||||
<source>Migration Complete</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Migração Completa</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="259"/>
|
||||
<source>Save data has been migrated successfully.
|
||||
|
||||
Would you like to delete the old save data?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Salvamento foi migrado com sucesso
|
||||
|
||||
Gostaria de deletar o salvamento antigo?</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -3363,27 +3376,27 @@ Would you like to delete the old save data?</source>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_graphics_extensions.ui" line="14"/>
|
||||
<source>Form</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Formar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_graphics_extensions.ui" line="17"/>
|
||||
<source>Extras</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Extras</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_graphics_extensions.ui" line="23"/>
|
||||
<source>Hacks</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Hacks</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_graphics_extensions.ui" line="29"/>
|
||||
<source>Changing these options from their default may cause issues. Novitii cavete!</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Alterar essas opções de seus padrões pode causar problemas. È una trappola, Bino!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_graphics_extensions.ui" line="62"/>
|
||||
<source>Vulkan Extensions</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Extensões Vulkan</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_graphics_extensions.cpp" line="49"/>
|
||||
@@ -3394,7 +3407,7 @@ Would you like to delete the old save data?</source>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_graphics_extensions.cpp" line="68"/>
|
||||
<source>Extended Dynamic State is disabled on macOS due to MoltenVK compatibility issues that cause black screens.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Estado Dinâmico Extendido é desativado no MacOS, devido a problemas de compatibilidade com o MoltenVK, que causa telas pretas.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -4709,7 +4722,7 @@ Os valores atuais são %1% e %2% respectivamente.</translation>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_network.ui" line="41"/>
|
||||
<source>Enable Airplane Mode</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Habilitar Modo Avião</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -4792,7 +4805,7 @@ Os valores atuais são %1% e %2% respectivamente.</translation>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="82"/>
|
||||
<source>Ext. Graphics</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Gráficos Ext.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="83"/>
|
||||
@@ -4807,7 +4820,7 @@ Os valores atuais são %1% e %2% respectivamente.</translation>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="85"/>
|
||||
<source>Network</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Rede</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="86"/>
|
||||
@@ -4835,12 +4848,12 @@ Os valores atuais são %1% e %2% respectivamente.</translation>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game_addons.ui" line="23"/>
|
||||
<source>Import Mod from ZIP</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Importar Mod a partir de ZIP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game_addons.ui" line="30"/>
|
||||
<source>Import Mod from Folder</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Importar Mod a partir da Pasta</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="56"/>
|
||||
@@ -4855,87 +4868,92 @@ Os valores atuais são %1% e %2% respectivamente.</translation>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="153"/>
|
||||
<source>Mod Install Succeeded</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Instalação de Mod Concluída</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="154"/>
|
||||
<source>Successfully installed all mods.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Todos os Mods foram instalados com sucesso.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="163"/>
|
||||
<source>Mod Install Failed</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Instalação de Mod Falhou</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="164"/>
|
||||
<source>Failed to install the following mods:
|
||||
%1
|
||||
Check the log for details.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Falha ao instalar os seguintes Mods:
|
||||
%1
|
||||
Cheque o log para mais detalhes.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="183"/>
|
||||
<source>Mod Folder</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Pasta de Mods</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="194"/>
|
||||
<source>Zipped Mod Location</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Local de Mod Empacotado</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="196"/>
|
||||
<source>Zipped Archives (*.zip)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Arquivos Empacotados (*.zip)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="214"/>
|
||||
<source>Invalid Selection</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Seleção inválida</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="215"/>
|
||||
<source>Only mods, cheats, and patches can be deleted.
|
||||
To delete NAND-installed updates, right-click the game in the game list and click Remove -> Remove Installed Update.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Apenas Mods, trapaças e correções podem ser deletadas.
|
||||
Para deletar atualizações instaladas na NAND, pressione o botão direito no jogo na lista e clique em Remover -> Remova Atualização Instalada.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="221"/>
|
||||
<source>You are about to delete the following installed mods:
|
||||
</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Você está a prestes de deletar os seguintes Mods instalados:
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="227"/>
|
||||
<source>
|
||||
Once deleted, these can NOT be recovered. Are you 100% sure you want to delete them?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>
|
||||
Uma vez deletado, esses NÃO podem ser recuperados. Você tem 100% de certeza que quer deletar eles?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="232"/>
|
||||
<source>Delete add-on(s)?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Deletar add-on(s)?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="243"/>
|
||||
<source>Successfully deleted</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Deletado com sucesso</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="244"/>
|
||||
<source>Successfully deleted all selected mods.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Todos os Mods selecionados foram deletados com sucesso.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="267"/>
|
||||
<source>&Delete</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Deletar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="274"/>
|
||||
<source>&Open in File Manager</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Abrir Gerenciador de Arquivos</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -5021,27 +5039,27 @@ Once deleted, these can NOT be recovered. Are you 100% sure you want to delete t
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="217"/>
|
||||
<source>Error saving user image</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Erro ao salvar imagem de usuário</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="218"/>
|
||||
<source>Unable to save image to file</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Incapaz de salvar imagem para arquivo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="230"/>
|
||||
<source>&Edit</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Editar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="231"/>
|
||||
<source>&Delete</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Deletar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="291"/>
|
||||
<source>Edit User</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Editar Usuário</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -5226,7 +5244,7 @@ UUID: %2</translation>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_tas.ui" line="17"/>
|
||||
<source><html><head/><body><p>Reads controller input from scripts in the same format as TAS-nx scripts.<br/>For a more detailed explanation, please consult the user handbook.</p></body></html></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation><html><head/><body><p>Lê as entradas do controle a partir de scripts no mesmo formato que o TAS-nx.<br/>Para uma explicação mais detalhada, por favor consulte o manual do usuário.</p></body></html></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_tas.ui" line="27"/>
|
||||
@@ -5261,7 +5279,7 @@ UUID: %2</translation>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_tas.ui" line="84"/>
|
||||
<source>Show recording dialog</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Mostrar gravações de diálogo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_tas.ui" line="98"/>
|
||||
@@ -5394,7 +5412,7 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="26"/>
|
||||
<source>Warning: The settings in this page affect the inner workings of Eden's emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Atenção: As configurações desta página podem mudar o funcionamento interno do touchscreen emulado do Eden. Mudar elas pode causar problemas indesejados, como o touchscreen parando de funcionar parcialmente e até mesmo por completo. Você deverá apenas usar essa página se você sabe o que está fazendo.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="52"/>
|
||||
@@ -5690,7 +5708,7 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_web.ui" line="25"/>
|
||||
<source>Eden Web Service</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Serviço Web do Eden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_web.ui" line="33"/>
|
||||
@@ -5705,7 +5723,7 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_web.ui" line="105"/>
|
||||
<source>Generate</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Gerar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_web.ui" line="130"/>
|
||||
@@ -5727,19 +5745,19 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta
|
||||
<location filename="../../src/yuzu/configuration/configure_web.cpp" line="118"/>
|
||||
<source>All Good</source>
|
||||
<comment>Tooltip</comment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Tudo certo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_web.cpp" line="113"/>
|
||||
<source>Must be between 4-20 characters</source>
|
||||
<comment>Tooltip</comment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Deve conter entre 4-20 caracteres</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/configuration/configure_web.cpp" line="122"/>
|
||||
<source>Must be 48 characters, and lowercase a-z</source>
|
||||
<comment>Tooltip</comment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Deve ser em 48 caracteres, e letras minúsculas de a-z</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -5760,12 +5778,12 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta
|
||||
<message>
|
||||
<location filename="../../src/yuzu/data_dialog.ui" line="26"/>
|
||||
<source>Data Manager</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Gestor de Dados</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/data_dialog.ui" line="48"/>
|
||||
<source>Deleting ANY data is IRREVERSABLE!</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Deletar QUAISQUER dados é IRREVERSÍVEL!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/data_dialog.cpp" line="31"/>
|
||||
@@ -5775,12 +5793,12 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta
|
||||
<message>
|
||||
<location filename="../../src/yuzu/data_dialog.cpp" line="32"/>
|
||||
<source>User NAND</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>NAND de Usuário</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/data_dialog.cpp" line="33"/>
|
||||
<source>System NAND</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>NAND do Sistema</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/data_dialog.cpp" line="34"/>
|
||||
@@ -5790,7 +5808,7 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta
|
||||
<message>
|
||||
<location filename="../../src/yuzu/data_dialog.cpp" line="35"/>
|
||||
<source>Saves</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Salvamentos</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -5798,17 +5816,17 @@ Arrasta os pontos para mudar a posição, ou dá duplo-clique nas células da ta
|
||||
<message>
|
||||
<location filename="../../src/yuzu/data_widget.ui" line="14"/>
|
||||
<source>Form</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Formar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/data_widget.ui" line="22"/>
|
||||
<source>Tooltip</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Dica de Ferramenta</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/data_widget.ui" line="70"/>
|
||||
<source>Open with your system file manager</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Abrir gerenciador de arquivos do sistema</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/data_widget.ui" line="106"/>
|
||||
@@ -6063,7 +6081,7 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation>O teu GPU pode não suportar OpenGL 4.6, ou não tem os drivers gráficos mais recentes.</translation>
|
||||
<translation>A sua GPU não tem suporte a OpenGL 4.6, talvez você não esteja usando os drivers gráficos mais atuais.<br><br>Renderizador GL:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
|
||||
+21
-6
@@ -1,3 +1,6 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -16,15 +19,24 @@ if (NOT FILE_LIST)
|
||||
endif()
|
||||
|
||||
set(DIRECTORY_NAME ${HEADER_NAME})
|
||||
|
||||
set(FILE_DATA "")
|
||||
|
||||
string(APPEND FILE_DATA "[[nodiscard]] static inline std::vector<FileSys::VirtualFile> CollectFiles_${DIRECTORY_NAME}() {\n")
|
||||
string(APPEND FILE_DATA [[
|
||||
std::vector<FileSys::VirtualFile> vfs_files;
|
||||
auto const fn = [&](std::string_view name, std::span<const u8> data) {
|
||||
vfs_files.push_back(std::make_shared<FileSys::VectorVfsFile>(
|
||||
std::vector<u8>(data.begin(), data.end()),
|
||||
std::string{name}
|
||||
));
|
||||
};
|
||||
]])
|
||||
foreach(ZONE_FILE ${FILE_LIST})
|
||||
if (ZONE_FILE STREQUAL "\n")
|
||||
continue()
|
||||
endif()
|
||||
|
||||
string(APPEND FILE_DATA "{\"${ZONE_FILE}\",\n{")
|
||||
|
||||
string(APPEND FILE_DATA " {\n")
|
||||
string(APPEND FILE_DATA " constexpr uint8_t tzdb_data[] = {\n")
|
||||
file(READ ${ZONE_PATH}/${ZONE_FILE} ZONE_DATA HEX)
|
||||
string(LENGTH "${ZONE_DATA}" ZONE_DATA_LEN)
|
||||
foreach(I RANGE 0 ${ZONE_DATA_LEN} 2)
|
||||
@@ -42,9 +54,12 @@ foreach(ZONE_FILE ${FILE_LIST})
|
||||
string(APPEND FILE_DATA " ")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
string(APPEND FILE_DATA "}},\n")
|
||||
string(APPEND FILE_DATA " };\n")
|
||||
string(APPEND FILE_DATA " fn(\"${ZONE_FILE}\", tzdb_data);\n")
|
||||
string(APPEND FILE_DATA " }\n")
|
||||
endforeach()
|
||||
string(APPEND FILE_DATA " return vfs_files;\n")
|
||||
string(APPEND FILE_DATA "}\n")
|
||||
|
||||
file(READ ${NX_TZDB_SOURCE_DIR}/tzdb_template.h.in NX_TZDB_TEMPLATE_H_IN)
|
||||
file(CONFIGURE OUTPUT ${NX_TZDB_INCLUDE_DIR}/nx_tzdb/${HEADER_NAME}.h CONTENT "${NX_TZDB_TEMPLATE_H_IN}")
|
||||
|
||||
Vendored
+3
-3
@@ -9,10 +9,10 @@
|
||||
|
||||
namespace NxTzdb {
|
||||
|
||||
// @DIRECTORY_NAME@
|
||||
|
||||
// clang-format off
|
||||
const static std::map<const char*, const std::vector<uint8_t>> @DIRECTORY_NAME@ =
|
||||
{
|
||||
@FILE_DATA@};
|
||||
@FILE_DATA@
|
||||
// clang-format on
|
||||
|
||||
} // namespace NxTzdb
|
||||
|
||||
+4
-1
@@ -159,7 +159,10 @@ else()
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
add_compile_options(-mcx16)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mcx16>)
|
||||
if (PLATFORM_LINUX OR PLATFORM_FREEBSD)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mtls-dialect=gnu2>)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (APPLE AND CXX_CLANG)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
@@ -7,5 +7,5 @@
|
||||
package org.yuzu.yuzu_emu.utils
|
||||
|
||||
object AddonUtil {
|
||||
val validAddonDirectories = listOf("cheats", "exefs", "romfs", "romfslite")
|
||||
val validAddonDirectories = listOf("cheats", "exefs", "romfs", "romfslite", "romfs_ext")
|
||||
}
|
||||
|
||||
@@ -66,6 +66,8 @@
|
||||
<string name="show_power_info_description">عرض استهلاك الطاقة الحالي والسعة المتبقية في البطارية</string>
|
||||
<string name="show_shaders_building">عرض بناء التظليل</string>
|
||||
<string name="show_shaders_building_description">عرض عدد التظليل التي يتم بناؤها</string>
|
||||
<string name="pipeline_worker_cores">خيوط عامل خط الأنابيب</string>
|
||||
<string name="pipeline_worker_cores_description">قم بإدارة عدد النوى المستخدمة لبناء خطوط أنابيب Vulkan، فالقيمة الأعلى ستحسن أداء تجميع خط الأنابيب ولكن درجات الحرارة سترتفع أيضًا.</string>
|
||||
<string name="overlay_position">موضع الطبقة</string>
|
||||
<string name="overlay_position_description">حدد مكان عرض الطبقة على الشاشة</string>
|
||||
<string name="overlay_position_top_left">أعلى اليمين</string>
|
||||
@@ -485,15 +487,15 @@
|
||||
<string name="renderer_force_max_clock">إجبار السرعة القصوى (لأجهزة Adreno فقط)</string>
|
||||
<string name="renderer_force_max_clock_description">يجبر وحدة معالجة الرسومات على العمل بأقصى سرعة ممكنة (سيظل يتم تطبيق القيود الحرارية).</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation">محاكاة غير متزامنة لوحدة معالجة الرسومات</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation_description">يُشغّل هذا الخيار محاكاة وحدة معالجة الرسومات بشكل غير متزامن لتقليل توقف وحدة المعالجة المركزية وتحسين الإنتاجية. عطّل هذا الخيار فقط في حال واجهت مشاكل متعلقة بالتوقيت.</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation_description">يمكن لهذه الحيلة أن تزيد الأداء عن طريق تشغيل محاكاة وحدة معالجة الرسومات بشكل غير متزامن على حساب مشاكل الرسومات وزيادة معدلات الأعطال بسبب العمليات المتعلقة بالتوقيت.</string>
|
||||
<string name="renderer_async_presentation">عرض غير متزامن</string>
|
||||
<string name="renderer_async_presentation_description">يحسّن الأداء بشكل طفيف عن طريق نقل عملية العرض إلى خيط معالجة منفصل لوحدة المعالجة المركزية.</string>
|
||||
<string name="renderer_async_presentation_description">يمكن لهذه الحيلة أن تزيد من الأداء عن طريق نقل عملية العرض إلى خيط معالجة منفصل على حساب مشاكل الرسوميات.</string>
|
||||
<string name="renderer_reactive_flushing">استخدم التنظيف التفاعلي</string>
|
||||
<string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string>
|
||||
<string name="enable_buffer_history">تمكين سجل التخزين المؤقت</string>
|
||||
<string name="enable_buffer_history_description">يُتيح هذا الخيار الوصول إلى حالات التخزين المؤقت السابقة. وقد يُحسّن جودة العرض وثبات الأداء في بعض الألعاب.</string>
|
||||
<string name="use_optimized_vertex_buffers">مخازن الرؤوس المُحسّنة</string>
|
||||
<string name="use_optimized_vertex_buffers_description">يتيح ربط مخزن الرؤوس المُحسّن لتحسين الأداء. يتطلب برامج تشغيل Turnip/QCOM من إصدار Mesa 26.0 أو أحدث. سيؤدي إلى تعطل النظام عند استخدام برامج تشغيل Turnip الأقدم.</string>
|
||||
<string name="use_optimized_vertex_buffers_description">يُتيح ربطًا مُحسَّنًا لمخازن الرؤوس لتحسين الأداء. يتطلب برامج تشغيل Mesa 26.0+ Turnip/ برامج تشغيل QCOM. قد يتعطل على برامج تشغيل Turnip القديمة (25.3 وما دون).</string>
|
||||
|
||||
<string name="hacks">اختراقات</string>
|
||||
|
||||
@@ -505,6 +507,8 @@
|
||||
<string name="fix_bloom_effects_description">يقلل من ضبابية التوهج في LA/EOW (Adreno A6XX - A7XX/ Turnip)، ويزيل التوهج في Burnout. تحذير: قد يسبب تشوهات رسومية في ألعاب أخرى.</string>
|
||||
<string name="emulate_bgr565">محاكاة BGR565</string>
|
||||
<string name="emulate_bgr565_description">يُصلح مشاكل انعكاس الألوان في الألعاب أو ظهور تشوهات غريبة أو ظلال غريبة.</string>
|
||||
<string name="rescale_hack">تفعيل ميزة إعادة التحجيم القديمة</string>
|
||||
<string name="rescale_hack_description">يُمكّن هذا الخيار من التعامل مع عملية إعادة تحجيم الألعاب بطريقة تقليدية باستخدام مسار إعادة التحجيم السريع</string>
|
||||
<string name="renderer_asynchronous_shaders">استخدم تظليل غير متزامن</string>
|
||||
<string name="renderer_asynchronous_shaders_description">يقوم بتجميع التظليل بشكل غير متزامن. قد يقلل ذلك من التقطعات ولكنه قد يؤدي أيضًا إلى حدوث أخطاء.</string>
|
||||
<string name="gpu_unswizzle_settings">إعدادات إلغاء ترتيب بيانات وحدة معالجة الرسومات</string>
|
||||
@@ -523,10 +527,10 @@
|
||||
<string name="extensions">إضافات</string>
|
||||
|
||||
<string name="dyna_state">الحالة الديناميكية الموسعة</string>
|
||||
<string name="dyna_state_description">يتحكم هذا الخيار في عدد الميزات التي يمكن استخدامها في حالة الديناميكية الموسعة. تسمح الأرقام الأعلى بمزيد من الميزات ويمكن أن تزيد من الأداء، ولكنها قد تسبب مشاكل مع بعض برامج التشغيل والأجهزة.</string>
|
||||
<string name="dyna_state_description">يتحكم هذا الخيار في عدد الميزات التي يمكن استخدامها في ExtendedDynamicState (EDS). كلما زادت القيمة، قلّ عدد عمليات تجميع خط الأنابيب بناءً على الحالة الديناميكية التي يدعمها برنامج التشغيل.</string>
|
||||
<string name="disabled">معطل</string>
|
||||
<string name="vertex_input_dynamic_state">حالة ديناميكية لإدخال الرأس</string>
|
||||
<string name="vertex_input_dynamic_state_description">يتيح ميزة الحالة الديناميكية لإدخال الرأس لتحسين الجودة والأداء.</string>
|
||||
<string name="vertex_input_dynamic_state_description">يتيح تمكين هذه الميزة معالجة أكثر مرونة لمدخلات الرؤوس، مما قد يقلل من وقت تجميع خط الأنابيب في vertex/buffer.</string>
|
||||
<string name="sample_shading_fraction">تظليل العينة</string>
|
||||
<string name="sample_shading_fraction_description">يسمح هذا الخيار بتنفيذ مُظلل الأجزاء لكل عينة في جزء متعدد العينات بدلاً من تنفيذه مرة واحدة لكل جزء. يُحسّن هذا من جودة الرسومات على حساب بعض الأداء.</string>
|
||||
|
||||
|
||||
@@ -478,10 +478,8 @@
|
||||
<string name="extensions">Rozšíření</string>
|
||||
|
||||
<string name="dyna_state">Úroveň EDS</string>
|
||||
<string name="dyna_state_description">Určuje počet funkcí využívaných v rámci rozšířeného dynamického stavu API Vulkan (Extended Dynamic State). Vyšší hodnoty umožňují využít více funkcí a mohou zvýšit výkon, ale u některých ovladačů a výrobců grafických karet mohou způsobovat problémy s kompatibilitou.</string>
|
||||
<string name="disabled">Vypnuto</string>
|
||||
<string name="vertex_input_dynamic_state">Dynamický stav vstupu vrcholů (Vertex Input)</string>
|
||||
<string name="vertex_input_dynamic_state_description">Aktivuje funkci dynamického stavu vstupu vrcholů (Vertex Input Dynamic State) pro lepší kvalitu a výkon.</string>
|
||||
<string name="display">Zobrazení</string>
|
||||
|
||||
<string name="renderer_screen_layout">Orientace</string>
|
||||
|
||||
@@ -476,10 +476,8 @@ Wird der Handheld-Modus verwendet, verringert es die Auflösung und erhöht die
|
||||
<string name="extensions">Erweiterungen</string>
|
||||
|
||||
<string name="dyna_state">Erweiterter dynamischer Status</string>
|
||||
<string name="dyna_state_description">Steuert die Anzahl der Funktionen, die im \"Vertex Input Dynamic State\" werden können. Höhere Werte ermöglichen mehr Funktionen und können die Leistung steigern, können aber bei einigen Treibern und Anbietern zu Problemen führen.</string>
|
||||
<string name="disabled">Deaktiviert</string>
|
||||
<string name="vertex_input_dynamic_state">Vertex Input Dynamic State</string>
|
||||
<string name="vertex_input_dynamic_state_description">Aktiviert die Funktion \"Vertex Input Dynamic State\" für bessere Qualität und Leistung.</string>
|
||||
<string name="sample_shading_fraction">Sample Shading</string>
|
||||
<string name="sample_shading_fraction_description">Ermöglicht es dem Fragment-Shader, in einem Multisample-Fragment pro Sample anstatt einmal pro Fragment ausgeführt zu werden. Verbessert die Grafikqualität auf Kosten der Leistung.</string>
|
||||
|
||||
|
||||
@@ -66,6 +66,8 @@
|
||||
<string name="show_power_info_description">Muestra el consumo de energía actual y la capacidad restante de la batería</string>
|
||||
<string name="show_shaders_building">Mostrar construcción de sombreadores</string>
|
||||
<string name="show_shaders_building_description">Muestra el número actual de sombreadores que se están construyendo</string>
|
||||
<string name="pipeline_worker_cores">Hilos de trabajo de canalización</string>
|
||||
<string name="pipeline_worker_cores_description">Gestiona la cantidad de núcleos usados para la construcción de canalizaciones de Vulkan; un valor más alto mejorará el rendimiento de la contrucción de canalización, pero también aumentarán las temperaturas.</string>
|
||||
<string name="overlay_position">Posición de la superposición</string>
|
||||
<string name="overlay_position_description">Elige dónde se muestra la superposición en la pantalla</string>
|
||||
<string name="overlay_position_top_left">Superior izquierda</string>
|
||||
@@ -479,16 +481,14 @@
|
||||
<string name="renderer_force_max_clock">Forzar velocidad al máximo (solo Adreno)</string>
|
||||
<string name="renderer_force_max_clock_description">Fuerza a la GPU a ejecutarse a la velocidad máxima de reloj posible (se seguirán aplicando restricciones térmicas).</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation">Emulación de GPU asíncrona</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation_description">Ejecuta la emulación de la GPU de forma asíncrona para reducir los bloqueos de la CPU y mejorar el rendimiento. Desactiva esta opción solo si experimentas problemas de sincronización.</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation_description">Este hack puede aumentar el rendimiento ejecutando la emulación de la GPU de forma asíncrona, a costa de problemas gráficos y un aumento en la tasa de fallos debido a operaciones relacionadas con la sincronización.</string>
|
||||
<string name="renderer_async_presentation">Presentación asíncrona</string>
|
||||
<string name="renderer_async_presentation_description">Mejora ligeramente el rendimiento al mover la presentación a un hilo independiente de la CPU.</string>
|
||||
<string name="renderer_async_presentation_description">Este hack puede aumentar el rendimiento al mover la presentación a un hilo independiente de la CPU a costa de problemas gráficos.</string>
|
||||
<string name="renderer_reactive_flushing">Usar limpieza reactiva</string>
|
||||
<string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string>
|
||||
<string name="enable_buffer_history">Activar el historial del búfer</string>
|
||||
<string name="enable_buffer_history_description">Permite el acceso al estado del búfer anterior. Esta opción puede mejorar la calidad de renderizado y la consistencia en el rendimiento de algunos juegos.</string>
|
||||
<string name="use_optimized_vertex_buffers">Búferes de vértices optimizados</string>
|
||||
<string name="use_optimized_vertex_buffers_description">Permite la optimización del enlace del búfer de vértices para un mejor rendimiento. Requiere controladores Mesa 26.0+ Turnip/ controladores QCOM. Causará fallos con controladores Turnip más antiguos.</string>
|
||||
|
||||
<string name="hacks">Hacks</string>
|
||||
|
||||
<string name="fast_gpu_time">Tiempo rápido de la GPU</string>
|
||||
@@ -499,6 +499,7 @@
|
||||
<string name="fix_bloom_effects_description">Reduce el efecto de resplandor en LA/EOW (Adreno A6XX - A7XX/ Turnip), elimina el resplandor en Burnout. Advertencia: puede causar artefactos gráficos en otros juegos.</string>
|
||||
<string name="emulate_bgr565">Emular BGR565</string>
|
||||
<string name="emulate_bgr565_description">Soluciona problemas con colores invertidos en juegos, artefactos o sombras extrañas.</string>
|
||||
<string name="rescale_hack">Activar la pasada de reescalado heredada</string>
|
||||
<string name="renderer_asynchronous_shaders">Usar sombreadores asíncronos</string>
|
||||
<string name="renderer_asynchronous_shaders_description">Compila los sombreadores de forma asíncrona. Esto puede reducir los tirones, pero también puede introducir errores gráficos.</string>
|
||||
<string name="gpu_unswizzle_settings">Ajustes de desentrelazado de la GPU</string>
|
||||
@@ -517,10 +518,8 @@
|
||||
<string name="extensions">Extensiones</string>
|
||||
|
||||
<string name="dyna_state">Estado dinámico extendido</string>
|
||||
<string name="dyna_state_description">Controla la cantidad de funciones que se pueden usar en el Estado Dinámico Extendido. Un número mayor permite más funciones y puede aumentar el rendimiento, pero puede causar problemas con algunos controladores y proveedores.</string>
|
||||
<string name="disabled">Desactivado</string>
|
||||
<string name="vertex_input_dynamic_state">Estado dinámico de entrada de vértices</string>
|
||||
<string name="vertex_input_dynamic_state_description">Activa la función de estado dinámico de entrada de vértices para una mejor calidad y rendimiento.</string>
|
||||
<string name="sample_shading_fraction">Muestreo de sombreado</string>
|
||||
<string name="sample_shading_fraction_description">Permite que el sombreador de fragmentos se ejecute por muestra en un fragmento multimuestreado, en lugar de una sola vez por fragmento. Mejora la calidad de los gráficos a coste de algo de rendimiento.</string>
|
||||
|
||||
|
||||
@@ -483,7 +483,6 @@
|
||||
<string name="dyna_state">État dynamique étendu</string>
|
||||
<string name="disabled">Désactivé</string>
|
||||
<string name="vertex_input_dynamic_state">État dynamique d\'entrée de sommet</string>
|
||||
<string name="vertex_input_dynamic_state_description">Active la fonctionnalité d\'état dynamique des entrées de sommets pour une meilleure qualité et de meilleures performances.</string>
|
||||
<string name="display">Affichage</string>
|
||||
|
||||
<string name="renderer_screen_layout">Orientation</string>
|
||||
|
||||
@@ -476,10 +476,8 @@
|
||||
<string name="extensions">Rozszerzenia</string>
|
||||
|
||||
<string name="dyna_state">Rozszerzony stan dynamiczny</string>
|
||||
<string name="dyna_state_description">Kontroluje liczbę funkcji, które mogą być używane w Extended Dynamic State. Wyższe wartości pozwalają na użycie większej liczby funkcji i mogą zwiększyć wydajność, ale mogą powodować problemy z niektórymi sterownikami i u niektórych producentów.</string>
|
||||
<string name="disabled">Wyłączone</string>
|
||||
<string name="vertex_input_dynamic_state">Dynamiczny stan wejścia wierzchołków</string>
|
||||
<string name="vertex_input_dynamic_state_description">Włącza funkcję dynamicznego stanu wejścia wierzchołków, poprawiając jakość i wydajność.</string>
|
||||
<string name="sample_shading_fraction">Cieniowanie próbkowane</string>
|
||||
<string name="sample_shading_fraction_description">Pozwala uruchamiać shader fragmentów dla każdej próbki w wielopróbkowanym fragmencie zamiast raz na fragment. Poprawia jakość grafiki kosztem części wydajności.</string>
|
||||
|
||||
|
||||
@@ -463,7 +463,6 @@
|
||||
<string name="dyna_state">Extended Dynamic State</string>
|
||||
<string name="disabled">Desativado</string>
|
||||
<string name="vertex_input_dynamic_state">Vertex Input Dynamic State</string>
|
||||
<string name="vertex_input_dynamic_state_description">Ativa o recurso de vertex input dynamic state para melhor qualidade e desempenho.</string>
|
||||
<string name="display">Tela</string>
|
||||
|
||||
<string name="renderer_screen_layout">Orientação</string>
|
||||
|
||||
@@ -66,6 +66,8 @@
|
||||
<string name="show_power_info_description">Показать текущее энергопотребление и оставшуюся емкость аккумулятора</string>
|
||||
<string name="show_shaders_building">Показать компиляцию шейдеров</string>
|
||||
<string name="show_shaders_building_description">Отображает текущее количество шейдеров, которые компилируются</string>
|
||||
<string name="pipeline_worker_cores">Рабочие потоки конвейера</string>
|
||||
<string name="pipeline_worker_cores_description">Позволяет настроить количество ядер, задействованных при построении конвейеров Vulkan. Чем выше значение, тем быстрее будет компиляция конвейеров, однако это приведёт к повышению температуры устройства.</string>
|
||||
<string name="overlay_position">Позиция оверлея</string>
|
||||
<string name="overlay_position_description">Расположение оверлея на экране</string>
|
||||
<string name="overlay_position_top_left">Сверху слева</string>
|
||||
@@ -481,15 +483,15 @@
|
||||
<string name="renderer_force_max_clock">Принудительная максимальная тактовая частота (только для Adreno)</string>
|
||||
<string name="renderer_force_max_clock_description">Заставляет ГПУ работать на максимально возможных тактовых частотах (тепловые ограничения все равно будут применяться).</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation">Асинхронная эмуляция ГПУ</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation_description">Выполняет эмуляцию ГПУ асинхронно для снижения задержек ЦП и увеличения производительности. Отключайте только при возникновении проблем с таймингами.</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation_description">Может повысить производительность за счёт асинхронного запуска эмуляции ГПУ, но ценой появления графических ошибок и увеличения частоты вылетов из-за операций, зависящих от синхронизации.</string>
|
||||
<string name="renderer_async_presentation">Асинхронная презентация</string>
|
||||
<string name="renderer_async_presentation_description">Немного улучшает производительность, перемещая презентацию в отдельный поток ЦП.</string>
|
||||
<string name="renderer_async_presentation_description">Может повысить производительность за счёт перемещения вывода кадров в отдельный поток ЦП, но ценой возникновения графических проблем.</string>
|
||||
<string name="renderer_reactive_flushing">Реактивная очистка</string>
|
||||
<string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string>
|
||||
<string name="enable_buffer_history">Включить историю буфера</string>
|
||||
<string name="enable_buffer_history_description">Позволяет обращаться к предыдущим состояниям буфера. Эта опция может повысить качество рендеринга и стабильность производительности в некоторых играх.</string>
|
||||
<string name="use_optimized_vertex_buffers">Оптимизированные вершинные буферы</string>
|
||||
<string name="use_optimized_vertex_buffers_description">Включает оптимизированную привязку вершинного буфера для повышения производительности. Требует Mesa Turnip 26.0+ / QCOM драйверы. Приводит к вылету на старых версиях Turnip.</string>
|
||||
<string name="use_optimized_vertex_buffers_description">Включает оптимизированную привязку вершинного буфера для повышения производительности. Требует Mesa Turnip 26.0+ / QCOM. Приводит к вылету на старых версиях драйверов Turnip (25.3 и ниже).</string>
|
||||
|
||||
<string name="hacks">Хаки</string>
|
||||
|
||||
@@ -501,6 +503,8 @@
|
||||
<string name="fix_bloom_effects_description">Частично убирает размытие в LA/EOW (Adreno A6XX - A7XX/ Turnip), полностью отключает его в Burnout. Внимание: может вызывать графические артефакты в других играх.</string>
|
||||
<string name="emulate_bgr565">Эмулировать BGR565</string>
|
||||
<string name="emulate_bgr565_description">Исправляет проблемы с инвертированными цветами в играх, а также со странными артефактами или некорректными тенями.</string>
|
||||
<string name="rescale_hack">Включить старый метод изменения разрешения</string>
|
||||
<string name="rescale_hack_description">Включает старый метод обработки этапа перенастройки масштабирования для игр за счёт использования быстрого алгоритма перемасштабирования.</string>
|
||||
<string name="renderer_asynchronous_shaders">Использовать асинхронные шейдеры</string>
|
||||
<string name="renderer_asynchronous_shaders_description">Компилирует шейдеры асинхронно. Это может уменьшить подтормаживания, но также может вызвать графические артефакты.</string>
|
||||
<string name="gpu_unswizzle_settings">Настройки распаковки текстур (Unswizzle)</string>
|
||||
@@ -519,10 +523,10 @@
|
||||
<string name="extensions">Расширения</string>
|
||||
|
||||
<string name="dyna_state">Расширенное динамическое состояние</string>
|
||||
<string name="dyna_state_description">Управляет количеством функций, доступных в режиме «Расширенное динамическое состояние». Большее число позволяет задействовать больше функций и может повысить производительность, но способно вызывать проблемы с некоторыми драйверами и графикой.</string>
|
||||
<string name="dyna_state_description">Управляет количеством функций, доступных для использования в расширенном динамическом состояние. Более высокое значение позволит сократить количество компиляций конвейеров за счёт поддержки динамического состояния драйвером.</string>
|
||||
<string name="disabled">Отключено</string>
|
||||
<string name="vertex_input_dynamic_state">Динамическое состояние ввода вершин</string>
|
||||
<string name="vertex_input_dynamic_state_description">Включает функцию динамического состояния ввода вершин для повышения качества и производительности</string>
|
||||
<string name="vertex_input_dynamic_state_description">Включение этой функции обеспечивает более гибкую обработку входных данных вершин, что потенциально сокращает время компиляции конвейера на этапе работы с вершинами и буферами.</string>
|
||||
<string name="sample_shading_fraction">Сэмпловое затенение</string>
|
||||
<string name="sample_shading_fraction_description">Позволяет шейдеру фрагментов выполняться для каждого сэмпла в многосэмпловом фрагменте, а не один раз на фрагмент. Улучшает качество графики ценой некоторого падения производительности.</string>
|
||||
|
||||
@@ -1155,4 +1159,6 @@
|
||||
<string name="license_fidelityfx_fsr_description">Высококачественное масштабирование от AMD</string>
|
||||
<string name="external_content">Дополнительный контент</string>
|
||||
<string name="add_folders">Добавить папку</string>
|
||||
</resources>
|
||||
<string name="percent">%1$d %%</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -66,6 +66,8 @@
|
||||
<string name="show_power_info_description">Показати поточне споживання енергії та залишкову ємність акумулятора</string>
|
||||
<string name="show_shaders_building">Показати побудову шейдерів</string>
|
||||
<string name="show_shaders_building_description">Показати поточну кількість шейдерів, які наразі компілюються</string>
|
||||
<string name="pipeline_worker_cores">Потоки обробника конвеєра</string>
|
||||
<string name="pipeline_worker_cores_description">Керує кількістю ядер, що використовуються для збірки конвеєрів Vulkan. Вище значення покращить продуктивність компіляції конвеєра, але також підвищить температуру.</string>
|
||||
<string name="overlay_position">Позиція оверлею</string>
|
||||
<string name="overlay_position_description">Обрати розташування виводу статистики на екрані</string>
|
||||
<string name="overlay_position_top_left">Вгорі ліворуч</string>
|
||||
@@ -481,15 +483,15 @@
|
||||
<string name="renderer_force_max_clock">Максимальна тактова частота (тільки Adreno)</string>
|
||||
<string name="renderer_force_max_clock_description">Змушує GPU працювати на максимальній тактовій частоті.</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation">Асинхронна емуляція ГП</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation_description">Емуляція ГП виконується асинхронно для зменшення затримок ЦП й покращення пропускної здатності. Вимкніть лише у випадку виникнення проблем із таймінгами.</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation_description">Це обхідне рішення може покращити продуктивність завдяки асинхронному виконанню емуляції ГП, але спричинить проблеми з графікою та збільшить частоту збоїв чутливих до таймінгів операцій.</string>
|
||||
<string name="renderer_async_presentation">Асинхронне подання</string>
|
||||
<string name="renderer_async_presentation_description">Трохи покращує продуктивність завдяки переміщенню подання на окремий потік ЦП.</string>
|
||||
<string name="renderer_async_presentation_description">Це обхідне рішення може покращити продуктивність завдяки переміщенню подання на окремий потік ЦП, але спричинить проблеми з графікою.</string>
|
||||
<string name="renderer_reactive_flushing">Реактивне очищення</string>
|
||||
<string name="renderer_reactive_flushing_description">Покращує точність рендерингу в деяких іграх.</string>
|
||||
<string name="enable_buffer_history">Увімкнути історію буфера</string>
|
||||
<string name="enable_buffer_history_description">Вмикає доступ до попередніх станів буфера. Цей параметр може покращити якість візуалізації та стабільну продуктивність у деяких іграх.</string>
|
||||
<string name="use_optimized_vertex_buffers">Оптимізовані буфери вершин</string>
|
||||
<string name="use_optimized_vertex_buffers_description">Застосовує оптимізований буфер вершин, щоб покращити продуктивність. Потребує драйверів Mesa 26.0+ Turnip / QCOM. На старіших драйверах Turnip виникатиме збій.</string>
|
||||
<string name="use_optimized_vertex_buffers_description">Застосовує оптимізований буфер вершин, щоб покращити продуктивність. Потребує драйверів Mesa 26.0+ Turnip / QCOM. На старіших драйверах Turnip виникатиме збій (25.3 і нижче).</string>
|
||||
|
||||
<string name="hacks">Обхідні рішення</string>
|
||||
|
||||
@@ -501,6 +503,8 @@
|
||||
<string name="fix_bloom_effects_description">Зменшує розмиття світіння в LA/EOW (Adreno A6XX–A7XX / Turnip), прибирає світіння в Burnout. Увага: може спричинити графічні артефакти в інших іграх.</string>
|
||||
<string name="emulate_bgr565">Емулювати BGR565</string>
|
||||
<string name="emulate_bgr565_description">Виправляє проблеми з інвертованими кольорами в іграх або дивними артефактами чи тінями.</string>
|
||||
<string name="rescale_hack">Увімкнути застаріле масштабування</string>
|
||||
<string name="rescale_hack_description">Вмикає застарілу обробку масштабування для ігор, використовуючи швидкий шлях масштабування</string>
|
||||
<string name="renderer_asynchronous_shaders">Асинхронні шейдери</string>
|
||||
<string name="renderer_asynchronous_shaders_description">Компілює шейдери асинхронно. Це може зменшити затримки, але також може спричинити графічні баги.</string>
|
||||
<string name="gpu_unswizzle_settings">Налаштування розпакування за допомогою ГП</string>
|
||||
@@ -519,10 +523,10 @@
|
||||
<string name="extensions">Розширення</string>
|
||||
|
||||
<string name="dyna_state">Розширений динамічний стан</string>
|
||||
<string name="dyna_state_description">Керує кількістю функцій, які можна використовувати в «Розширеному динамічному стані». Вище число дозволяє більше функцій і може покращити продуктивність, але може спричинити проблеми з деякими драйверами й виробниками.</string>
|
||||
<string name="dyna_state_description">Керує кількістю функцій, які можна використовувати в ExtendedDynamicState (EDS). Вище значення дозволить зменшити кількість компіляцій конвеєра на основі динамічного стану, підтримуваного драйвером.</string>
|
||||
<string name="disabled">Вимкнено</string>
|
||||
<string name="vertex_input_dynamic_state">Динамічний стан введення вершин</string>
|
||||
<string name="vertex_input_dynamic_state_description">Вмикає можливість динамічного стану введення вершин для кращих якості й продуктивності.</string>
|
||||
<string name="vertex_input_dynamic_state_description">Увімкнення цієї функції дозволить гнучкішу обробку введення вершин. Може зменшити час компіляції конвеєра для вершин/буфера.</string>
|
||||
<string name="sample_shading_fraction">Простий шейдинг</string>
|
||||
<string name="sample_shading_fraction_description">Дозволяє виконувати фрагмент шейдера для кожного зразка в багатозразковому фрагменті замість одного разу для кожного фрагмента. Покращує якість графікі ціною втрати продуктивності.</string>
|
||||
|
||||
|
||||
@@ -475,16 +475,12 @@
|
||||
<string name="renderer_force_max_clock">强制最大时钟 (仅限 Adreno)</string>
|
||||
<string name="renderer_force_max_clock_description">强制 GPU 以最大时钟运行 (仍被温控限制)。</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation">GPU 异步模拟</string>
|
||||
<string name="renderer_asynchronous_gpu_emulation_description">异步运行 GPU 模拟,以减少 CPU 停顿并提高吞吐量。仅当遇到与时序相关的问题时才应禁用此功能。</string>
|
||||
<string name="renderer_async_presentation">异步呈现</string>
|
||||
<string name="renderer_async_presentation_description">通过将呈现操作移至单独的 CPU 线程来略微提升性能。</string>
|
||||
<string name="renderer_reactive_flushing">启用反应性刷新</string>
|
||||
<string name="renderer_reactive_flushing_description">通过牺牲性能来提高某些游戏的渲染精度。</string>
|
||||
<string name="enable_buffer_history">启用缓冲区历史</string>
|
||||
<string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string>
|
||||
<string name="use_optimized_vertex_buffers">优化顶点缓冲区</string>
|
||||
<string name="use_optimized_vertex_buffers_description">启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 或 QCOM 驱动程序。若使用较旧版本的 Turnip 驱动则会导致崩溃。</string>
|
||||
|
||||
<string name="hacks">Hacks</string>
|
||||
|
||||
<string name="fast_gpu_time">GPU 超频频率</string>
|
||||
@@ -513,10 +509,8 @@
|
||||
<string name="extensions">扩展</string>
|
||||
|
||||
<string name="dyna_state">扩展动态状态</string>
|
||||
<string name="dyna_state_description">控制在扩展动态状态中可使用的函数数量。更高的数值允许启用更多功能,并可能提升性能,但同时也可能导致额外的图形问题。</string>
|
||||
<string name="disabled">已禁用</string>
|
||||
<string name="vertex_input_dynamic_state">顶点输入动态状态</string>
|
||||
<string name="vertex_input_dynamic_state_description">开启顶点输入动态状态功能来获得更好的质量和性能。</string>
|
||||
<string name="sample_shading_fraction">采样着色</string>
|
||||
<string name="sample_shading_fraction_description">允许片段着色器在多采样片段中每个样本执行一次,而不是每个片段执行一次。以提高性能为代价改善图形质量。</string>
|
||||
|
||||
|
||||
@@ -466,7 +466,6 @@
|
||||
<string name="dyna_state">擴展動態狀態</string>
|
||||
<string name="disabled">已停用</string>
|
||||
<string name="vertex_input_dynamic_state">頂點輸入動態狀態</string>
|
||||
<string name="vertex_input_dynamic_state_description">啟用頂點輸入動態狀態以取得更佳的品質及性能</string>
|
||||
<string name="display">顯示</string>
|
||||
|
||||
<string name="renderer_screen_layout">方向</string>
|
||||
|
||||
@@ -184,19 +184,12 @@ if(ARCHITECTURE_x86_64)
|
||||
x64/cpu_detect.h
|
||||
x64/cpu_wait.cpp
|
||||
x64/cpu_wait.h
|
||||
x64/native_clock.cpp
|
||||
x64/native_clock.h
|
||||
x64/rdtsc.cpp
|
||||
x64/rdtsc.h
|
||||
x64/xbyak_abi.h
|
||||
x64/xbyak_util.h)
|
||||
x64/xbyak.h)
|
||||
target_link_libraries(common PRIVATE xbyak::xbyak)
|
||||
endif()
|
||||
|
||||
if(HAS_NCE)
|
||||
target_sources(common PRIVATE arm64/native_clock.cpp arm64/native_clock.h)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
target_compile_definitions(
|
||||
common
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <sys/system_properties.h>
|
||||
#endif
|
||||
#include "common/arm64/native_clock.h"
|
||||
|
||||
namespace Common::Arm64 {
|
||||
|
||||
namespace {
|
||||
|
||||
NativeClock::FactorType GetFixedPointFactor(u64 num, u64 den) {
|
||||
return (static_cast<NativeClock::FactorType>(num) << 64) / den;
|
||||
}
|
||||
|
||||
u64 MultiplyHigh(u64 m, NativeClock::FactorType factor) {
|
||||
return static_cast<u64>((m * factor) >> 64);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NativeClock::NativeClock() {
|
||||
const u64 host_cntfrq = GetHostCNTFRQ();
|
||||
ns_cntfrq_factor = GetFixedPointFactor(NsRatio::den, host_cntfrq);
|
||||
us_cntfrq_factor = GetFixedPointFactor(UsRatio::den, host_cntfrq);
|
||||
ms_cntfrq_factor = GetFixedPointFactor(MsRatio::den, host_cntfrq);
|
||||
guest_cntfrq_factor = GetFixedPointFactor(CNTFRQ, host_cntfrq);
|
||||
gputick_cntfrq_factor = GetFixedPointFactor(GPUTickFreq, host_cntfrq);
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds NativeClock::GetTimeNS() const {
|
||||
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_cntfrq_factor)};
|
||||
}
|
||||
|
||||
std::chrono::microseconds NativeClock::GetTimeUS() const {
|
||||
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_cntfrq_factor)};
|
||||
}
|
||||
|
||||
std::chrono::milliseconds NativeClock::GetTimeMS() const {
|
||||
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_cntfrq_factor)};
|
||||
}
|
||||
|
||||
s64 NativeClock::GetCNTPCT() const {
|
||||
return MultiplyHigh(GetUptime(), guest_cntfrq_factor);
|
||||
}
|
||||
|
||||
s64 NativeClock::GetGPUTick() const {
|
||||
return MultiplyHigh(GetUptime(), gputick_cntfrq_factor);
|
||||
}
|
||||
|
||||
s64 NativeClock::GetUptime() const {
|
||||
s64 cntvct_el0 = 0;
|
||||
asm volatile("dsb ish\n\t"
|
||||
"mrs %[cntvct_el0], cntvct_el0\n\t"
|
||||
"dsb ish\n\t"
|
||||
: [cntvct_el0] "=r"(cntvct_el0));
|
||||
return cntvct_el0;
|
||||
}
|
||||
|
||||
bool NativeClock::IsNative() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
s64 NativeClock::GetHostCNTFRQ() {
|
||||
u64 cntfrq_el0 = 0;
|
||||
std::string_view board{""};
|
||||
#ifdef ANDROID
|
||||
char buffer[PROP_VALUE_MAX];
|
||||
int len{__system_property_get("ro.product.board", buffer)};
|
||||
board = std::string_view(buffer, static_cast<size_t>(len));
|
||||
#endif
|
||||
if (board == "s5e9925") { // Exynos 2200
|
||||
cntfrq_el0 = 25600000;
|
||||
} else if (board == "exynos2100") { // Exynos 2100
|
||||
cntfrq_el0 = 26000000;
|
||||
} else if (board == "exynos9810") { // Exynos 9810
|
||||
cntfrq_el0 = 26000000;
|
||||
} else if (board == "s5e8825") { // Exynos 1280
|
||||
cntfrq_el0 = 26000000;
|
||||
} else {
|
||||
asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
|
||||
}
|
||||
return cntfrq_el0;
|
||||
}
|
||||
|
||||
} // namespace Common::Arm64
|
||||
@@ -1,45 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/wall_clock.h"
|
||||
|
||||
namespace Common::Arm64 {
|
||||
|
||||
class NativeClock final : public WallClock {
|
||||
public:
|
||||
explicit NativeClock();
|
||||
|
||||
std::chrono::nanoseconds GetTimeNS() const override;
|
||||
|
||||
std::chrono::microseconds GetTimeUS() const override;
|
||||
|
||||
std::chrono::milliseconds GetTimeMS() const override;
|
||||
|
||||
s64 GetCNTPCT() const override;
|
||||
|
||||
s64 GetGPUTick() const override;
|
||||
|
||||
s64 GetUptime() const override;
|
||||
|
||||
bool IsNative() const override;
|
||||
|
||||
static s64 GetHostCNTFRQ();
|
||||
|
||||
public:
|
||||
using FactorType = unsigned __int128;
|
||||
|
||||
FactorType GetGuestCNTFRQFactor() const {
|
||||
return guest_cntfrq_factor;
|
||||
}
|
||||
|
||||
private:
|
||||
FactorType ns_cntfrq_factor;
|
||||
FactorType us_cntfrq_factor;
|
||||
FactorType ms_cntfrq_factor;
|
||||
FactorType guest_cntfrq_factor;
|
||||
FactorType gputick_cntfrq_factor;
|
||||
};
|
||||
|
||||
} // namespace Common::Arm64
|
||||
+10
-3
@@ -388,8 +388,13 @@ struct Values {
|
||||
true,
|
||||
true};
|
||||
|
||||
SwitchableSetting<bool> use_asynchronous_gpu_emulation{
|
||||
linkage, false, "use_asynchronous_gpu_emulation", Category::Renderer};
|
||||
SwitchableSetting<bool> use_asynchronous_gpu_emulation{linkage,
|
||||
#ifdef __ANDROID__
|
||||
false,
|
||||
#else
|
||||
true,
|
||||
#endif
|
||||
"use_asynchronous_gpu_emulation", Category::Renderer};
|
||||
// *nix platforms may have issues with the borderless windowed fullscreen mode.
|
||||
// Default to exclusive fullscreen on these platforms for now.
|
||||
SwitchableSetting<FullscreenMode, true> fullscreen_mode{linkage,
|
||||
@@ -586,6 +591,8 @@ struct Values {
|
||||
SwitchableSetting<bool> gpu_unswizzle_enabled{linkage, false, "gpu_unswizzle_enabled",
|
||||
Category::RendererHacks};
|
||||
|
||||
SwitchableSetting<bool> legacy_descriptor_indices{linkage, true, "legacy_descriptor_indices", Category::RendererHacks};
|
||||
|
||||
SwitchableSetting<ExtendedDynamicState> dyna_state{linkage,
|
||||
#if defined(ANDROID)
|
||||
ExtendedDynamicState::Disabled,
|
||||
@@ -609,7 +616,7 @@ struct Values {
|
||||
#if defined (ANDROID)
|
||||
false,
|
||||
#else
|
||||
false,
|
||||
true,
|
||||
#endif
|
||||
"vertex_input_dynamic_state", Category::RendererExtensions};
|
||||
|
||||
|
||||
+174
-55
@@ -1,77 +1,196 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/steady_clock.h"
|
||||
#include "common/uint128.h"
|
||||
#include "common/wall_clock.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <sys/system_properties.h>
|
||||
#endif
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "common/x64/cpu_detect.h"
|
||||
#include "common/x64/native_clock.h"
|
||||
#include "common/x64/rdtsc.h"
|
||||
#endif
|
||||
#ifdef HAS_NCE
|
||||
#include "common/arm64/native_clock.h"
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
class StandardWallClock final : public WallClock {
|
||||
public:
|
||||
explicit StandardWallClock() {}
|
||||
|
||||
std::chrono::nanoseconds GetTimeNS() const override {
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch());
|
||||
}
|
||||
|
||||
std::chrono::microseconds GetTimeUS() const override {
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch());
|
||||
}
|
||||
|
||||
std::chrono::milliseconds GetTimeMS() const override {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch());
|
||||
}
|
||||
|
||||
s64 GetCNTPCT() const override {
|
||||
return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
|
||||
}
|
||||
|
||||
s64 GetGPUTick() const override {
|
||||
return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
|
||||
}
|
||||
|
||||
s64 GetUptime() const override {
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
.count();
|
||||
}
|
||||
|
||||
bool IsNative() const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<WallClock> CreateOptimalClock() {
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
const auto& caps = GetCPUCaps();
|
||||
WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept
|
||||
: invariant{invariant_}
|
||||
, rdtsc_frequency{rdtsc_frequency_}
|
||||
, ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_)}
|
||||
, us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_)}
|
||||
, ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_)}
|
||||
, cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_)}
|
||||
, gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_)}
|
||||
{}
|
||||
|
||||
if (caps.invariant_tsc && caps.tsc_frequency >= std::nano::den) {
|
||||
return std::make_unique<X64::NativeClock>(caps.tsc_frequency);
|
||||
} else {
|
||||
// Fallback to StandardWallClock if the hardware TSC
|
||||
// - Is not invariant
|
||||
// - Is not more precise than 1 GHz (1ns resolution)
|
||||
return std::make_unique<StandardWallClock>();
|
||||
}
|
||||
std::chrono::nanoseconds WallClock::GetTimeNS() const {
|
||||
if (invariant)
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_rdtsc_factor)};
|
||||
}
|
||||
|
||||
std::chrono::microseconds WallClock::GetTimeUS() const {
|
||||
if (invariant)
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_rdtsc_factor)};
|
||||
}
|
||||
|
||||
std::chrono::milliseconds WallClock::GetTimeMS() const {
|
||||
if (invariant)
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_rdtsc_factor)};
|
||||
}
|
||||
|
||||
s64 WallClock::GetCNTPCT() const {
|
||||
if (invariant)
|
||||
return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
|
||||
return MultiplyHigh(GetUptime(), cntpct_rdtsc_factor);
|
||||
}
|
||||
|
||||
s64 WallClock::GetGPUTick() const {
|
||||
if (invariant)
|
||||
return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
|
||||
return MultiplyHigh(GetUptime(), gputick_rdtsc_factor);
|
||||
}
|
||||
|
||||
s64 WallClock::GetUptime() const {
|
||||
if (invariant)
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
return s64(Common::X64::FencedRDTSC());
|
||||
}
|
||||
|
||||
bool WallClock::IsNative() const {
|
||||
if (invariant)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
#elif defined(HAS_NCE)
|
||||
return std::make_unique<Arm64::NativeClock>();
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] WallClock::FactorType GetFixedPointFactor(u64 num, u64 den) noexcept {
|
||||
return (WallClock::FactorType(num) << 64) / den;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 MultiplyHigh(u64 m, WallClock::FactorType factor) noexcept {
|
||||
return static_cast<u64>((m * factor) >> 64);
|
||||
}
|
||||
|
||||
[[nodiscard]] s64 GetHostCNTFRQ() noexcept {
|
||||
u64 cntfrq_el0 = 0;
|
||||
#ifdef ANDROID
|
||||
std::string_view board{""};
|
||||
char buffer[PROP_VALUE_MAX];
|
||||
int len{__system_property_get("ro.product.board", buffer)};
|
||||
board = std::string_view(buffer, static_cast<size_t>(len));
|
||||
if (board == "s5e9925") { // Exynos 2200
|
||||
cntfrq_el0 = 25600000;
|
||||
} else if (board == "exynos2100") { // Exynos 2100
|
||||
cntfrq_el0 = 26000000;
|
||||
} else if (board == "exynos9810") { // Exynos 9810
|
||||
cntfrq_el0 = 26000000;
|
||||
} else if (board == "s5e8825") { // Exynos 1280
|
||||
cntfrq_el0 = 26000000;
|
||||
} else {
|
||||
asm volatile("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
|
||||
}
|
||||
return cntfrq_el0;
|
||||
#else
|
||||
return std::make_unique<StandardWallClock>();
|
||||
asm volatile("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
|
||||
return cntfrq_el0;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept {
|
||||
const u64 host_cntfrq = std::max<u64>(GetHostCNTFRQ(), 1);
|
||||
ns_cntfrq_factor = GetFixedPointFactor(NsRatio::den, host_cntfrq);
|
||||
us_cntfrq_factor = GetFixedPointFactor(UsRatio::den, host_cntfrq);
|
||||
ms_cntfrq_factor = GetFixedPointFactor(MsRatio::den, host_cntfrq);
|
||||
guest_cntfrq_factor = GetFixedPointFactor(CNTFRQ, host_cntfrq);
|
||||
gputick_cntfrq_factor = GetFixedPointFactor(GPUTickFreq, host_cntfrq);
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds WallClock::GetTimeNS() const {
|
||||
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_cntfrq_factor)};
|
||||
}
|
||||
|
||||
std::chrono::microseconds WallClock::GetTimeUS() const {
|
||||
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_cntfrq_factor)};
|
||||
}
|
||||
|
||||
std::chrono::milliseconds WallClock::GetTimeMS() const {
|
||||
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_cntfrq_factor)};
|
||||
}
|
||||
|
||||
s64 WallClock::GetCNTPCT() const {
|
||||
return MultiplyHigh(GetUptime(), guest_cntfrq_factor);
|
||||
}
|
||||
|
||||
s64 WallClock::GetGPUTick() const {
|
||||
return MultiplyHigh(GetUptime(), gputick_cntfrq_factor);
|
||||
}
|
||||
|
||||
s64 WallClock::GetUptime() const {
|
||||
s64 cntvct_el0 = 0;
|
||||
asm volatile(
|
||||
"dsb ish\n\t"
|
||||
"mrs %[cntvct_el0], cntvct_el0\n\t"
|
||||
"dsb ish\n\t"
|
||||
: [cntvct_el0] "=r"(cntvct_el0)
|
||||
);
|
||||
return cntvct_el0;
|
||||
}
|
||||
|
||||
bool WallClock::IsNative() const {
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept {}
|
||||
|
||||
std::chrono::nanoseconds WallClock::GetTimeNS() const {
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||
}
|
||||
|
||||
std::chrono::microseconds WallClock::GetTimeUS() const {
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||
}
|
||||
|
||||
std::chrono::milliseconds WallClock::GetTimeMS() const {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||
}
|
||||
|
||||
s64 WallClock::GetCNTPCT() const {
|
||||
return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
|
||||
}
|
||||
|
||||
s64 WallClock::GetGPUTick() const {
|
||||
return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
|
||||
}
|
||||
|
||||
s64 WallClock::GetUptime() const {
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
bool WallClock::IsNative() const {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
WallClock CreateOptimalClock() noexcept {
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
auto const& caps = GetCPUCaps();
|
||||
return WallClock(!(caps.invariant_tsc && caps.tsc_frequency >= std::nano::den), std::max<u64>(caps.tsc_frequency, 1));
|
||||
#elif defined(HAS_NCE)
|
||||
return WallClock(false, 1);
|
||||
#else
|
||||
return WallClock(true, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+35
-10
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -20,28 +20,28 @@ public:
|
||||
static constexpr u64 GPUTickFreq = 614'400'000; // GM20B GPU Tick Frequency = 614.4 MHz
|
||||
static constexpr u64 CPUTickFreq = 1'020'000'000; // T210/4 A57 CPU Tick Frequency = 1020.0 MHz
|
||||
|
||||
virtual ~WallClock() = default;
|
||||
explicit WallClock(bool invariant, u64 rdtsc_frequency_) noexcept;
|
||||
|
||||
/// @returns The time in nanoseconds since the construction of this clock.
|
||||
virtual std::chrono::nanoseconds GetTimeNS() const = 0;
|
||||
std::chrono::nanoseconds GetTimeNS() const;
|
||||
|
||||
/// @returns The time in microseconds since the construction of this clock.
|
||||
virtual std::chrono::microseconds GetTimeUS() const = 0;
|
||||
std::chrono::microseconds GetTimeUS() const;
|
||||
|
||||
/// @returns The time in milliseconds since the construction of this clock.
|
||||
virtual std::chrono::milliseconds GetTimeMS() const = 0;
|
||||
std::chrono::milliseconds GetTimeMS() const;
|
||||
|
||||
/// @returns The guest CNTPCT ticks since the construction of this clock.
|
||||
virtual s64 GetCNTPCT() const = 0;
|
||||
s64 GetCNTPCT() const;
|
||||
|
||||
/// @returns The guest GPU ticks since the construction of this clock.
|
||||
virtual s64 GetGPUTick() const = 0;
|
||||
s64 GetGPUTick() const;
|
||||
|
||||
/// @returns The raw host timer ticks since an indeterminate epoch.
|
||||
virtual s64 GetUptime() const = 0;
|
||||
s64 GetUptime() const;
|
||||
|
||||
/// @returns Whether the clock directly uses the host's hardware clock.
|
||||
virtual bool IsNative() const = 0;
|
||||
bool IsNative() const;
|
||||
|
||||
static inline u64 NSToCNTPCT(u64 ns) {
|
||||
return ns * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
|
||||
@@ -85,8 +85,33 @@ protected:
|
||||
using CPUTickToUsRatio = std::ratio<std::micro::den, CPUTickFreq>;
|
||||
using CPUTickToCNTPCTRatio = std::ratio<CNTFRQ, CPUTickFreq>;
|
||||
using CPUTickToGPUTickRatio = std::ratio<GPUTickFreq, CPUTickFreq>;
|
||||
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
bool invariant;
|
||||
u64 rdtsc_frequency;
|
||||
u64 ns_rdtsc_factor;
|
||||
u64 us_rdtsc_factor;
|
||||
u64 ms_rdtsc_factor;
|
||||
u64 cntpct_rdtsc_factor;
|
||||
u64 gputick_rdtsc_factor;
|
||||
#elif defined(HAS_NCE)
|
||||
public:
|
||||
using FactorType = unsigned __int128;
|
||||
|
||||
FactorType GetGuestCNTFRQFactor() const {
|
||||
return guest_cntfrq_factor;
|
||||
}
|
||||
protected:
|
||||
FactorType ns_cntfrq_factor;
|
||||
FactorType us_cntfrq_factor;
|
||||
FactorType ms_cntfrq_factor;
|
||||
FactorType guest_cntfrq_factor;
|
||||
FactorType gputick_cntfrq_factor;
|
||||
#else
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
[[nodiscard]] std::unique_ptr<WallClock> CreateOptimalClock();
|
||||
[[nodiscard]] WallClock CreateOptimalClock() noexcept;
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/uint128.h"
|
||||
#include "common/x64/native_clock.h"
|
||||
#include "common/x64/rdtsc.h"
|
||||
|
||||
namespace Common::X64 {
|
||||
|
||||
NativeClock::NativeClock(u64 rdtsc_frequency_)
|
||||
: rdtsc_frequency{rdtsc_frequency_}, ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den,
|
||||
rdtsc_frequency)},
|
||||
us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)},
|
||||
ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)},
|
||||
cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)},
|
||||
gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} {}
|
||||
|
||||
std::chrono::nanoseconds NativeClock::GetTimeNS() const {
|
||||
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_rdtsc_factor)};
|
||||
}
|
||||
|
||||
std::chrono::microseconds NativeClock::GetTimeUS() const {
|
||||
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_rdtsc_factor)};
|
||||
}
|
||||
|
||||
std::chrono::milliseconds NativeClock::GetTimeMS() const {
|
||||
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_rdtsc_factor)};
|
||||
}
|
||||
|
||||
s64 NativeClock::GetCNTPCT() const {
|
||||
return MultiplyHigh(GetUptime(), cntpct_rdtsc_factor);
|
||||
}
|
||||
|
||||
s64 NativeClock::GetGPUTick() const {
|
||||
return MultiplyHigh(GetUptime(), gputick_rdtsc_factor);
|
||||
}
|
||||
|
||||
s64 NativeClock::GetUptime() const {
|
||||
return static_cast<s64>(FencedRDTSC());
|
||||
}
|
||||
|
||||
bool NativeClock::IsNative() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Common::X64
|
||||
@@ -1,38 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/wall_clock.h"
|
||||
|
||||
namespace Common::X64 {
|
||||
|
||||
class NativeClock final : public WallClock {
|
||||
public:
|
||||
explicit NativeClock(u64 rdtsc_frequency_);
|
||||
|
||||
std::chrono::nanoseconds GetTimeNS() const override;
|
||||
|
||||
std::chrono::microseconds GetTimeUS() const override;
|
||||
|
||||
std::chrono::milliseconds GetTimeMS() const override;
|
||||
|
||||
s64 GetCNTPCT() const override;
|
||||
|
||||
s64 GetGPUTick() const override;
|
||||
|
||||
s64 GetUptime() const override;
|
||||
|
||||
bool IsNative() const override;
|
||||
|
||||
private:
|
||||
u64 rdtsc_frequency;
|
||||
|
||||
u64 ns_rdtsc_factor;
|
||||
u64 us_rdtsc_factor;
|
||||
u64 ms_rdtsc_factor;
|
||||
u64 cntpct_rdtsc_factor;
|
||||
u64 gputick_rdtsc_factor;
|
||||
};
|
||||
|
||||
} // namespace Common::X64
|
||||
@@ -1,13 +1,37 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <bitset>
|
||||
#include <initializer_list>
|
||||
#include <xbyak/xbyak.h>
|
||||
#include "common/assert.h"
|
||||
|
||||
// xbyak hates human beings
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-Wconversion"
|
||||
#pragma clang diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
|
||||
// You must ensure this matches with src/common/x64/xbyak.h on root dir
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#define XBYAK_STD_UNORDERED_SET ankerl::unordered_dense::set
|
||||
#define XBYAK_STD_UNORDERED_MAP ankerl::unordered_dense::map
|
||||
#define XBYAK_STD_UNORDERED_MULTIMAP boost::unordered_multimap
|
||||
#include <xbyak/xbyak.h>
|
||||
#include <xbyak/xbyak_util.h>
|
||||
|
||||
#include <xbyak/xbyak.h>
|
||||
|
||||
namespace Common::X64 {
|
||||
|
||||
constexpr size_t RegToIndex(const Xbyak::Reg& reg) {
|
||||
@@ -174,12 +198,13 @@ inline ABIFrameInfo ABI_CalculateFrameSize(std::bitset<32> regs, size_t rsp_alig
|
||||
rsp_alignment -= subtraction;
|
||||
subtraction += rsp_alignment & 0xF;
|
||||
|
||||
return ABIFrameInfo{static_cast<s32>(subtraction),
|
||||
static_cast<s32>(subtraction - xmm_base_subtraction)};
|
||||
return ABIFrameInfo{
|
||||
s32(subtraction),
|
||||
s32(subtraction - xmm_base_subtraction)
|
||||
};
|
||||
}
|
||||
|
||||
inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
|
||||
size_t rsp_alignment, size_t needed_frame_size = 0) {
|
||||
inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, size_t rsp_alignment, size_t needed_frame_size = 0) {
|
||||
auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
|
||||
|
||||
for (size_t i = 0; i < regs.size(); ++i) {
|
||||
@@ -202,8 +227,7 @@ inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::b
|
||||
return ABI_SHADOW_SPACE;
|
||||
}
|
||||
|
||||
inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
|
||||
size_t rsp_alignment, size_t needed_frame_size = 0) {
|
||||
inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, size_t rsp_alignment, size_t needed_frame_size = 0) {
|
||||
auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
|
||||
|
||||
for (size_t i = 0; i < regs.size(); ++i) {
|
||||
@@ -226,4 +250,38 @@ inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bits
|
||||
}
|
||||
}
|
||||
|
||||
// Constants for use with cmpps/cmpss
|
||||
enum {
|
||||
CMP_EQ = 0,
|
||||
CMP_LT = 1,
|
||||
CMP_LE = 2,
|
||||
CMP_UNORD = 3,
|
||||
CMP_NEQ = 4,
|
||||
CMP_NLT = 5,
|
||||
CMP_NLE = 6,
|
||||
CMP_ORD = 7,
|
||||
};
|
||||
|
||||
constexpr bool IsWithin2G(uintptr_t ref, uintptr_t target) {
|
||||
const u64 distance = target - (ref + 5);
|
||||
return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
|
||||
}
|
||||
|
||||
inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
|
||||
return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
|
||||
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
|
||||
size_t addr = reinterpret_cast<size_t>(f);
|
||||
if (IsWithin2G(code, addr)) {
|
||||
code.call(f);
|
||||
} else {
|
||||
// ABI_RETURN is a safe temp register to use before a call
|
||||
code.mov(ABI_RETURN, addr);
|
||||
code.call(ABI_RETURN);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Common::X64
|
||||
@@ -1,46 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <xbyak/xbyak.h>
|
||||
#include "common/x64/xbyak_abi.h"
|
||||
|
||||
namespace Common::X64 {
|
||||
|
||||
// Constants for use with cmpps/cmpss
|
||||
enum {
|
||||
CMP_EQ = 0,
|
||||
CMP_LT = 1,
|
||||
CMP_LE = 2,
|
||||
CMP_UNORD = 3,
|
||||
CMP_NEQ = 4,
|
||||
CMP_NLT = 5,
|
||||
CMP_NLE = 6,
|
||||
CMP_ORD = 7,
|
||||
};
|
||||
|
||||
constexpr bool IsWithin2G(uintptr_t ref, uintptr_t target) {
|
||||
const u64 distance = target - (ref + 5);
|
||||
return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
|
||||
}
|
||||
|
||||
inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
|
||||
return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
|
||||
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
|
||||
size_t addr = reinterpret_cast<size_t>(f);
|
||||
if (IsWithin2G(code, addr)) {
|
||||
code.call(f);
|
||||
} else {
|
||||
// ABI_RETURN is a safe temp register to use before a call
|
||||
code.mov(ABI_RETURN, addr);
|
||||
code.call(ABI_RETURN);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Common::X64
|
||||
@@ -113,8 +113,7 @@ void DynarmicCallbacks32::CallSVC(u32 swi) {
|
||||
}
|
||||
|
||||
void DynarmicCallbacks32::AddTicks(u64 ticks) {
|
||||
ASSERT_MSG(!m_parent.m_uses_wall_clock, "Dynarmic ticking disabled");
|
||||
|
||||
ASSERT(!m_parent.m_uses_wall_clock && "Dynarmic ticking disabled");
|
||||
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
|
||||
// rough approximation of the amount of executed ticks in the system, it may be thrown off
|
||||
// if not all cores are doing a similar amount of work. Instead of doing this, we should
|
||||
@@ -123,14 +122,12 @@ void DynarmicCallbacks32::AddTicks(u64 ticks) {
|
||||
u64 amortized_ticks = ticks / Core::Hardware::NUM_CPU_CORES;
|
||||
// Always execute at least one tick.
|
||||
amortized_ticks = std::max<u64>(amortized_ticks, 1);
|
||||
|
||||
m_parent.m_system.CoreTiming().AddTicks(amortized_ticks);
|
||||
}
|
||||
|
||||
u64 DynarmicCallbacks32::GetTicksRemaining() {
|
||||
ASSERT_MSG(!m_parent.m_uses_wall_clock, "Dynarmic ticking disabled");
|
||||
|
||||
return std::max<s64>(m_parent.m_system.CoreTiming().GetDowncount(), 0);
|
||||
ASSERT(!m_parent.m_uses_wall_clock && "Dynarmic ticking disabled");
|
||||
return std::max<s64>(m_parent.m_system.CoreTiming().downcount, 0);
|
||||
}
|
||||
|
||||
bool DynarmicCallbacks32::CheckMemoryAccess(u64 addr, u64 size, Kernel::DebugWatchpointType type) {
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
u64 MemoryRead64(u32 vaddr) override;
|
||||
std::optional<u32> MemoryReadCode(u32 vaddr) override;
|
||||
void InstructionSynchronizationBarrierRaised() override {
|
||||
last_code_addr = 0; //reset back, force refetch
|
||||
last_code_addr = u64(-1); //reset back, force refetch
|
||||
}
|
||||
void MemoryWrite8(u32 vaddr, u8 value) override;
|
||||
void MemoryWrite16(u32 vaddr, u16 value) override;
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
void ReturnException(u32 pc, Dynarmic::HaltReason hr);
|
||||
//
|
||||
Dynarmic::CodePage cached_code_page;
|
||||
u64 last_code_addr = 0;
|
||||
u64 last_code_addr = u64(-1);
|
||||
ArmDynarmic32& m_parent;
|
||||
Core::Memory::Memory& m_memory;
|
||||
Kernel::KProcess* m_process{};
|
||||
|
||||
@@ -45,7 +45,6 @@ Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) {
|
||||
std::optional<u32> DynarmicCallbacks64::MemoryReadCode(u64 vaddr) {
|
||||
if (!m_memory.IsValidVirtualAddressRange(vaddr, sizeof(u32)))
|
||||
return std::nullopt;
|
||||
// return m_memory.Read32(vaddr);
|
||||
auto const aligned_vaddr = vaddr & ~Core::Memory::YUZU_PAGEMASK;
|
||||
if (last_code_addr != aligned_vaddr) {
|
||||
m_memory.ReadBlock(aligned_vaddr, &cached_code_page, sizeof(cached_code_page));
|
||||
@@ -103,6 +102,7 @@ bool DynarmicCallbacks64::MemoryWriteExclusive128(u64 vaddr, Dynarmic::A64::Vect
|
||||
}
|
||||
|
||||
void DynarmicCallbacks64::InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op, u64 value) {
|
||||
last_code_addr = u64(-1); //invalidate cached page
|
||||
switch (op) {
|
||||
case Dynarmic::A64::InstructionCacheOperation::InvalidateByVAToPoU: {
|
||||
static constexpr u64 ICACHE_LINE_SIZE = 64;
|
||||
@@ -128,7 +128,7 @@ void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception excep
|
||||
case Dynarmic::A64::Exception::SendEvent:
|
||||
case Dynarmic::A64::Exception::SendEventLocal:
|
||||
case Dynarmic::A64::Exception::Yield:
|
||||
LOG_TRACE(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", static_cast<std::size_t>(exception), pc, m_memory.Read32(pc));
|
||||
LOG_TRACE(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, cached = {:08X})", std::size_t(exception), pc, m_memory.Read32(pc), MemoryReadCode(pc).value_or(0));
|
||||
return;
|
||||
case Dynarmic::A64::Exception::NoExecuteFault:
|
||||
LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc);
|
||||
@@ -150,8 +150,7 @@ void DynarmicCallbacks64::CallSVC(u32 svc) {
|
||||
}
|
||||
|
||||
void DynarmicCallbacks64::AddTicks(u64 ticks) {
|
||||
ASSERT_MSG(!m_parent.m_uses_wall_clock, "Dynarmic ticking disabled");
|
||||
|
||||
ASSERT(!m_parent.m_uses_wall_clock && "Dynarmic ticking disabled");
|
||||
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
|
||||
// rough approximation of the amount of executed ticks in the system, it may be thrown off
|
||||
// if not all cores are doing a similar amount of work. Instead of doing this, we should
|
||||
@@ -160,13 +159,12 @@ void DynarmicCallbacks64::AddTicks(u64 ticks) {
|
||||
u64 amortized_ticks = ticks / Core::Hardware::NUM_CPU_CORES;
|
||||
// Always execute at least one tick.
|
||||
amortized_ticks = std::max<u64>(amortized_ticks, 1);
|
||||
|
||||
m_parent.m_system.CoreTiming().AddTicks(amortized_ticks);
|
||||
}
|
||||
|
||||
u64 DynarmicCallbacks64::GetTicksRemaining() {
|
||||
ASSERT(!m_parent.m_uses_wall_clock && "Dynarmic ticking disabled");
|
||||
return std::max<s64>(m_parent.m_system.CoreTiming().GetDowncount(), 0);
|
||||
return std::max<s64>(m_parent.m_system.CoreTiming().downcount, 0);
|
||||
}
|
||||
|
||||
u64 DynarmicCallbacks64::GetCNTPCT() {
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override;
|
||||
std::optional<u32> MemoryReadCode(u64 vaddr) override;
|
||||
void InstructionSynchronizationBarrierRaised() override {
|
||||
last_code_addr = 0; //reset back, force refetch
|
||||
last_code_addr = u64(-1); //reset back, force refetch
|
||||
}
|
||||
void MemoryWrite8(u64 vaddr, u8 value) override;
|
||||
void MemoryWrite16(u64 vaddr, u16 value) override;
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
void ReturnException(u64 pc, Dynarmic::HaltReason hr);
|
||||
|
||||
Dynarmic::CodePage cached_code_page;
|
||||
u64 last_code_addr = 0;
|
||||
u64 last_code_addr = u64(-1);
|
||||
ArmDynarmic64& m_parent;
|
||||
Core::Memory::Memory& m_memory;
|
||||
u64 m_tpidrro_el0{};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include <numeric>
|
||||
#include <bit>
|
||||
#include "common/arm64/native_clock.h"
|
||||
#include "common/wall_clock.h"
|
||||
#include "common/alignment.h"
|
||||
#include "common/literals.h"
|
||||
#include "core/arm/nce/arm_nce.h"
|
||||
@@ -578,7 +578,11 @@ void Patcher::WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg,
|
||||
}
|
||||
|
||||
void Patcher::WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& cg) {
|
||||
static Common::Arm64::NativeClock clock{};
|
||||
#if defined(HAS_NCE)
|
||||
static Common::WallClock clock(false, 1);
|
||||
#else
|
||||
static Common::WallClock clock(true, 1);
|
||||
#endif
|
||||
const auto factor = clock.GetGuestCNTFRQFactor();
|
||||
const auto raw_factor = std::bit_cast<std::array<u64, 2>>(factor);
|
||||
|
||||
|
||||
+84
-87
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -13,117 +16,111 @@ using namespace Common::ELF;
|
||||
namespace Core {
|
||||
namespace Symbols {
|
||||
|
||||
template <typename Word, typename ELFSymbol, typename ByteReader>
|
||||
static Symbols GetSymbols(ByteReader ReadBytes) {
|
||||
const auto Read8{[&](u64 index) {
|
||||
struct ModuleHeaderLocation {
|
||||
u32 version;
|
||||
u32 header_offset;
|
||||
u32 version_offset;
|
||||
};
|
||||
static_assert(sizeof(ModuleHeaderLocation) == 0x0C);
|
||||
struct ModuleHeader {
|
||||
u32 signature;
|
||||
u32 dynamic_offset;
|
||||
u32 bss_start_offset;
|
||||
u32 bss_end_offset;
|
||||
u32 exception_info_start_offset;
|
||||
u32 exception_info_end_offset;
|
||||
u32 module_offset;
|
||||
u32 relro_start_offset;
|
||||
u32 full_relro_end_offset;
|
||||
u32 nx_debug_link_start_offset;
|
||||
u32 nx_debug_link_end_offset;
|
||||
u32 note_gnu_build_id_start_offset;
|
||||
u32 note_gnu_build_id_end_offset;
|
||||
};
|
||||
static_assert(sizeof(ModuleHeader) == 0x34);
|
||||
|
||||
struct Mod32 {
|
||||
using Sym = Elf32_Sym;
|
||||
using Dyn = Elf32_Dyn;
|
||||
};
|
||||
struct Mod64 {
|
||||
using Sym = Elf64_Sym;
|
||||
using Dyn = Elf64_Dyn;
|
||||
};
|
||||
|
||||
template <typename M, typename F>
|
||||
static Symbols GetSymbols(F&& ReadBytes) {
|
||||
const auto Read8 = [&](u64 index) {
|
||||
u8 ret;
|
||||
ReadBytes(&ret, index, sizeof(u8));
|
||||
return ret;
|
||||
}};
|
||||
|
||||
const auto Read32{[&](u64 index) {
|
||||
u32 ret;
|
||||
ReadBytes(&ret, index, sizeof(u32));
|
||||
return ret;
|
||||
}};
|
||||
|
||||
const auto ReadWord{[&](u64 index) {
|
||||
Word ret;
|
||||
ReadBytes(&ret, index, sizeof(Word));
|
||||
return ret;
|
||||
}};
|
||||
|
||||
const u32 mod_offset = Read32(4);
|
||||
|
||||
if (Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
|
||||
};
|
||||
ModuleHeaderLocation loc{};
|
||||
ReadBytes(&loc, 0, sizeof(ModuleHeaderLocation));
|
||||
ModuleHeader hdr;
|
||||
ReadBytes(&hdr, loc.header_offset, sizeof(ModuleHeader));
|
||||
if (hdr.signature != Common::MakeMagic('M', 'O', 'D', '0')) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VAddr string_table_offset{};
|
||||
VAddr symbol_table_offset{};
|
||||
u64 symbol_entry_size{};
|
||||
|
||||
const auto dynamic_offset = Read32(mod_offset + 0x4) + mod_offset;
|
||||
|
||||
VAddr dynamic_index = dynamic_offset;
|
||||
VAddr strtab_offs{};
|
||||
VAddr symtab_offs{};
|
||||
u64 syment_size = sizeof(typename M::Sym);
|
||||
VAddr dynamic_offset = loc.header_offset + hdr.dynamic_offset;
|
||||
while (true) {
|
||||
const Word tag = ReadWord(dynamic_index);
|
||||
const Word value = ReadWord(dynamic_index + sizeof(Word));
|
||||
dynamic_index += 2 * sizeof(Word);
|
||||
|
||||
if (tag == ElfDtNull) {
|
||||
typename M::Dyn dyn;
|
||||
ReadBytes(&dyn, dynamic_offset, sizeof(typename M::Dyn));
|
||||
dynamic_offset += sizeof(typename M::Dyn);
|
||||
if (dyn.d_tag == ElfDtNull) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (tag == ElfDtStrtab) {
|
||||
string_table_offset = value;
|
||||
} else if (tag == ElfDtSymtab) {
|
||||
symbol_table_offset = value;
|
||||
} else if (tag == ElfDtSyment) {
|
||||
symbol_entry_size = value;
|
||||
if (dyn.d_tag == ElfDtStrtab) {
|
||||
strtab_offs = dyn.d_un.d_ptr;
|
||||
} else if (dyn.d_tag == ElfDtSymtab) {
|
||||
symtab_offs = dyn.d_un.d_ptr;
|
||||
} else if (dyn.d_tag == ElfDtSyment) {
|
||||
syment_size = dyn.d_un.d_val;
|
||||
}
|
||||
}
|
||||
|
||||
if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Symbols out;
|
||||
|
||||
VAddr symbol_index = symbol_table_offset;
|
||||
while (symbol_index < string_table_offset) {
|
||||
ELFSymbol symbol{};
|
||||
ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol));
|
||||
|
||||
VAddr string_offset = string_table_offset + symbol.st_name;
|
||||
std::string name;
|
||||
for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) {
|
||||
name += static_cast<char>(c);
|
||||
if (strtab_offs > 0 && symtab_offs > 0) {
|
||||
Symbols out;
|
||||
VAddr symbol_index = symtab_offs;
|
||||
while (symbol_index < strtab_offs) {
|
||||
typename M::Sym symbol{};
|
||||
ReadBytes(&symbol, symbol_index, sizeof(typename M::Sym));
|
||||
VAddr offs = strtab_offs + symbol.st_name;
|
||||
std::string name{};
|
||||
for (u8 c = Read8(offs); c != 0; c = Read8(++offs))
|
||||
name += char(c);
|
||||
symbol_index += syment_size;
|
||||
out[name] = std::make_pair(symbol.st_value, symbol.st_size);
|
||||
}
|
||||
|
||||
symbol_index += symbol_entry_size;
|
||||
out[name] = std::make_pair(symbol.st_value, symbol.st_size);
|
||||
return out;
|
||||
}
|
||||
|
||||
return out;
|
||||
return {};
|
||||
}
|
||||
|
||||
Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) {
|
||||
const auto ReadBytes{
|
||||
[&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }};
|
||||
|
||||
if (is_64) {
|
||||
return GetSymbols<u64, Elf64_Sym>(ReadBytes);
|
||||
} else {
|
||||
return GetSymbols<u32, Elf32_Sym>(ReadBytes);
|
||||
}
|
||||
const auto f = [base, &memory](void* ptr, size_t offset, size_t size) {
|
||||
memory.ReadBlock(base + offset, ptr, size);
|
||||
};
|
||||
return is_64 ? GetSymbols<Mod64>(f) : GetSymbols<Mod32>(f);
|
||||
}
|
||||
|
||||
Symbols GetSymbols(std::span<const u8> data, bool is_64) {
|
||||
const auto ReadBytes{[&](void* ptr, size_t offset, size_t size) {
|
||||
const auto f = [data](void* ptr, size_t offset, size_t size) {
|
||||
std::memcpy(ptr, data.data() + offset, size);
|
||||
}};
|
||||
|
||||
if (is_64) {
|
||||
return GetSymbols<u64, Elf64_Sym>(ReadBytes);
|
||||
} else {
|
||||
return GetSymbols<u32, Elf32_Sym>(ReadBytes);
|
||||
}
|
||||
};
|
||||
return is_64 ? GetSymbols<Mod64>(f) : GetSymbols<Mod32>(f);
|
||||
}
|
||||
|
||||
std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr) {
|
||||
const auto iter = std::find_if(symbols.cbegin(), symbols.cend(), [addr](const auto& pair) {
|
||||
const auto& [name, sym_info] = pair;
|
||||
const auto& [start_address, size] = sym_info;
|
||||
const auto end_address = start_address + size;
|
||||
return addr >= start_address && addr < end_address;
|
||||
const auto it = std::find_if(symbols.cbegin(), symbols.cend(), [addr](const auto& e) {
|
||||
auto const [start, size] = e.second;
|
||||
auto const end = start + size;
|
||||
return addr >= start && addr < end;
|
||||
});
|
||||
|
||||
if (iter == symbols.cend()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return iter->first;
|
||||
return it != symbols.cend() ? std::optional<std::string>{it->first} : std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace Symbols
|
||||
|
||||
+67
-88
@@ -57,15 +57,51 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
|
||||
Reset();
|
||||
on_thread_init = std::move(on_thread_init_);
|
||||
event_fifo_id = 0;
|
||||
shutting_down = false;
|
||||
cpu_ticks = 0;
|
||||
if (is_multicore) {
|
||||
timer_thread.emplace([](CoreTiming& instance) {
|
||||
timer_thread = std::jthread([this](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("HostTiming");
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
|
||||
instance.on_thread_init();
|
||||
instance.ThreadLoop();
|
||||
}, std::ref(*this));
|
||||
on_thread_init();
|
||||
has_started = true;
|
||||
while (!stop_token.stop_requested()) {
|
||||
while (!paused && !stop_token.stop_requested()) {
|
||||
paused_set = false;
|
||||
if (auto const next_time = Advance(); next_time) {
|
||||
// There are more events left in the queue, wait until the next event.
|
||||
auto wait_time = *next_time - GetGlobalTimeNs().count();
|
||||
if (wait_time > 0) {
|
||||
#ifdef _WIN32
|
||||
while (!paused && !event.IsSet() && wait_time > 0) {
|
||||
wait_time = *next_time - GetGlobalTimeNs().count();
|
||||
if (wait_time >= timer_resolution_ns) {
|
||||
Common::Windows::SleepForOneTick();
|
||||
} else {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
Common::X64::MicroSleep();
|
||||
#else
|
||||
std::this_thread::yield();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (event.IsSet())
|
||||
event.Reset();
|
||||
#else
|
||||
event.WaitFor(std::chrono::nanoseconds(wait_time));
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
// Queue is empty, wait until another event is scheduled and signals us to
|
||||
// continue.
|
||||
wait_set = true;
|
||||
event.Wait();
|
||||
}
|
||||
wait_set = false;
|
||||
}
|
||||
paused_set = true;
|
||||
pause_event.Wait();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +126,7 @@ void CoreTiming::SyncPause(bool is_paused) {
|
||||
}
|
||||
|
||||
Pause(is_paused);
|
||||
if (timer_thread) {
|
||||
if (timer_thread.joinable()) {
|
||||
if (!is_paused) {
|
||||
pause_event.Set();
|
||||
}
|
||||
@@ -190,33 +226,22 @@ void CoreTiming::ResetTicks() {
|
||||
}
|
||||
|
||||
u64 CoreTiming::GetClockTicks() const {
|
||||
u64 fres;
|
||||
if (is_multicore) [[likely]] {
|
||||
fres = clock->GetCNTPCT();
|
||||
} else {
|
||||
fres = Common::WallClock::CPUTickToCNTPCT(cpu_ticks);
|
||||
u64 fres = is_multicore ? clock.GetCNTPCT() : Common::WallClock::CPUTickToCNTPCT(cpu_ticks);
|
||||
if (auto const overclock = Settings::values.fast_cpu_time.GetValue(); overclock != Settings::CpuClock::Off) {
|
||||
fres = u64(f64(fres) * (1.7 + 0.3 * u32(overclock)));
|
||||
}
|
||||
|
||||
const auto overclock = Settings::values.fast_cpu_time.GetValue();
|
||||
|
||||
if (overclock != Settings::CpuClock::Off) {
|
||||
fres = (u64) ((double) fres * (1.7 + 0.3 * u32(overclock)));
|
||||
}
|
||||
|
||||
if (Settings::values.sync_core_speed.GetValue()) {
|
||||
const auto ticks = double(fres);
|
||||
const auto speed_limit = double(Settings::SpeedLimit())*0.01;
|
||||
return u64(ticks/speed_limit);
|
||||
} else {
|
||||
return fres;
|
||||
}
|
||||
if (::Settings::values.sync_core_speed.GetValue()) {
|
||||
auto const ticks = f64(fres);
|
||||
auto const speed_limit = f64(Settings::SpeedLimit()) * 0.01;
|
||||
return u64(ticks / speed_limit);
|
||||
}
|
||||
return fres;
|
||||
}
|
||||
|
||||
u64 CoreTiming::GetGPUTicks() const {
|
||||
if (is_multicore) [[likely]] {
|
||||
return clock->GetGPUTick();
|
||||
}
|
||||
return Common::WallClock::CPUTickToGPUTick(cpu_ticks);
|
||||
return is_multicore
|
||||
? clock.GetGPUTick()
|
||||
: Common::WallClock::CPUTickToGPUTick(cpu_ticks);
|
||||
}
|
||||
|
||||
std::optional<s64> CoreTiming::Advance() {
|
||||
@@ -278,75 +303,29 @@ std::optional<s64> CoreTiming::Advance() {
|
||||
}
|
||||
}
|
||||
|
||||
void CoreTiming::ThreadLoop() {
|
||||
has_started = true;
|
||||
while (!shutting_down) {
|
||||
while (!paused) {
|
||||
paused_set = false;
|
||||
const auto next_time = Advance();
|
||||
if (next_time) {
|
||||
// There are more events left in the queue, wait until the next event.
|
||||
auto wait_time = *next_time - GetGlobalTimeNs().count();
|
||||
if (wait_time > 0) {
|
||||
#ifdef _WIN32
|
||||
while (!paused && !event.IsSet() && wait_time > 0) {
|
||||
wait_time = *next_time - GetGlobalTimeNs().count();
|
||||
if (wait_time >= timer_resolution_ns) {
|
||||
Common::Windows::SleepForOneTick();
|
||||
} else {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
Common::X64::MicroSleep();
|
||||
#else
|
||||
std::this_thread::yield();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (event.IsSet()) {
|
||||
event.Reset();
|
||||
}
|
||||
#else
|
||||
event.WaitFor(std::chrono::nanoseconds(wait_time));
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
// Queue is empty, wait until another event is scheduled and signals us to
|
||||
// continue.
|
||||
wait_set = true;
|
||||
event.Wait();
|
||||
}
|
||||
wait_set = false;
|
||||
}
|
||||
|
||||
paused_set = true;
|
||||
pause_event.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
void CoreTiming::Reset() {
|
||||
paused = true;
|
||||
shutting_down = true;
|
||||
pause_event.Set();
|
||||
event.Set();
|
||||
if (timer_thread) {
|
||||
timer_thread->join();
|
||||
if (timer_thread.joinable()) {
|
||||
timer_thread.request_stop();
|
||||
timer_thread.join();
|
||||
}
|
||||
timer_thread.reset();
|
||||
has_started = false;
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const {
|
||||
if (is_multicore) [[likely]] {
|
||||
return clock->GetTimeNS();
|
||||
}
|
||||
return std::chrono::nanoseconds{Common::WallClock::CPUTickToNS(cpu_ticks)};
|
||||
/// @brief Returns current time in nanoseconds.
|
||||
std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const noexcept {
|
||||
return is_multicore
|
||||
? clock.GetTimeNS()
|
||||
: std::chrono::nanoseconds{Common::WallClock::CPUTickToNS(cpu_ticks)};
|
||||
}
|
||||
|
||||
std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const {
|
||||
if (is_multicore) [[likely]] {
|
||||
return clock->GetTimeUS();
|
||||
}
|
||||
return std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)};
|
||||
/// @brief Returns current time in microseconds.
|
||||
std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const noexcept {
|
||||
return is_multicore
|
||||
? clock.GetTimeUS()
|
||||
: std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)};
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
+6
-12
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -118,7 +118,7 @@ public:
|
||||
|
||||
void Idle();
|
||||
|
||||
s64 GetDowncount() const {
|
||||
s64 GetDowncount() const noexcept {
|
||||
return downcount;
|
||||
}
|
||||
|
||||
@@ -128,11 +128,8 @@ public:
|
||||
/// Returns the current GPU tick value.
|
||||
u64 GetGPUTicks() const;
|
||||
|
||||
/// Returns current time in microseconds.
|
||||
std::chrono::microseconds GetGlobalTimeUs() const;
|
||||
|
||||
/// Returns current time in nanoseconds.
|
||||
std::chrono::nanoseconds GetGlobalTimeNs() const;
|
||||
[[nodiscard]] std::chrono::microseconds GetGlobalTimeUs() const noexcept;
|
||||
[[nodiscard]] std::chrono::nanoseconds GetGlobalTimeNs() const noexcept;
|
||||
|
||||
/// Checks for events manually and returns time in nanoseconds for next event, threadsafe.
|
||||
std::optional<s64> Advance();
|
||||
@@ -141,13 +138,11 @@ public:
|
||||
void SetTimerResolutionNs(std::chrono::nanoseconds ns);
|
||||
#endif
|
||||
|
||||
private:
|
||||
struct Event;
|
||||
void ThreadLoop();
|
||||
|
||||
void Reset();
|
||||
|
||||
std::unique_ptr<Common::WallClock> clock;
|
||||
Common::WallClock clock;
|
||||
|
||||
s64 global_timer = 0;
|
||||
|
||||
@@ -165,11 +160,10 @@ private:
|
||||
Common::Event pause_event{};
|
||||
mutable std::mutex basic_lock;
|
||||
std::mutex advance_lock;
|
||||
std::optional<std::jthread> timer_thread;
|
||||
std::jthread timer_thread;
|
||||
std::atomic<bool> paused{};
|
||||
std::atomic<bool> paused_set{};
|
||||
std::atomic<bool> wait_set{};
|
||||
std::atomic<bool> shutting_down{};
|
||||
std::atomic<bool> has_started{};
|
||||
std::function<void()> on_thread_init{};
|
||||
|
||||
|
||||
@@ -569,12 +569,11 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
|
||||
layers.emplace_back(std::move(extracted));
|
||||
|
||||
auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
|
||||
if (layered == nullptr) {
|
||||
auto layered_ext = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers_ext));
|
||||
if (layered == nullptr && layered_ext == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto layered_ext = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers_ext));
|
||||
|
||||
auto packed = CreateRomFS(std::move(layered), std::move(layered_ext));
|
||||
if (packed == nullptr) {
|
||||
return;
|
||||
@@ -943,6 +942,8 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
||||
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "romfs")) ||
|
||||
IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "romfslite")))
|
||||
AppendCommaIfNotEmpty(types, "LayeredFS");
|
||||
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "romfs_ext")))
|
||||
AppendCommaIfNotEmpty(types, "ExtLayeredFS");
|
||||
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "cheats")))
|
||||
AppendCommaIfNotEmpty(types, "Cheats");
|
||||
|
||||
@@ -965,13 +966,13 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
||||
const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
||||
if (sdmc_mod_dir != nullptr) {
|
||||
std::string types;
|
||||
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) {
|
||||
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs")))
|
||||
AppendCommaIfNotEmpty(types, "LayeredExeFS");
|
||||
}
|
||||
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs")) ||
|
||||
IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfslite"))) {
|
||||
IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfslite")))
|
||||
AppendCommaIfNotEmpty(types, "LayeredFS");
|
||||
}
|
||||
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs_ext")))
|
||||
AppendCommaIfNotEmpty(types, "ExtLayeredFS");
|
||||
|
||||
if (!types.empty()) {
|
||||
const auto mod_disabled =
|
||||
|
||||
@@ -1,86 +1,48 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/system_archive/time_zone_binary.h"
|
||||
#include "core/file_sys/vfs/vfs_static.h"
|
||||
#include "core/file_sys/vfs/vfs_types.h"
|
||||
#include "core/file_sys/vfs/vfs_vector.h"
|
||||
|
||||
#include "nx_tzdb.h"
|
||||
|
||||
namespace FileSys::SystemArchive {
|
||||
|
||||
const static std::map<std::string, const std::map<const char*, const std::vector<u8>>&>
|
||||
tzdb_zoneinfo_dirs = {{"Africa", NxTzdb::africa},
|
||||
{"America", NxTzdb::america},
|
||||
{"Antarctica", NxTzdb::antarctica},
|
||||
{"Arctic", NxTzdb::arctic},
|
||||
{"Asia", NxTzdb::asia},
|
||||
{"Atlantic", NxTzdb::atlantic},
|
||||
{"Australia", NxTzdb::australia},
|
||||
{"Brazil", NxTzdb::brazil},
|
||||
{"Canada", NxTzdb::canada},
|
||||
{"Chile", NxTzdb::chile},
|
||||
{"Etc", NxTzdb::etc},
|
||||
{"Europe", NxTzdb::europe},
|
||||
{"Indian", NxTzdb::indian},
|
||||
{"Mexico", NxTzdb::mexico},
|
||||
{"Pacific", NxTzdb::pacific},
|
||||
{"US", NxTzdb::us}};
|
||||
|
||||
const static std::map<std::string, const std::map<const char*, const std::vector<u8>>&>
|
||||
tzdb_america_dirs = {{"Argentina", NxTzdb::america_argentina},
|
||||
{"Indiana", NxTzdb::america_indiana},
|
||||
{"Kentucky", NxTzdb::america_kentucky},
|
||||
{"North_Dakota", NxTzdb::america_north_dakota}};
|
||||
|
||||
static void GenerateFiles(std::vector<VirtualFile>& directory,
|
||||
const std::map<const char*, const std::vector<u8>>& files) {
|
||||
for (const auto& [filename, data] : files) {
|
||||
const auto data_copy{data};
|
||||
const std::string filename_copy{filename};
|
||||
VirtualFile file{
|
||||
std::make_shared<VectorVfsFile>(std::move(data_copy), std::move(filename_copy))};
|
||||
directory.push_back(file);
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<VirtualFile> GenerateZoneinfoFiles() {
|
||||
std::vector<VirtualFile> zoneinfo_files;
|
||||
GenerateFiles(zoneinfo_files, NxTzdb::zoneinfo);
|
||||
return zoneinfo_files;
|
||||
}
|
||||
|
||||
VirtualDir TimeZoneBinary() {
|
||||
std::vector<VirtualDir> america_sub_dirs;
|
||||
for (const auto& [dir_name, files] : tzdb_america_dirs) {
|
||||
std::vector<VirtualFile> vfs_files;
|
||||
GenerateFiles(vfs_files, files);
|
||||
america_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(
|
||||
std::move(vfs_files), std::vector<VirtualDir>{}, dir_name));
|
||||
}
|
||||
|
||||
america_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_america_argentina(), std::vector<VirtualDir>{}, "Argentina"));
|
||||
america_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_america_indiana(), std::vector<VirtualDir>{}, "Indiana"));
|
||||
america_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_america_kentucky(), std::vector<VirtualDir>{}, "Kentucky"));
|
||||
america_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_america_north_dakota(), std::vector<VirtualDir>{}, "North_Dakota"));
|
||||
std::vector<VirtualDir> zoneinfo_sub_dirs;
|
||||
for (const auto& [dir_name, files] : tzdb_zoneinfo_dirs) {
|
||||
std::vector<VirtualFile> vfs_files;
|
||||
GenerateFiles(vfs_files, files);
|
||||
if (dir_name == "America") {
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(
|
||||
std::move(vfs_files), std::move(america_sub_dirs), dir_name));
|
||||
} else {
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(
|
||||
std::move(vfs_files), std::vector<VirtualDir>{}, dir_name));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<VirtualDir> zoneinfo_dir{std::make_shared<VectorVfsDirectory>(
|
||||
GenerateZoneinfoFiles(), std::move(zoneinfo_sub_dirs), "zoneinfo")};
|
||||
std::vector<VirtualFile> root_files;
|
||||
GenerateFiles(root_files, NxTzdb::base);
|
||||
|
||||
return std::make_shared<VectorVfsDirectory>(std::move(root_files), std::move(zoneinfo_dir),
|
||||
"data");
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_africa(), std::vector<VirtualDir>{}, "Africa"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_america(), std::move(america_sub_dirs), "America"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_antarctica(), std::vector<VirtualDir>{}, "Antarctica"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_arctic(), std::vector<VirtualDir>{}, "Arctic"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_asia(), std::vector<VirtualDir>{}, "Asia"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_atlantic(), std::vector<VirtualDir>{}, "Atlantic"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_australia(), std::vector<VirtualDir>{}, "Australia"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_brazil(), std::vector<VirtualDir>{}, "Brazil"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_canada(), std::vector<VirtualDir>{}, "Canada"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_chile(), std::vector<VirtualDir>{}, "Chile"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_etc(), std::vector<VirtualDir>{}, "Etc"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_europe(), std::vector<VirtualDir>{}, "Europe"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_indian(), std::vector<VirtualDir>{}, "Indian"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_mexico(), std::vector<VirtualDir>{}, "Mexico"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_pacific(), std::vector<VirtualDir>{}, "Pacific"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_us(), std::vector<VirtualDir>{}, "US"));
|
||||
std::vector<VirtualDir> zoneinfo_dir{std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_zoneinfo(), std::move(zoneinfo_sub_dirs), "zoneinfo")};
|
||||
// last files (root)
|
||||
return std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_base(), std::move(zoneinfo_dir), "data");
|
||||
}
|
||||
|
||||
} // namespace FileSys::SystemArchive
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -13,6 +16,16 @@ namespace IPC {
|
||||
/// Size of the command buffer area, in 32-bit words.
|
||||
constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
|
||||
|
||||
/// Must match bitfields
|
||||
constexpr std::size_t MAX_BUFFER_DESCRIPTORS = 16;
|
||||
constexpr std::size_t MAX_INCOMING_MOVE_HANDLERS = 16;
|
||||
constexpr std::size_t MAX_INCOMING_COPY_HANDLERS = 16;
|
||||
|
||||
/// Doesn't need to match bitfields but usually not big enough
|
||||
constexpr std::size_t MAX_OUTGOING_COPY_OBJECTS = 16;
|
||||
constexpr std::size_t MAX_OUTGOING_MOVE_OBJECTS = 16;
|
||||
constexpr std::size_t MAX_OUTGOING_DOMAIN_OBJECTS = 16;
|
||||
|
||||
enum class ControlCommand : u32 {
|
||||
ConvertSessionToDomain = 0,
|
||||
ConvertDomainToSession = 1,
|
||||
|
||||
@@ -33,8 +33,7 @@ IScreenShotApplicationService::IScreenShotApplicationService(
|
||||
|
||||
IScreenShotApplicationService::~IScreenShotApplicationService() = default;
|
||||
|
||||
Result IScreenShotApplicationService::SetShimLibraryVersion(ShimLibraryVersion library_version,
|
||||
ClientAppletResourceUserId aruid) {
|
||||
Result IScreenShotApplicationService::SetShimLibraryVersion(ShimLibraryVersion library_version, ClientAppletResourceUserId aruid) {
|
||||
LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
|
||||
library_version, aruid.pid);
|
||||
R_SUCCEED();
|
||||
|
||||
@@ -128,10 +128,12 @@ Result SessionRequestManager::HandleDomainSyncRequest(Kernel::KServerSession* se
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
HLERequestContext::HLERequestContext(Kernel::KernelCore& kernel_, Core::Memory::Memory& memory_,
|
||||
Kernel::KServerSession* server_session_,
|
||||
Kernel::KThread* thread_)
|
||||
: server_session(server_session_), thread(thread_), kernel{kernel_}, memory{memory_} {
|
||||
HLERequestContext::HLERequestContext(Kernel::KernelCore& kernel_, Core::Memory::Memory& memory_, Kernel::KServerSession* server_session_, Kernel::KThread* thread_)
|
||||
: server_session(server_session_)
|
||||
, thread(thread_)
|
||||
, kernel{kernel_}
|
||||
, memory{memory_}
|
||||
{
|
||||
cmd_buf[0] = 0;
|
||||
}
|
||||
|
||||
@@ -155,9 +157,6 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
}
|
||||
if (incoming) {
|
||||
// Populate the object lists with the data in the IPC request.
|
||||
incoming_copy_handles.reserve(handle_descriptor_header->num_handles_to_copy);
|
||||
incoming_move_handles.reserve(handle_descriptor_header->num_handles_to_move);
|
||||
|
||||
for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {
|
||||
incoming_copy_handles.push_back(rp.Pop<Handle>());
|
||||
}
|
||||
@@ -172,11 +171,6 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
}
|
||||
}
|
||||
|
||||
buffer_x_descriptors.reserve(command_header->num_buf_x_descriptors);
|
||||
buffer_a_descriptors.reserve(command_header->num_buf_a_descriptors);
|
||||
buffer_b_descriptors.reserve(command_header->num_buf_b_descriptors);
|
||||
buffer_w_descriptors.reserve(command_header->num_buf_w_descriptors);
|
||||
|
||||
for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) {
|
||||
buffer_x_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorX>());
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -11,6 +14,7 @@
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -181,8 +185,7 @@ private:
|
||||
*/
|
||||
class HLERequestContext {
|
||||
public:
|
||||
explicit HLERequestContext(Kernel::KernelCore& kernel, Core::Memory::Memory& memory,
|
||||
Kernel::KServerSession* session, Kernel::KThread* thread);
|
||||
explicit HLERequestContext(Kernel::KernelCore& kernel, Core::Memory::Memory& memory, Kernel::KServerSession* session, Kernel::KThread* thread);
|
||||
~HLERequestContext();
|
||||
|
||||
/// Returns a pointer to the IPC command buffer for this request.
|
||||
@@ -233,19 +236,19 @@ public:
|
||||
return data_payload_offset;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const {
|
||||
[[nodiscard]] const boost::container::static_vector<IPC::BufferDescriptorX, 16>& BufferDescriptorX() const {
|
||||
return buffer_x_descriptors;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const {
|
||||
[[nodiscard]] const boost::container::static_vector<IPC::BufferDescriptorABW, 16>& BufferDescriptorA() const {
|
||||
return buffer_a_descriptors;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const {
|
||||
[[nodiscard]] const boost::container::static_vector<IPC::BufferDescriptorABW, 16>& BufferDescriptorB() const {
|
||||
return buffer_b_descriptors;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const {
|
||||
[[nodiscard]] const boost::container::static_vector<IPC::BufferDescriptorC, 16>& BufferDescriptorC() const {
|
||||
return buffer_c_descriptors;
|
||||
}
|
||||
|
||||
@@ -399,38 +402,35 @@ private:
|
||||
Kernel::KHandleTable* client_handle_table{};
|
||||
Kernel::KThread* thread{};
|
||||
|
||||
std::vector<Handle> incoming_move_handles;
|
||||
std::vector<Handle> incoming_copy_handles;
|
||||
boost::container::static_vector<IPC::BufferDescriptorX, IPC::MAX_BUFFER_DESCRIPTORS> buffer_x_descriptors;
|
||||
boost::container::static_vector<IPC::BufferDescriptorABW, IPC::MAX_BUFFER_DESCRIPTORS> buffer_a_descriptors;
|
||||
boost::container::static_vector<IPC::BufferDescriptorABW, IPC::MAX_BUFFER_DESCRIPTORS> buffer_b_descriptors;
|
||||
boost::container::static_vector<IPC::BufferDescriptorABW, IPC::MAX_BUFFER_DESCRIPTORS> buffer_w_descriptors;
|
||||
boost::container::static_vector<IPC::BufferDescriptorC, IPC::MAX_BUFFER_DESCRIPTORS> buffer_c_descriptors;
|
||||
boost::container::static_vector<Handle, IPC::MAX_INCOMING_MOVE_HANDLERS> incoming_move_handles;
|
||||
boost::container::static_vector<Handle, IPC::MAX_INCOMING_COPY_HANDLERS> incoming_copy_handles;
|
||||
boost::container::static_vector<Kernel::KAutoObject*, IPC::MAX_OUTGOING_MOVE_OBJECTS> outgoing_move_objects;
|
||||
boost::container::static_vector<Kernel::KAutoObject*, IPC::MAX_OUTGOING_COPY_OBJECTS> outgoing_copy_objects;
|
||||
boost::container::static_vector<SessionRequestHandlerPtr, IPC::MAX_OUTGOING_DOMAIN_OBJECTS> outgoing_domain_objects;
|
||||
|
||||
std::vector<Kernel::KAutoObject*> outgoing_move_objects;
|
||||
std::vector<Kernel::KAutoObject*> outgoing_copy_objects;
|
||||
std::vector<SessionRequestHandlerPtr> outgoing_domain_objects;
|
||||
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_a{};
|
||||
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_x{};
|
||||
|
||||
std::optional<IPC::CommandHeader> command_header;
|
||||
std::optional<IPC::HandleDescriptorHeader> handle_descriptor_header;
|
||||
std::optional<IPC::DataPayloadHeader> data_payload_header;
|
||||
std::optional<IPC::DomainMessageHeader> domain_message_header;
|
||||
std::vector<IPC::BufferDescriptorX> buffer_x_descriptors;
|
||||
std::vector<IPC::BufferDescriptorABW> buffer_a_descriptors;
|
||||
std::vector<IPC::BufferDescriptorABW> buffer_b_descriptors;
|
||||
std::vector<IPC::BufferDescriptorABW> buffer_w_descriptors;
|
||||
std::vector<IPC::BufferDescriptorC> buffer_c_descriptors;
|
||||
std::weak_ptr<SessionRequestManager> manager{};
|
||||
Kernel::KernelCore& kernel;
|
||||
Core::Memory::Memory& memory;
|
||||
|
||||
u32_le command{};
|
||||
u64 pid{};
|
||||
u32_le command{};
|
||||
u32 write_size{};
|
||||
u32 data_payload_offset{};
|
||||
u32 handles_offset{};
|
||||
u32 domain_offset{};
|
||||
|
||||
std::weak_ptr<SessionRequestManager> manager{};
|
||||
bool is_deferred{false};
|
||||
|
||||
Kernel::KernelCore& kernel;
|
||||
Core::Memory::Memory& memory;
|
||||
|
||||
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_a{};
|
||||
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_x{};
|
||||
bool is_deferred = false;
|
||||
};
|
||||
|
||||
} // namespace Service
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "core/arm/debug.h"
|
||||
#include "core/arm/symbols.h"
|
||||
#include "core/core.h"
|
||||
@@ -111,10 +115,7 @@ public:
|
||||
const VAddr input_ptr{context.AddHeap(in_data.data(), in_data.size())};
|
||||
const VAddr output_ptr{context.AddHeap(out_data.data(), out_data.size())};
|
||||
|
||||
const u64 wrapper_value{context.CallFunction(callbacks.Control, ret_ptr, configuration_ptr,
|
||||
command, input_ptr, in_data.size(), output_ptr,
|
||||
out_data.size())};
|
||||
|
||||
const u64 wrapper_value = context.CallFunction(callbacks.Control, ret_ptr, configuration_ptr, command, input_ptr, in_data.size(), output_ptr, out_data.size());
|
||||
*out_return_value = context.GetHeap<s32>(ret_ptr);
|
||||
context.GetHeap(output_ptr, out_data.data(), out_data.size());
|
||||
|
||||
@@ -126,9 +127,7 @@ public:
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
Result LoadPlugin(u64 tmem_size, InCopyHandle<Kernel::KTransferMemory> tmem,
|
||||
InBuffer<BufferAttr_HipcMapAlias> nrr,
|
||||
InBuffer<BufferAttr_HipcMapAlias> nro) {
|
||||
Result LoadPlugin(u64 tmem_size, InCopyHandle<Kernel::KTransferMemory> tmem, InBuffer<BufferAttr_HipcMapAlias> nrr, InBuffer<BufferAttr_HipcMapAlias> nro) {
|
||||
if (!tmem) {
|
||||
LOG_ERROR(Service_JIT, "Invalid transfer memory handle!");
|
||||
R_THROW(ResultUnknown);
|
||||
@@ -139,9 +138,10 @@ public:
|
||||
configuration.transfer_memory.size = tmem_size;
|
||||
|
||||
// Gather up all the callbacks from the loaded plugin
|
||||
auto symbols{Core::Symbols::GetSymbols(nro, true)};
|
||||
const auto GetSymbol{[&](const std::string& name) { return symbols[name].first; }};
|
||||
|
||||
auto symbols = Core::Symbols::GetSymbols(nro, true);
|
||||
const auto GetSymbol = [&](const std::string& name) {
|
||||
return symbols[name].first;
|
||||
};
|
||||
callbacks.rtld_fini = GetSymbol("_fini");
|
||||
callbacks.rtld_init = GetSymbol("_init");
|
||||
callbacks.Control = GetSymbol("nnjitpluginControl");
|
||||
@@ -153,8 +153,7 @@ public:
|
||||
callbacks.OnPrepared = GetSymbol("nnjitpluginOnPrepared");
|
||||
callbacks.Keeper = GetSymbol("nnjitpluginKeeper");
|
||||
|
||||
if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 ||
|
||||
callbacks.OnPrepared == 0) {
|
||||
if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 || callbacks.OnPrepared == 0 || callbacks.Control == 0) {
|
||||
LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality");
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
@@ -164,12 +163,9 @@ public:
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
context.MapProcessMemory(configuration.sys_ro_memory.offset,
|
||||
configuration.sys_ro_memory.size);
|
||||
context.MapProcessMemory(configuration.sys_rx_memory.offset,
|
||||
configuration.sys_rx_memory.size);
|
||||
context.MapProcessMemory(configuration.transfer_memory.offset,
|
||||
configuration.transfer_memory.size);
|
||||
context.MapProcessMemory(configuration.sys_ro_memory.offset, configuration.sys_ro_memory.size);
|
||||
context.MapProcessMemory(configuration.sys_rx_memory.offset, configuration.sys_rx_memory.size);
|
||||
context.MapProcessMemory(configuration.transfer_memory.offset, configuration.transfer_memory.size);
|
||||
|
||||
// Run ELF constructors, if needed
|
||||
if (callbacks.rtld_init != 0) {
|
||||
@@ -179,7 +175,7 @@ public:
|
||||
// Function prototype:
|
||||
// u64 GetVersion();
|
||||
const auto version{context.CallFunction(callbacks.GetVersion)};
|
||||
if (version != 1) {
|
||||
if (version > 1) {
|
||||
LOG_ERROR(Service_JIT, "unknown plugin version {}", version);
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
@@ -24,15 +24,25 @@ using namespace Common::ELF;
|
||||
|
||||
namespace Service::JIT {
|
||||
|
||||
enum HelperFn {
|
||||
None,
|
||||
Stop,
|
||||
Resolve,
|
||||
Panic,
|
||||
Memcpy,
|
||||
Memmove,
|
||||
Memset,
|
||||
// sm64
|
||||
PanicForPlugin,
|
||||
AbortImpl,
|
||||
UnexpectedImpl,
|
||||
Count
|
||||
};
|
||||
|
||||
constexpr std::array<u8, 8> SVC0_ARM64 = {
|
||||
0x01, 0x00, 0x00, 0xd4, // svc #0
|
||||
0xc0, 0x03, 0x5f, 0xd6, // ret
|
||||
};
|
||||
|
||||
constexpr std::array HELPER_FUNCTIONS{
|
||||
"_stop", "_resolve", "_panic", "memcpy", "memmove", "memset",
|
||||
};
|
||||
|
||||
constexpr size_t STACK_ALIGN = 16;
|
||||
|
||||
class JITContextImpl;
|
||||
@@ -42,10 +52,12 @@ using IntervalType = boost::icl::interval_set<VAddr>::interval_type;
|
||||
|
||||
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
|
||||
public:
|
||||
explicit DynarmicCallbacks64(Core::Memory::Memory& memory_, std::vector<u8>& local_memory_,
|
||||
IntervalSet& mapped_ranges_, JITContextImpl& parent_)
|
||||
: memory{memory_}, local_memory{local_memory_},
|
||||
mapped_ranges{mapped_ranges_}, parent{parent_} {}
|
||||
explicit DynarmicCallbacks64(Core::Memory::Memory& memory_, std::vector<u8>& local_memory_, IntervalSet& mapped_ranges_, JITContextImpl& parent_)
|
||||
: memory{memory_}
|
||||
, local_memory{local_memory_}
|
||||
, mapped_ranges{mapped_ranges_}
|
||||
, parent{parent_}
|
||||
{}
|
||||
|
||||
std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) override {
|
||||
static_assert(Core::Memory::YUZU_PAGESIZE == Dynarmic::CODE_PAGE_SIZE);
|
||||
@@ -57,7 +69,7 @@ public:
|
||||
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
||||
}
|
||||
void InstructionSynchronizationBarrierRaised() override {
|
||||
last_code_addr = 0; //reset back, force refetch
|
||||
last_code_addr = u64(-1); //reset back, force refetch
|
||||
}
|
||||
u8 MemoryRead8(u64 vaddr) override {
|
||||
return ReadMemory<u8>(vaddr);
|
||||
@@ -75,13 +87,10 @@ public:
|
||||
return ReadMemory<u128>(vaddr);
|
||||
}
|
||||
std::string MemoryReadCString(u64 vaddr) {
|
||||
std::string result;
|
||||
std::string result{};
|
||||
u8 next;
|
||||
|
||||
while ((next = MemoryRead8(vaddr++)) != 0) {
|
||||
result += next;
|
||||
}
|
||||
|
||||
while ((next = MemoryRead8(vaddr++)) != 0)
|
||||
result += char(next);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -157,32 +166,26 @@ private:
|
||||
std::vector<u8>& local_memory;
|
||||
IntervalSet& mapped_ranges;
|
||||
JITContextImpl& parent;
|
||||
|
||||
Dynarmic::CodePage cached_code_page;
|
||||
u64 last_code_addr = 0;
|
||||
u64 last_code_addr = u64(-1);
|
||||
};
|
||||
|
||||
class JITContextImpl {
|
||||
public:
|
||||
explicit JITContextImpl(Core::Memory::Memory& memory_) : memory{memory_} {
|
||||
callbacks =
|
||||
std::make_unique<DynarmicCallbacks64>(memory, local_memory, mapped_ranges, *this);
|
||||
user_config.callbacks = callbacks.get();
|
||||
jit = std::make_unique<Dynarmic::A64::Jit>(user_config);
|
||||
callbacks.emplace(memory, local_memory, mapped_ranges, *this);
|
||||
user_config.callbacks = std::addressof(callbacks.value());
|
||||
jit.emplace(user_config);
|
||||
}
|
||||
|
||||
bool LoadNRO(std::span<const u8> data) {
|
||||
local_memory.clear();
|
||||
|
||||
relocbase = local_memory.size();
|
||||
local_memory.insert(local_memory.end(), data.begin(), data.end());
|
||||
|
||||
if (FixupRelocations()) {
|
||||
InsertHelperFunctions();
|
||||
InsertStack();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -190,11 +193,9 @@ public:
|
||||
// The loaded NRO file has ELF relocations that must be processed before it can run.
|
||||
// Normally this would be processed by RTLD, but in HLE context, we don't have
|
||||
// the linker available, so we have to do it ourselves.
|
||||
|
||||
const VAddr mod_offset{callbacks->MemoryRead32(4)};
|
||||
if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
|
||||
if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0'))
|
||||
return false;
|
||||
}
|
||||
|
||||
// For more info about dynamic entries, see the ELF ABI specification:
|
||||
// https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html
|
||||
@@ -205,40 +206,33 @@ public:
|
||||
while (true) {
|
||||
const auto dyn{callbacks->ReadMemory<Elf64_Dyn>(dynamic_offset)};
|
||||
dynamic_offset += sizeof(Elf64_Dyn);
|
||||
|
||||
if (!dyn.d_tag) {
|
||||
break;
|
||||
}
|
||||
if (dyn.d_tag == ElfDtRela) {
|
||||
} else if (dyn.d_tag == ElfDtRela) {
|
||||
rela_dyn = dyn.d_un.d_ptr;
|
||||
}
|
||||
if (dyn.d_tag == ElfDtRelasz) {
|
||||
} else if (dyn.d_tag == ElfDtRelasz) {
|
||||
num_rela = dyn.d_un.d_val / sizeof(Elf64_Rela);
|
||||
}
|
||||
if (dyn.d_tag == ElfDtRelr) {
|
||||
} else if (dyn.d_tag == ElfDtRelr) {
|
||||
relr_dyn = dyn.d_un.d_ptr;
|
||||
}
|
||||
if (dyn.d_tag == ElfDtRelrsz) {
|
||||
} else if (dyn.d_tag == ElfDtRelrsz) {
|
||||
num_relr = dyn.d_un.d_val / sizeof(Elf64_Relr);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_rela; i++) {
|
||||
const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
|
||||
if (Elf64RelType(rela.r_info) != ElfAArch64Relative) {
|
||||
continue;
|
||||
if (Elf64RelType(rela.r_info) == ElfAArch64Relative) {
|
||||
const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
|
||||
callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
|
||||
}
|
||||
const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
|
||||
callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
|
||||
}
|
||||
|
||||
VAddr relr_where = 0;
|
||||
for (size_t i = 0; i < num_relr; i++) {
|
||||
const auto relr{callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr))};
|
||||
const auto incr{[&](VAddr where) {
|
||||
const auto relr = callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr));
|
||||
const auto incr = [&](VAddr where) {
|
||||
callbacks->MemoryWrite64(where, callbacks->MemoryRead64(where) + relocbase);
|
||||
}};
|
||||
|
||||
};
|
||||
if ((relr & 1) == 0) {
|
||||
// where pointer
|
||||
relr_where = relocbase + relr;
|
||||
@@ -254,13 +248,12 @@ public:
|
||||
relr_where += 63 * sizeof(Elf64_Addr);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void InsertHelperFunctions() {
|
||||
for (const auto& name : HELPER_FUNCTIONS) {
|
||||
helpers[name] = local_memory.size();
|
||||
for (size_t i = 0; i < size_t(HelperFn::Count); ++i) {
|
||||
helpers[i] = local_memory.size();
|
||||
local_memory.insert(local_memory.end(), SVC0_ARM64.begin(), SVC0_ARM64.end());
|
||||
}
|
||||
}
|
||||
@@ -268,9 +261,8 @@ public:
|
||||
void InsertStack() {
|
||||
// Allocate enough space to avoid any reasonable risk of
|
||||
// overflowing the stack during plugin execution
|
||||
const u64 pad_amount{Common::AlignUp(local_memory.size(), STACK_ALIGN) -
|
||||
local_memory.size()};
|
||||
local_memory.insert(local_memory.end(), 0x10000 + pad_amount, 0);
|
||||
const u64 pad_amount = Common::AlignUp(local_memory.size(), STACK_ALIGN) - local_memory.size();
|
||||
local_memory.insert(local_memory.end(), (4096 * 32) + pad_amount, 0);
|
||||
top_of_stack = local_memory.size();
|
||||
heap_pointer = top_of_stack;
|
||||
}
|
||||
@@ -297,27 +289,21 @@ public:
|
||||
//
|
||||
// For more info, see the AArch64 ABI PCS:
|
||||
// https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst
|
||||
|
||||
for (size_t i = 0; i < 8 && i < argument_stack.size(); i++) {
|
||||
for (size_t i = 0; i < 8 && i < argument_stack.size(); i++)
|
||||
jit->SetRegister(i, argument_stack[i]);
|
||||
}
|
||||
|
||||
if (argument_stack.size() > 8) {
|
||||
const VAddr new_sp = Common::AlignDown(
|
||||
top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN);
|
||||
for (size_t i = 8; i < argument_stack.size(); i++) {
|
||||
const VAddr new_sp = Common::AlignDown(top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN);
|
||||
for (size_t i = 8; i < argument_stack.size(); i++)
|
||||
callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]);
|
||||
}
|
||||
jit->SetSP(new_sp);
|
||||
}
|
||||
|
||||
// Reset the call state for the next invocation
|
||||
argument_stack.clear();
|
||||
heap_pointer = top_of_stack;
|
||||
}
|
||||
|
||||
u64 CallFunction(VAddr func) {
|
||||
jit->SetRegister(30, helpers["_stop"]);
|
||||
jit->SetRegister(30, helpers[size_t(HelperFn::Stop)]);
|
||||
jit->SetSP(top_of_stack);
|
||||
SetupArguments();
|
||||
|
||||
@@ -326,21 +312,14 @@ public:
|
||||
return jit->GetRegister(0);
|
||||
}
|
||||
|
||||
VAddr GetHelper(const std::string& name) {
|
||||
return helpers[name];
|
||||
}
|
||||
|
||||
VAddr AddHeap(const void* data, size_t size) {
|
||||
// Require all heap data types to have the same alignment as the
|
||||
// stack pointer, for compatibility
|
||||
const size_t num_bytes{Common::AlignUp(size, STACK_ALIGN)};
|
||||
|
||||
const size_t num_bytes = Common::AlignUp(size, STACK_ALIGN);
|
||||
// Make additional memory space if required
|
||||
if (heap_pointer + num_bytes > local_memory.size()) {
|
||||
local_memory.insert(local_memory.end(),
|
||||
(heap_pointer + num_bytes) - local_memory.size(), 0);
|
||||
local_memory.insert(local_memory.end(), (heap_pointer + num_bytes) - local_memory.size(), 0);
|
||||
}
|
||||
|
||||
const VAddr location{heap_pointer};
|
||||
std::memcpy(local_memory.data() + location, data, size);
|
||||
heap_pointer += num_bytes;
|
||||
@@ -351,13 +330,29 @@ public:
|
||||
std::memcpy(data, local_memory.data() + location, size);
|
||||
}
|
||||
|
||||
std::unique_ptr<DynarmicCallbacks64> callbacks;
|
||||
VAddr GetHelper(const std::string& name) {
|
||||
if (name == "_resolve") return helpers[HelperFn::Resolve];
|
||||
else if (name == "_panic") return helpers[HelperFn::Panic];
|
||||
else if (name == "_stop") return helpers[HelperFn::Stop];
|
||||
else if (name == "memset") return helpers[HelperFn::Memset];
|
||||
else if (name == "memcpy") return helpers[HelperFn::Memcpy];
|
||||
else if (name == "memmove") return helpers[HelperFn::Memmove];
|
||||
else if (name == "PanicForPlugin") return helpers[HelperFn::PanicForPlugin];
|
||||
else if (name == "_ZN2nn4diag6detail9AbortImplEPKcS3_S3_i") return helpers[HelperFn::AbortImpl];
|
||||
else if (name == "_ZN2nn6detail21UnexpectedDefaultImplEPKcS2_i") return helpers[HelperFn::UnexpectedImpl];
|
||||
else {
|
||||
LOG_CRITICAL(Service_JIT, "unresolved {}", name);
|
||||
return helpers[HelperFn::Panic];
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<DynarmicCallbacks64> callbacks;
|
||||
std::optional<Dynarmic::A64::Jit> jit;
|
||||
std::vector<u8> local_memory;
|
||||
std::vector<u64> argument_stack;
|
||||
std::vector<VAddr> argument_stack;
|
||||
IntervalSet mapped_ranges;
|
||||
Dynarmic::A64::UserConfig user_config;
|
||||
std::unique_ptr<Dynarmic::A64::Jit> jit;
|
||||
std::map<std::string, VAddr, std::less<>> helpers;
|
||||
std::array<VAddr, size_t(HelperFn::Count)> helpers;
|
||||
Core::Memory::Memory& memory;
|
||||
VAddr top_of_stack;
|
||||
VAddr heap_pointer;
|
||||
@@ -383,44 +378,41 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
|
||||
}
|
||||
|
||||
u64 pc{parent.jit->GetPC() - 4};
|
||||
auto& helpers{parent.helpers};
|
||||
|
||||
if (pc == helpers["memcpy"] || pc == helpers["memmove"]) {
|
||||
if (pc == parent.helpers[size_t(HelperFn::Memcpy)] || pc == parent.helpers[size_t(HelperFn::Memmove)]) {
|
||||
const VAddr dest{parent.jit->GetRegister(0)};
|
||||
const VAddr src{parent.jit->GetRegister(1)};
|
||||
const size_t n{parent.jit->GetRegister(2)};
|
||||
|
||||
if (dest < src) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
for (size_t i = 0; i < n; i++)
|
||||
MemoryWrite8(dest + i, MemoryRead8(src + i));
|
||||
}
|
||||
} else {
|
||||
for (size_t i = n; i > 0; i--) {
|
||||
for (size_t i = n; i > 0; i--)
|
||||
MemoryWrite8(dest + i - 1, MemoryRead8(src + i - 1));
|
||||
}
|
||||
}
|
||||
} else if (pc == helpers["memset"]) {
|
||||
} else if (pc == parent.helpers[size_t(HelperFn::Memset)]) {
|
||||
const VAddr dest{parent.jit->GetRegister(0)};
|
||||
const u64 c{parent.jit->GetRegister(1)};
|
||||
const size_t n{parent.jit->GetRegister(2)};
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
MemoryWrite8(dest + i, static_cast<u8>(c));
|
||||
}
|
||||
} else if (pc == helpers["_resolve"]) {
|
||||
for (size_t i = 0; i < n; i++)
|
||||
MemoryWrite8(dest + i, u8(c));
|
||||
} else if (pc == parent.helpers[size_t(HelperFn::Resolve)]) {
|
||||
// X0 contains a char* for a symbol to resolve
|
||||
const auto name{MemoryReadCString(parent.jit->GetRegister(0))};
|
||||
const auto helper{helpers[name]};
|
||||
|
||||
if (helper != 0) {
|
||||
parent.jit->SetRegister(0, helper);
|
||||
} else {
|
||||
LOG_WARNING(Service_JIT, "plugin requested unknown function {}", name);
|
||||
parent.jit->SetRegister(0, helpers["_panic"]);
|
||||
}
|
||||
} else if (pc == helpers["_stop"]) {
|
||||
parent.jit->SetRegister(0, u64(parent.GetHelper(name)));
|
||||
} else if (pc == parent.helpers[size_t(HelperFn::Stop)]) {
|
||||
parent.jit->HaltExecution();
|
||||
} else if (pc == helpers["_panic"]) {
|
||||
} else if (pc == parent.helpers[size_t(HelperFn::Panic)]) {
|
||||
LOG_CRITICAL(Service_JIT, "plugin panicked!");
|
||||
parent.jit->HaltExecution();
|
||||
// SM64
|
||||
} else if (pc == parent.helpers[size_t(HelperFn::PanicForPlugin)]) {
|
||||
LOG_CRITICAL(Service_JIT, "plugin panicked!");
|
||||
parent.jit->HaltExecution();
|
||||
} else if (pc == parent.helpers[size_t(HelperFn::AbortImpl)]) {
|
||||
LOG_CRITICAL(Service_JIT, "plugin panicked!");
|
||||
parent.jit->HaltExecution();
|
||||
} else if (pc == parent.helpers[size_t(HelperFn::UnexpectedImpl)]) {
|
||||
LOG_CRITICAL(Service_JIT, "plugin panicked!");
|
||||
parent.jit->HaltExecution();
|
||||
} else {
|
||||
@@ -430,7 +422,8 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
|
||||
}
|
||||
|
||||
void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) {
|
||||
LOG_CRITICAL(Service_JIT, "Illegal operation PC @ {:08x}", pc);
|
||||
auto const inst = MemoryRead32(pc);
|
||||
LOG_CRITICAL(Service_JIT, "{} PC @ {:08x}, data = {:08x}", exception, pc, inst);
|
||||
parent.jit->HaltExecution();
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,11 @@ namespace Service::android {
|
||||
BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
|
||||
std::shared_ptr<BufferQueueCore> buffer_queue_core_,
|
||||
Service::Nvidia::NvCore::NvMap& nvmap_)
|
||||
: service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots),
|
||||
clock{Common::CreateOptimalClock()}, nvmap(nvmap_) {
|
||||
: service_context{service_context_}, core{std::move(buffer_queue_core_)}
|
||||
, slots(core->slots)
|
||||
, clock{Common::CreateOptimalClock()}
|
||||
, nvmap(nvmap_)
|
||||
{
|
||||
buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
|
||||
}
|
||||
|
||||
@@ -485,7 +488,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
|
||||
slots[slot].buffer_state = BufferState::Queued;
|
||||
slots[slot].frame_number = core->frame_counter;
|
||||
slots[slot].queue_time = timestamp;
|
||||
slots[slot].presentation_time = clock->GetTimeNS().count();
|
||||
slots[slot].presentation_time = clock.GetTimeNS().count();
|
||||
slots[slot].fence = fence;
|
||||
|
||||
item.slot = slot;
|
||||
|
||||
@@ -89,8 +89,7 @@ private:
|
||||
s32 next_callback_ticket{};
|
||||
s32 current_callback_ticket{};
|
||||
std::condition_variable_any callback_condition;
|
||||
std::unique_ptr<Common::WallClock> clock;
|
||||
|
||||
Common::WallClock clock;
|
||||
Service::Nvidia::NvCore::NvMap& nvmap;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "common/settings.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/k_page_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/result.h"
|
||||
@@ -124,8 +125,7 @@ json GetFullDataAuto(const std::string& timestamp, u64 title_id, Core::System& s
|
||||
}
|
||||
|
||||
template <bool read_value, typename DescriptorType>
|
||||
json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer,
|
||||
Core::Memory::Memory& memory) {
|
||||
json GetHLEBufferDescriptorData(const boost::container::static_vector<DescriptorType, IPC::MAX_BUFFER_DESCRIPTORS>& buffer, Core::Memory::Memory& memory) {
|
||||
auto buffer_out = json::array();
|
||||
for (const auto& desc : buffer) {
|
||||
auto entry = json{
|
||||
|
||||
@@ -224,7 +224,7 @@ void A64EmitX64::GenTerminalHandlers() {
|
||||
terminal_handler_fast_dispatch_hint = code.getCurr<const void*>();
|
||||
calculate_location_descriptor();
|
||||
code.L(rsb_cache_miss);
|
||||
code.mov(r8, reinterpret_cast<u64>(fast_dispatch_table.data()));
|
||||
code.mov(r8, u64(fast_dispatch_table.data()));
|
||||
//code.mov(r12, qword[code.ABI_JIT_PTR + offsetof(A64JitState, pc)]);
|
||||
code.mov(r12, rbx);
|
||||
if (code.HasHostFeature(HostFeature::SSE42)) {
|
||||
@@ -244,7 +244,7 @@ void A64EmitX64::GenTerminalHandlers() {
|
||||
|
||||
code.align();
|
||||
fast_dispatch_table_lookup = code.getCurr<FastDispatchEntry& (*)(u64)>();
|
||||
code.mov(code.ABI_PARAM2, reinterpret_cast<u64>(fast_dispatch_table.data()));
|
||||
code.mov(code.ABI_PARAM2, u64(fast_dispatch_table.data()));
|
||||
if (code.HasHostFeature(HostFeature::SSE42)) {
|
||||
code.crc32(code.ABI_PARAM1, code.ABI_PARAM2);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ struct FrameInfo {
|
||||
};
|
||||
static_assert(ABI_SHADOW_SPACE <= 32);
|
||||
|
||||
static FrameInfo CalculateFrameInfo(const size_t num_gprs, const size_t num_xmms, size_t frame_size) {
|
||||
static FrameInfo CalculateFrameInfo(const size_t num_gprs, const size_t num_xmms, size_t frame_size) noexcept {
|
||||
// We are initially 8 byte aligned because the return value is pushed onto an aligned stack after a call.
|
||||
const size_t rsp_alignment = (num_gprs % 2 == 0) ? 8 : 0;
|
||||
const size_t total_xmm_size = num_xmms * XMM_SIZE;
|
||||
@@ -40,7 +40,7 @@ static FrameInfo CalculateFrameInfo(const size_t num_gprs, const size_t num_xmms
|
||||
};
|
||||
}
|
||||
|
||||
void ABI_PushRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size, std::bitset<32> const& regs) {
|
||||
static void ABI_PushRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size, std::bitset<32> regs) noexcept {
|
||||
using namespace Xbyak::util;
|
||||
|
||||
const size_t num_gprs = (ABI_ALL_GPRS & regs).count();
|
||||
@@ -65,7 +65,7 @@ void ABI_PushRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size,
|
||||
}
|
||||
}
|
||||
|
||||
void ABI_PopRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size, std::bitset<32> const& regs) {
|
||||
static void ABI_PopRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size, std::bitset<32> regs) noexcept {
|
||||
using namespace Xbyak::util;
|
||||
|
||||
const size_t num_gprs = (ABI_ALL_GPRS & regs).count();
|
||||
@@ -107,13 +107,13 @@ void ABI_PopCallerSaveRegistersAndAdjustStack(BlockOfCode& code, const std::size
|
||||
|
||||
// Windows ABI registers are not in the same allocation algorithm as unix's
|
||||
void ABI_PushCallerSaveRegistersAndAdjustStackExcept(BlockOfCode& code, const HostLoc exception) {
|
||||
std::bitset<32> regs = ABI_ALL_CALLER_SAVE;
|
||||
auto regs = ABI_ALL_CALLER_SAVE;
|
||||
regs.reset(size_t(exception));
|
||||
ABI_PushRegistersAndAdjustStack(code, 0, regs);
|
||||
}
|
||||
|
||||
void ABI_PopCallerSaveRegistersAndAdjustStackExcept(BlockOfCode& code, const HostLoc exception) {
|
||||
std::bitset<32> regs = ABI_ALL_CALLER_SAVE;
|
||||
auto regs = ABI_ALL_CALLER_SAVE;
|
||||
regs.reset(size_t(exception));
|
||||
ABI_PopRegistersAndAdjustStack(code, 0, regs);
|
||||
}
|
||||
|
||||
@@ -6,23 +6,23 @@
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include "dynarmic/backend/x64/constant_pool.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/assert.h"
|
||||
|
||||
#include "dynarmic/backend/x64/block_of_code.h"
|
||||
#include "dynarmic/backend/x64/constant_pool.h"
|
||||
|
||||
namespace Dynarmic::Backend::X64 {
|
||||
|
||||
ConstantPool::ConstantPool(BlockOfCode& code, size_t size)
|
||||
: code(code), insertion_point(0) {
|
||||
: code(code)
|
||||
, insertion_point(0)
|
||||
{
|
||||
code.EnsureMemoryCommitted(align_size + size);
|
||||
code.int3();
|
||||
code.align(align_size);
|
||||
pool = std::span<ConstantT>(
|
||||
reinterpret_cast<ConstantT*>(code.AllocateFromCodeSpace(size)), size / align_size);
|
||||
pool = std::span<ConstantT>(reinterpret_cast<ConstantT*>(code.AllocateFromCodeSpace(size)), size / align_size);
|
||||
}
|
||||
|
||||
Xbyak::Address ConstantPool::GetConstant(const Xbyak::AddressFrame& frame, u64 lower, u64 upper) {
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
#include <xbyak/xbyak.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
// TODO: Defining this crashes e v e r y t h i n g
|
||||
// #define XBYAK_STD_UNORDERED_SET ankerl::unordered_dense::set
|
||||
// #define XBYAK_STD_UNORDERED_MAP ankerl::unordered_dense::map
|
||||
// #define XBYAK_STD_UNORDERED_MULTIMAP boost::unordered_multimap
|
||||
|
||||
// You must ensure this matches with src/common/x64/xbyak.h on root dir
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#define XBYAK_STD_UNORDERED_SET ankerl::unordered_dense::set
|
||||
#define XBYAK_STD_UNORDERED_MAP ankerl::unordered_dense::map
|
||||
#define XBYAK_STD_UNORDERED_MULTIMAP boost::unordered_multimap
|
||||
#include <xbyak/xbyak.h>
|
||||
#include <xbyak/xbyak_util.h>
|
||||
|
||||
@@ -78,7 +78,7 @@ u64 FPToFixed(size_t ibits, FPT op, size_t fbits, bool unsigned_, FPCR fpcr, Rou
|
||||
}
|
||||
|
||||
// Detect Overflow
|
||||
const int min_exponent_for_overflow = static_cast<int>(ibits) - static_cast<int>(mcl::bit::highest_set_bit(value.mantissa + (round_up ? Safe::LogicalShiftRight<u64>(1, exponent) : 0))) - (unsigned_ ? 0 : 1);
|
||||
const int min_exponent_for_overflow = int(ibits) - int(mcl::bit::highest_set_bit(value.mantissa + (round_up ? Safe::LogicalShiftRight<u64>(1, exponent) : 0))) - (unsigned_ ? 0 : 1);
|
||||
if (exponent >= min_exponent_for_overflow) {
|
||||
// Positive overflow
|
||||
if (unsigned_ || !sign) {
|
||||
@@ -87,10 +87,10 @@ u64 FPToFixed(size_t ibits, FPT op, size_t fbits, bool unsigned_, FPCR fpcr, Rou
|
||||
}
|
||||
|
||||
// Negative overflow
|
||||
const u64 min_value = Safe::Negate<u64>(static_cast<u64>(1) << (ibits - 1));
|
||||
const u64 min_value = Safe::Negate<u64>(u64(1) << (ibits - 1));
|
||||
if (!(exponent == min_exponent_for_overflow && int_result == min_value)) {
|
||||
FPProcessException(FPExc::InvalidOp, fpcr, fpsr);
|
||||
return static_cast<u64>(1) << (ibits - 1);
|
||||
return u64(1) << (ibits - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32
|
||||
Xbyak::Label start, loop;
|
||||
code.jmp(start, code.T_NEAR);
|
||||
code.L(loop);
|
||||
|
||||
if (waitpkg) {
|
||||
// TODO: this is because we lack regalloc - so better to be safe :(
|
||||
code.push(Xbyak::util::rax);
|
||||
@@ -33,7 +34,14 @@ void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32
|
||||
code.push(Xbyak::util::rdx);
|
||||
// TODO: This clobbers EAX and EDX did we tell the regalloc?
|
||||
// ARM ptr for address-monitoring
|
||||
code.umonitor(ptr);
|
||||
|
||||
// XBYAK BUG: code.umonitor(ptr); see issue #255
|
||||
// replace once xbyak has been fixed
|
||||
code.db(0xF3);
|
||||
if (ptr.getIdx() >= 8) code.db(0x41);
|
||||
code.db(0x0F); code.db(0xAE);
|
||||
code.db(uint8_t((3 << 6) | ((6 & 7) << 3) | (ptr.getIdx() & 7)));
|
||||
|
||||
// tmp.bit[0] = 0: C0.1 | Slow Wakup | Better Savings
|
||||
// tmp.bit[0] = 1: C0.2 | Fast Wakup | Lesser Savings
|
||||
// edx:eax is implicitly used as a 64-bit deadline timestamp
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2020 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
@@ -11,7 +14,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <xbyak/xbyak_util.h>
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
|
||||
TEST_CASE("Host CPU supports", "[a64]") {
|
||||
using Cpu = Xbyak::util::Cpu;
|
||||
|
||||
@@ -46,10 +46,10 @@ const std::string GetDataDirString(DataDir dir, const std::string &user_id)
|
||||
u64 ClearDir(DataDir dir, const std::string &user_id)
|
||||
{
|
||||
fs::path data_dir = GetDataDir(dir, user_id);
|
||||
u64 result = fs::remove_all(data_dir);
|
||||
|
||||
std::error_code ec;
|
||||
u64 result = fs::remove_all(data_dir, ec);
|
||||
// mkpath at the end just so it actually exists
|
||||
fs::create_directories(data_dir);
|
||||
fs::create_directories(data_dir, ec);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
@@ -108,10 +108,10 @@ void EmulatedController::ReloadFromSettings() {
|
||||
// Other or debug controller should always be a pro controller
|
||||
if (npad_id_type != NpadIdType::Other) {
|
||||
SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
|
||||
original_npad_type = npad_type;
|
||||
original_npad_type = npad_type.load();
|
||||
} else {
|
||||
SetNpadStyleIndex(NpadStyleIndex::Fullkey);
|
||||
original_npad_type = npad_type;
|
||||
original_npad_type = npad_type.load();
|
||||
}
|
||||
|
||||
// Disable special features before disconnecting
|
||||
@@ -577,10 +577,9 @@ void EmulatedController::UnloadInput() {
|
||||
}
|
||||
|
||||
void EmulatedController::EnableConfiguration() {
|
||||
std::unique_lock lock1{connect_mutex}, lock2{npad_mutex};
|
||||
is_configuring = true;
|
||||
tmp_is_connected = is_connected;
|
||||
tmp_npad_type = npad_type;
|
||||
is_configuring.store(true);
|
||||
tmp_is_connected.store(is_connected);
|
||||
tmp_npad_type.store(npad_type);
|
||||
}
|
||||
|
||||
void EmulatedController::DisableConfiguration() {
|
||||
@@ -600,7 +599,7 @@ void EmulatedController::DisableConfiguration() {
|
||||
Disconnect();
|
||||
}
|
||||
SetNpadStyleIndex(tmp_npad_type);
|
||||
original_npad_type = tmp_npad_type;
|
||||
original_npad_type.store(tmp_npad_type);
|
||||
}
|
||||
|
||||
// Apply temporary connected status to the real controller
|
||||
@@ -614,19 +613,16 @@ void EmulatedController::DisableConfiguration() {
|
||||
}
|
||||
|
||||
void EmulatedController::EnableSystemButtons() {
|
||||
std::unique_lock lock{mutex};
|
||||
system_buttons_enabled = true;
|
||||
}
|
||||
|
||||
void EmulatedController::DisableSystemButtons() {
|
||||
std::unique_lock lock{mutex};
|
||||
system_buttons_enabled = false;
|
||||
controller.home_button_state.raw = 0;
|
||||
controller.capture_button_state.raw = 0;
|
||||
}
|
||||
|
||||
void EmulatedController::ResetSystemButtons() {
|
||||
std::unique_lock lock{mutex};
|
||||
controller.home_button_state.home.Assign(false);
|
||||
controller.capture_button_state.capture.Assign(false);
|
||||
}
|
||||
@@ -763,8 +759,7 @@ void EmulatedController::StartMotionCalibration() {
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
|
||||
Common::UUID uuid) {
|
||||
void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, Common::UUID uuid) {
|
||||
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
|
||||
const auto& player = Settings::values.players.GetValue()[player_index];
|
||||
|
||||
@@ -772,7 +767,6 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_lock lock{mutex};
|
||||
bool value_changed = false;
|
||||
const auto new_status = TransformToButton(callback);
|
||||
auto& current_status = controller.button_values[index];
|
||||
@@ -818,7 +812,6 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
|
||||
controller.debug_pad_button_state.raw = 0;
|
||||
controller.home_button_state.raw = 0;
|
||||
controller.capture_button_state.raw = 0;
|
||||
lock.unlock();
|
||||
TriggerOnChange(ControllerTriggerType::Button, false);
|
||||
return;
|
||||
}
|
||||
@@ -922,8 +915,6 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
|
||||
break;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
|
||||
if (!is_connected) {
|
||||
if (npad_type == NpadStyleIndex::Handheld) {
|
||||
if (npad_id_type == NpadIdType::Handheld) {
|
||||
@@ -952,7 +943,6 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
|
||||
auto trigger_guard = SCOPE_GUARD {
|
||||
TriggerOnChange(ControllerTriggerType::Stick, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock{mutex};
|
||||
const auto stick_value = TransformToStick(callback);
|
||||
|
||||
// Only read stick values that have the same uuid or are over the threshold to avoid flapping
|
||||
@@ -1009,7 +999,6 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
|
||||
auto trigger_guard = SCOPE_GUARD {
|
||||
TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock{mutex};
|
||||
const auto trigger_value = TransformToTrigger(callback);
|
||||
|
||||
// Only read trigger values that have the same uuid or are pressed once
|
||||
@@ -1057,7 +1046,6 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
|
||||
SCOPE_EXIT {
|
||||
TriggerOnChange(ControllerTriggerType::Motion, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock{mutex};
|
||||
auto& raw_status = controller.motion_values[index].raw_status;
|
||||
auto& emulated = controller.motion_values[index].emulated;
|
||||
|
||||
@@ -1093,7 +1081,6 @@ void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback
|
||||
auto trigger_guard = SCOPE_GUARD {
|
||||
TriggerOnChange(ControllerTriggerType::Color, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock{mutex};
|
||||
controller.color_values[index] = TransformToColor(callback);
|
||||
|
||||
if (is_configuring) {
|
||||
@@ -1136,15 +1123,13 @@ void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback,
|
||||
std::size_t index) {
|
||||
void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index) {
|
||||
if (index >= controller.battery_values.size()) {
|
||||
return;
|
||||
}
|
||||
SCOPE_EXIT {
|
||||
TriggerOnChange(ControllerTriggerType::Battery, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock{mutex};
|
||||
controller.battery_values[index] = TransformToBattery(callback);
|
||||
|
||||
if (is_configuring) {
|
||||
@@ -1209,47 +1194,33 @@ void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback
|
||||
SCOPE_EXIT {
|
||||
TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock{mutex};
|
||||
controller.camera_values = TransformToCamera(callback);
|
||||
|
||||
if (is_configuring) {
|
||||
return;
|
||||
if (!is_configuring) {
|
||||
controller.camera_state.sample++;
|
||||
controller.camera_state.format = Core::IrSensor::ImageTransferProcessorFormat(controller.camera_values.format);
|
||||
controller.camera_state.data = controller.camera_values.data;
|
||||
}
|
||||
|
||||
controller.camera_state.sample++;
|
||||
controller.camera_state.format =
|
||||
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format);
|
||||
controller.camera_state.data = controller.camera_values.data;
|
||||
}
|
||||
|
||||
void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
|
||||
SCOPE_EXIT {
|
||||
TriggerOnChange(ControllerTriggerType::RingController, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock{mutex};
|
||||
const auto force_value = TransformToStick(callback);
|
||||
|
||||
controller.ring_analog_value = force_value.x;
|
||||
|
||||
if (is_configuring) {
|
||||
return;
|
||||
if (!is_configuring) {
|
||||
controller.ring_analog_state.force = force_value.x.value;
|
||||
}
|
||||
|
||||
controller.ring_analog_state.force = force_value.x.value;
|
||||
}
|
||||
|
||||
void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
|
||||
SCOPE_EXIT {
|
||||
TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock{mutex};
|
||||
controller.nfc_values = TransformToNfc(callback);
|
||||
|
||||
if (is_configuring) {
|
||||
return;
|
||||
if (!is_configuring) {
|
||||
controller.nfc_state = controller.nfc_values;
|
||||
}
|
||||
|
||||
controller.nfc_state = controller.nfc_values;
|
||||
}
|
||||
|
||||
bool EmulatedController::SetVibration(bool should_vibrate) {
|
||||
@@ -1258,7 +1229,6 @@ bool EmulatedController::SetVibration(bool should_vibrate) {
|
||||
vibration_value.high_amplitude = 1.0f;
|
||||
vibration_value.low_amplitude = 1.0f;
|
||||
}
|
||||
|
||||
return SetVibration(DeviceIndex::Left, vibration_value);
|
||||
}
|
||||
|
||||
@@ -1268,7 +1238,6 @@ bool EmulatedController::SetVibration(u32 slot, Core::HID::VibrationGcErmCommand
|
||||
vibration_value.high_amplitude = 1.0f;
|
||||
vibration_value.low_amplitude = 1.0f;
|
||||
}
|
||||
|
||||
return SetVibration(DeviceIndex::Left, vibration_value);
|
||||
}
|
||||
|
||||
@@ -1632,7 +1601,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
|
||||
// Attempt to reconnect with the original type
|
||||
if (npad_type != original_npad_type) {
|
||||
Disconnect();
|
||||
const auto current_npad_type = npad_type;
|
||||
const auto current_npad_type = npad_type.load();
|
||||
SetNpadStyleIndex(original_npad_type);
|
||||
if (IsControllerSupported()) {
|
||||
Connect();
|
||||
@@ -1650,7 +1619,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
|
||||
|
||||
// Fallback Fullkey controllers to Pro controllers
|
||||
if (IsControllerFullkey() && supported_style_tag.fullkey) {
|
||||
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
|
||||
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type.load());
|
||||
SetNpadStyleIndex(NpadStyleIndex::Fullkey);
|
||||
Connect();
|
||||
return;
|
||||
@@ -1658,7 +1627,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
|
||||
|
||||
// Fallback Dual joycon controllers to Pro controllers
|
||||
if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) {
|
||||
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
|
||||
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type.load());
|
||||
SetNpadStyleIndex(NpadStyleIndex::Fullkey);
|
||||
Connect();
|
||||
return;
|
||||
@@ -1666,19 +1635,16 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
|
||||
|
||||
// Fallback Pro controllers to Dual joycon
|
||||
if (npad_type == NpadStyleIndex::Fullkey && supported_style_tag.joycon_dual) {
|
||||
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type);
|
||||
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type.load());
|
||||
SetNpadStyleIndex(NpadStyleIndex::JoyconDual);
|
||||
Connect();
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
|
||||
npad_type);
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller", npad_type.load());
|
||||
}
|
||||
|
||||
bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
|
||||
std::unique_lock lock{mutex};
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
const auto type = is_configuring.load() && use_temporary_value ? tmp_npad_type.load() : npad_type.load();
|
||||
switch (type) {
|
||||
case NpadStyleIndex::Fullkey:
|
||||
case NpadStyleIndex::GameCube:
|
||||
@@ -1693,39 +1659,26 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
|
||||
}
|
||||
|
||||
bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
|
||||
std::unique_lock lock{mutex};
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
const auto type = is_configuring.load() && use_temporary_value ? tmp_npad_type.load() : npad_type.load();
|
||||
switch (type) {
|
||||
case NpadStyleIndex::Fullkey:
|
||||
return supported_style_tag.fullkey.As<bool>();
|
||||
case NpadStyleIndex::Handheld:
|
||||
return supported_style_tag.handheld.As<bool>();
|
||||
case NpadStyleIndex::JoyconDual:
|
||||
return supported_style_tag.joycon_dual.As<bool>();
|
||||
case NpadStyleIndex::JoyconLeft:
|
||||
return supported_style_tag.joycon_left.As<bool>();
|
||||
case NpadStyleIndex::JoyconRight:
|
||||
return supported_style_tag.joycon_right.As<bool>();
|
||||
case NpadStyleIndex::GameCube:
|
||||
return supported_style_tag.gamecube.As<bool>();
|
||||
case NpadStyleIndex::Pokeball:
|
||||
return supported_style_tag.palma.As<bool>();
|
||||
case NpadStyleIndex::NES:
|
||||
return supported_style_tag.lark.As<bool>();
|
||||
case NpadStyleIndex::SNES:
|
||||
return supported_style_tag.lucia.As<bool>();
|
||||
case NpadStyleIndex::N64:
|
||||
return supported_style_tag.lagoon.As<bool>();
|
||||
case NpadStyleIndex::SegaGenesis:
|
||||
return supported_style_tag.lager.As<bool>();
|
||||
default:
|
||||
return false;
|
||||
case NpadStyleIndex::Fullkey: return supported_style_tag.fullkey.As<bool>();
|
||||
case NpadStyleIndex::Handheld: return supported_style_tag.handheld.As<bool>();
|
||||
case NpadStyleIndex::JoyconDual: return supported_style_tag.joycon_dual.As<bool>();
|
||||
case NpadStyleIndex::JoyconLeft: return supported_style_tag.joycon_left.As<bool>();
|
||||
case NpadStyleIndex::JoyconRight: return supported_style_tag.joycon_right.As<bool>();
|
||||
case NpadStyleIndex::GameCube: return supported_style_tag.gamecube.As<bool>();
|
||||
case NpadStyleIndex::Pokeball: return supported_style_tag.palma.As<bool>();
|
||||
case NpadStyleIndex::NES: return supported_style_tag.lark.As<bool>();
|
||||
case NpadStyleIndex::SNES: return supported_style_tag.lucia.As<bool>();
|
||||
case NpadStyleIndex::N64: return supported_style_tag.lagoon.As<bool>();
|
||||
case NpadStyleIndex::SegaGenesis: return supported_style_tag.lager.As<bool>();
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedController::Connect(bool use_temporary_value) {
|
||||
if (!IsControllerSupported(use_temporary_value)) {
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
const auto type = is_configuring.load() && use_temporary_value ? tmp_npad_type.load() : npad_type.load();
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported", type);
|
||||
return;
|
||||
}
|
||||
@@ -1733,12 +1686,10 @@ void EmulatedController::Connect(bool use_temporary_value) {
|
||||
auto trigger_guard = SCOPE_GUARD {
|
||||
TriggerOnChange(ControllerTriggerType::Connected, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock1{connect_mutex}, lock2{mutex};
|
||||
if (is_configuring) {
|
||||
tmp_is_connected = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_connected) {
|
||||
trigger_guard.Cancel();
|
||||
return;
|
||||
@@ -1750,12 +1701,10 @@ void EmulatedController::Disconnect() {
|
||||
auto trigger_guard = SCOPE_GUARD {
|
||||
TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock1{connect_mutex}, lock2{mutex};
|
||||
if (is_configuring) {
|
||||
tmp_is_connected = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_connected) {
|
||||
trigger_guard.Cancel();
|
||||
return;
|
||||
@@ -1764,19 +1713,16 @@ void EmulatedController::Disconnect() {
|
||||
}
|
||||
|
||||
bool EmulatedController::IsConnected(bool get_temporary_value) const {
|
||||
std::shared_lock lock{connect_mutex};
|
||||
if (get_temporary_value && is_configuring)
|
||||
return tmp_is_connected;
|
||||
return is_connected;
|
||||
}
|
||||
|
||||
NpadIdType EmulatedController::GetNpadIdType() const {
|
||||
std::shared_lock lock{mutex};
|
||||
return npad_id_type;
|
||||
}
|
||||
|
||||
NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
|
||||
std::shared_lock lock{npad_mutex};
|
||||
if (get_temporary_value && is_configuring)
|
||||
return tmp_npad_type;
|
||||
return npad_type;
|
||||
@@ -1786,8 +1732,6 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
|
||||
auto trigger_guard = SCOPE_GUARD {
|
||||
TriggerOnChange(ControllerTriggerType::Type, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock1{mutex}, lock2{npad_mutex};
|
||||
|
||||
if (is_configuring) {
|
||||
if (tmp_npad_type == npad_type_) {
|
||||
trigger_guard.Cancel();
|
||||
@@ -1796,14 +1740,12 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
|
||||
tmp_npad_type = npad_type_;
|
||||
return;
|
||||
}
|
||||
|
||||
if (npad_type == npad_type_) {
|
||||
trigger_guard.Cancel();
|
||||
return;
|
||||
}
|
||||
if (is_connected) {
|
||||
LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
|
||||
Service::HID::NpadIdTypeToIndex(npad_id_type));
|
||||
LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", Service::HID::NpadIdTypeToIndex(npad_id_type));
|
||||
}
|
||||
npad_type = npad_type_;
|
||||
}
|
||||
@@ -1832,37 +1774,30 @@ LedPattern EmulatedController::GetLedPattern() const {
|
||||
}
|
||||
|
||||
ButtonValues EmulatedController::GetButtonsValues() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.button_values;
|
||||
}
|
||||
|
||||
SticksValues EmulatedController::GetSticksValues() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.stick_values;
|
||||
}
|
||||
|
||||
TriggerValues EmulatedController::GetTriggersValues() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.trigger_values;
|
||||
}
|
||||
|
||||
ControllerMotionValues EmulatedController::GetMotionValues() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.motion_values;
|
||||
}
|
||||
|
||||
ColorValues EmulatedController::GetColorsValues() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.color_values;
|
||||
}
|
||||
|
||||
BatteryValues EmulatedController::GetBatteryValues() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.battery_values;
|
||||
}
|
||||
|
||||
CameraValues EmulatedController::GetCameraValues() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.camera_values;
|
||||
}
|
||||
|
||||
@@ -1871,72 +1806,54 @@ RingAnalogValue EmulatedController::GetRingSensorValues() const {
|
||||
}
|
||||
|
||||
HomeButtonState EmulatedController::GetHomeButtons() const {
|
||||
std::unique_lock lock{mutex};
|
||||
if (is_configuring) {
|
||||
if (is_configuring)
|
||||
return {};
|
||||
}
|
||||
return controller.home_button_state;
|
||||
}
|
||||
|
||||
CaptureButtonState EmulatedController::GetCaptureButtons() const {
|
||||
std::unique_lock lock{mutex};
|
||||
if (is_configuring) {
|
||||
if (is_configuring)
|
||||
return {};
|
||||
}
|
||||
return controller.capture_button_state;
|
||||
}
|
||||
|
||||
NpadButtonState EmulatedController::GetNpadButtons() const {
|
||||
std::unique_lock lock{mutex};
|
||||
if (is_configuring) {
|
||||
if (is_configuring)
|
||||
return {};
|
||||
}
|
||||
return {controller.npad_button_state.raw & GetTurboButtonMask()};
|
||||
}
|
||||
|
||||
DebugPadButton EmulatedController::GetDebugPadButtons() const {
|
||||
std::unique_lock lock{mutex};
|
||||
if (is_configuring) {
|
||||
if (is_configuring)
|
||||
return {};
|
||||
}
|
||||
return controller.debug_pad_button_state;
|
||||
}
|
||||
|
||||
AnalogSticks EmulatedController::GetSticks() const {
|
||||
std::unique_lock lock{mutex};
|
||||
|
||||
if (is_configuring) {
|
||||
if (is_configuring)
|
||||
return {};
|
||||
}
|
||||
|
||||
return controller.analog_stick_state;
|
||||
}
|
||||
|
||||
NpadGcTriggerState EmulatedController::GetTriggers() const {
|
||||
std::unique_lock lock{mutex};
|
||||
if (is_configuring) {
|
||||
if (is_configuring)
|
||||
return {};
|
||||
}
|
||||
return controller.gc_trigger_state;
|
||||
}
|
||||
|
||||
MotionState EmulatedController::GetMotions() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.motion_state;
|
||||
}
|
||||
|
||||
ControllerColors EmulatedController::GetColors() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.colors_state;
|
||||
}
|
||||
|
||||
BatteryLevelState EmulatedController::GetBattery() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.battery_state;
|
||||
}
|
||||
|
||||
const CameraState& EmulatedController::GetCamera() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.camera_state;
|
||||
}
|
||||
|
||||
@@ -1945,15 +1862,14 @@ RingSensorForce EmulatedController::GetRingSensorForce() const {
|
||||
}
|
||||
|
||||
const NfcState& EmulatedController::GetNfc() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.nfc_state;
|
||||
}
|
||||
|
||||
NpadColor EmulatedController::GetNpadColor(u32 color) {
|
||||
return {
|
||||
.r = static_cast<u8>((color >> 16) & 0xFF),
|
||||
.g = static_cast<u8>((color >> 8) & 0xFF),
|
||||
.b = static_cast<u8>(color & 0xFF),
|
||||
.r = u8((color >> 16) & 0xFF),
|
||||
.g = u8((color >> 8) & 0xFF),
|
||||
.b = u8(color & 0xFF),
|
||||
.a = 0xff,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,8 +11,10 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/input.h"
|
||||
@@ -576,13 +578,7 @@ private:
|
||||
NpadButton GetTurboButtonMask() const;
|
||||
|
||||
const NpadIdType npad_id_type;
|
||||
NpadStyleIndex npad_type{NpadStyleIndex::None};
|
||||
NpadStyleIndex original_npad_type{NpadStyleIndex::None};
|
||||
NpadStyleTag supported_style_tag{NpadStyleSet::All};
|
||||
bool is_connected{false};
|
||||
bool is_configuring{false};
|
||||
bool is_initialized{false};
|
||||
bool system_buttons_enabled{true};
|
||||
f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
|
||||
u32 turbo_button_state{0};
|
||||
std::size_t nfc_handles{0};
|
||||
@@ -591,9 +587,16 @@ private:
|
||||
std::array<std::chrono::steady_clock::time_point, 2> last_vibration_timepoint{};
|
||||
std::array<bool, HIDCore::available_controllers> controller_connected{};
|
||||
|
||||
// Atomically synched values
|
||||
std::atomic<HID::NpadStyleIndex> npad_type{HID::NpadStyleIndex::None};
|
||||
std::atomic<HID::NpadStyleIndex> original_npad_type{HID::NpadStyleIndex::None};
|
||||
// Temporary values to avoid doing changes while the controller is in configuring mode
|
||||
NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
|
||||
bool tmp_is_connected{false};
|
||||
std::atomic<HID::NpadStyleIndex> tmp_npad_type{HID::NpadStyleIndex::None};
|
||||
std::atomic<bool> tmp_is_connected{false};
|
||||
std::atomic<bool> is_connected{false};
|
||||
std::atomic<bool> is_configuring{false};
|
||||
std::atomic<bool> is_initialized{false};
|
||||
std::atomic<bool> system_buttons_enabled{true};
|
||||
|
||||
ButtonParams button_params;
|
||||
StickParams stick_params;
|
||||
@@ -632,10 +635,7 @@ private:
|
||||
StickDevices virtual_stick_devices;
|
||||
ControllerMotionDevices virtual_motion_devices;
|
||||
|
||||
mutable std::shared_mutex mutex;
|
||||
mutable std::shared_mutex callback_mutex;
|
||||
mutable std::shared_mutex npad_mutex;
|
||||
mutable std::shared_mutex connect_mutex;
|
||||
mutable std::mutex callback_mutex;
|
||||
ankerl::unordered_dense::map<int, ControllerUpdateCallback> callback_list;
|
||||
int last_callback_key = 0;
|
||||
|
||||
|
||||
@@ -181,15 +181,13 @@ void InstallFirmware(const QString& location, bool recursive) {
|
||||
|
||||
QString UnzipFirmwareToTmp(const QString& location) {
|
||||
namespace fs = std::filesystem;
|
||||
fs::path tmp{fs::temp_directory_path()};
|
||||
|
||||
if (!fs::create_directories(tmp / "eden" / "firmware")) {
|
||||
fs::path tmp{fs::temp_directory_path() / "eden" / "firmware"};
|
||||
std::error_code ec;
|
||||
fs::remove_all(tmp, ec);
|
||||
if (!fs::create_directories(tmp, ec)) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
tmp /= "eden";
|
||||
tmp /= "firmware";
|
||||
|
||||
QString qCacheDir = QString::fromStdString(tmp.string());
|
||||
|
||||
QFile zip(location);
|
||||
|
||||
@@ -83,8 +83,9 @@ QStringList GetModFolders(const QString& root, const QString& fallbackName) {
|
||||
// now make a temp directory...
|
||||
const auto mod_dir = fs::temp_directory_path() / "eden" / "mod" / name.toStdString();
|
||||
const auto tmp = mod_dir / to_make;
|
||||
fs::remove_all(mod_dir);
|
||||
if (!fs::create_directories(tmp)) {
|
||||
std::error_code ec;
|
||||
fs::remove_all(mod_dir, ec);
|
||||
if (!fs::create_directories(tmp, ec)) {
|
||||
LOG_ERROR(Frontend, "Failed to create temporary directory {}", tmp.string());
|
||||
return {};
|
||||
}
|
||||
@@ -116,9 +117,9 @@ QStringList GetModFolders(const QString& root, const QString& fallbackName) {
|
||||
const QString ExtractMod(const QString& path) {
|
||||
namespace fs = std::filesystem;
|
||||
fs::path tmp{fs::temp_directory_path() / "eden" / "unzip_mod"};
|
||||
|
||||
fs::remove_all(tmp);
|
||||
if (!fs::create_directories(tmp)) {
|
||||
std::error_code ec;
|
||||
fs::remove_all(tmp, ec);
|
||||
if (!fs::create_directories(tmp, ec)) {
|
||||
QtCommon::Frontend::Critical(tr("Mod Extract Failed"),
|
||||
tr("Failed to create temporary directory %1")
|
||||
.arg(QString::fromStdString(tmp.string())));
|
||||
|
||||
@@ -221,12 +221,17 @@ private:
|
||||
Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR::Value& index) {
|
||||
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
|
||||
if (def.count > 1) {
|
||||
const DescriptorIndex idx{ctx, index};
|
||||
const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, idx.Value())};
|
||||
idx.Decorate(ctx, pointer);
|
||||
const Id object{ctx.OpLoad(def.sampled_type, pointer)};
|
||||
idx.Decorate(ctx, object);
|
||||
return object;
|
||||
if (Settings::values.legacy_descriptor_indices.GetValue()) {
|
||||
const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, ctx.Def(index))};
|
||||
return ctx.OpLoad(def.sampled_type, pointer);
|
||||
} else {
|
||||
const DescriptorIndex idx{ctx, index};
|
||||
const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, idx.Value())};
|
||||
idx.Decorate(ctx, pointer);
|
||||
const Id object{ctx.OpLoad(def.sampled_type, pointer)};
|
||||
idx.Decorate(ctx, object);
|
||||
return object;
|
||||
}
|
||||
} else {
|
||||
return ctx.OpLoad(def.sampled_type, def.id);
|
||||
}
|
||||
@@ -244,14 +249,20 @@ Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& ind
|
||||
} else {
|
||||
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
|
||||
if (def.count > 1) {
|
||||
const DescriptorIndex idx{ctx, index};
|
||||
const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx.Value())};
|
||||
idx.Decorate(ctx, ptr);
|
||||
const Id object{ctx.OpLoad(def.sampled_type, ptr)};
|
||||
idx.Decorate(ctx, object);
|
||||
const Id image{ctx.OpImage(def.image_type, object)};
|
||||
idx.Decorate(ctx, image);
|
||||
return image;
|
||||
if (Settings::values.legacy_descriptor_indices.GetValue()) {
|
||||
const Id idx{index.IsImmediate() ? ctx.Const(index.U32()) : ctx.Def(index)};
|
||||
const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx)};
|
||||
return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, ptr));
|
||||
} else {
|
||||
const DescriptorIndex idx{ctx, index};
|
||||
const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx.Value())};
|
||||
idx.Decorate(ctx, ptr);
|
||||
const Id object{ctx.OpLoad(def.sampled_type, ptr)};
|
||||
idx.Decorate(ctx, object);
|
||||
const Id image{ctx.OpImage(def.image_type, object)};
|
||||
idx.Decorate(ctx, image);
|
||||
return image;
|
||||
}
|
||||
}
|
||||
return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, def.id));
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <limits>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "shader_recompiler/environment.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/breadth_first_search.h"
|
||||
@@ -461,7 +462,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
|
||||
.secondary_offset = 0,
|
||||
.secondary_shift_left = 0,
|
||||
.dynamic_offset = dynamic_offset,
|
||||
.count = DynamicDescriptorCount(base_offset, size_shift),
|
||||
.count = Settings::values.legacy_descriptor_indices.GetValue() ? 8 : DynamicDescriptorCount(base_offset, size_shift),
|
||||
.has_secondary = false,
|
||||
};
|
||||
}
|
||||
@@ -733,9 +734,10 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||
break;
|
||||
}
|
||||
u32 index;
|
||||
const u32 size_shift{cbuf.count > 1 ? DynamicDescriptorSizeShift(cbuf.dynamic_offset)
|
||||
: DESCRIPTOR_SIZE_SHIFT};
|
||||
u32 count{cbuf.count};
|
||||
u32 size_shift = cbuf.count > 1 ? DynamicDescriptorSizeShift(cbuf.dynamic_offset) : DESCRIPTOR_SIZE_SHIFT;
|
||||
if (Settings::values.legacy_descriptor_indices.GetValue())
|
||||
size_shift = DESCRIPTOR_SIZE_SHIFT;
|
||||
u32 count = cbuf.count;
|
||||
switch (inst->GetOpcode()) {
|
||||
case IR::Opcode::ImageRead:
|
||||
case IR::Opcode::ImageAtomicIAdd32:
|
||||
@@ -821,8 +823,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||
const auto insert_point{IR::Block::InstructionList::s_iterator_to(*inst)};
|
||||
IR::IREmitter ir{*texture_inst.block, insert_point};
|
||||
const IR::U32 shift{ir.Imm32(size_shift)};
|
||||
inst->SetArg(0, ir.UMin(ir.ShiftRightLogical(cbuf.dynamic_offset, shift),
|
||||
ir.Imm32(count - 1)));
|
||||
inst->SetArg(0, ir.UMin(ir.ShiftRightLogical(cbuf.dynamic_offset, shift), ir.Imm32(count - 1)));
|
||||
} else {
|
||||
inst->SetArg(0, IR::Value{});
|
||||
}
|
||||
|
||||
@@ -11,17 +11,9 @@
|
||||
|
||||
#include <fstream>
|
||||
#include <variant>
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
// xbyak hates human beings
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-Wconversion"
|
||||
#pragma clang diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
#include <xbyak/xbyak.h>
|
||||
#include "common/x64/xbyak.h"
|
||||
#endif
|
||||
|
||||
#include "common/assert.h"
|
||||
@@ -39,10 +31,6 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_field.h"
|
||||
#include "common/logging.h"
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "common/x64/xbyak_abi.h"
|
||||
#include "common/x64/xbyak_util.h"
|
||||
#endif
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
@@ -54,7 +54,7 @@ using VideoCommon::LoadPipelines;
|
||||
using VideoCommon::SerializePipeline;
|
||||
using Context = ShaderContext::Context;
|
||||
|
||||
constexpr u32 CACHE_VERSION = 14;
|
||||
constexpr u32 CACHE_VERSION = 15;
|
||||
|
||||
template <typename Container>
|
||||
auto MakeSpan(Container& container) {
|
||||
|
||||
@@ -217,6 +217,12 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ASTC_6x5_UNORM_BLOCK, 0, ASTC_2D_6X5_UNORM) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ASTC_6x5_SRGB_BLOCK, 0, ASTC_2D_6X5_SRGB) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, 0, E5B9G9R9_FLOAT) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, 0, ETC2_RGB_UNORM) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, 0, ETC2_RGBA_UNORM) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, 0, ETC2_RGB_PTA_UNORM) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, 0, ETC2_RGB_SRGB) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, 0, ETC2_RGBA_SRGB) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, 0, ETC2_RGB_PTA_SRGB) \
|
||||
/* Depth formats */ \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_D32_SFLOAT, usage_attachable, D32_FLOAT) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_D16_UNORM, usage_attachable, D16_UNORM) \
|
||||
@@ -253,8 +259,8 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Transcode on hardware that doesn't support BCn natively
|
||||
if (!device.IsOptimalBcnSupported() && VideoCore::Surface::IsPixelFormatBCn(pixel_format)) {
|
||||
// Transcode on hardware that doesn't support BCn natively
|
||||
if (pixel_format == PixelFormat::BC4_SNORM) {
|
||||
tuple.format = VK_FORMAT_R8_SNORM;
|
||||
} else if (pixel_format == PixelFormat::BC4_UNORM) {
|
||||
@@ -270,6 +276,9 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with
|
||||
} else {
|
||||
tuple.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
}
|
||||
} else if (!device.IsOptimalEtc2Supported() && VideoCore::Surface::IsPixelFormatETC2(pixel_format)) {
|
||||
// Transcode on hardware that doesn't support ETC2 natively
|
||||
tuple.format = is_srgb ? VK_FORMAT_A8B8G8R8_SRGB_PACK32 : VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
}
|
||||
bool const attachable = (tuple.usage & usage_attachable) != 0;
|
||||
bool const storage = (tuple.usage & usage_storage) != 0;
|
||||
|
||||
@@ -62,7 +62,7 @@ using VideoCommon::FileEnvironment;
|
||||
using VideoCommon::GenericEnvironment;
|
||||
using VideoCommon::GraphicsEnvironment;
|
||||
|
||||
constexpr u32 CACHE_VERSION = 16;
|
||||
constexpr u32 CACHE_VERSION = 17;
|
||||
constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
|
||||
|
||||
template <typename Container>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
@@ -336,6 +336,20 @@ bool IsPixelFormatBCn(PixelFormat format) {
|
||||
}
|
||||
}
|
||||
|
||||
bool IsPixelFormatETC2(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::ETC2_RGB_UNORM:
|
||||
case PixelFormat::ETC2_RGBA_UNORM:
|
||||
case PixelFormat::ETC2_RGB_PTA_UNORM:
|
||||
case PixelFormat::ETC2_RGB_SRGB:
|
||||
case PixelFormat::ETC2_RGBA_SRGB:
|
||||
case PixelFormat::ETC2_RGB_PTA_SRGB:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsPixelFormatSRGB(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::A8B8G8R8_SRGB:
|
||||
@@ -344,6 +358,9 @@ bool IsPixelFormatSRGB(PixelFormat format) {
|
||||
case PixelFormat::BC2_SRGB:
|
||||
case PixelFormat::BC3_SRGB:
|
||||
case PixelFormat::BC7_SRGB:
|
||||
case PixelFormat::ETC2_RGB_SRGB:
|
||||
case PixelFormat::ETC2_RGBA_SRGB:
|
||||
case PixelFormat::ETC2_RGB_PTA_SRGB:
|
||||
case PixelFormat::ASTC_2D_4X4_SRGB:
|
||||
case PixelFormat::ASTC_2D_8X8_SRGB:
|
||||
case PixelFormat::ASTC_2D_8X5_SRGB:
|
||||
|
||||
@@ -111,6 +111,12 @@ namespace VideoCore::Surface {
|
||||
PIXEL_FORMAT_ELEM(ASTC_2D_6X5_UNORM, 6, 5, 128) \
|
||||
PIXEL_FORMAT_ELEM(ASTC_2D_6X5_SRGB, 6, 5, 128) \
|
||||
PIXEL_FORMAT_ELEM(E5B9G9R9_FLOAT, 1, 1, 32) \
|
||||
PIXEL_FORMAT_ELEM(ETC2_RGB_UNORM, 4, 4, 64) \
|
||||
PIXEL_FORMAT_ELEM(ETC2_RGBA_UNORM, 4, 4, 128) \
|
||||
PIXEL_FORMAT_ELEM(ETC2_RGB_PTA_UNORM, 4, 4, 64) \
|
||||
PIXEL_FORMAT_ELEM(ETC2_RGB_SRGB, 4, 4, 64) \
|
||||
PIXEL_FORMAT_ELEM(ETC2_RGBA_SRGB, 4, 4, 128) \
|
||||
PIXEL_FORMAT_ELEM(ETC2_RGB_PTA_SRGB, 4, 4, 64) \
|
||||
/* Depth formats */ \
|
||||
PIXEL_FORMAT_ELEM(D32_FLOAT, 1, 1, 32) \
|
||||
PIXEL_FORMAT_ELEM(D16_UNORM, 1, 1, 16) \
|
||||
@@ -181,8 +187,6 @@ constexpr u32 BitsPerBlock(PixelFormat format) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
#undef PIXEL_FORMAT_LIST
|
||||
|
||||
/// Returns the sizer in bytes of the specified pixel format
|
||||
constexpr u32 BytesPerBlock(PixelFormat pixel_format) {
|
||||
return BitsPerBlock(pixel_format) / CHAR_BIT;
|
||||
@@ -198,6 +202,7 @@ SurfaceType GetFormatType(PixelFormat pixel_format);
|
||||
bool HasAlpha(PixelFormat pixel_format);
|
||||
bool IsPixelFormatASTC(PixelFormat format);
|
||||
bool IsPixelFormatBCn(PixelFormat format);
|
||||
bool IsPixelFormatETC2(PixelFormat format);
|
||||
bool IsPixelFormatSRGB(PixelFormat format);
|
||||
bool IsPixelFormatInteger(PixelFormat format);
|
||||
bool IsPixelFormatSignedInteger(PixelFormat format);
|
||||
|
||||
@@ -191,6 +191,20 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
|
||||
return PixelFormat::BC6H_SFLOAT;
|
||||
case Hash(TextureFormat::BC6H_U16, FLOAT):
|
||||
return PixelFormat::BC6H_UFLOAT;
|
||||
/* ETC2 */
|
||||
case Hash(TextureFormat::ETC2_RGB, UNORM, LINEAR):
|
||||
return PixelFormat::ETC2_RGB_UNORM;
|
||||
case Hash(TextureFormat::ETC2_RGB_PTA, UNORM, LINEAR):
|
||||
return PixelFormat::ETC2_RGB_PTA_UNORM;
|
||||
case Hash(TextureFormat::ETC2_RGBA, UNORM, LINEAR):
|
||||
return PixelFormat::ETC2_RGBA_UNORM;
|
||||
case Hash(TextureFormat::ETC2_RGB, UNORM, SRGB):
|
||||
return PixelFormat::ETC2_RGB_SRGB;
|
||||
case Hash(TextureFormat::ETC2_RGB_PTA, UNORM, SRGB):
|
||||
return PixelFormat::ETC2_RGB_PTA_SRGB;
|
||||
case Hash(TextureFormat::ETC2_RGBA, UNORM, SRGB):
|
||||
return PixelFormat::ETC2_RGBA_SRGB;
|
||||
/* ASTC */
|
||||
case Hash(TextureFormat::ASTC_2D_4X4, UNORM, LINEAR):
|
||||
return PixelFormat::ASTC_2D_4X4_UNORM;
|
||||
case Hash(TextureFormat::ASTC_2D_4X4, UNORM, SRGB):
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -17,210 +20,9 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
|
||||
using VideoCore::Surface::PixelFormat;
|
||||
const string_view name = [format] {
|
||||
switch (format) {
|
||||
case PixelFormat::A8B8G8R8_UNORM:
|
||||
return "A8B8G8R8_UNORM";
|
||||
case PixelFormat::A8B8G8R8_SNORM:
|
||||
return "A8B8G8R8_SNORM";
|
||||
case PixelFormat::A8B8G8R8_SINT:
|
||||
return "A8B8G8R8_SINT";
|
||||
case PixelFormat::A8B8G8R8_UINT:
|
||||
return "A8B8G8R8_UINT";
|
||||
case PixelFormat::R5G6B5_UNORM:
|
||||
return "R5G6B5_UNORM";
|
||||
case PixelFormat::B5G6R5_UNORM:
|
||||
return "B5G6R5_UNORM";
|
||||
case PixelFormat::A1R5G5B5_UNORM:
|
||||
return "A1R5G5B5_UNORM";
|
||||
case PixelFormat::A2B10G10R10_UNORM:
|
||||
return "A2B10G10R10_UNORM";
|
||||
case PixelFormat::A2B10G10R10_UINT:
|
||||
return "A2B10G10R10_UINT";
|
||||
case PixelFormat::A2R10G10B10_UNORM:
|
||||
return "A2R10G10B10_UNORM";
|
||||
case PixelFormat::A1B5G5R5_UNORM:
|
||||
return "A1B5G5R5_UNORM";
|
||||
case PixelFormat::A5B5G5R1_UNORM:
|
||||
return "A5B5G5R1_UNORM";
|
||||
case PixelFormat::R8_UNORM:
|
||||
return "R8_UNORM";
|
||||
case PixelFormat::R8_SNORM:
|
||||
return "R8_SNORM";
|
||||
case PixelFormat::R8_SINT:
|
||||
return "R8_SINT";
|
||||
case PixelFormat::R8_UINT:
|
||||
return "R8_UINT";
|
||||
case PixelFormat::R16G16B16A16_FLOAT:
|
||||
return "R16G16B16A16_FLOAT";
|
||||
case PixelFormat::R16G16B16A16_UNORM:
|
||||
return "R16G16B16A16_UNORM";
|
||||
case PixelFormat::R16G16B16A16_SNORM:
|
||||
return "R16G16B16A16_SNORM";
|
||||
case PixelFormat::R16G16B16A16_SINT:
|
||||
return "R16G16B16A16_SINT";
|
||||
case PixelFormat::R16G16B16A16_UINT:
|
||||
return "R16G16B16A16_UINT";
|
||||
case PixelFormat::B10G11R11_FLOAT:
|
||||
return "B10G11R11_FLOAT";
|
||||
case PixelFormat::R32G32B32A32_UINT:
|
||||
return "R32G32B32A32_UINT";
|
||||
case PixelFormat::BC1_RGBA_UNORM:
|
||||
return "BC1_RGBA_UNORM";
|
||||
case PixelFormat::BC2_UNORM:
|
||||
return "BC2_UNORM";
|
||||
case PixelFormat::BC3_UNORM:
|
||||
return "BC3_UNORM";
|
||||
case PixelFormat::BC4_UNORM:
|
||||
return "BC4_UNORM";
|
||||
case PixelFormat::BC4_SNORM:
|
||||
return "BC4_SNORM";
|
||||
case PixelFormat::BC5_UNORM:
|
||||
return "BC5_UNORM";
|
||||
case PixelFormat::BC5_SNORM:
|
||||
return "BC5_SNORM";
|
||||
case PixelFormat::BC7_UNORM:
|
||||
return "BC7_UNORM";
|
||||
case PixelFormat::BC6H_UFLOAT:
|
||||
return "BC6H_UFLOAT";
|
||||
case PixelFormat::BC6H_SFLOAT:
|
||||
return "BC6H_SFLOAT";
|
||||
case PixelFormat::ASTC_2D_4X4_UNORM:
|
||||
return "ASTC_2D_4X4_UNORM";
|
||||
case PixelFormat::B8G8R8A8_UNORM:
|
||||
return "B8G8R8A8_UNORM";
|
||||
case PixelFormat::R32G32B32A32_FLOAT:
|
||||
return "R32G32B32A32_FLOAT";
|
||||
case PixelFormat::R32G32B32A32_SINT:
|
||||
return "R32G32B32A32_SINT";
|
||||
case PixelFormat::R32G32_FLOAT:
|
||||
return "R32G32_FLOAT";
|
||||
case PixelFormat::R32G32_SINT:
|
||||
return "R32G32_SINT";
|
||||
case PixelFormat::R32_FLOAT:
|
||||
return "R32_FLOAT";
|
||||
case PixelFormat::R16_FLOAT:
|
||||
return "R16_FLOAT";
|
||||
case PixelFormat::R16_UNORM:
|
||||
return "R16_UNORM";
|
||||
case PixelFormat::R16_SNORM:
|
||||
return "R16_SNORM";
|
||||
case PixelFormat::R16_UINT:
|
||||
return "R16_UINT";
|
||||
case PixelFormat::R16_SINT:
|
||||
return "R16_SINT";
|
||||
case PixelFormat::R16G16_UNORM:
|
||||
return "R16G16_UNORM";
|
||||
case PixelFormat::R16G16_FLOAT:
|
||||
return "R16G16_FLOAT";
|
||||
case PixelFormat::R16G16_UINT:
|
||||
return "R16G16_UINT";
|
||||
case PixelFormat::R16G16_SINT:
|
||||
return "R16G16_SINT";
|
||||
case PixelFormat::R16G16_SNORM:
|
||||
return "R16G16_SNORM";
|
||||
case PixelFormat::R32G32B32_FLOAT:
|
||||
return "R32G32B32_FLOAT";
|
||||
case PixelFormat::A8B8G8R8_SRGB:
|
||||
return "A8B8G8R8_SRGB";
|
||||
case PixelFormat::R8G8_UNORM:
|
||||
return "R8G8_UNORM";
|
||||
case PixelFormat::R8G8_SNORM:
|
||||
return "R8G8_SNORM";
|
||||
case PixelFormat::R8G8_SINT:
|
||||
return "R8G8_SINT";
|
||||
case PixelFormat::R8G8_UINT:
|
||||
return "R8G8_UINT";
|
||||
case PixelFormat::R32G32_UINT:
|
||||
return "R32G32_UINT";
|
||||
case PixelFormat::R16G16B16X16_FLOAT:
|
||||
return "R16G16B16X16_FLOAT";
|
||||
case PixelFormat::R32_UINT:
|
||||
return "R32_UINT";
|
||||
case PixelFormat::R32_SINT:
|
||||
return "R32_SINT";
|
||||
case PixelFormat::ASTC_2D_8X8_UNORM:
|
||||
return "ASTC_2D_8X8_UNORM";
|
||||
case PixelFormat::ASTC_2D_8X5_UNORM:
|
||||
return "ASTC_2D_8X5_UNORM";
|
||||
case PixelFormat::ASTC_2D_5X4_UNORM:
|
||||
return "ASTC_2D_5X4_UNORM";
|
||||
case PixelFormat::B8G8R8A8_SRGB:
|
||||
return "B8G8R8A8_SRGB";
|
||||
case PixelFormat::BC1_RGBA_SRGB:
|
||||
return "BC1_RGBA_SRGB";
|
||||
case PixelFormat::BC2_SRGB:
|
||||
return "BC2_SRGB";
|
||||
case PixelFormat::BC3_SRGB:
|
||||
return "BC3_SRGB";
|
||||
case PixelFormat::BC7_SRGB:
|
||||
return "BC7_SRGB";
|
||||
case PixelFormat::A4B4G4R4_UNORM:
|
||||
return "A4B4G4R4_UNORM";
|
||||
case PixelFormat::G4R4_UNORM:
|
||||
return "G4R4_UNORM";
|
||||
case PixelFormat::ASTC_2D_4X4_SRGB:
|
||||
return "ASTC_2D_4X4_SRGB";
|
||||
case PixelFormat::ASTC_2D_8X8_SRGB:
|
||||
return "ASTC_2D_8X8_SRGB";
|
||||
case PixelFormat::ASTC_2D_8X5_SRGB:
|
||||
return "ASTC_2D_8X5_SRGB";
|
||||
case PixelFormat::ASTC_2D_5X4_SRGB:
|
||||
return "ASTC_2D_5X4_SRGB";
|
||||
case PixelFormat::ASTC_2D_5X5_UNORM:
|
||||
return "ASTC_2D_5X5_UNORM";
|
||||
case PixelFormat::ASTC_2D_5X5_SRGB:
|
||||
return "ASTC_2D_5X5_SRGB";
|
||||
case PixelFormat::ASTC_2D_10X8_UNORM:
|
||||
return "ASTC_2D_10X8_UNORM";
|
||||
case PixelFormat::ASTC_2D_10X8_SRGB:
|
||||
return "ASTC_2D_10X8_SRGB";
|
||||
case PixelFormat::ASTC_2D_6X6_UNORM:
|
||||
return "ASTC_2D_6X6_UNORM";
|
||||
case PixelFormat::ASTC_2D_6X6_SRGB:
|
||||
return "ASTC_2D_6X6_SRGB";
|
||||
case PixelFormat::ASTC_2D_10X6_UNORM:
|
||||
return "ASTC_2D_10X6_UNORM";
|
||||
case PixelFormat::ASTC_2D_10X6_SRGB:
|
||||
return "ASTC_2D_10X6_SRGB";
|
||||
case PixelFormat::ASTC_2D_10X5_UNORM:
|
||||
return "ASTC_2D_10X5_UNORM";
|
||||
case PixelFormat::ASTC_2D_10X5_SRGB:
|
||||
return "ASTC_2D_10X5_SRGB";
|
||||
case PixelFormat::ASTC_2D_10X10_UNORM:
|
||||
return "ASTC_2D_10X10_UNORM";
|
||||
case PixelFormat::ASTC_2D_10X10_SRGB:
|
||||
return "ASTC_2D_10X10_SRGB";
|
||||
case PixelFormat::ASTC_2D_12X10_UNORM:
|
||||
return "ASTC_2D_12X10_UNORM";
|
||||
case PixelFormat::ASTC_2D_12X10_SRGB:
|
||||
return "ASTC_2D_12X10_SRGB";
|
||||
case PixelFormat::ASTC_2D_12X12_UNORM:
|
||||
return "ASTC_2D_12X12_UNORM";
|
||||
case PixelFormat::ASTC_2D_12X12_SRGB:
|
||||
return "ASTC_2D_12X12_SRGB";
|
||||
case PixelFormat::ASTC_2D_8X6_UNORM:
|
||||
return "ASTC_2D_8X6_UNORM";
|
||||
case PixelFormat::ASTC_2D_8X6_SRGB:
|
||||
return "ASTC_2D_8X6_SRGB";
|
||||
case PixelFormat::ASTC_2D_6X5_UNORM:
|
||||
return "ASTC_2D_6X5_UNORM";
|
||||
case PixelFormat::ASTC_2D_6X5_SRGB:
|
||||
return "ASTC_2D_6X5_SRGB";
|
||||
case PixelFormat::E5B9G9R9_FLOAT:
|
||||
return "E5B9G9R9_FLOAT";
|
||||
case PixelFormat::D32_FLOAT:
|
||||
return "D32_FLOAT";
|
||||
case PixelFormat::D16_UNORM:
|
||||
return "D16_UNORM";
|
||||
case PixelFormat::X8_D24_UNORM:
|
||||
return "X8_D24_UNORM";
|
||||
case PixelFormat::S8_UINT:
|
||||
return "S8_UINT";
|
||||
case PixelFormat::D24_UNORM_S8_UINT:
|
||||
return "D24_UNORM_S8_UINT";
|
||||
case PixelFormat::S8_UINT_D24_UNORM:
|
||||
return "S8_UINT_D24_UNORM";
|
||||
case PixelFormat::D32_FLOAT_S8_UINT:
|
||||
return "D32_FLOAT_S8_UINT";
|
||||
#define PIXEL_FORMAT_ELEM(NAME, ...) case PixelFormat::NAME: return #NAME;
|
||||
PIXEL_FORMAT_LIST
|
||||
#undef PIXEL_FORMAT_ELEM
|
||||
case PixelFormat::MaxDepthStencilFormat:
|
||||
case PixelFormat::Invalid:
|
||||
return "Invalid";
|
||||
|
||||
@@ -363,6 +363,11 @@ public:
|
||||
return features.features.textureCompressionBC;
|
||||
}
|
||||
|
||||
/// Returns true if ETC2 is natively supported.
|
||||
bool IsOptimalEtc2Supported() const {
|
||||
return features.features.textureCompressionETC2;
|
||||
}
|
||||
|
||||
/// Returns true if descriptor aliasing is natively supported.
|
||||
bool IsDescriptorAliasingSupported() const {
|
||||
return GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY;
|
||||
|
||||
Reference in New Issue
Block a user