mirror of
https://github.com/azahar-emu/azahar.git
synced 2026-06-06 10:43:39 -04:00
Compare commits
16 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3703dcec3e | ||
|
|
64b7f7e5b1 | ||
|
|
fa3be2d9a5 | ||
|
|
b900b635ad | ||
|
|
3d954bc74d | ||
|
|
e10999fe81 | ||
|
|
6020f48e06 | ||
|
|
c7dda3c444 | ||
|
|
e96e84e1b3 | ||
|
|
8c5161e88f | ||
|
|
15e06bd000 | ||
|
|
656ab12542 | ||
|
|
933a9e5596 | ||
|
|
6fa51c2fe4 | ||
|
|
9dd0149ec7 | ||
|
|
b038cdaf86 |
70 changed files with 6609 additions and 6572 deletions
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
|
|
@ -38,11 +38,14 @@ jobs:
|
|||
CCACHE_SLOPPINESS: time_macros
|
||||
OS: linux
|
||||
TARGET: ${{ matrix.target }}
|
||||
SHOULD_RUN: ${{ (matrix.target != 'appimage-wayland' || github.ref_type == 'tag') }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
if: ${{ env.SHOULD_RUN == 'true' }}
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Set up cache
|
||||
if: ${{ env.SHOULD_RUN == 'true' }}
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
|
|
@ -50,18 +53,19 @@ jobs:
|
|||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.target }}-
|
||||
- name: Build
|
||||
if: ${{ env.SHOULD_RUN == 'true' }}
|
||||
run: ./.ci/linux.sh
|
||||
- name: Move AppImage to artifacts directory
|
||||
if: ${{ contains(matrix.target, 'appimage') }}
|
||||
if: ${{ contains(matrix.target, 'appimage') && env.SHOULD_RUN == 'true' }}
|
||||
run: |
|
||||
mkdir -p artifacts
|
||||
mv build/bundle/*.AppImage artifacts/
|
||||
- name: Rename AppImage
|
||||
if: ${{ matrix.target == 'appimage-wayland' }}
|
||||
if: ${{ matrix.target == 'appimage-wayland' && env.SHOULD_RUN == 'true' }}
|
||||
run: |
|
||||
mv artifacts/azahar.AppImage artifacts/azahar-wayland.AppImage
|
||||
- name: Upload
|
||||
if: ${{ contains(matrix.target, 'appimage') }}
|
||||
if: ${{ contains(matrix.target, 'appimage') && env.SHOULD_RUN == 'true' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.OS }}-${{ env.TARGET }}
|
||||
|
|
|
|||
514
dist/languages/ca_ES_valencia.ts
vendored
514
dist/languages/ca_ES_valencia.ts
vendored
File diff suppressed because it is too large
Load diff
512
dist/languages/da_DK.ts
vendored
512
dist/languages/da_DK.ts
vendored
File diff suppressed because it is too large
Load diff
514
dist/languages/de.ts
vendored
514
dist/languages/de.ts
vendored
File diff suppressed because it is too large
Load diff
512
dist/languages/el.ts
vendored
512
dist/languages/el.ts
vendored
File diff suppressed because it is too large
Load diff
514
dist/languages/es_ES.ts
vendored
514
dist/languages/es_ES.ts
vendored
File diff suppressed because it is too large
Load diff
512
dist/languages/fi.ts
vendored
512
dist/languages/fi.ts
vendored
File diff suppressed because it is too large
Load diff
514
dist/languages/fr.ts
vendored
514
dist/languages/fr.ts
vendored
File diff suppressed because it is too large
Load diff
512
dist/languages/hu_HU.ts
vendored
512
dist/languages/hu_HU.ts
vendored
File diff suppressed because it is too large
Load diff
512
dist/languages/id.ts
vendored
512
dist/languages/id.ts
vendored
File diff suppressed because it is too large
Load diff
514
dist/languages/it.ts
vendored
514
dist/languages/it.ts
vendored
File diff suppressed because it is too large
Load diff
512
dist/languages/ja_JP.ts
vendored
512
dist/languages/ja_JP.ts
vendored
File diff suppressed because it is too large
Load diff
512
dist/languages/ko_KR.ts
vendored
512
dist/languages/ko_KR.ts
vendored
File diff suppressed because it is too large
Load diff
512
dist/languages/lt_LT.ts
vendored
512
dist/languages/lt_LT.ts
vendored
File diff suppressed because it is too large
Load diff
512
dist/languages/nb.ts
vendored
512
dist/languages/nb.ts
vendored
File diff suppressed because it is too large
Load diff
512
dist/languages/nl.ts
vendored
512
dist/languages/nl.ts
vendored
File diff suppressed because it is too large
Load diff
514
dist/languages/pl_PL.ts
vendored
514
dist/languages/pl_PL.ts
vendored
File diff suppressed because it is too large
Load diff
520
dist/languages/pt_BR.ts
vendored
520
dist/languages/pt_BR.ts
vendored
File diff suppressed because it is too large
Load diff
512
dist/languages/ro_RO.ts
vendored
512
dist/languages/ro_RO.ts
vendored
File diff suppressed because it is too large
Load diff
514
dist/languages/ru_RU.ts
vendored
514
dist/languages/ru_RU.ts
vendored
File diff suppressed because it is too large
Load diff
514
dist/languages/sv.ts
vendored
514
dist/languages/sv.ts
vendored
File diff suppressed because it is too large
Load diff
514
dist/languages/tr_TR.ts
vendored
514
dist/languages/tr_TR.ts
vendored
File diff suppressed because it is too large
Load diff
512
dist/languages/vi_VN.ts
vendored
512
dist/languages/vi_VN.ts
vendored
File diff suppressed because it is too large
Load diff
514
dist/languages/zh_CN.ts
vendored
514
dist/languages/zh_CN.ts
vendored
File diff suppressed because it is too large
Load diff
512
dist/languages/zh_TW.ts
vendored
512
dist/languages/zh_TW.ts
vendored
File diff suppressed because it is too large
Load diff
2
externals/sdl2/SDL
vendored
2
externals/sdl2/SDL
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 2359383fc187386204c3bb22de89655a494cd128
|
||||
Subproject commit 5d249570393f7a37e037abf22cd6012a4cc56a71
|
||||
|
|
@ -50,7 +50,7 @@ class SecondaryDisplay(val context: Context) : DisplayManager.DisplayListener {
|
|||
val currentDisplayId = context.display.displayId
|
||||
val displays = dm.displays
|
||||
val presDisplays = dm.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
|
||||
return displays.firstOrNull {
|
||||
val extDisplays = displays.filter {
|
||||
val isPresentable = presDisplays.any { pd -> pd.displayId == it.displayId }
|
||||
val isNotDefaultOrPresentable = it.displayId != Display.DEFAULT_DISPLAY || isPresentable
|
||||
isNotDefaultOrPresentable &&
|
||||
|
|
@ -59,6 +59,10 @@ class SecondaryDisplay(val context: Context) : DisplayManager.DisplayListener {
|
|||
it.state != Display.STATE_OFF &&
|
||||
it.isValid
|
||||
}
|
||||
// if there is a display called Built-In Display or Built-In Screen, prioritize the OTHER screen
|
||||
val selected = extDisplays.firstOrNull { ! it.name.contains("Built",true) }
|
||||
?: extDisplays.firstOrNull()
|
||||
return selected
|
||||
}
|
||||
|
||||
fun updateDisplay() {
|
||||
|
|
|
|||
|
|
@ -255,7 +255,9 @@ class InputBindingSetting(
|
|||
} else {
|
||||
buttonCode
|
||||
}
|
||||
writeAxisMapping(motionRange.axis, button, axisDir == '-')
|
||||
// use UP (-) to map vertical, but use RIGHT (+) to map horizontal
|
||||
val inverted = if (isHorizontalOrientation()) axisDir == '-' else axisDir == '+'
|
||||
writeAxisMapping(motionRange.axis, button, inverted)
|
||||
val uiString = "${device.name}: Axis ${motionRange.axis}" + axisDir
|
||||
value = uiString
|
||||
}
|
||||
|
|
|
|||
|
|
@ -269,6 +269,8 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
|||
|
||||
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||
|
||||
system.GPU().ApplyPerProgramSettings(program_id);
|
||||
|
||||
std::unique_ptr<Frontend::GraphicsContext> cpu_context;
|
||||
system.GPU().Renderer().Rasterizer()->LoadDefaultDiskResources(stop_run,
|
||||
&LoadDiskCacheProgress);
|
||||
|
|
|
|||
|
|
@ -112,8 +112,6 @@
|
|||
<string name="controller_dpad_axis_description">És possible que alguns controladors no puguen assignar el D-pad com un eix. Si aquest és el cas, utilitza la secció D-Pad (botons).</string>
|
||||
<string name="controller_dpad_button">D-Pad (Botó)</string>
|
||||
<string name="controller_dpad_button_description">Assigna només el D-pad a aquests si tens problemes amb les assignacions de botons del D-Pad (Eix).</string>
|
||||
<string name="controller_axis_vertical">Eix Vertical</string>
|
||||
<string name="controller_axis_horizontal">Eix Horitzontal</string>
|
||||
<string name="direction_up">Amunt</string>
|
||||
<string name="direction_down">Avall</string>
|
||||
<string name="direction_left">Esquerra</string>
|
||||
|
|
|
|||
|
|
@ -119,8 +119,6 @@
|
|||
<string name="controller_dpad_axis_description">Nogle kontrollere er muligvis ikke i stand til at tilknytte deres D-Pad som en akse. Hvis det er tilfældet, skal du bruge afsnittet D-Pad (knapper).</string>
|
||||
<string name="controller_dpad_button">D-Pad (knapper)</string>
|
||||
<string name="controller_dpad_button_description">Udfyld kun disse D-Pad, hvis du har problemer med opsætningen af D-Pad (akser).</string>
|
||||
<string name="controller_axis_vertical">Op/ned akse</string>
|
||||
<string name="controller_axis_horizontal">Venstre/højre akse</string>
|
||||
<string name="direction_up">Op</string>
|
||||
<string name="direction_down">Ned</string>
|
||||
<string name="direction_left">Venstre</string>
|
||||
|
|
@ -129,8 +127,6 @@
|
|||
<string name="input_dialog_description">Tryk på eller flyt et input.</string>
|
||||
<string name="input_binding">Inputbinding</string>
|
||||
<string name="input_binding_description">Tryk på eller flyt et input for at binde det til %1$s.</string>
|
||||
<string name="input_binding_description_vertical_axis">Bevæg joysticket ned</string>
|
||||
<string name="input_binding_description_horizontal_axis">Bevæg joysticket til højre</string>
|
||||
<string name="button_home">HOME</string>
|
||||
<string name="button_swap">Byt skærme</string>
|
||||
<string name="button_turbo">Turbo</string>
|
||||
|
|
|
|||
|
|
@ -116,8 +116,6 @@
|
|||
<string name="controller_dpad_axis_description">Es posible que algunos controladores no puedan asignar su D-pad como un eje. Si ese es el caso, utilice la sección D-Pad (botones).</string>
|
||||
<string name="controller_dpad_button">D-Pad (Botón)</string>
|
||||
<string name="controller_dpad_button_description">Asigne solo el D-pad a éstos si tiene problemas con las asignaciones de botones del D-Pad (Eje).</string>
|
||||
<string name="controller_axis_vertical">Eje Vertical</string>
|
||||
<string name="controller_axis_horizontal">Eje Horizontal</string>
|
||||
<string name="direction_up">Arriba</string>
|
||||
<string name="direction_down">Abajo</string>
|
||||
<string name="direction_left">Izquierda</string>
|
||||
|
|
@ -126,8 +124,6 @@
|
|||
<string name="input_dialog_description">Pulsa o mueve un botón/palanca.</string>
|
||||
<string name="input_binding">Asignación de botones</string>
|
||||
<string name="input_binding_description">Pulsa o mueve un botón para asignarlo a %1$s.</string>
|
||||
<string name="input_binding_description_vertical_axis">Mueve el joystick abajo</string>
|
||||
<string name="input_binding_description_horizontal_axis">Mueve el joystick a la derecha</string>
|
||||
<string name="button_home">HOME</string>
|
||||
<string name="button_swap">Intercambiar Pantallas</string>
|
||||
<string name="button_turbo">Turbo</string>
|
||||
|
|
|
|||
|
|
@ -119,8 +119,6 @@
|
|||
<string name="controller_dpad_axis_description">Niektóre kontrolery mogą nie być w stanie zmapować swojego D-pada jako osi. W takim przypadku należy użyć sekcji D-Pad (przyciski).</string>
|
||||
<string name="controller_dpad_button">Krzyżak (Przycisk)</string>
|
||||
<string name="controller_dpad_button_description">Zmapuj D-pad na te ustawienia tylko wtedy, gdy masz problemy z mapowaniem przycisków D-Pada (Oś).</string>
|
||||
<string name="controller_axis_vertical">Oś góra/dół</string>
|
||||
<string name="controller_axis_horizontal">Oś lewa/prawa</string>
|
||||
<string name="direction_up">Góra</string>
|
||||
<string name="direction_down">Dół</string>
|
||||
<string name="direction_left">Lewo</string>
|
||||
|
|
@ -129,8 +127,6 @@
|
|||
<string name="input_dialog_description">Naciśnij lub przenieś wejście.</string>
|
||||
<string name="input_binding">Powiązanie wejścia</string>
|
||||
<string name="input_binding_description">Naciśnij lub przesuń wejście, aby powiązać je z %1$s.</string>
|
||||
<string name="input_binding_description_vertical_axis">Przesuń analog w dół.</string>
|
||||
<string name="input_binding_description_horizontal_axis">Przesuń analog w prawo.</string>
|
||||
<string name="button_home">HOME</string>
|
||||
<string name="button_swap">Zamień ekrany</string>
|
||||
<string name="button_turbo">Turbo</string>
|
||||
|
|
|
|||
|
|
@ -119,8 +119,6 @@
|
|||
<string name="controller_dpad_axis_description">Alguns controles podem não ser capazes de mapear os D-pads para um eixo. Se esse for o caso, use a seção de D-Pad (Botões).</string>
|
||||
<string name="controller_dpad_button">D-Pad (Botão)</string>
|
||||
<string name="controller_dpad_button_description">Só mapeie o D-pad para isso se você se você estiver encontrando problemas com o mapeamento de botão do D-Pad (Eixo).</string>
|
||||
<string name="controller_axis_vertical">Eixo vertical</string>
|
||||
<string name="controller_axis_horizontal">Eixo horizontal</string>
|
||||
<string name="direction_up">Cima</string>
|
||||
<string name="direction_down">Baixo</string>
|
||||
<string name="direction_left">Esquerda</string>
|
||||
|
|
@ -129,8 +127,6 @@
|
|||
<string name="input_dialog_description">Pressione ou mova uma entrada.</string>
|
||||
<string name="input_binding">Mapeamento de controles</string>
|
||||
<string name="input_binding_description">Pressione ou mova um botão/alavanca para mapear para %1$s.</string>
|
||||
<string name="input_binding_description_vertical_axis">Mova o seu joystick para baixo</string>
|
||||
<string name="input_binding_description_horizontal_axis">Mova o seu joystick para a direita</string>
|
||||
<string name="button_home">Menu Principal</string>
|
||||
<string name="button_swap">Trocar telas</string>
|
||||
<string name="button_turbo">Turbo</string>
|
||||
|
|
|
|||
|
|
@ -64,8 +64,6 @@
|
|||
<string name="controller_triggers">Триггеры</string>
|
||||
<string name="controller_trigger">Триггер</string>
|
||||
<string name="controller_dpad">Крестовина</string>
|
||||
<string name="controller_axis_vertical">Ось вверх/вниз</string>
|
||||
<string name="controller_axis_horizontal">Ось влево/вправо</string>
|
||||
<string name="input_dialog_title">Привязка %1$s %2$s</string>
|
||||
<string name="input_dialog_description">Нажмите или отклоните элемент управления.</string>
|
||||
<string name="input_binding">Привязки ввода</string>
|
||||
|
|
|
|||
|
|
@ -111,8 +111,6 @@
|
|||
<string name="controller_dpad_axis_description">Bazı oyun kolları D-pad\'lerini bir eksen olarak atayamayabilir. Durum buysa D-Pad (butonlar) bölümünü kullanın.</string>
|
||||
<string name="controller_dpad_button">D-pad (Buton)</string>
|
||||
<string name="controller_dpad_button_description">Sadece D-Pad (Eksen) atamasında sorun yaşıyorsanız D-Pad\'inizi bunlara atayın.</string>
|
||||
<string name="controller_axis_vertical">Yukarı/Aşağı Eksen</string>
|
||||
<string name="controller_axis_horizontal">Sol/Sağ Eksen</string>
|
||||
<string name="direction_up">Yukarı</string>
|
||||
<string name="direction_down">Aşağı</string>
|
||||
<string name="direction_left">Sol</string>
|
||||
|
|
|
|||
|
|
@ -112,8 +112,6 @@
|
|||
<string name="controller_dpad_axis_description">有些控制器可能无法将其方向键映射为轴。如果是这种情况,只能使用方向键(按键)部分。</string>
|
||||
<string name="controller_dpad_button">十字键(按键)</string>
|
||||
<string name="controller_dpad_button_description">仅当您在方向键 (轴) 映射遇到问题时,才将方向键映射到这些按键。</string>
|
||||
<string name="controller_axis_vertical">上/下轴</string>
|
||||
<string name="controller_axis_horizontal">左/右轴</string>
|
||||
<string name="direction_up">上</string>
|
||||
<string name="direction_down">下</string>
|
||||
<string name="direction_left">左</string>
|
||||
|
|
|
|||
|
|
@ -112,8 +112,6 @@
|
|||
<string name="controller_dpad_axis_description">Einige Controller sind nicht in der Lage, das Steuerkreuz als Achse zuzuordnen. Wenn das der Fall ist, dann nutze den Abschnitt: „Steuerkreuz (Knöpfe)“.</string>
|
||||
<string name="controller_dpad_button">Steuerkreuz (Knöpfe)</string>
|
||||
<string name="controller_dpad_button_description">Nutze diese Funktion nur, wenn du Probleme mit der Funktion „Steuerkreuz (Achse) hast“.</string>
|
||||
<string name="controller_axis_vertical">Achse (Oben/Unten)</string>
|
||||
<string name="controller_axis_horizontal">Achse (Links/Rechts)</string>
|
||||
<string name="direction_up">Hoch</string>
|
||||
<string name="direction_down">Runter</string>
|
||||
<string name="direction_left">Links</string>
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@
|
|||
<string name="controller_c">C-Tikku</string>
|
||||
<string name="controller_triggers">Liipaisimet</string>
|
||||
<string name="controller_dpad">D-Pad</string>
|
||||
<string name="controller_axis_vertical">Ylä/ala-akseli</string>
|
||||
<string name="controller_axis_horizontal">Vasen/oikea akseli</string>
|
||||
<!-- Generic buttons (Shared with lots of stuff) -->
|
||||
<string name="generic_buttons">Nappulat</string>
|
||||
<string name="init_clock">Järjestelmän kellotyyppi</string>
|
||||
|
|
|
|||
|
|
@ -112,8 +112,6 @@
|
|||
<string name="controller_dpad_axis_description">Certaines manettes ne sont pas en mesure d\'affecter leur manette + à un axe. Dans ce cas, utilisez la section manette + (boutons).</string>
|
||||
<string name="controller_dpad_button">Manette + (bouton)</string>
|
||||
<string name="controller_dpad_button_description">N\'affectez la manette + à ces boutons que si vous rencontrez des problèmes avec l\'affectation des boutons de la manette + (axes).</string>
|
||||
<string name="controller_axis_vertical">Axe Haut/Bas</string>
|
||||
<string name="controller_axis_horizontal">Axe Gauche/Droite</string>
|
||||
<string name="direction_up">Haut</string>
|
||||
<string name="direction_down">Bas</string>
|
||||
<string name="direction_left">Gauche</string>
|
||||
|
|
|
|||
|
|
@ -124,8 +124,6 @@ Divertiti usando l\'emulatore!</string>
|
|||
<string name="controller_dpad_axis_description">Alcuni controller potrebbero non essere in grado di mappare i tasti direzionali come un asse. Se questo è il caso, usa la sezione Tasti direzionali (pulsante).</string>
|
||||
<string name="controller_dpad_button">Tasti direzionali (pulsante)</string>
|
||||
<string name="controller_dpad_button_description">Mappa i tasti direzionali in questa sezione solo se riscontri problemi con le mappature della sezione Tasti direzionali (asse).</string>
|
||||
<string name="controller_axis_vertical">Asse verticale</string>
|
||||
<string name="controller_axis_horizontal">Asse orizzontale</string>
|
||||
<string name="direction_up">Su</string>
|
||||
<string name="direction_down">Giù</string>
|
||||
<string name="direction_left">Sinistra</string>
|
||||
|
|
@ -134,8 +132,6 @@ Divertiti usando l\'emulatore!</string>
|
|||
<string name="input_dialog_description">Premi o sposta un comando</string>
|
||||
<string name="input_binding">Assegnazione Input</string>
|
||||
<string name="input_binding_description">Premi o muovi un comando per assegnarlo a %1$s.</string>
|
||||
<string name="input_binding_description_vertical_axis">Sposta il joystick verso il basso</string>
|
||||
<string name="input_binding_description_horizontal_axis">Sposta il joystick verso destra</string>
|
||||
<string name="button_home">Home</string>
|
||||
<string name="button_swap">Inverti schermi</string>
|
||||
<string name="button_turbo">Turbo</string>
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@
|
|||
<string name="controller_c">C-Spak</string>
|
||||
<string name="controller_triggers">Utløser</string>
|
||||
<string name="controller_dpad">Kontrollpluss</string>
|
||||
<string name="controller_axis_vertical">Opp/Ned Akse</string>
|
||||
<string name="controller_axis_horizontal">Venstre/Høyre Akse</string>
|
||||
<string name="input_binding">Inngangsinnbinding</string>
|
||||
<string name="input_binding_description">Trykk eller flytt en inngang for å binde den til %1$s.</string>
|
||||
<string name="input_message_analog_only">Denne kontrollen må være bundet til en håndkontroller\'s analog spak eller kontrollpluss akse!</string>
|
||||
|
|
|
|||
|
|
@ -83,8 +83,6 @@ Vink deze optie in de toekomst nogmaals aan om te zien of er ondersteuning is to
|
|||
<string name="controller_dpad_axis_description">Sommige controllers kunnen hun D-pad mogelijk niet als as in kaart brengen. Als dat het geval is, gebruik dan de D-Pad (knoppen) sectie.</string>
|
||||
<string name="controller_dpad_button">D-Pad (Knop)</string>
|
||||
<string name="controller_dpad_button_description">Wijs het D-pad hier alleen aan toe als je problemen ondervindt met de toewijzing van de D-Pad (as)-knoppen.</string>
|
||||
<string name="controller_axis_vertical">Op/neer-as</string>
|
||||
<string name="controller_axis_horizontal">Links/rechts as</string>
|
||||
<string name="direction_up">Op</string>
|
||||
<string name="direction_down">Neer</string>
|
||||
<string name="direction_left">Links</string>
|
||||
|
|
|
|||
|
|
@ -119,8 +119,6 @@
|
|||
<string name="controller_dpad_axis_description">Vissa styrenheter kanske inte kan mappa sina riktningsknappar som en axel. Om så är fallet, använd avsnittet Riktningsknappar (knappar).</string>
|
||||
<string name="controller_dpad_button">Riktningsknappar (knapp)</string>
|
||||
<string name="controller_dpad_button_description">Mappa endast D-pad till dessa om du har problem med knappmappningarna för D-pad (axel).</string>
|
||||
<string name="controller_axis_vertical">Axel upp/ner</string>
|
||||
<string name="controller_axis_horizontal">Vänster/höger-axel</string>
|
||||
<string name="direction_up">Upp</string>
|
||||
<string name="direction_down">Ner</string>
|
||||
<string name="direction_left">Vänster</string>
|
||||
|
|
@ -129,8 +127,6 @@
|
|||
<string name="input_dialog_description">Tryck på eller flytta en inmatning.</string>
|
||||
<string name="input_binding">Inmatningsbindning</string>
|
||||
<string name="input_binding_description">Tryck eller flytta en inmatning för att binda den till %1$s.</string>
|
||||
<string name="input_binding_description_vertical_axis">Rör din joystick neråt</string>
|
||||
<string name="input_binding_description_horizontal_axis">Rör din joystick åt höger</string>
|
||||
<string name="button_home">HOME</string>
|
||||
<string name="button_swap">Byt skärm</string>
|
||||
<string name="button_turbo">Turbo</string>
|
||||
|
|
|
|||
|
|
@ -130,8 +130,8 @@
|
|||
<string name="controller_dpad_axis_description">Some controllers may not be able to map their D-pad as an axis. If that\'s the case, use the D-Pad (buttons) section.</string>
|
||||
<string name="controller_dpad_button">D-Pad (Button)</string>
|
||||
<string name="controller_dpad_button_description">Only map the D-pad to these if you\'re facing issues with the D-Pad (Axis) button mappings.</string>
|
||||
<string name="controller_axis_vertical">Up/Down Axis</string>
|
||||
<string name="controller_axis_horizontal">Left/Right Axis</string>
|
||||
<string name="controller_axis_vertical">Vertical Axis</string>
|
||||
<string name="controller_axis_horizontal">Horizontal Axis</string>
|
||||
<string name="direction_up">Up</string>
|
||||
<string name="direction_down">Down</string>
|
||||
<string name="direction_left">Left</string>
|
||||
|
|
@ -140,8 +140,8 @@
|
|||
<string name="input_dialog_description">Press or move an input.</string>
|
||||
<string name="input_binding">Input Binding</string>
|
||||
<string name="input_binding_description">Press or move an input to bind it to %1$s.</string>
|
||||
<string name="input_binding_description_vertical_axis">Move your joystick down</string>
|
||||
<string name="input_binding_description_horizontal_axis">Move your joystick right</string>
|
||||
<string name="input_binding_description_vertical_axis">Press UP on your joystick.</string>
|
||||
<string name="input_binding_description_horizontal_axis">Press RIGHT on your joystick.</string>
|
||||
<string name="button_a" translatable="false">A</string>
|
||||
<string name="button_b" translatable="false">B</string>
|
||||
<string name="button_select" translatable="false">SELECT</string>
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ if (ENABLE_QT AND UNIX AND NOT APPLE)
|
|||
endif()
|
||||
|
||||
if (ENABLE_QT AND APPLE)
|
||||
find_package(Qt6 REQUIRED COMPONENTS GuiPrivate)
|
||||
target_link_libraries(citra_meta PRIVATE Qt6::GuiPrivate)
|
||||
endif()
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include "core/3ds.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "input_common/keyboard.h"
|
||||
#include "input_common/main.h"
|
||||
|
|
@ -78,6 +79,10 @@ void EmuThread::run() {
|
|||
|
||||
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||
|
||||
u64 program_id{};
|
||||
system.GetAppLoader().ReadProgramId(program_id);
|
||||
system.GPU().ApplyPerProgramSettings(program_id);
|
||||
|
||||
system.GPU().Renderer().Rasterizer()->LoadDefaultDiskResources(
|
||||
stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
|
||||
emit LoadProgress(stage, value, total);
|
||||
|
|
|
|||
|
|
@ -171,12 +171,21 @@ void GMainWindow::ShowCommandOutput(std::string title, std::string message) {
|
|||
#endif
|
||||
}
|
||||
|
||||
bool IsPrerelease() {
|
||||
bool IsPrereleaseBuild() {
|
||||
return ((strstr(Common::g_build_fullname, "alpha") != NULL) ||
|
||||
(strstr(Common::g_build_fullname, "beta") != NULL) ||
|
||||
(strstr(Common::g_build_fullname, "rc") != NULL));
|
||||
}
|
||||
|
||||
#ifdef ENABLE_QT_UPDATE_CHECKER
|
||||
bool ShouldCheckForPrereleaseUpdates() {
|
||||
const bool update_channel = UISettings::values.update_check_channel.GetValue();
|
||||
const bool using_prerelease_channel =
|
||||
(update_channel == UISettings::UpdateCheckChannels::PRERELEASE);
|
||||
return (IsPrereleaseBuild() || using_prerelease_channel);
|
||||
}
|
||||
#endif
|
||||
|
||||
GMainWindow::GMainWindow(Core::System& system_)
|
||||
: ui{std::make_unique<Ui::MainWindow>()}, system{system_}, movie{system.Movie()},
|
||||
user_data_migrator{this}, config{std::make_unique<QtConfig>()}, emu_thread{nullptr} {
|
||||
|
|
@ -406,7 +415,8 @@ GMainWindow::GMainWindow(Core::System& system_)
|
|||
if (UISettings::values.check_for_update_on_start) {
|
||||
update_future = QtConcurrent::run([]() -> QString {
|
||||
const std::optional<std::string> latest_release_tag =
|
||||
UpdateChecker::GetLatestRelease(IsPrerelease());
|
||||
UpdateChecker::GetLatestRelease(ShouldCheckForPrereleaseUpdates());
|
||||
|
||||
if (latest_release_tag && latest_release_tag.value() != Common::g_build_fullname) {
|
||||
return QString::fromStdString(latest_release_tag.value());
|
||||
}
|
||||
|
|
@ -4057,7 +4067,7 @@ void GMainWindow::OnEmulatorUpdateAvailable() {
|
|||
update_prompt.exec();
|
||||
if (update_prompt.button(QMessageBox::Yes) == update_prompt.clickedButton()) {
|
||||
std::string update_page_url;
|
||||
if (IsPrerelease()) {
|
||||
if (ShouldCheckForPrereleaseUpdates()) {
|
||||
update_page_url = "https://github.com/azahar-emu/azahar/releases";
|
||||
} else {
|
||||
update_page_url = "https://azahar-emu.org/pages/download/";
|
||||
|
|
|
|||
|
|
@ -568,6 +568,7 @@ void QtConfig::ReadMiscellaneousValues() {
|
|||
ReadBasicSetting(Settings::values.log_regex_filter);
|
||||
ReadBasicSetting(Settings::values.enable_gamemode);
|
||||
ReadBasicSetting(UISettings::values.check_for_update_on_start);
|
||||
ReadBasicSetting(UISettings::values.update_check_channel);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
|
@ -1139,7 +1140,7 @@ void QtConfig::SaveMiscellaneousValues() {
|
|||
WriteBasicSetting(Settings::values.log_regex_filter);
|
||||
WriteBasicSetting(Settings::values.enable_gamemode);
|
||||
WriteBasicSetting(UISettings::values.check_for_update_on_start);
|
||||
|
||||
WriteBasicSetting(UISettings::values.update_check_channel);
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
|||
ui->toggle_gamemode->setVisible(false);
|
||||
#endif
|
||||
#ifndef ENABLE_QT_UPDATE_CHECKER
|
||||
ui->toggle_update_checker->setVisible(false);
|
||||
ui->updates_group->setVisible(false);
|
||||
#endif
|
||||
|
||||
SetupPerGameUI();
|
||||
|
|
@ -92,6 +92,8 @@ void ConfigureGeneral::SetConfiguration() {
|
|||
ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue());
|
||||
ui->toggle_update_checker->setChecked(
|
||||
UISettings::values.check_for_update_on_start.GetValue());
|
||||
ui->update_channel_combobox->setCurrentIndex(
|
||||
UISettings::values.update_check_channel.GetValue());
|
||||
#ifdef __unix__
|
||||
ui->toggle_gamemode->setChecked(Settings::values.enable_gamemode.GetValue());
|
||||
#endif
|
||||
|
|
@ -179,6 +181,7 @@ void ConfigureGeneral::ApplyConfiguration() {
|
|||
UISettings::values.mute_when_in_background = ui->toggle_background_mute->isChecked();
|
||||
UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
|
||||
UISettings::values.check_for_update_on_start = ui->toggle_update_checker->isChecked();
|
||||
UISettings::values.update_check_channel = ui->update_channel_combobox->currentIndex();
|
||||
#ifdef __unix__
|
||||
Settings::values.enable_gamemode = ui->toggle_gamemode->isChecked();
|
||||
#endif
|
||||
|
|
@ -211,5 +214,5 @@ void ConfigureGeneral::SetupPerGameUI() {
|
|||
ui->general_group->setVisible(false);
|
||||
ui->button_reset_defaults->setVisible(false);
|
||||
ui->toggle_gamemode->setVisible(false);
|
||||
ui->toggle_update_checker->setVisible(false);
|
||||
ui->updates_group->setVisible(false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,43 @@
|
|||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="updates_group">
|
||||
<property name="title">
|
||||
<string>Updates</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="toggle_update_checker">
|
||||
<property name="text">
|
||||
<string>Check for updates</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="update_channel_label">
|
||||
<property name="text">
|
||||
<string>Update Channel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="update_channel_combobox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Stable</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Prerelease</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="general_group">
|
||||
<property name="title">
|
||||
|
|
@ -57,13 +94,6 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_update_checker">
|
||||
<property name="text">
|
||||
<string>Check for updates</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
@ -315,7 +345,6 @@
|
|||
<tabstop>toggle_check_exit</tabstop>
|
||||
<tabstop>toggle_background_pause</tabstop>
|
||||
<tabstop>toggle_hide_mouse</tabstop>
|
||||
<tabstop>toggle_update_checker</tabstop>
|
||||
<tabstop>button_reset_defaults</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@
|
|||
<item>
|
||||
<widget class="QCheckBox" name="toggle_async_present">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Perform presentation on separate threads. Improves performance when using Vulkan in most applications.</p></body></html></string>
|
||||
<string><html><head/><body><p>Perform presentation on separate threads. Improves performance when using Vulkan in most applications. Adds ~1 frame of input lag.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable async presentation</string>
|
||||
|
|
|
|||
|
|
@ -1113,13 +1113,21 @@ const QStringList GameList::supported_file_extensions = {
|
|||
};
|
||||
|
||||
void GameList::RefreshGameDirectory() {
|
||||
|
||||
// Do not scan directories when the system is powered on, it will be
|
||||
// repopulated on shutdown anyways.
|
||||
if (Core::System::GetInstance().IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto time_now = std::chrono::steady_clock::now();
|
||||
|
||||
// Max of 1 refresh every 1 second.
|
||||
if (time_last_refresh + std::chrono::seconds(1) > time_now) {
|
||||
return;
|
||||
}
|
||||
|
||||
time_last_refresh = time_now;
|
||||
|
||||
if (!UISettings::values.game_dirs.isEmpty() && current_worker != nullptr) {
|
||||
LOG_INFO(Frontend, "Change detected in the applications directory. Reloading game list.");
|
||||
PopulateAsync(UISettings::values.game_dirs);
|
||||
|
|
|
|||
|
|
@ -151,6 +151,8 @@ private:
|
|||
friend class GameListSearchField;
|
||||
|
||||
const PlayTime::PlayTimeManager& play_time_manager;
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> time_last_refresh;
|
||||
};
|
||||
|
||||
class GameListPlaceholder : public QWidget {
|
||||
|
|
|
|||
|
|
@ -59,6 +59,12 @@ enum class GameListText : s32 {
|
|||
ListEnd, ///< Keep this at the end of the enum.
|
||||
};
|
||||
|
||||
class UpdateCheckChannels {
|
||||
public:
|
||||
static constexpr int STABLE = 0;
|
||||
static constexpr int PRERELEASE = 1;
|
||||
};
|
||||
|
||||
struct Values {
|
||||
QByteArray geometry;
|
||||
QByteArray state;
|
||||
|
|
@ -84,6 +90,8 @@ struct Values {
|
|||
Settings::Setting<bool> mute_when_in_background{false, "muteWhenInBackground"};
|
||||
Settings::Setting<bool> hide_mouse{false, "hideInactiveMouse"};
|
||||
Settings::Setting<bool> check_for_update_on_start{true, "check_for_update_on_start"};
|
||||
Settings::Setting<int> update_check_channel{UpdateCheckChannels::STABLE,
|
||||
"update_check_channel"};
|
||||
|
||||
Settings::Setting<std::string> inserted_cartridge{"", "inserted_cartridge"};
|
||||
|
||||
|
|
|
|||
|
|
@ -474,6 +474,10 @@ int LaunchSdlFrontend(int argc, char** argv) {
|
|||
}
|
||||
});
|
||||
|
||||
u64 program_id{};
|
||||
system.GetAppLoader().ReadProgramId(program_id);
|
||||
system.GPU().ApplyPerProgramSettings(program_id);
|
||||
|
||||
std::atomic_bool stop_run;
|
||||
system.GPU().Renderer().Rasterizer()->LoadDefaultDiskResources(
|
||||
stop_run, [](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
|
||||
|
|
|
|||
|
|
@ -330,9 +330,9 @@ private:
|
|||
}
|
||||
backend_thread.request_stop();
|
||||
backend_thread.join();
|
||||
const auto signal_entry =
|
||||
CreateEntry(Class::Log, Level::Critical, "?", 0, "?",
|
||||
fmt::vformat("Received signal {}", fmt::make_format_args(sig)));
|
||||
const auto signal_entry = CreateEntry(
|
||||
Class::Log, Level::Critical, "?", 0, "?",
|
||||
fmt::vformat("Received signal {}", fmt::make_format_args(sig)), time_origin);
|
||||
ForEachBackend([&signal_entry](Backend& backend) {
|
||||
backend.EnableForStacktrace();
|
||||
backend.Write(signal_entry);
|
||||
|
|
@ -345,12 +345,13 @@ private:
|
|||
abort();
|
||||
}
|
||||
line.pop_back(); // Remove newline
|
||||
const auto frame_entry =
|
||||
CreateEntry(Class::Log, Level::Critical, "?", 0, "?", std::move(line));
|
||||
const auto frame_entry = CreateEntry(Class::Log, Level::Critical, "?", 0, "?",
|
||||
std::move(line), time_origin);
|
||||
ForEachBackend([&frame_entry](Backend& backend) { backend.Write(frame_entry); });
|
||||
}
|
||||
using namespace std::literals;
|
||||
const auto rip_entry = CreateEntry(Class::Log, Level::Critical, "?", 0, "?", "RIP"s);
|
||||
const auto rip_entry =
|
||||
CreateEntry(Class::Log, Level::Critical, "?", 0, "?", "RIP"s, time_origin);
|
||||
ForEachBackend([&rip_entry](Backend& backend) {
|
||||
backend.Write(rip_entry);
|
||||
backend.Flush();
|
||||
|
|
|
|||
|
|
@ -515,7 +515,11 @@ struct Values {
|
|||
SwitchableSetting<bool> use_hw_shader{true, "use_hw_shader"};
|
||||
SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
|
||||
SwitchableSetting<bool> shaders_accurate_mul{true, "shaders_accurate_mul"};
|
||||
#ifdef ANDROID // TODO: Fuck this -OS
|
||||
SwitchableSetting<bool> use_vsync{false, "use_vsync"};
|
||||
#else
|
||||
SwitchableSetting<bool> use_vsync{true, "use_vsync"};
|
||||
#endif
|
||||
Setting<bool> use_shader_jit{true, "use_shader_jit"};
|
||||
SwitchableSetting<u32, true> resolution_factor{1, 0, 10, "resolution_factor"};
|
||||
SwitchableSetting<double, true> frame_limit{100, 0, 1000, "frame_limit"};
|
||||
|
|
|
|||
|
|
@ -403,8 +403,6 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
|
|||
|
||||
kernel->UpdateCPUAndMemoryState(program_id, app_mem_mode, app_n3ds_hw_capabilities);
|
||||
|
||||
gpu->ReportLoadingProgramID(program_id);
|
||||
|
||||
// Restore any parameters that should be carried through a reset.
|
||||
if (auto apt = Service::APT::GetModule(*this)) {
|
||||
if (restore_deliver_arg.has_value()) {
|
||||
|
|
@ -896,19 +894,26 @@ void System::serialize(Archive& ar, const unsigned int file_version) {
|
|||
timing->UnlockEventQueue();
|
||||
cheat_engine.Connect(cheats_pid);
|
||||
|
||||
if (Settings::values.custom_textures) {
|
||||
custom_tex_manager->FindCustomTextures();
|
||||
}
|
||||
|
||||
// Re-register gpu callback, because gsp service changed after service_manager got
|
||||
// serialized
|
||||
auto gsp = service_manager->GetService<Service::GSP::GSP_GPU>("gsp::Gpu");
|
||||
gpu->SetInterruptHandler(
|
||||
[gsp](Service::GSP::InterruptId interrupt_id) { gsp->SignalInterrupt(interrupt_id); });
|
||||
|
||||
// Switch the shader cache to the title running when the savestate was created
|
||||
// Apply per program settings and switch the shader cache to the title running when the
|
||||
// savestate was created.
|
||||
// TODO(PabloMK7): Find better way to obtain the program ID.
|
||||
const u32 thread_id = gsp->GetActiveClientThreadId();
|
||||
if (thread_id != std::numeric_limits<u32>::max()) {
|
||||
const auto thread = kernel->GetThreadByID(thread_id);
|
||||
if (thread) {
|
||||
const std::shared_ptr<Kernel::Process> process = thread->owner_process.lock();
|
||||
if (process) {
|
||||
gpu->ApplyPerProgramSettings(process->codeset->program_id);
|
||||
gpu->Renderer().Rasterizer()->SwitchDiskResources(process->codeset->program_id);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -693,6 +693,7 @@ Result GSP_GPU::AcquireGpuRight(const Kernel::HLERequestContext& ctx,
|
|||
Common::Hacks::HackAllowMode::DISALLOW) != Common::Hacks::HackAllowMode::DISALLOW;
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
gpu.ApplyPerProgramSettings(process->codeset->program_id);
|
||||
gpu.GetRightEyeDisabler().SetEnabled(right_eye_disable_allow);
|
||||
gpu.PicaCore().vs_setup.requires_fixup = requires_shader_fixup;
|
||||
gpu.PicaCore().gs_setup.requires_fixup = requires_shader_fixup;
|
||||
|
|
|
|||
|
|
@ -115,9 +115,10 @@ void PLG_LDR::OnProcessRun(Kernel::Process& process, Kernel::KernelSystem& kerne
|
|||
}
|
||||
}
|
||||
FileSys::Plugin3GXLoader plugin_loader;
|
||||
const auto low_title_Id = plgldr_context.user_load_parameters.low_title_Id;
|
||||
if (plgldr_context.use_user_load_parameters &&
|
||||
plgldr_context.user_load_parameters.low_title_Id ==
|
||||
static_cast<u32>(process.codeset->program_id) &&
|
||||
(low_title_Id == static_cast<u32>(process.codeset->program_id) ||
|
||||
low_title_Id == 0 /* Should load for any title */) &&
|
||||
plgldr_context.user_load_parameters.path[0]) {
|
||||
std::string plugin_file = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) +
|
||||
std::string(plgldr_context.user_load_parameters.path + 1);
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(u32* cmd_buf, const Funct
|
|||
void ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& context) {
|
||||
auto itr = handlers.find(context.CommandHeader().command_id.Value());
|
||||
const FunctionInfoBase* info = itr == handlers.end() ? nullptr : &itr->second;
|
||||
if (info == nullptr || !info->implemented) {
|
||||
if (info == nullptr || info->handler_callback == nullptr) {
|
||||
context.ReportUnimplemented();
|
||||
return ReportUnimplementedFunction(context.CommandBuffer(), info);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,6 @@ private:
|
|||
|
||||
struct FunctionInfoBase {
|
||||
u32 command_id;
|
||||
bool implemented;
|
||||
HandlerFnP<ServiceFrameworkBase> handler_callback;
|
||||
const char* name;
|
||||
};
|
||||
|
|
@ -97,8 +96,6 @@ private:
|
|||
void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n);
|
||||
void ReportUnimplementedFunction(u32* cmd_buf, const FunctionInfoBase* info);
|
||||
|
||||
void Empty(Kernel::HLERequestContext& ctx) {}
|
||||
|
||||
/// Identifier string used to connect to the service.
|
||||
std::string service_name;
|
||||
/// Maximum number of concurrent sessions that this service can handle.
|
||||
|
|
@ -137,11 +134,9 @@ protected:
|
|||
*/
|
||||
constexpr FunctionInfo(u32 command_id, HandlerFnP<Self> handler_callback, const char* name)
|
||||
: FunctionInfoBase{
|
||||
command_id, handler_callback != nullptr,
|
||||
command_id,
|
||||
// Type-erase member function pointer by casting it down to the base class.
|
||||
handler_callback ? static_cast<HandlerFnP<ServiceFrameworkBase>>(handler_callback)
|
||||
: &ServiceFrameworkBase::Empty,
|
||||
name} {}
|
||||
static_cast<HandlerFnP<ServiceFrameworkBase>>(handler_callback), name} {}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -311,7 +311,7 @@ GraphicsDebugger& GPU::Debugger() {
|
|||
return impl->gpu_debugger;
|
||||
}
|
||||
|
||||
void GPU::ReportLoadingProgramID(u64 program_ID) {
|
||||
void GPU::ApplyPerProgramSettings(u64 program_ID) {
|
||||
auto hack = Common::Hacks::hack_manager.GetHack(
|
||||
Common::Hacks::HackType::ACCURATE_MULTIPLICATION, program_ID);
|
||||
bool use_accurate_mul = Settings::values.shaders_accurate_mul.GetValue();
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ public:
|
|||
return *right_eye_disabler;
|
||||
}
|
||||
|
||||
void ReportLoadingProgramID(u64 program_ID);
|
||||
void ApplyPerProgramSettings(u64 program_ID);
|
||||
|
||||
private:
|
||||
void SubmitCmdList(u32 index);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "video_core/renderer_vulkan/vk_blit_helper.h"
|
||||
#include "video_core/renderer_vulkan/vk_descriptor_update_queue.h"
|
||||
|
|
@ -17,19 +16,8 @@
|
|||
#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag.h"
|
||||
#include "video_core/host_shaders/vulkan_depth_to_buffer_comp.h"
|
||||
|
||||
// Texture filtering shader includes
|
||||
#include "video_core/host_shaders/texture_filtering/bicubic_frag.h"
|
||||
#include "video_core/host_shaders/texture_filtering/mmpx_frag.h"
|
||||
#include "video_core/host_shaders/texture_filtering/refine_frag.h"
|
||||
#include "video_core/host_shaders/texture_filtering/scale_force_frag.h"
|
||||
#include "video_core/host_shaders/texture_filtering/x_gradient_frag.h"
|
||||
#include "video_core/host_shaders/texture_filtering/xbrz_freescale_frag.h"
|
||||
#include "video_core/host_shaders/texture_filtering/y_gradient_frag.h"
|
||||
#include "vk_blit_helper.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
using Settings::TextureFilter;
|
||||
using VideoCore::PixelFormat;
|
||||
|
||||
namespace {
|
||||
|
|
@ -67,33 +55,8 @@ constexpr std::array<vk::DescriptorSetLayoutBinding, 2> TWO_TEXTURES_BINDINGS =
|
|||
{1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
|
||||
}};
|
||||
|
||||
// Texture filtering descriptor set bindings
|
||||
constexpr std::array<vk::DescriptorSetLayoutBinding, 1> SINGLE_TEXTURE_BINDINGS = {{
|
||||
{0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
|
||||
}};
|
||||
|
||||
constexpr std::array<vk::DescriptorSetLayoutBinding, 3> THREE_TEXTURES_BINDINGS = {{
|
||||
{0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
|
||||
{1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
|
||||
{2, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
|
||||
}};
|
||||
|
||||
// Note: Removed FILTER_UTILITY_BINDINGS as texture filtering doesn't need shadow buffers
|
||||
|
||||
// Push constant structure for texture filtering
|
||||
struct FilterPushConstants {
|
||||
std::array<float, 2> tex_scale;
|
||||
std::array<float, 2> tex_offset;
|
||||
float res_scale; // For xBRZ filter
|
||||
};
|
||||
|
||||
inline constexpr vk::PushConstantRange FILTER_PUSH_CONSTANT_RANGE{
|
||||
.stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
|
||||
.offset = 0,
|
||||
.size = sizeof(FilterPushConstants),
|
||||
};
|
||||
inline constexpr vk::PushConstantRange PUSH_CONSTANT_RANGE{
|
||||
.stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eVertex,
|
||||
.offset = 0,
|
||||
.size = sizeof(PushConstants),
|
||||
};
|
||||
|
|
@ -141,17 +104,12 @@ constexpr vk::PipelineDynamicStateCreateInfo PIPELINE_DYNAMIC_STATE_CREATE_INFO{
|
|||
.dynamicStateCount = static_cast<u32>(DYNAMIC_STATES.size()),
|
||||
.pDynamicStates = DYNAMIC_STATES.data(),
|
||||
};
|
||||
|
||||
constexpr vk::PipelineColorBlendAttachmentState COLOR_BLEND_ATTACHMENT{
|
||||
.blendEnable = VK_FALSE,
|
||||
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
|
||||
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
|
||||
};
|
||||
|
||||
constexpr vk::PipelineColorBlendStateCreateInfo PIPELINE_COLOR_BLEND_STATE_CREATE_INFO{
|
||||
constexpr vk::PipelineColorBlendStateCreateInfo PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO{
|
||||
.logicOpEnable = VK_FALSE,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &COLOR_BLEND_ATTACHMENT,
|
||||
.logicOp = vk::LogicOp::eClear,
|
||||
.attachmentCount = 0,
|
||||
.pAttachments = nullptr,
|
||||
.blendConstants = std::array{0.0f, 0.0f, 0.0f, 0.0f},
|
||||
};
|
||||
constexpr vk::PipelineDepthStencilStateCreateInfo PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO{
|
||||
.depthTestEnable = VK_TRUE,
|
||||
|
|
@ -170,9 +128,9 @@ inline constexpr vk::SamplerCreateInfo SAMPLER_CREATE_INFO{
|
|||
.magFilter = filter,
|
||||
.minFilter = filter,
|
||||
.mipmapMode = vk::SamplerMipmapMode::eNearest,
|
||||
.addressModeU = vk::SamplerAddressMode::eClampToEdge,
|
||||
.addressModeV = vk::SamplerAddressMode::eClampToEdge,
|
||||
.addressModeW = vk::SamplerAddressMode::eClampToEdge,
|
||||
.addressModeU = vk::SamplerAddressMode::eClampToBorder,
|
||||
.addressModeV = vk::SamplerAddressMode::eClampToBorder,
|
||||
.addressModeW = vk::SamplerAddressMode::eClampToBorder,
|
||||
.mipLodBias = 0.0f,
|
||||
.anisotropyEnable = VK_FALSE,
|
||||
.maxAnisotropy = 0.0f,
|
||||
|
|
@ -185,14 +143,12 @@ inline constexpr vk::SamplerCreateInfo SAMPLER_CREATE_INFO{
|
|||
};
|
||||
|
||||
constexpr vk::PipelineLayoutCreateInfo PipelineLayoutCreateInfo(
|
||||
const vk::DescriptorSetLayout* set_layout, bool compute = false, bool filter = false) {
|
||||
const vk::DescriptorSetLayout* set_layout, bool compute = false) {
|
||||
return vk::PipelineLayoutCreateInfo{
|
||||
.setLayoutCount = 1,
|
||||
.pSetLayouts = set_layout,
|
||||
.pushConstantRangeCount = 1,
|
||||
.pPushConstantRanges =
|
||||
(compute ? &COMPUTE_PUSH_CONSTANT_RANGE
|
||||
: (filter ? &FILTER_PUSH_CONSTANT_RANGE : &PUSH_CONSTANT_RANGE)),
|
||||
.pPushConstantRanges = (compute ? &COMPUTE_PUSH_CONSTANT_RANGE : &PUSH_CONSTANT_RANGE),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -229,20 +185,12 @@ BlitHelper::BlitHelper(const Instance& instance_, Scheduler& scheduler_,
|
|||
compute_provider{instance, scheduler.GetMasterSemaphore(), COMPUTE_BINDINGS},
|
||||
compute_buffer_provider{instance, scheduler.GetMasterSemaphore(), COMPUTE_BUFFER_BINDINGS},
|
||||
two_textures_provider{instance, scheduler.GetMasterSemaphore(), TWO_TEXTURES_BINDINGS, 16},
|
||||
single_texture_provider{instance, scheduler.GetMasterSemaphore(), SINGLE_TEXTURE_BINDINGS,
|
||||
16},
|
||||
three_textures_provider{instance, scheduler.GetMasterSemaphore(), THREE_TEXTURES_BINDINGS,
|
||||
16},
|
||||
compute_pipeline_layout{
|
||||
device.createPipelineLayout(PipelineLayoutCreateInfo(&compute_provider.Layout(), true))},
|
||||
compute_buffer_pipeline_layout{device.createPipelineLayout(
|
||||
PipelineLayoutCreateInfo(&compute_buffer_provider.Layout(), true))},
|
||||
two_textures_pipeline_layout{
|
||||
device.createPipelineLayout(PipelineLayoutCreateInfo(&two_textures_provider.Layout()))},
|
||||
single_texture_pipeline_layout{device.createPipelineLayout(
|
||||
PipelineLayoutCreateInfo(&single_texture_provider.Layout(), false, true))},
|
||||
three_textures_pipeline_layout{device.createPipelineLayout(
|
||||
PipelineLayoutCreateInfo(&three_textures_provider.Layout(), false, true))},
|
||||
full_screen_vert{Compile(HostShaders::FULL_SCREEN_TRIANGLE_VERT,
|
||||
vk::ShaderStageFlagBits::eVertex, device)},
|
||||
d24s8_to_rgba8_comp{Compile(HostShaders::VULKAN_D24S8_TO_RGBA8_COMP,
|
||||
|
|
@ -251,14 +199,6 @@ BlitHelper::BlitHelper(const Instance& instance_, Scheduler& scheduler_,
|
|||
vk::ShaderStageFlagBits::eCompute, device)},
|
||||
blit_depth_stencil_frag{Compile(HostShaders::VULKAN_BLIT_DEPTH_STENCIL_FRAG,
|
||||
vk::ShaderStageFlagBits::eFragment, device)},
|
||||
// Texture filtering shader modules
|
||||
bicubic_frag{Compile(HostShaders::BICUBIC_FRAG, vk::ShaderStageFlagBits::eFragment, device)},
|
||||
scale_force_frag{
|
||||
Compile(HostShaders::SCALE_FORCE_FRAG, vk::ShaderStageFlagBits::eFragment, device)},
|
||||
xbrz_frag{
|
||||
Compile(HostShaders::XBRZ_FREESCALE_FRAG, vk::ShaderStageFlagBits::eFragment, device)},
|
||||
mmpx_frag{Compile(HostShaders::MMPX_FRAG, vk::ShaderStageFlagBits::eFragment, device)},
|
||||
refine_frag{Compile(HostShaders::REFINE_FRAG, vk::ShaderStageFlagBits::eFragment, device)},
|
||||
d24s8_to_rgba8_pipeline{MakeComputePipeline(d24s8_to_rgba8_comp, compute_pipeline_layout)},
|
||||
depth_to_buffer_pipeline{
|
||||
MakeComputePipeline(depth_to_buffer_comp, compute_buffer_pipeline_layout)},
|
||||
|
|
@ -290,18 +230,10 @@ BlitHelper::~BlitHelper() {
|
|||
device.destroyPipelineLayout(compute_pipeline_layout);
|
||||
device.destroyPipelineLayout(compute_buffer_pipeline_layout);
|
||||
device.destroyPipelineLayout(two_textures_pipeline_layout);
|
||||
device.destroyPipelineLayout(single_texture_pipeline_layout);
|
||||
device.destroyPipelineLayout(three_textures_pipeline_layout);
|
||||
device.destroyShaderModule(full_screen_vert);
|
||||
device.destroyShaderModule(d24s8_to_rgba8_comp);
|
||||
device.destroyShaderModule(depth_to_buffer_comp);
|
||||
device.destroyShaderModule(blit_depth_stencil_frag);
|
||||
// Destroy texture filtering shader modules
|
||||
device.destroyShaderModule(bicubic_frag);
|
||||
device.destroyShaderModule(scale_force_frag);
|
||||
device.destroyShaderModule(xbrz_frag);
|
||||
device.destroyShaderModule(mmpx_frag);
|
||||
device.destroyShaderModule(refine_frag);
|
||||
device.destroyPipeline(depth_to_buffer_pipeline);
|
||||
device.destroyPipeline(d24s8_to_rgba8_pipeline);
|
||||
device.destroyPipeline(depth_blit_pipeline);
|
||||
|
|
@ -310,7 +242,7 @@ BlitHelper::~BlitHelper() {
|
|||
}
|
||||
|
||||
void BindBlitState(vk::CommandBuffer cmdbuf, vk::PipelineLayout layout,
|
||||
const VideoCore::TextureBlit& blit, const Surface& dest) {
|
||||
const VideoCore::TextureBlit& blit) {
|
||||
const vk::Offset2D offset{
|
||||
.x = std::min<s32>(blit.dst_rect.left, blit.dst_rect.right),
|
||||
.y = std::min<s32>(blit.dst_rect.bottom, blit.dst_rect.top),
|
||||
|
|
@ -340,9 +272,8 @@ void BindBlitState(vk::CommandBuffer cmdbuf, vk::PipelineLayout layout,
|
|||
};
|
||||
cmdbuf.setViewport(0, viewport);
|
||||
cmdbuf.setScissor(0, scissor);
|
||||
cmdbuf.pushConstants(layout,
|
||||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, 0,
|
||||
sizeof(push_constants), &push_constants);
|
||||
cmdbuf.pushConstants(layout, vk::ShaderStageFlagBits::eVertex, 0, sizeof(push_constants),
|
||||
&push_constants);
|
||||
}
|
||||
|
||||
bool BlitHelper::BlitDepthStencil(Surface& source, Surface& dest,
|
||||
|
|
@ -369,12 +300,12 @@ bool BlitHelper::BlitDepthStencil(Surface& source, Surface& dest,
|
|||
};
|
||||
renderpass_cache.BeginRendering(depth_pass);
|
||||
|
||||
scheduler.Record([blit, descriptor_set, &dest, this](vk::CommandBuffer cmdbuf) {
|
||||
scheduler.Record([blit, descriptor_set, this](vk::CommandBuffer cmdbuf) {
|
||||
const vk::PipelineLayout layout = two_textures_pipeline_layout;
|
||||
|
||||
cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, depth_blit_pipeline);
|
||||
cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, layout, 0, descriptor_set, {});
|
||||
BindBlitState(cmdbuf, layout, blit, dest);
|
||||
BindBlitState(cmdbuf, layout, blit);
|
||||
cmdbuf.draw(3, 1, 0, 0);
|
||||
});
|
||||
scheduler.MakeDirty(StateFlags::Pipeline);
|
||||
|
|
@ -600,7 +531,7 @@ vk::Pipeline BlitHelper::MakeDepthStencilBlitPipeline() {
|
|||
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||||
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
|
||||
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.layout = two_textures_pipeline_layout,
|
||||
.renderPass = renderpass,
|
||||
|
|
@ -616,280 +547,4 @@ vk::Pipeline BlitHelper::MakeDepthStencilBlitPipeline() {
|
|||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
bool BlitHelper::Filter(Surface& surface, const VideoCore::TextureBlit& blit) {
|
||||
const auto filter = Settings::values.texture_filter.GetValue();
|
||||
const bool is_depth =
|
||||
surface.type == VideoCore::SurfaceType::Depth ||
|
||||
surface.type == VideoCore::SurfaceType::DepthStencil; // Skip filtering for depth textures
|
||||
// and when no filter is selected
|
||||
if (filter == Settings::TextureFilter::NoFilter || is_depth) {
|
||||
return false;
|
||||
} // Only filter base mipmap level
|
||||
if (blit.src_level != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (filter) {
|
||||
case TextureFilter::Anime4K:
|
||||
FilterAnime4K(surface, blit);
|
||||
break;
|
||||
case TextureFilter::Bicubic:
|
||||
FilterBicubic(surface, blit);
|
||||
break;
|
||||
case TextureFilter::ScaleForce:
|
||||
FilterScaleForce(surface, blit);
|
||||
break;
|
||||
case TextureFilter::xBRZ:
|
||||
FilterXbrz(surface, blit);
|
||||
break;
|
||||
case TextureFilter::MMPX:
|
||||
FilterMMPX(surface, blit);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Render_Vulkan, "Unknown texture filter {}", filter);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BlitHelper::FilterAnime4K(Surface& surface, const VideoCore::TextureBlit& blit) {
|
||||
const bool is_depth = surface.type == VideoCore::SurfaceType::Depth ||
|
||||
surface.type == VideoCore::SurfaceType::DepthStencil;
|
||||
const auto color_format = is_depth ? VideoCore::PixelFormat::Invalid : surface.pixel_format;
|
||||
auto pipeline = MakeFilterPipeline(refine_frag, three_textures_pipeline_layout, color_format);
|
||||
FilterPassThreeTextures(surface, surface, surface, surface, pipeline,
|
||||
three_textures_pipeline_layout, blit);
|
||||
}
|
||||
|
||||
void BlitHelper::FilterBicubic(Surface& surface, const VideoCore::TextureBlit& blit) {
|
||||
const bool is_depth = surface.type == VideoCore::SurfaceType::Depth ||
|
||||
surface.type == VideoCore::SurfaceType::DepthStencil;
|
||||
const auto color_format = is_depth ? VideoCore::PixelFormat::Invalid : surface.pixel_format;
|
||||
auto pipeline = MakeFilterPipeline(bicubic_frag, single_texture_pipeline_layout, color_format);
|
||||
FilterPass(surface, surface, pipeline, single_texture_pipeline_layout, blit);
|
||||
}
|
||||
|
||||
void BlitHelper::FilterScaleForce(Surface& surface, const VideoCore::TextureBlit& blit) {
|
||||
const bool is_depth = surface.type == VideoCore::SurfaceType::Depth ||
|
||||
surface.type == VideoCore::SurfaceType::DepthStencil;
|
||||
const auto color_format = is_depth ? VideoCore::PixelFormat::Invalid : surface.pixel_format;
|
||||
auto pipeline =
|
||||
MakeFilterPipeline(scale_force_frag, single_texture_pipeline_layout, color_format);
|
||||
FilterPass(surface, surface, pipeline, single_texture_pipeline_layout, blit);
|
||||
}
|
||||
|
||||
void BlitHelper::FilterXbrz(Surface& surface, const VideoCore::TextureBlit& blit) {
|
||||
const bool is_depth = surface.type == VideoCore::SurfaceType::Depth ||
|
||||
surface.type == VideoCore::SurfaceType::DepthStencil;
|
||||
const auto color_format = is_depth ? VideoCore::PixelFormat::Invalid : surface.pixel_format;
|
||||
auto pipeline = MakeFilterPipeline(xbrz_frag, single_texture_pipeline_layout, color_format);
|
||||
FilterPass(surface, surface, pipeline, single_texture_pipeline_layout, blit);
|
||||
}
|
||||
|
||||
void BlitHelper::FilterMMPX(Surface& surface, const VideoCore::TextureBlit& blit) {
|
||||
const bool is_depth = surface.type == VideoCore::SurfaceType::Depth ||
|
||||
surface.type == VideoCore::SurfaceType::DepthStencil;
|
||||
const auto color_format = is_depth ? VideoCore::PixelFormat::Invalid : surface.pixel_format;
|
||||
auto pipeline = MakeFilterPipeline(mmpx_frag, single_texture_pipeline_layout, color_format);
|
||||
FilterPass(surface, surface, pipeline, single_texture_pipeline_layout, blit);
|
||||
}
|
||||
|
||||
vk::Pipeline BlitHelper::MakeFilterPipeline(vk::ShaderModule fragment_shader,
|
||||
vk::PipelineLayout layout,
|
||||
VideoCore::PixelFormat color_format) {
|
||||
const std::array stages = MakeStages(full_screen_vert, fragment_shader);
|
||||
// Use the provided color format for render pass compatibility
|
||||
const auto renderpass =
|
||||
renderpass_cache.GetRenderpass(color_format, VideoCore::PixelFormat::Invalid, false);
|
||||
|
||||
vk::GraphicsPipelineCreateInfo pipeline_info = {
|
||||
.stageCount = static_cast<u32>(stages.size()),
|
||||
.pStages = stages.data(),
|
||||
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
.pTessellationState = nullptr,
|
||||
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.pDepthStencilState = nullptr,
|
||||
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.layout = layout,
|
||||
.renderPass = renderpass,
|
||||
};
|
||||
|
||||
if (const auto result = device.createGraphicsPipeline({}, pipeline_info);
|
||||
result.result == vk::Result::eSuccess) {
|
||||
return result.value;
|
||||
} else {
|
||||
LOG_CRITICAL(Render_Vulkan, "Filter pipeline creation failed!");
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void BlitHelper::FilterPass(Surface& source, Surface& dest, vk::Pipeline pipeline,
|
||||
vk::PipelineLayout layout, const VideoCore::TextureBlit& blit) {
|
||||
const auto texture_descriptor_set = single_texture_provider.Commit();
|
||||
update_queue.AddImageSampler(texture_descriptor_set, 0, 0, source.ImageView(0), linear_sampler,
|
||||
vk::ImageLayout::eGeneral);
|
||||
|
||||
const bool is_depth = dest.type == VideoCore::SurfaceType::Depth ||
|
||||
dest.type == VideoCore::SurfaceType::DepthStencil;
|
||||
const auto color_format = is_depth ? VideoCore::PixelFormat::Invalid : dest.pixel_format;
|
||||
const auto depth_format = is_depth ? dest.pixel_format : VideoCore::PixelFormat::Invalid;
|
||||
const auto renderpass = renderpass_cache.GetRenderpass(color_format, depth_format, false);
|
||||
|
||||
const RenderPass render_pass = {
|
||||
.framebuffer = dest.Framebuffer(),
|
||||
.render_pass = renderpass,
|
||||
.render_area =
|
||||
{
|
||||
.offset = {0, 0},
|
||||
.extent = {dest.GetScaledWidth(), dest.GetScaledHeight()},
|
||||
},
|
||||
};
|
||||
renderpass_cache.BeginRendering(render_pass);
|
||||
const float src_scale = static_cast<float>(source.GetResScale());
|
||||
// Calculate normalized texture coordinates like OpenGL does
|
||||
const auto src_extent = source.RealExtent(false); // Get unscaled texture extent
|
||||
const float tex_scale_x =
|
||||
static_cast<float>(blit.src_rect.GetWidth()) / static_cast<float>(src_extent.width);
|
||||
const float tex_scale_y =
|
||||
static_cast<float>(blit.src_rect.GetHeight()) / static_cast<float>(src_extent.height);
|
||||
const float tex_offset_x =
|
||||
static_cast<float>(blit.src_rect.left) / static_cast<float>(src_extent.width);
|
||||
const float tex_offset_y =
|
||||
static_cast<float>(blit.src_rect.bottom) / static_cast<float>(src_extent.height);
|
||||
|
||||
scheduler.Record([pipeline, layout, texture_descriptor_set, blit, tex_scale_x, tex_scale_y,
|
||||
tex_offset_x, tex_offset_y, src_scale](vk::CommandBuffer cmdbuf) {
|
||||
const FilterPushConstants push_constants{.tex_scale = {tex_scale_x, tex_scale_y},
|
||||
.tex_offset = {tex_offset_x, tex_offset_y},
|
||||
.res_scale = src_scale};
|
||||
|
||||
cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
|
||||
|
||||
// Bind single texture descriptor set
|
||||
cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, layout, 0,
|
||||
texture_descriptor_set, {});
|
||||
|
||||
cmdbuf.pushConstants(layout, FILTER_PUSH_CONSTANT_RANGE.stageFlags,
|
||||
FILTER_PUSH_CONSTANT_RANGE.offset, FILTER_PUSH_CONSTANT_RANGE.size,
|
||||
&push_constants);
|
||||
|
||||
// Set up viewport and scissor for filtering (don't use BindBlitState as it overwrites push
|
||||
// constants)
|
||||
const vk::Offset2D offset{
|
||||
.x = std::min<s32>(blit.dst_rect.left, blit.dst_rect.right),
|
||||
.y = std::min<s32>(blit.dst_rect.bottom, blit.dst_rect.top),
|
||||
};
|
||||
const vk::Extent2D extent{
|
||||
.width = blit.dst_rect.GetWidth(),
|
||||
.height = blit.dst_rect.GetHeight(),
|
||||
};
|
||||
const vk::Viewport viewport{
|
||||
.x = static_cast<float>(offset.x),
|
||||
.y = static_cast<float>(offset.y),
|
||||
.width = static_cast<float>(extent.width),
|
||||
.height = static_cast<float>(extent.height),
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f,
|
||||
};
|
||||
const vk::Rect2D scissor{
|
||||
.offset = offset,
|
||||
.extent = extent,
|
||||
};
|
||||
cmdbuf.setViewport(0, viewport);
|
||||
cmdbuf.setScissor(0, scissor);
|
||||
cmdbuf.draw(3, 1, 0, 0);
|
||||
});
|
||||
scheduler.MakeDirty(StateFlags::Pipeline);
|
||||
}
|
||||
|
||||
void BlitHelper::FilterPassThreeTextures(Surface& source1, Surface& source2, Surface& source3,
|
||||
Surface& dest, vk::Pipeline pipeline,
|
||||
vk::PipelineLayout layout,
|
||||
const VideoCore::TextureBlit& blit) {
|
||||
const auto texture_descriptor_set = three_textures_provider.Commit();
|
||||
|
||||
update_queue.AddImageSampler(texture_descriptor_set, 0, 0, source1.ImageView(0), linear_sampler,
|
||||
vk::ImageLayout::eGeneral);
|
||||
update_queue.AddImageSampler(texture_descriptor_set, 1, 0, source2.ImageView(0), linear_sampler,
|
||||
vk::ImageLayout::eGeneral);
|
||||
update_queue.AddImageSampler(texture_descriptor_set, 2, 0, source3.ImageView(0), linear_sampler,
|
||||
vk::ImageLayout::eGeneral);
|
||||
|
||||
const bool is_depth = dest.type == VideoCore::SurfaceType::Depth ||
|
||||
dest.type == VideoCore::SurfaceType::DepthStencil;
|
||||
const auto color_format = is_depth ? VideoCore::PixelFormat::Invalid : dest.pixel_format;
|
||||
const auto depth_format = is_depth ? dest.pixel_format : VideoCore::PixelFormat::Invalid;
|
||||
const auto renderpass = renderpass_cache.GetRenderpass(color_format, depth_format, false);
|
||||
|
||||
const RenderPass render_pass = {
|
||||
.framebuffer = dest.Framebuffer(),
|
||||
.render_pass = renderpass,
|
||||
.render_area =
|
||||
{
|
||||
.offset = {0, 0},
|
||||
.extent = {dest.GetScaledWidth(), dest.GetScaledHeight()},
|
||||
},
|
||||
};
|
||||
renderpass_cache.BeginRendering(render_pass);
|
||||
|
||||
const float src_scale = static_cast<float>(source1.GetResScale());
|
||||
// Calculate normalized texture coordinates like OpenGL does
|
||||
const auto src_extent = source1.RealExtent(false); // Get unscaled texture extent
|
||||
const float tex_scale_x =
|
||||
static_cast<float>(blit.src_rect.GetWidth()) / static_cast<float>(src_extent.width);
|
||||
const float tex_scale_y =
|
||||
static_cast<float>(blit.src_rect.GetHeight()) / static_cast<float>(src_extent.height);
|
||||
const float tex_offset_x =
|
||||
static_cast<float>(blit.src_rect.left) / static_cast<float>(src_extent.width);
|
||||
const float tex_offset_y =
|
||||
static_cast<float>(blit.src_rect.bottom) / static_cast<float>(src_extent.height);
|
||||
|
||||
scheduler.Record([pipeline, layout, texture_descriptor_set, blit, tex_scale_x, tex_scale_y,
|
||||
tex_offset_x, tex_offset_y, src_scale](vk::CommandBuffer cmdbuf) {
|
||||
const FilterPushConstants push_constants{.tex_scale = {tex_scale_x, tex_scale_y},
|
||||
.tex_offset = {tex_offset_x, tex_offset_y},
|
||||
.res_scale = src_scale};
|
||||
|
||||
cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
|
||||
|
||||
// Bind single texture descriptor set
|
||||
cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, layout, 0,
|
||||
texture_descriptor_set, {});
|
||||
|
||||
cmdbuf.pushConstants(layout, FILTER_PUSH_CONSTANT_RANGE.stageFlags,
|
||||
FILTER_PUSH_CONSTANT_RANGE.offset, FILTER_PUSH_CONSTANT_RANGE.size,
|
||||
&push_constants);
|
||||
|
||||
// Set up viewport and scissor using safe viewport like working filters
|
||||
const vk::Offset2D offset{
|
||||
.x = std::min<s32>(blit.dst_rect.left, blit.dst_rect.right),
|
||||
.y = std::min<s32>(blit.dst_rect.bottom, blit.dst_rect.top),
|
||||
};
|
||||
const vk::Extent2D extent{
|
||||
.width = blit.dst_rect.GetWidth(),
|
||||
.height = blit.dst_rect.GetHeight(),
|
||||
};
|
||||
const vk::Viewport viewport{
|
||||
.x = static_cast<float>(offset.x),
|
||||
.y = static_cast<float>(offset.y),
|
||||
.width = static_cast<float>(extent.width),
|
||||
.height = static_cast<float>(extent.height),
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f,
|
||||
};
|
||||
const vk::Rect2D scissor{
|
||||
.offset = offset,
|
||||
.extent = extent,
|
||||
};
|
||||
cmdbuf.setViewport(0, viewport);
|
||||
cmdbuf.setScissor(0, scissor);
|
||||
cmdbuf.draw(3, 1, 0, 0);
|
||||
});
|
||||
scheduler.MakeDirty(StateFlags::Pipeline);
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||
#include "video_core/renderer_vulkan/vk_resource_pool.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
|
@ -28,7 +27,6 @@ public:
|
|||
explicit BlitHelper(const Instance& instance, Scheduler& scheduler,
|
||||
RenderManager& renderpass_cache, DescriptorUpdateQueue& update_queue);
|
||||
~BlitHelper();
|
||||
bool Filter(Surface& surface, const VideoCore::TextureBlit& blit);
|
||||
|
||||
bool BlitDepthStencil(Surface& source, Surface& dest, const VideoCore::TextureBlit& blit);
|
||||
|
||||
|
|
@ -40,25 +38,6 @@ public:
|
|||
private:
|
||||
vk::Pipeline MakeComputePipeline(vk::ShaderModule shader, vk::PipelineLayout layout);
|
||||
vk::Pipeline MakeDepthStencilBlitPipeline();
|
||||
vk::Pipeline MakeFilterPipeline(
|
||||
vk::ShaderModule fragment_shader, vk::PipelineLayout layout,
|
||||
VideoCore::PixelFormat color_format = VideoCore::PixelFormat::RGBA8);
|
||||
|
||||
void FilterAnime4K(Surface& surface, const VideoCore::TextureBlit& blit);
|
||||
void FilterBicubic(Surface& surface, const VideoCore::TextureBlit& blit);
|
||||
void FilterScaleForce(Surface& surface, const VideoCore::TextureBlit& blit);
|
||||
void FilterXbrz(Surface& surface, const VideoCore::TextureBlit& blit);
|
||||
void FilterMMPX(Surface& surface, const VideoCore::TextureBlit& blit);
|
||||
|
||||
void FilterPass(Surface& source, Surface& dest, vk::Pipeline pipeline,
|
||||
vk::PipelineLayout layout, const VideoCore::TextureBlit& blit);
|
||||
|
||||
void FilterPassThreeTextures(Surface& source1, Surface& source2, Surface& source3,
|
||||
Surface& dest, vk::Pipeline pipeline, vk::PipelineLayout layout,
|
||||
const VideoCore::TextureBlit& blit);
|
||||
|
||||
void FilterPassYGradient(Surface& source, Surface& dest, vk::Pipeline pipeline,
|
||||
vk::PipelineLayout layout, const VideoCore::TextureBlit& blit);
|
||||
|
||||
private:
|
||||
const Instance& instance;
|
||||
|
|
@ -72,23 +51,14 @@ private:
|
|||
DescriptorHeap compute_provider;
|
||||
DescriptorHeap compute_buffer_provider;
|
||||
DescriptorHeap two_textures_provider;
|
||||
DescriptorHeap single_texture_provider;
|
||||
DescriptorHeap three_textures_provider;
|
||||
vk::PipelineLayout compute_pipeline_layout;
|
||||
vk::PipelineLayout compute_buffer_pipeline_layout;
|
||||
vk::PipelineLayout two_textures_pipeline_layout;
|
||||
vk::PipelineLayout single_texture_pipeline_layout;
|
||||
vk::PipelineLayout three_textures_pipeline_layout;
|
||||
|
||||
vk::ShaderModule full_screen_vert;
|
||||
vk::ShaderModule d24s8_to_rgba8_comp;
|
||||
vk::ShaderModule depth_to_buffer_comp;
|
||||
vk::ShaderModule blit_depth_stencil_frag;
|
||||
vk::ShaderModule bicubic_frag;
|
||||
vk::ShaderModule scale_force_frag;
|
||||
vk::ShaderModule xbrz_frag;
|
||||
vk::ShaderModule mmpx_frag;
|
||||
vk::ShaderModule refine_frag;
|
||||
|
||||
vk::Pipeline d24s8_to_rgba8_pipeline;
|
||||
vk::Pipeline depth_to_buffer_pipeline;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
|
@ -103,14 +103,13 @@ vk::CommandBuffer CommandPool::Commit() {
|
|||
return cmd_buffers[index];
|
||||
}
|
||||
|
||||
constexpr u32 DESCRIPTOR_SET_BATCH = 64;
|
||||
constexpr u32 DESCRIPTOR_MULTIPLIER = 4; // Increase capacity of each pool
|
||||
constexpr u32 DESCRIPTOR_SET_BATCH = 32;
|
||||
|
||||
DescriptorHeap::DescriptorHeap(const Instance& instance, MasterSemaphore* master_semaphore,
|
||||
std::span<const vk::DescriptorSetLayoutBinding> bindings,
|
||||
u32 descriptor_heap_count_)
|
||||
: ResourcePool{master_semaphore, DESCRIPTOR_SET_BATCH}, device{instance.GetDevice()},
|
||||
descriptor_heap_count{descriptor_heap_count_ * DESCRIPTOR_MULTIPLIER} { // Increase pool size
|
||||
descriptor_heap_count{descriptor_heap_count_} {
|
||||
// Create descriptor set layout.
|
||||
const vk::DescriptorSetLayoutCreateInfo layout_ci = {
|
||||
.bindingCount = static_cast<u32>(bindings.size()),
|
||||
|
|
|
|||
|
|
@ -1,23 +1,9 @@
|
|||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "video_core/renderer_vulkan/vk_texture_runtime.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include "video_core/custom_textures/custom_tex_manager.h"
|
||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||
#include "video_core/rasterizer_cache/surface_params.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/renderer_vulkan/vk_blit_helper.h"
|
||||
#include "video_core/renderer_vulkan/vk_descriptor_update_queue.h"
|
||||
#include "video_core/renderer_vulkan/vk_stream_buffer.h"
|
||||
|
||||
#include "common/literals.h"
|
||||
#include "common/microprofile.h"
|
||||
|
|
@ -714,7 +700,7 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& param
|
|||
: SurfaceBase{params}, runtime{&runtime_}, instance{&runtime_.GetInstance()},
|
||||
scheduler{&runtime_.GetScheduler()}, traits{instance->GetTraits(pixel_format)} {
|
||||
|
||||
if (pixel_format == VideoCore::PixelFormat::Invalid || !traits.transfer_support) {
|
||||
if (pixel_format == VideoCore::PixelFormat::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -734,25 +720,18 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& param
|
|||
flags |= vk::ImageCreateFlagBits::eMutableFormat;
|
||||
}
|
||||
|
||||
// Ensure color formats have the color attachment bit set for framebuffers
|
||||
auto usage = traits.usage;
|
||||
const bool is_color =
|
||||
(traits.aspect & vk::ImageAspectFlagBits::eColor) != vk::ImageAspectFlags{};
|
||||
if (is_color) {
|
||||
usage |= vk::ImageUsageFlagBits::eColorAttachment;
|
||||
}
|
||||
|
||||
const bool need_format_list = is_mutable && instance->IsImageFormatListSupported();
|
||||
handles[0] = MakeHandle(instance, width, height, levels, texture_type, format, usage, flags,
|
||||
traits.aspect, need_format_list, DebugName(false));
|
||||
handles[0] = MakeHandle(instance, width, height, levels, texture_type, format, traits.usage,
|
||||
flags, traits.aspect, need_format_list, DebugName(false));
|
||||
raw_images.emplace_back(handles[0].image);
|
||||
|
||||
if (res_scale != 1) {
|
||||
handles[1] =
|
||||
MakeHandle(instance, GetScaledWidth(), GetScaledHeight(), levels, texture_type, format,
|
||||
usage, flags, traits.aspect, need_format_list, DebugName(true));
|
||||
traits.usage, flags, traits.aspect, need_format_list, DebugName(true));
|
||||
raw_images.emplace_back(handles[1].image);
|
||||
}
|
||||
|
||||
runtime->renderpass_cache.EndRendering();
|
||||
scheduler->Record([raw_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) {
|
||||
const auto barriers = MakeInitBarriers(aspect, raw_images);
|
||||
|
|
@ -809,49 +788,6 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceBase& surface
|
|||
material = mat;
|
||||
}
|
||||
|
||||
Surface::Surface(TextureRuntime& runtime_, u32 width_, u32 height_, VideoCore::PixelFormat format_)
|
||||
: SurfaceBase{{
|
||||
.width = width_,
|
||||
.height = height_,
|
||||
.pixel_format = format_,
|
||||
.type = VideoCore::SurfaceType::Texture,
|
||||
}},
|
||||
runtime{&runtime_}, instance{&runtime_.GetInstance()}, scheduler{&runtime_.GetScheduler()},
|
||||
traits{instance->GetTraits(format_)} {
|
||||
|
||||
// Create texture with requested size and format
|
||||
const vk::ImageUsageFlags usage =
|
||||
vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst |
|
||||
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled;
|
||||
handles[0] = MakeHandle(instance, width_, height_, 1, VideoCore::TextureType::Texture2D,
|
||||
traits.native, usage, {}, traits.aspect, false, "Temporary Surface");
|
||||
|
||||
// Create image view
|
||||
const vk::ImageViewCreateInfo view_info = {
|
||||
.image = handles[0].image,
|
||||
.viewType = vk::ImageViewType::e2D,
|
||||
.format = traits.native,
|
||||
.subresourceRange{
|
||||
.aspectMask = traits.aspect,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
|
||||
handles[0].image_view = instance->GetDevice().createImageViewUnique(view_info);
|
||||
|
||||
runtime->renderpass_cache.EndRendering();
|
||||
scheduler->Record(
|
||||
[raw_images = std::array{Image()}, aspect = traits.aspect](vk::CommandBuffer cmdbuf) {
|
||||
const auto barriers = MakeInitBarriers(aspect, raw_images);
|
||||
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
|
||||
vk::PipelineStageFlagBits::eTopOfPipe,
|
||||
vk::DependencyFlagBits::eByRegion, {}, {}, barriers);
|
||||
});
|
||||
}
|
||||
|
||||
Surface::~Surface() {
|
||||
if (!handles[0].image_view) {
|
||||
return;
|
||||
|
|
@ -940,23 +876,14 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload,
|
|||
runtime->upload_buffer.Commit(staging.size);
|
||||
|
||||
if (res_scale != 1) {
|
||||
// Always ensure the scaled image exists
|
||||
if (!handles[1].image) {
|
||||
// This will create handles[1] and perform the initial scaling
|
||||
ScaleUp(res_scale);
|
||||
} else {
|
||||
// Update the scaled version of the uploaded area
|
||||
const VideoCore::TextureBlit blit = {
|
||||
.src_level = upload.texture_level,
|
||||
.dst_level = upload.texture_level,
|
||||
.src_rect = upload.texture_rect,
|
||||
.dst_rect = upload.texture_rect * res_scale,
|
||||
};
|
||||
// Only apply texture filtering when upscaling, matching OpenGL behavior
|
||||
if (res_scale != 1 && !runtime->blit_helper.Filter(*this, blit)) {
|
||||
BlitScale(blit, true);
|
||||
}
|
||||
}
|
||||
const VideoCore::TextureBlit blit = {
|
||||
.src_level = upload.texture_level,
|
||||
.dst_level = upload.texture_level,
|
||||
.src_rect = upload.texture_rect,
|
||||
.dst_rect = upload.texture_rect * res_scale,
|
||||
};
|
||||
|
||||
BlitScale(blit, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1322,6 +1249,11 @@ vk::ImageView Surface::ImageView(u32 index) const noexcept {
|
|||
return image_view;
|
||||
}
|
||||
|
||||
vk::ImageView Surface::FramebufferView() noexcept {
|
||||
is_framebuffer = true;
|
||||
return ImageView();
|
||||
}
|
||||
|
||||
vk::ImageView Surface::DepthView() noexcept {
|
||||
if (depth_view) {
|
||||
return depth_view.get();
|
||||
|
|
@ -1397,8 +1329,6 @@ vk::ImageView Surface::StorageView() noexcept {
|
|||
}
|
||||
|
||||
vk::Framebuffer Surface::Framebuffer() noexcept {
|
||||
is_framebuffer = true;
|
||||
|
||||
const u32 index = res_scale == 1 ? 0u : 1u;
|
||||
if (framebuffers[index]) {
|
||||
return framebuffers[index].get();
|
||||
|
|
@ -1409,29 +1339,12 @@ vk::Framebuffer Surface::Framebuffer() noexcept {
|
|||
const auto depth_format = is_depth ? pixel_format : PixelFormat::Invalid;
|
||||
const auto render_pass =
|
||||
runtime->renderpass_cache.GetRenderpass(color_format, depth_format, false);
|
||||
// Use AttachmentView() to get single mip level view for framebuffer
|
||||
const auto attachments = std::array{AttachmentView()};
|
||||
const auto attachments = std::array{ImageView()};
|
||||
framebuffers[index] = MakeFramebuffer(instance->GetDevice(), render_pass, GetScaledWidth(),
|
||||
GetScaledHeight(), attachments);
|
||||
return framebuffers[index].get();
|
||||
}
|
||||
|
||||
vk::ImageView Surface::AttachmentView() noexcept {
|
||||
const vk::ImageViewCreateInfo view_info = {
|
||||
.image = Image(),
|
||||
.viewType = vk::ImageViewType::e2D,
|
||||
.format = traits.native,
|
||||
.subresourceRange{
|
||||
.aspectMask = traits.aspect,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1, // Single mip level for framebuffer
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
return instance->GetDevice().createImageViewUnique(view_info).release();
|
||||
}
|
||||
|
||||
void Surface::BlitScale(const VideoCore::TextureBlit& blit, bool up_scale) {
|
||||
const FormatTraits& depth_traits = instance->GetTraits(pixel_format);
|
||||
const bool is_depth_stencil = pixel_format == PixelFormat::D24S8;
|
||||
|
|
@ -1440,16 +1353,9 @@ void Surface::BlitScale(const VideoCore::TextureBlit& blit, bool up_scale) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Always use consistent source and destination images for proper scaling
|
||||
// When upscaling: source = unscaled (0), destination = scaled (1)
|
||||
// When downscaling: source = scaled (1), destination = unscaled (0)
|
||||
const vk::Image src_image = up_scale ? Image(0) : Image(1);
|
||||
const vk::Image dst_image = up_scale ? Image(1) : Image(0);
|
||||
|
||||
scheduler->Record([src_image, aspect = Aspect(), filter = MakeFilter(pixel_format), dst_image,
|
||||
src_access = AccessFlags(), dst_access = AccessFlags(),
|
||||
scheduler->Record([src_image = Image(!up_scale), aspect = Aspect(),
|
||||
filter = MakeFilter(pixel_format), dst_image = Image(up_scale),
|
||||
blit](vk::CommandBuffer render_cmdbuf) {
|
||||
// Adjust blitting parameters for filtered upscaling
|
||||
const std::array source_offsets = {
|
||||
vk::Offset3D{static_cast<s32>(blit.src_rect.left),
|
||||
static_cast<s32>(blit.src_rect.bottom), 0},
|
||||
|
|
@ -1483,7 +1389,7 @@ void Surface::BlitScale(const VideoCore::TextureBlit& blit, bool up_scale) {
|
|||
|
||||
const std::array read_barriers = {
|
||||
vk::ImageMemoryBarrier{
|
||||
.srcAccessMask = src_access,
|
||||
.srcAccessMask = vk::AccessFlagBits::eMemoryWrite,
|
||||
.dstAccessMask = vk::AccessFlagBits::eTransferRead,
|
||||
.oldLayout = vk::ImageLayout::eGeneral,
|
||||
.newLayout = vk::ImageLayout::eTransferSrcOptimal,
|
||||
|
|
@ -1493,7 +1399,10 @@ void Surface::BlitScale(const VideoCore::TextureBlit& blit, bool up_scale) {
|
|||
.subresourceRange = MakeSubresourceRange(aspect, blit.src_level),
|
||||
},
|
||||
vk::ImageMemoryBarrier{
|
||||
.srcAccessMask = dst_access,
|
||||
.srcAccessMask = vk::AccessFlagBits::eShaderRead |
|
||||
vk::AccessFlagBits::eDepthStencilAttachmentRead |
|
||||
vk::AccessFlagBits::eColorAttachmentRead |
|
||||
vk::AccessFlagBits::eTransferRead,
|
||||
.dstAccessMask = vk::AccessFlagBits::eTransferWrite,
|
||||
.oldLayout = vk::ImageLayout::eGeneral,
|
||||
.newLayout = vk::ImageLayout::eTransferDstOptimal,
|
||||
|
|
@ -1540,9 +1449,9 @@ void Surface::BlitScale(const VideoCore::TextureBlit& blit, bool up_scale) {
|
|||
}
|
||||
|
||||
Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferParams& params,
|
||||
Surface* color, Surface* depth_stencil)
|
||||
Surface* color, Surface* depth)
|
||||
: VideoCore::FramebufferParams{params},
|
||||
res_scale{color ? color->res_scale : (depth_stencil ? depth_stencil->res_scale : 1u)} {
|
||||
res_scale{color ? color->res_scale : (depth ? depth->res_scale : 1u)} {
|
||||
auto& renderpass_cache = runtime.GetRenderpassCache();
|
||||
if (shadow_rendering && !color) {
|
||||
return;
|
||||
|
|
@ -1559,41 +1468,27 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferPa
|
|||
}
|
||||
images[index] = surface->Image();
|
||||
aspects[index] = surface->Aspect();
|
||||
// Use AttachmentView() for single-mip-level framebuffer attachment
|
||||
image_views[index] = surface->AttachmentView();
|
||||
image_views[index] = shadow_rendering ? surface->StorageView() : surface->FramebufferView();
|
||||
};
|
||||
|
||||
boost::container::static_vector<vk::ImageView, 2> attachments;
|
||||
|
||||
if (!shadow_rendering) {
|
||||
// Prepare the surfaces for use in framebuffer
|
||||
if (color) {
|
||||
prepare(0, color);
|
||||
attachments.emplace_back(image_views[0]);
|
||||
}
|
||||
if (color) {
|
||||
prepare(0, color);
|
||||
attachments.emplace_back(image_views[0]);
|
||||
}
|
||||
|
||||
if (depth_stencil) {
|
||||
prepare(1, depth_stencil);
|
||||
attachments.emplace_back(image_views[1]);
|
||||
}
|
||||
} else {
|
||||
// For shadow rendering, just collect surface info without adding attachments
|
||||
if (color) {
|
||||
prepare(0, color);
|
||||
}
|
||||
if (depth_stencil) {
|
||||
prepare(1, depth_stencil);
|
||||
}
|
||||
if (depth) {
|
||||
prepare(1, depth);
|
||||
attachments.emplace_back(image_views[1]);
|
||||
}
|
||||
|
||||
const vk::Device device = runtime.GetInstance().GetDevice();
|
||||
|
||||
if (shadow_rendering) {
|
||||
// For shadow rendering, we don't need a framebuffer with attachments
|
||||
// Just create a dummy render pass for the rendering pipeline
|
||||
render_pass =
|
||||
renderpass_cache.GetRenderpass(PixelFormat::Invalid, PixelFormat::Invalid, false);
|
||||
// Don't create a framebuffer for shadow rendering
|
||||
framebuffer.reset();
|
||||
framebuffer = MakeFramebuffer(device, render_pass, color->GetScaledWidth(),
|
||||
color->GetScaledHeight(), {});
|
||||
} else {
|
||||
render_pass = renderpass_cache.GetRenderpass(formats[0], formats[1], false);
|
||||
framebuffer = MakeFramebuffer(device, render_pass, width, height, attachments);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
|
@ -110,8 +110,6 @@ public:
|
|||
explicit Surface(TextureRuntime& runtime, const VideoCore::SurfaceParams& params);
|
||||
explicit Surface(TextureRuntime& runtime, const VideoCore::SurfaceBase& surface,
|
||||
const VideoCore::Material* materal);
|
||||
explicit Surface(TextureRuntime& runtime, u32 width_, u32 height_,
|
||||
VideoCore::PixelFormat format_);
|
||||
~Surface();
|
||||
|
||||
Surface(const Surface&) = delete;
|
||||
|
|
@ -130,24 +128,12 @@ public:
|
|||
/// Returns the image view at index, otherwise the base view
|
||||
vk::ImageView ImageView(u32 index = 1) const noexcept;
|
||||
|
||||
/// Returns width of the surface
|
||||
u32 GetWidth() const noexcept {
|
||||
return width;
|
||||
}
|
||||
|
||||
/// Returns height of the surface
|
||||
u32 GetHeight() const noexcept {
|
||||
return height;
|
||||
}
|
||||
|
||||
/// Returns resolution scale of the surface
|
||||
u32 GetResScale() const noexcept {
|
||||
return res_scale;
|
||||
}
|
||||
|
||||
/// Returns a copy of the upscaled image handle, used for feedback loops.
|
||||
vk::ImageView CopyImageView() noexcept;
|
||||
|
||||
/// Returns the framebuffer view of the surface image
|
||||
vk::ImageView FramebufferView() noexcept;
|
||||
|
||||
/// Returns the depth view of the surface image
|
||||
vk::ImageView DepthView() noexcept;
|
||||
|
||||
|
|
@ -160,9 +146,6 @@ public:
|
|||
/// Returns a framebuffer handle for rendering to this surface
|
||||
vk::Framebuffer Framebuffer() noexcept;
|
||||
|
||||
/// Returns a single-mip-level view suitable for framebuffer attachments
|
||||
vk::ImageView AttachmentView() noexcept;
|
||||
|
||||
/// Uploads pixel data in staging to a rectangle region of the surface texture
|
||||
void Upload(const VideoCore::BufferTextureCopy& upload, const VideoCore::StagingData& staging);
|
||||
|
||||
|
|
@ -248,12 +231,19 @@ public:
|
|||
return res_scale;
|
||||
}
|
||||
|
||||
u32 Width() const noexcept {
|
||||
return width;
|
||||
}
|
||||
|
||||
u32 Height() const noexcept {
|
||||
return height;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<vk::Image, 2> images{};
|
||||
std::array<vk::ImageView, 2> image_views{};
|
||||
vk::UniqueFramebuffer framebuffer;
|
||||
vk::RenderPass render_pass;
|
||||
std::vector<vk::UniqueImageView> framebuffer_views;
|
||||
std::array<vk::ImageAspectFlags, 2> aspects{};
|
||||
std::array<VideoCore::PixelFormat, 2> formats{VideoCore::PixelFormat::Invalid,
|
||||
VideoCore::PixelFormat::Invalid};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue