mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-29 09:58:05 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c7760b85f0 |
+8
-24
@@ -43,7 +43,6 @@ This guide will walk you through adding a new boolean toggle setting to Eden's c
|
|||||||
Firstly add your desired toggle:
|
Firstly add your desired toggle:
|
||||||
|
|
||||||
Example: `src/common/setting.h`
|
Example: `src/common/setting.h`
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
SwitchableSetting<bool> your_setting_name{linkage, false, "your_setting_name", Category::RendererExtensions};
|
SwitchableSetting<bool> your_setting_name{linkage, false, "your_setting_name", Category::RendererExtensions};
|
||||||
```
|
```
|
||||||
@@ -68,7 +67,6 @@ Common Categories:
|
|||||||
Add the toggle to the Qt UI, where you wish for it to appear and place it there.
|
Add the toggle to the Qt UI, where you wish for it to appear and place it there.
|
||||||
|
|
||||||
Example: `src/qt_common/config/shared_translation.cpp`
|
Example: `src/qt_common/config/shared_translation.cpp`
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
INSERT(Settings,
|
INSERT(Settings,
|
||||||
your_setting_name,
|
your_setting_name,
|
||||||
@@ -93,7 +91,6 @@ INSERT(Settings,
|
|||||||
Add where it should be in the settings.
|
Add where it should be in the settings.
|
||||||
|
|
||||||
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt`
|
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt`
|
||||||
|
|
||||||
```kts
|
```kts
|
||||||
RENDERER_YOUR_SETTING_NAME("your_setting_name"),
|
RENDERER_YOUR_SETTING_NAME("your_setting_name"),
|
||||||
```
|
```
|
||||||
@@ -109,7 +106,6 @@ RENDERER_YOUR_SETTING_NAME("your_setting_name"),
|
|||||||
Add the toggle to the Kotlin (Android) UI
|
Add the toggle to the Kotlin (Android) UI
|
||||||
|
|
||||||
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt`
|
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt`
|
||||||
|
|
||||||
```kts
|
```kts
|
||||||
put(
|
put(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
@@ -127,7 +123,6 @@ put(
|
|||||||
Add your setting within the right category.
|
Add your setting within the right category.
|
||||||
|
|
||||||
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt`
|
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt`
|
||||||
|
|
||||||
```kts
|
```kts
|
||||||
add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
|
add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
|
||||||
```
|
```
|
||||||
@@ -142,7 +137,6 @@ add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
|
|||||||
Add your setting and description in the appropriate place.
|
Add your setting and description in the appropriate place.
|
||||||
|
|
||||||
Example: `src/android/app/src/main/res/values/strings.xml`
|
Example: `src/android/app/src/main/res/values/strings.xml`
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<string name="your_setting_name">Your Setting Display Name</string>
|
<string name="your_setting_name">Your Setting Display Name</string>
|
||||||
<string name="your_setting_name_description">Detailed description of what this setting does. Explain any caveats, requirements, or warnings here.</string>
|
<string name="your_setting_name_description">Detailed description of what this setting does. Explain any caveats, requirements, or warnings here.</string>
|
||||||
@@ -156,7 +150,6 @@ Now the UI part is done find a place in the code for the toggle,
|
|||||||
And use it to your heart's desire!
|
And use it to your heart's desire!
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
const bool your_value = Settings::values.your_setting_name.GetValue();
|
const bool your_value = Settings::values.your_setting_name.GetValue();
|
||||||
|
|
||||||
@@ -203,31 +196,25 @@ Common advantages recap:
|
|||||||
|
|
||||||
#### Accessing Debug Knobs (dev side)
|
#### Accessing Debug Knobs (dev side)
|
||||||
|
|
||||||
Use the `Settings::GetDebugKnobAt(u8 i)` function to check if a specific bit is set:
|
Use the `Settings::getDebugKnobAt(u8 i)` function to check if a specific bit is set:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
//cpp side
|
//cpp side
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
|
||||||
//To use it as a general purpose uint var:
|
|
||||||
unsigned int debug_knobs = Settings::values.debug_knobs.GetValue();
|
|
||||||
|
|
||||||
// Check if bit 0 is set
|
// Check if bit 0 is set
|
||||||
bool feature_enabled = Settings::GetDebugKnobAt(0);
|
bool feature_enabled = Settings::getDebugKnobAt(0);
|
||||||
|
|
||||||
// Check if bit 15 is set
|
// Check if bit 15 is set
|
||||||
bool another_feature = Settings::GetDebugKnobAt(15);
|
bool another_feature = Settings::getDebugKnobAt(15);
|
||||||
```
|
```
|
||||||
|
|
||||||
```kts
|
```kts
|
||||||
//kotlin side
|
//kotlin side
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||||
|
|
||||||
//To use it as a general purpose uint var
|
|
||||||
val debug_knobs: Int = UShortSetting.DEBUG_KNOBS.getInt()
|
|
||||||
|
|
||||||
// Check if bit x is set
|
// Check if bit x is set
|
||||||
bool feature_enabled = Settings.GetDebugKnobAt(x); //x as integer from 0 to 15
|
bool feature_enabled = Settings.getDebugKnobAt(x); //x as integer from 0 to 15
|
||||||
```
|
```
|
||||||
|
|
||||||
The function returns `true` if the specified bit (0-15) is set in the `debug_knobs` value, `false` otherwise.
|
The function returns `true` if the specified bit (0-15) is set in the `debug_knobs` value, `false` otherwise.
|
||||||
@@ -260,7 +247,6 @@ There are two main confusions when talking about knobs:
|
|||||||
Sometimes when an user reports: knobs 1 and 2 gets better performance, dev may get confuse whether he means the knobs 1 and 2 literally, or the 1st and 2nd knobs (knobs 0 and 1).
|
Sometimes when an user reports: knobs 1 and 2 gets better performance, dev may get confuse whether he means the knobs 1 and 2 literally, or the 1st and 2nd knobs (knobs 0 and 1).
|
||||||
|
|
||||||
Debug knobs are **zero-based**, which means:
|
Debug knobs are **zero-based**, which means:
|
||||||
|
|
||||||
* The first knob is the knob(0) (or knob0 henceforth), and the last one is the 15 (knob15, likewise)
|
* The first knob is the knob(0) (or knob0 henceforth), and the last one is the 15 (knob15, likewise)
|
||||||
* You can talk: "knob0 is enabled/disabled", "In this video i was using only knobs 0 and 2", etc.
|
* You can talk: "knob0 is enabled/disabled", "In this video i was using only knobs 0 and 2", etc.
|
||||||
|
|
||||||
@@ -273,7 +259,6 @@ Whenever you're instructing tests or reporting results, be precise about whether
|
|||||||
|
|
||||||
ALWAYS use the word in PLURAL (knobs), without mentioning which one, to refer to the setting, aka multiple knobs at once:
|
ALWAYS use the word in PLURAL (knobs), without mentioning which one, to refer to the setting, aka multiple knobs at once:
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
- **knobs=0**: no knobs enabled
|
- **knobs=0**: no knobs enabled
|
||||||
- **knobs=1**: knob0 enabled, others disabled
|
- **knobs=1**: knob0 enabled, others disabled
|
||||||
- **knobs=2**: knob1 enabled, others disabled
|
- **knobs=2**: knob1 enabled, others disabled
|
||||||
@@ -285,7 +270,6 @@ Examples:
|
|||||||
|
|
||||||
Use the word in SINGULAR (knob), or in plural but referring which ones, when meaning multiple knobs at once:
|
Use the word in SINGULAR (knob), or in plural but referring which ones, when meaning multiple knobs at once:
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
- **knob0**: knob 0 enabled, others disabled
|
- **knob0**: knob 0 enabled, others disabled
|
||||||
- **knob1**: knob 1 enabled, others disabled
|
- **knob1**: knob 1 enabled, others disabled
|
||||||
- **knobs 0 and 1**: knobs 0 and 1 enabled, others disabled
|
- **knobs 0 and 1**: knobs 0 and 1 enabled, others disabled
|
||||||
@@ -298,12 +282,12 @@ Examples:
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
void SomeFunction() {
|
void SomeFunction() {
|
||||||
if (Settings::GetDebugKnobAt(0)) {
|
if (Settings::getDebugKnobAt(0)) {
|
||||||
LOG_DEBUG(Common, "Debug feature 0 is enabled");
|
LOG_DEBUG(Common, "Debug feature 0 is enabled");
|
||||||
// Additional debug code here
|
// Additional debug code here
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::GetDebugKnobAt(1)) {
|
if (Settings::getDebugKnobAt(1)) {
|
||||||
LOG_DEBUG(Common, "Debug feature 1 is enabled");
|
LOG_DEBUG(Common, "Debug feature 1 is enabled");
|
||||||
// Different debug behavior
|
// Different debug behavior
|
||||||
}
|
}
|
||||||
@@ -315,7 +299,7 @@ void SomeFunction() {
|
|||||||
```cpp
|
```cpp
|
||||||
bool UseOptimizedPath() {
|
bool UseOptimizedPath() {
|
||||||
// Skip optimization if debug bit 2 is set for testing
|
// Skip optimization if debug bit 2 is set for testing
|
||||||
return !Settings::GetDebugKnobAt(2);
|
return !Settings::getDebugKnobAt(2);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -325,7 +309,7 @@ bool UseOptimizedPath() {
|
|||||||
void ExperimentalFeature() {
|
void ExperimentalFeature() {
|
||||||
static constexpr u8 EXPERIMENTAL_FEATURE_BIT = 3;
|
static constexpr u8 EXPERIMENTAL_FEATURE_BIT = 3;
|
||||||
|
|
||||||
if (!Settings::GetDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) {
|
if (!Settings::getDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) {
|
||||||
// Fallback to stable implementation
|
// Fallback to stable implementation
|
||||||
StableImplementation();
|
StableImplementation();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ object NativeLibrary {
|
|||||||
|
|
||||||
external fun refreshThreadPolicies()
|
external fun refreshThreadPolicies()
|
||||||
|
|
||||||
external fun GetDebugKnobAt(index: Int): Boolean
|
external fun getDebugKnobAt(index: Int): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the current speed limit to the configured turbo speed.
|
* Set the current speed limit to the configured turbo speed.
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ object Settings {
|
|||||||
fun getPlayerString(player: Int): String =
|
fun getPlayerString(player: Int): String =
|
||||||
YuzuApplication.appContext.getString(R.string.preferences_player, player)
|
YuzuApplication.appContext.getString(R.string.preferences_player, player)
|
||||||
|
|
||||||
fun GetDebugKnobAt(index: Int): Boolean {
|
fun getDebugKnobAt(index: Int): Boolean {
|
||||||
return org.yuzu.yuzu_emu.NativeLibrary.GetDebugKnobAt(index)
|
return org.yuzu.yuzu_emu.NativeLibrary.getDebugKnobAt(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
|
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
|
||||||
|
|||||||
+2
-1
@@ -11,7 +11,8 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
|
|||||||
enum class ShortSetting(override val key: String) : AbstractShortSetting {
|
enum class ShortSetting(override val key: String) : AbstractShortSetting {
|
||||||
RENDERER_SPEED_LIMIT("speed_limit"),
|
RENDERER_SPEED_LIMIT("speed_limit"),
|
||||||
RENDERER_TURBO_SPEED_LIMIT("turbo_speed_limit"),
|
RENDERER_TURBO_SPEED_LIMIT("turbo_speed_limit"),
|
||||||
RENDERER_SLOW_SPEED_LIMIT("slow_speed_limit")
|
RENDERER_SLOW_SPEED_LIMIT("slow_speed_limit"),
|
||||||
|
DEBUG_KNOBS("debug_knobs")
|
||||||
;
|
;
|
||||||
|
|
||||||
override fun getShort(needsGlobal: Boolean): Short = NativeConfig.getShort(key, needsGlobal)
|
override fun getShort(needsGlobal: Boolean): Short = NativeConfig.getShort(key, needsGlobal)
|
||||||
|
|||||||
-30
@@ -1,30 +0,0 @@
|
|||||||
// 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
|
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.features.settings.model
|
|
||||||
|
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
|
||||||
|
|
||||||
enum class UShortSetting(override val key: String) : AbstractIntSetting {
|
|
||||||
DEBUG_KNOBS("debug_knobs")
|
|
||||||
;
|
|
||||||
|
|
||||||
override fun getInt(needsGlobal: Boolean): Int =
|
|
||||||
NativeConfig.getUnsignedShort(key, needsGlobal)
|
|
||||||
|
|
||||||
override fun setInt(value: Int) {
|
|
||||||
if (NativeConfig.isPerGameConfigLoaded()) {
|
|
||||||
global = false
|
|
||||||
}
|
|
||||||
NativeConfig.setUnsignedShort(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val defaultValue: Int by lazy { NativeConfig.getDefaultToString(key).toInt() }
|
|
||||||
|
|
||||||
override fun getValueAsString(needsGlobal: Boolean): String = getInt(needsGlobal).toString()
|
|
||||||
|
|
||||||
override fun reset() = NativeConfig.setUnsignedShort(key, defaultValue)
|
|
||||||
}
|
|
||||||
+1
-3
@@ -20,7 +20,6 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
|||||||
import org.yuzu.yuzu_emu.features.settings.model.LongSetting
|
import org.yuzu.yuzu_emu.features.settings.model.LongSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.UShortSetting
|
|
||||||
import org.yuzu.yuzu_emu.network.NetDataValidators
|
import org.yuzu.yuzu_emu.network.NetDataValidators
|
||||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||||
@@ -625,7 +624,6 @@ abstract class SettingsItem(
|
|||||||
IntSetting.FSR_SHARPENING_SLIDER,
|
IntSetting.FSR_SHARPENING_SLIDER,
|
||||||
titleId = R.string.fsr_sharpness,
|
titleId = R.string.fsr_sharpness,
|
||||||
descriptionId = R.string.fsr_sharpness_description,
|
descriptionId = R.string.fsr_sharpness_description,
|
||||||
max = 200,
|
|
||||||
units = "%"
|
units = "%"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -1035,7 +1033,7 @@ abstract class SettingsItem(
|
|||||||
)
|
)
|
||||||
put(
|
put(
|
||||||
SpinBoxSetting(
|
SpinBoxSetting(
|
||||||
UShortSetting.DEBUG_KNOBS,
|
ShortSetting.DEBUG_KNOBS,
|
||||||
titleId = R.string.debug_knobs,
|
titleId = R.string.debug_knobs,
|
||||||
descriptionId = R.string.debug_knobs_description,
|
descriptionId = R.string.debug_knobs_description,
|
||||||
valueHint = R.string.debug_knobs_hint,
|
valueHint = R.string.debug_knobs_hint,
|
||||||
|
|||||||
+4
-9
@@ -25,7 +25,6 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings
|
|||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag
|
import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.UShortSetting
|
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.view.*
|
import org.yuzu.yuzu_emu.features.settings.model.view.*
|
||||||
import org.yuzu.yuzu_emu.utils.InputHandler
|
import org.yuzu.yuzu_emu.utils.InputHandler
|
||||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||||
@@ -100,12 +99,7 @@ class SettingsFragmentPresenter(
|
|||||||
|
|
||||||
add(BooleanSetting.RENDERER_FRAME_GEN.key)
|
add(BooleanSetting.RENDERER_FRAME_GEN.key)
|
||||||
add(IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.key)
|
add(IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.key)
|
||||||
if (IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.getInt(
|
add(IntSetting.RENDERER_FRAME_GEN_MULTIPLIER.key)
|
||||||
getNeedsGlobalForKey(IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.key)
|
|
||||||
) == 0
|
|
||||||
) {
|
|
||||||
add(IntSetting.RENDERER_FRAME_GEN_MULTIPLIER.key)
|
|
||||||
}
|
|
||||||
add(IntSetting.RENDERER_FRAME_GEN_QUEUE_TARGET.key)
|
add(IntSetting.RENDERER_FRAME_GEN_QUEUE_TARGET.key)
|
||||||
add(BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.key)
|
add(BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.key)
|
||||||
if (!BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.getBoolean(
|
if (!BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.getBoolean(
|
||||||
@@ -313,6 +307,8 @@ class SettingsFragmentPresenter(
|
|||||||
// TODO(crueter): sub-submenus?
|
// TODO(crueter): sub-submenus?
|
||||||
private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
|
private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
|
||||||
sl.apply {
|
sl.apply {
|
||||||
|
// add(IntSetting.RENDERER_NVDEC_EMULATION.key)
|
||||||
|
|
||||||
add(IntSetting.RENDERER_RESOLUTION.key)
|
add(IntSetting.RENDERER_RESOLUTION.key)
|
||||||
add(IntSetting.RENDERER_VSYNC.key)
|
add(IntSetting.RENDERER_VSYNC.key)
|
||||||
add(IntSetting.RENDERER_SCALING_FILTER.key)
|
add(IntSetting.RENDERER_SCALING_FILTER.key)
|
||||||
@@ -329,7 +325,6 @@ class SettingsFragmentPresenter(
|
|||||||
add(IntSetting.MAX_ANISOTROPY.key)
|
add(IntSetting.MAX_ANISOTROPY.key)
|
||||||
add(IntSetting.RENDERER_VRAM_USAGE_MODE.key)
|
add(IntSetting.RENDERER_VRAM_USAGE_MODE.key)
|
||||||
add(IntSetting.RENDERER_ASTC_DECODE_METHOD.key)
|
add(IntSetting.RENDERER_ASTC_DECODE_METHOD.key)
|
||||||
add(IntSetting.RENDERER_NVDEC_EMULATION.key)
|
|
||||||
|
|
||||||
add(BooleanSetting.SYNC_MEMORY_OPERATIONS.key)
|
add(BooleanSetting.SYNC_MEMORY_OPERATIONS.key)
|
||||||
add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
|
add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
|
||||||
@@ -1327,7 +1322,7 @@ class SettingsFragmentPresenter(
|
|||||||
|
|
||||||
add(HeaderSetting(R.string.general))
|
add(HeaderSetting(R.string.general))
|
||||||
|
|
||||||
add(UShortSetting.DEBUG_KNOBS.key)
|
add(ShortSetting.DEBUG_KNOBS.key)
|
||||||
add(StringSetting.PROGRAM_ARGS.key)
|
add(StringSetting.PROGRAM_ARGS.key)
|
||||||
|
|
||||||
if (!NativeConfig.isPerGameConfigLoaded()) {
|
if (!NativeConfig.isPerGameConfigLoaded()) {
|
||||||
|
|||||||
@@ -1182,7 +1182,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
container,
|
container,
|
||||||
IntSetting.FSR_SHARPENING_SLIDER,
|
IntSetting.FSR_SHARPENING_SLIDER,
|
||||||
minValue = 0,
|
minValue = 0,
|
||||||
maxValue = 200,
|
maxValue = 100,
|
||||||
units = "%"
|
units = "%"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import androidx.activity.result.contract.ActivityResultContracts
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.doOnPreDraw
|
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.core.widget.doOnTextChanged
|
import androidx.core.widget.doOnTextChanged
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
@@ -60,10 +59,6 @@ class GamesFragment : Fragment() {
|
|||||||
|
|
||||||
private var lastViewType: Int = GameAdapter.VIEW_TYPE_GRID
|
private var lastViewType: Int = GameAdapter.VIEW_TYPE_GRID
|
||||||
private var fallbackBottomInset: Int = 0
|
private var fallbackBottomInset: Int = 0
|
||||||
private var pendingPostReloadListSettle = false
|
|
||||||
private var pendingPostReloadListSettleGeneration = 0
|
|
||||||
private var gameListSubmitGeneration = 0
|
|
||||||
private var committedGameListSubmitGeneration = 0
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val SEARCH_TEXT = "SearchText"
|
private const val SEARCH_TEXT = "SearchText"
|
||||||
@@ -173,9 +168,10 @@ class GamesFragment : Fragment() {
|
|||||||
|
|
||||||
gamesViewModel.shouldScrollAfterReload.collect(viewLifecycleOwner) { shouldScroll ->
|
gamesViewModel.shouldScrollAfterReload.collect(viewLifecycleOwner) { shouldScroll ->
|
||||||
if (shouldScroll) {
|
if (shouldScroll) {
|
||||||
pendingPostReloadListSettle = true
|
binding.gridGames.post {
|
||||||
pendingPostReloadListSettleGeneration = gameListSubmitGeneration
|
(binding.gridGames as? CarouselRecyclerView)?.pendingScrollAfterReload = true
|
||||||
schedulePostReloadListSettle()
|
gameAdapter.notifyDataSetChanged()
|
||||||
|
}
|
||||||
gamesViewModel.setShouldScrollAfterReload(false)
|
gamesViewModel.setShouldScrollAfterReload(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -277,42 +273,11 @@ class GamesFragment : Fragment() {
|
|||||||
lastSearchText = currentSearchText
|
lastSearchText = currentSearchText
|
||||||
lastFilter = currentFilter
|
lastFilter = currentFilter
|
||||||
} else {
|
} else {
|
||||||
submitGameList(games)
|
((binding.gridGames as? RecyclerView)?.adapter as? GameAdapter)?.submitList(games)
|
||||||
gamesViewModel.setFilteredGames(games)
|
gamesViewModel.setFilteredGames(games)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun submitGameList(games: List<Game>) {
|
|
||||||
val adapter = (binding.gridGames as? RecyclerView)?.adapter as? GameAdapter
|
|
||||||
if (adapter == null) {
|
|
||||||
schedulePostReloadListSettle()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val submitGeneration = ++gameListSubmitGeneration
|
|
||||||
adapter.submitList(games) {
|
|
||||||
if (committedGameListSubmitGeneration < submitGeneration) {
|
|
||||||
committedGameListSubmitGeneration = submitGeneration
|
|
||||||
}
|
|
||||||
schedulePostReloadListSettle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun schedulePostReloadListSettle() {
|
|
||||||
if (!pendingPostReloadListSettle || _binding == null) return
|
|
||||||
|
|
||||||
binding.gridGames.doOnPreDraw {
|
|
||||||
if (!pendingPostReloadListSettle || _binding == null) return@doOnPreDraw
|
|
||||||
if (committedGameListSubmitGeneration < pendingPostReloadListSettleGeneration) {
|
|
||||||
schedulePostReloadListSettle()
|
|
||||||
return@doOnPreDraw
|
|
||||||
}
|
|
||||||
pendingPostReloadListSettle = false
|
|
||||||
|
|
||||||
(binding.gridGames as? CarouselRecyclerView)?.refreshView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private fun setupTopView() {
|
private fun setupTopView() {
|
||||||
binding.searchText.doOnTextChanged() { text: CharSequence?, _: Int, _: Int, _: Int ->
|
binding.searchText.doOnTextChanged() { text: CharSequence?, _: Int, _: Int, _: Int ->
|
||||||
if (text.toString().isNotEmpty()) {
|
if (text.toString().isNotEmpty()) {
|
||||||
@@ -449,7 +414,9 @@ class GamesFragment : Fragment() {
|
|||||||
|
|
||||||
val searchTerm = binding.searchText.text.toString().lowercase(Locale.getDefault())
|
val searchTerm = binding.searchText.text.toString().lowercase(Locale.getDefault())
|
||||||
if (searchTerm.isEmpty()) {
|
if (searchTerm.isEmpty()) {
|
||||||
submitGameList(filteredList)
|
((binding.gridGames as? RecyclerView)?.adapter as? GameAdapter)?.submitList(
|
||||||
|
filteredList
|
||||||
|
)
|
||||||
gamesViewModel.setFilteredGames(filteredList)
|
gamesViewModel.setFilteredGames(filteredList)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -465,7 +432,7 @@ class GamesFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}.sortedByDescending { it.score }.map { it.item }
|
}.sortedByDescending { it.score }.map { it.item }
|
||||||
|
|
||||||
submitGameList(sortedList)
|
((binding.gridGames as? RecyclerView)?.adapter as? GameAdapter)?.submitList(sortedList)
|
||||||
gamesViewModel.setFilteredGames(sortedList)
|
gamesViewModel.setFilteredGames(sortedList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ object GameHelper {
|
|||||||
|
|
||||||
fun getGames(): List<Game> {
|
fun getGames(): List<Game> {
|
||||||
val games = mutableListOf<Game>()
|
val games = mutableListOf<Game>()
|
||||||
val gamesByProgramId = mutableMapOf<String, Game>()
|
|
||||||
val context = YuzuApplication.appContext
|
val context = YuzuApplication.appContext
|
||||||
preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
|
||||||
@@ -64,7 +63,6 @@ object GameHelper {
|
|||||||
|
|
||||||
addGamesRecursive(
|
addGamesRecursive(
|
||||||
games,
|
games,
|
||||||
gamesByProgramId,
|
|
||||||
FileUtil.listFiles(gameDirUri),
|
FileUtil.listFiles(gameDirUri),
|
||||||
scanDepth,
|
scanDepth,
|
||||||
mountedContainerUris
|
mountedContainerUris
|
||||||
@@ -138,7 +136,6 @@ object GameHelper {
|
|||||||
|
|
||||||
private fun addGamesRecursive(
|
private fun addGamesRecursive(
|
||||||
games: MutableList<Game>,
|
games: MutableList<Game>,
|
||||||
gamesByProgramId: MutableMap<String, Game>,
|
|
||||||
files: Array<MinimalDocumentFile>,
|
files: Array<MinimalDocumentFile>,
|
||||||
depth: Int,
|
depth: Int,
|
||||||
mountedContainerUris: MutableSet<String>
|
mountedContainerUris: MutableSet<String>
|
||||||
@@ -151,7 +148,6 @@ object GameHelper {
|
|||||||
if (it.isDirectory) {
|
if (it.isDirectory) {
|
||||||
addGamesRecursive(
|
addGamesRecursive(
|
||||||
games,
|
games,
|
||||||
gamesByProgramId,
|
|
||||||
FileUtil.listFiles(it.uri),
|
FileUtil.listFiles(it.uri),
|
||||||
depth - 1,
|
depth - 1,
|
||||||
mountedContainerUris
|
mountedContainerUris
|
||||||
@@ -160,9 +156,8 @@ object GameHelper {
|
|||||||
val extension = FileUtil.getExtension(it.uri).lowercase()
|
val extension = FileUtil.getExtension(it.uri).lowercase()
|
||||||
val filePath = it.uri.toString()
|
val filePath = it.uri.toString()
|
||||||
|
|
||||||
val mountedContainer = externalContentExtensions.contains(extension) &&
|
if (externalContentExtensions.contains(extension) &&
|
||||||
mountedContainerUris.add(filePath)
|
mountedContainerUris.add(filePath)) {
|
||||||
if (mountedContainer) {
|
|
||||||
NativeLibrary.addGameFolderFileToFilesystemProvider(filePath)
|
NativeLibrary.addGameFolderFileToFilesystemProvider(filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,20 +165,6 @@ object GameHelper {
|
|||||||
val game = getGame(it.uri, true, false)
|
val game = getGame(it.uri, true, false)
|
||||||
if (game != null) {
|
if (game != null) {
|
||||||
games.add(game)
|
games.add(game)
|
||||||
if (game.programId != "0") {
|
|
||||||
gamesByProgramId[game.programId] = game
|
|
||||||
}
|
|
||||||
} else if (mountedContainer) {
|
|
||||||
GameMetadata.getProgramId(filePath).toLongOrNull()?.let { programId ->
|
|
||||||
gamesByProgramId[(programId and 0x800L.inv()).toString()]
|
|
||||||
}?.let { existingGame ->
|
|
||||||
NativeLibrary.getPatchesForFile(existingGame.path, existingGame.programId)
|
|
||||||
existingGame.version = GameMetadata.getVersion(
|
|
||||||
existingGame.path,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
GameIconUtils.refreshGameIcon(existingGame)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -27,15 +24,6 @@ import coil.request.Options
|
|||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
import org.yuzu.yuzu_emu.model.Game
|
import org.yuzu.yuzu_emu.model.Game
|
||||||
import java.util.Collections
|
|
||||||
import java.util.WeakHashMap
|
|
||||||
|
|
||||||
private val gameIconHashes = Collections.synchronizedMap(mutableMapOf<String, Int>())
|
|
||||||
private val gameIconTargets = Collections.synchronizedMap(WeakHashMap<ImageView, GameIconTarget>())
|
|
||||||
|
|
||||||
private fun Game.iconCacheKey(): String = "$path|$version"
|
|
||||||
|
|
||||||
private data class GameIconTarget(val game: Game, var iconHash: Int? = null)
|
|
||||||
|
|
||||||
class GameIconFetcher(
|
class GameIconFetcher(
|
||||||
private val game: Game,
|
private val game: Game,
|
||||||
@@ -43,15 +31,14 @@ class GameIconFetcher(
|
|||||||
) : Fetcher {
|
) : Fetcher {
|
||||||
override suspend fun fetch(): FetchResult {
|
override suspend fun fetch(): FetchResult {
|
||||||
return DrawableResult(
|
return DrawableResult(
|
||||||
drawable = decodeGameIcon(game)!!.toDrawable(options.context.resources),
|
drawable = decodeGameIcon(game.path)!!.toDrawable(options.context.resources),
|
||||||
isSampled = false,
|
isSampled = false,
|
||||||
dataSource = DataSource.DISK
|
dataSource = DataSource.DISK
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decodeGameIcon(game: Game): Bitmap? {
|
private fun decodeGameIcon(uri: String): Bitmap? {
|
||||||
val data = GameMetadata.getIcon(game.path)
|
val data = GameMetadata.getIcon(uri)
|
||||||
gameIconHashes[game.iconCacheKey()] = data.contentHashCode()
|
|
||||||
return BitmapFactory.decodeByteArray(
|
return BitmapFactory.decodeByteArray(
|
||||||
data,
|
data,
|
||||||
0,
|
0,
|
||||||
@@ -67,7 +54,7 @@ class GameIconFetcher(
|
|||||||
}
|
}
|
||||||
|
|
||||||
class GameIconKeyer : Keyer<Game> {
|
class GameIconKeyer : Keyer<Game> {
|
||||||
override fun key(data: Game, options: Options): String = data.iconCacheKey()
|
override fun key(data: Game, options: Options): String = data.path
|
||||||
}
|
}
|
||||||
|
|
||||||
object GameIconUtils {
|
object GameIconUtils {
|
||||||
@@ -84,58 +71,14 @@ object GameIconUtils {
|
|||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun loadGameIcon(game: Game, imageView: ImageView) {
|
fun loadGameIcon(game: Game, imageView: ImageView) {
|
||||||
gameIconTargets[imageView] = GameIconTarget(game)
|
|
||||||
val request = ImageRequest.Builder(YuzuApplication.appContext)
|
val request = ImageRequest.Builder(YuzuApplication.appContext)
|
||||||
.data(game)
|
.data(game)
|
||||||
.target(imageView)
|
.target(imageView)
|
||||||
.error(R.drawable.default_icon)
|
.error(R.drawable.default_icon)
|
||||||
.listener(
|
|
||||||
onSuccess = { _, _ ->
|
|
||||||
val target = gameIconTargets[imageView]
|
|
||||||
if (target?.game?.iconCacheKey() == game.iconCacheKey()) {
|
|
||||||
gameIconHashes[game.iconCacheKey()]?.let {
|
|
||||||
target.iconHash = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError = { _, _ ->
|
|
||||||
gameIconTargets[imageView]?.iconHash = null
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.build()
|
.build()
|
||||||
imageLoader.enqueue(request)
|
imageLoader.enqueue(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun refreshGameIcon(game: Game) {
|
|
||||||
val targets = synchronized(gameIconTargets) {
|
|
||||||
gameIconTargets
|
|
||||||
.filterValues { it.game.path == game.path && it.game.programId == game.programId }
|
|
||||||
.keys
|
|
||||||
.toList()
|
|
||||||
}
|
|
||||||
if (targets.isEmpty()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val iconHash = GameMetadata.getIcon(game.path).contentHashCode()
|
|
||||||
val targetsToRefresh = targets.filter { gameIconTargets[it]?.iconHash != iconHash }
|
|
||||||
if (targetsToRefresh.isEmpty()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
imageLoader.memoryCache?.remove(MemoryCache.Key(game.iconCacheKey()))
|
|
||||||
targetsToRefresh.forEach { imageView ->
|
|
||||||
imageView.post {
|
|
||||||
val target = gameIconTargets[imageView] ?: return@post
|
|
||||||
if (target.game.path == game.path && target.game.programId == game.programId) {
|
|
||||||
if (target.iconHash != iconHash) {
|
|
||||||
loadGameIcon(game, imageView)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getGameIcon(lifecycleOwner: LifecycleOwner, game: Game): Bitmap {
|
suspend fun getGameIcon(lifecycleOwner: LifecycleOwner, game: Game): Bitmap {
|
||||||
val request = ImageRequest.Builder(YuzuApplication.appContext)
|
val request = ImageRequest.Builder(YuzuApplication.appContext)
|
||||||
.data(game)
|
.data(game)
|
||||||
|
|||||||
@@ -80,12 +80,6 @@ object NativeConfig {
|
|||||||
@Synchronized
|
@Synchronized
|
||||||
external fun setShort(key: String, value: Short)
|
external fun setShort(key: String, value: Short)
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
external fun getUnsignedShort(key: String, needsGlobal: Boolean): Int
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
external fun setUnsignedShort(key: String, value: Int)
|
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
external fun getInt(key: String, needsGlobal: Boolean): Int
|
external fun getInt(key: String, needsGlobal: Boolean): Int
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.ui
|
package org.yuzu.yuzu_emu.ui
|
||||||
@@ -11,8 +11,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||||||
import androidx.recyclerview.widget.PagerSnapHelper
|
import androidx.recyclerview.widget.PagerSnapHelper
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.cos
|
|
||||||
import kotlin.math.sin
|
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.adapters.GameAdapter
|
import org.yuzu.yuzu_emu.adapters.GameAdapter
|
||||||
import androidx.core.view.doOnNextLayout
|
import androidx.core.view.doOnNextLayout
|
||||||
@@ -36,7 +34,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
private var overlapDecoration: OverlappingDecoration? = null
|
private var overlapDecoration: OverlappingDecoration? = null
|
||||||
private var pagerSnapHelper: PagerSnapHelper? = null
|
private var pagerSnapHelper: PagerSnapHelper? = null
|
||||||
private var scalingScrollListener: OnScrollListener? = null
|
private var scalingScrollListener: OnScrollListener? = null
|
||||||
private var savedItemAnimator: RecyclerView.ItemAnimator? = null
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val CAROUSEL_CARD_SIZE_FACTOR = "CarouselCardSizeMultiplier"
|
private const val CAROUSEL_CARD_SIZE_FACTOR = "CarouselCardSizeMultiplier"
|
||||||
@@ -45,13 +42,8 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
private const val CAROUSEL_OVERLAP_FACTOR = "CarouselOverlapFactor"
|
private const val CAROUSEL_OVERLAP_FACTOR = "CarouselOverlapFactor"
|
||||||
private const val CAROUSEL_MAX_FLING_COUNT = "CarouselMaxFlingCount"
|
private const val CAROUSEL_MAX_FLING_COUNT = "CarouselMaxFlingCount"
|
||||||
private const val CAROUSEL_FLING_MULTIPLIER = "CarouselFlingMultiplier"
|
private const val CAROUSEL_FLING_MULTIPLIER = "CarouselFlingMultiplier"
|
||||||
private const val CAROUSEL_ARC_ANGLE_STEP_DEGREES = 15.0
|
private const val CAROUSEL_CARDS_SCALING_SHAPE = "CarouselCardsScalingShape"
|
||||||
private const val CAROUSEL_ARC_MAX_ANGLE_DEGREES = 165.0
|
private const val CAROUSEL_CARDS_ALPHA_SHAPE = "CarouselCardsAlphaShape"
|
||||||
private const val CAROUSEL_ARC_DEPTH_MAX_ANGLE_DEGREES = 85.0
|
|
||||||
private const val CAROUSEL_ARC_DEPTH_STRETCH = 5.0f
|
|
||||||
private const val CAROUSEL_ARC_X_DEPTH_FACTOR = 0.55f
|
|
||||||
private const val CAROUSEL_ARC_FADE_OUT_START_DEGREES = 60.0
|
|
||||||
private const val CAROUSEL_ARC_FADE_OUT_END_DEGREES = 95.0
|
|
||||||
const val CAROUSEL_LAST_SCROLL_POSITION = "CarouselLastScrollPosition"
|
const val CAROUSEL_LAST_SCROLL_POSITION = "CarouselLastScrollPosition"
|
||||||
const val CAROUSEL_VIEW_TYPE_PORTRAIT = "GamesViewTypePortrait"
|
const val CAROUSEL_VIEW_TYPE_PORTRAIT = "GamesViewTypePortrait"
|
||||||
const val CAROUSEL_VIEW_TYPE_LANDSCAPE = "GamesViewTypeLandscape"
|
const val CAROUSEL_VIEW_TYPE_LANDSCAPE = "GamesViewTypeLandscape"
|
||||||
@@ -168,52 +160,46 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun shapingFunction(x: Float, option: Int = 0): Float {
|
||||||
|
return when (option) {
|
||||||
|
0 -> 1f // Off
|
||||||
|
1 -> 1f - x // linear descending
|
||||||
|
2 -> (1f - x) * (1f - x) // Ease out
|
||||||
|
3 -> if (x < 0.05f) 1f else (1f - x) * 0.8f
|
||||||
|
4 -> kotlin.math.cos(x * Math.PI).toFloat() // Cosine
|
||||||
|
5 -> kotlin.math.cos((1.5f * x).coerceIn(0f, 1f) * Math.PI).toFloat() // Cosine 1.5x trimmed
|
||||||
|
else -> 1f // Default to Off
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun updateChildScaleAndAlphaForPosition(child: View) {
|
fun updateChildScaleAndAlphaForPosition(child: View) {
|
||||||
val cardSize = (adapter as? GameAdapter ?: return).cardSize
|
val cardSize = (adapter as? GameAdapter ?: return).cardSize
|
||||||
val position = getChildViewHolder(child).bindingAdapterPosition
|
val position = getChildViewHolder(child).bindingAdapterPosition
|
||||||
if (position == RecyclerView.NO_POSITION || cardSize <= 0) {
|
if (position == RecyclerView.NO_POSITION || cardSize <= 0) {
|
||||||
return // No valid position or card size
|
return // No valid position or card size
|
||||||
}
|
}
|
||||||
val layoutParams = child.layoutParams
|
child.layoutParams.width = cardSize
|
||||||
if (layoutParams.width != cardSize || layoutParams.height != cardSize) {
|
child.layoutParams.height = cardSize
|
||||||
child.layoutParams = layoutParams.apply {
|
|
||||||
width = cardSize
|
|
||||||
height = cardSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val signedDistance = getChildDistanceToCenter(child)
|
|
||||||
val itemStep = (cardSize - overlapPx).toFloat().coerceAtLeast(1f)
|
|
||||||
val angleStep = Math.toRadians(CAROUSEL_ARC_ANGLE_STEP_DEGREES).toFloat()
|
|
||||||
val maxAngle = Math.toRadians(CAROUSEL_ARC_MAX_ANGLE_DEGREES).toFloat()
|
|
||||||
val depthMaxAngle = Math.toRadians(CAROUSEL_ARC_DEPTH_MAX_ANGLE_DEGREES).toFloat()
|
|
||||||
val fadeOutStartAngle = Math.toRadians(CAROUSEL_ARC_FADE_OUT_START_DEGREES).toFloat()
|
|
||||||
val fadeOutEndAngle = Math.toRadians(CAROUSEL_ARC_FADE_OUT_END_DEGREES).toFloat()
|
|
||||||
val angle = (signedDistance / itemStep * angleStep).coerceIn(-maxAngle, maxAngle)
|
|
||||||
val arcRadius = itemStep / angleStep
|
|
||||||
val arcX = sin(angle) * arcRadius
|
|
||||||
val absoluteAngle = abs(angle)
|
|
||||||
val rawDepthInput = ((1f - cos(absoluteAngle)) / (1f - cos(depthMaxAngle)))
|
|
||||||
.coerceIn(0f, 1f)
|
|
||||||
val easedDepthTail = Math.pow(
|
|
||||||
(1f - rawDepthInput).toDouble(),
|
|
||||||
CAROUSEL_ARC_DEPTH_STRETCH.toDouble()
|
|
||||||
).toFloat()
|
|
||||||
val depthInput = (1f - easedDepthTail).coerceIn(0f, 1f)
|
|
||||||
val projectedArcX = arcX * (1f - rawDepthInput * CAROUSEL_ARC_X_DEPTH_FACTOR)
|
|
||||||
|
|
||||||
child.animate().cancel()
|
|
||||||
child.translationX = projectedArcX - signedDistance
|
|
||||||
|
|
||||||
|
val center = getRecyclerViewCenter()
|
||||||
|
val distance = abs(getChildDistanceToCenter(child))
|
||||||
val internalBorderScale = resources.getFraction(R.fraction.carousel_bordercards_scale, 1, 1)
|
val internalBorderScale = resources.getFraction(R.fraction.carousel_bordercards_scale, 1, 1)
|
||||||
val borderScale = preferences.getFloat(CAROUSEL_BORDERCARDS_SCALE, internalBorderScale).coerceIn(
|
val borderScale = preferences.getFloat(CAROUSEL_BORDERCARDS_SCALE, internalBorderScale).coerceIn(
|
||||||
0f,
|
0f,
|
||||||
1f
|
1f
|
||||||
)
|
)
|
||||||
|
|
||||||
val shapedScaling = 1f - depthInput
|
val shapeInput = (distance / center).coerceIn(0f, 1f)
|
||||||
|
val internalShapeSetting = resources.getInteger(R.integer.carousel_cards_scaling_shape)
|
||||||
|
val scalingShapeSetting = preferences.getInt(
|
||||||
|
CAROUSEL_CARDS_SCALING_SHAPE,
|
||||||
|
internalShapeSetting
|
||||||
|
)
|
||||||
|
val shapedScaling = shapingFunction(shapeInput, scalingShapeSetting)
|
||||||
val scale = (borderScale + (1f - borderScale) * shapedScaling).coerceIn(0f, 1f)
|
val scale = (borderScale + (1f - borderScale) * shapedScaling).coerceIn(0f, 1f)
|
||||||
|
|
||||||
|
val maxDistance = width / 2f
|
||||||
|
val alphaInput = (distance / maxDistance).coerceIn(0f, 1f)
|
||||||
val internalBordersAlpha = resources.getFraction(
|
val internalBordersAlpha = resources.getFraction(
|
||||||
R.fraction.carousel_bordercards_alpha,
|
R.fraction.carousel_bordercards_alpha,
|
||||||
1,
|
1,
|
||||||
@@ -223,12 +209,15 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
0f,
|
0f,
|
||||||
1f
|
1f
|
||||||
)
|
)
|
||||||
val shapedAlpha = cos(depthInput * Math.PI).toFloat()
|
val internalAlphaShapeSetting = resources.getInteger(R.integer.carousel_cards_alpha_shape)
|
||||||
val baseAlpha = (borderAlpha + (1f - borderAlpha) * shapedAlpha).coerceIn(0f, 1f)
|
val alphaShapeSetting = preferences.getInt(
|
||||||
val rearPresence = (1f - (absoluteAngle - fadeOutStartAngle) /
|
CAROUSEL_CARDS_ALPHA_SHAPE,
|
||||||
(fadeOutEndAngle - fadeOutStartAngle)).coerceIn(0f, 1f)
|
internalAlphaShapeSetting
|
||||||
val alpha = (baseAlpha * rearPresence).coerceIn(0f, 1f)
|
)
|
||||||
|
val shapedAlpha = shapingFunction(alphaInput, alphaShapeSetting)
|
||||||
|
val alpha = (borderAlpha + (1f - borderAlpha) * shapedAlpha).coerceIn(0f, 1f)
|
||||||
|
|
||||||
|
child.animate().cancel()
|
||||||
child.alpha = alpha
|
child.alpha = alpha
|
||||||
child.scaleX = scale
|
child.scaleX = scale
|
||||||
child.scaleY = scale
|
child.scaleY = scale
|
||||||
@@ -284,9 +273,7 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
0f,
|
0f,
|
||||||
1f
|
1f
|
||||||
)
|
)
|
||||||
val scaledHeight = height * userFactor
|
return (userFactor * (height - bottomInset)).toInt()
|
||||||
val availableHeight = height - bottomInset
|
|
||||||
return minOf(scaledHeight.toInt(), availableHeight.toInt())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setupCarousel(enabled: Boolean) {
|
fun setupCarousel(enabled: Boolean) {
|
||||||
@@ -295,13 +282,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
if (gameAdapter.cardSize == 0) return
|
if (gameAdapter.cardSize == 0) return
|
||||||
if (bottomInset < 0) return
|
if (bottomInset < 0) return
|
||||||
|
|
||||||
itemAnimator?.let {
|
|
||||||
if (savedItemAnimator == null) {
|
|
||||||
savedItemAnimator = it
|
|
||||||
}
|
|
||||||
itemAnimator = null
|
|
||||||
}
|
|
||||||
|
|
||||||
useCustomDrawingOrder = true
|
useCustomDrawingOrder = true
|
||||||
val cardSize = gameAdapter.cardSize
|
val cardSize = gameAdapter.cardSize
|
||||||
|
|
||||||
@@ -356,12 +336,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
// Detach PagerSnapHelper
|
// Detach PagerSnapHelper
|
||||||
pagerSnapHelper?.attachToRecyclerView(null)
|
pagerSnapHelper?.attachToRecyclerView(null)
|
||||||
pagerSnapHelper = null
|
pagerSnapHelper = null
|
||||||
savedItemAnimator?.let {
|
|
||||||
if (itemAnimator == null) {
|
|
||||||
itemAnimator = it
|
|
||||||
}
|
|
||||||
savedItemAnimator = null
|
|
||||||
}
|
|
||||||
useCustomDrawingOrder = false
|
useCustomDrawingOrder = false
|
||||||
// Reset padding and fling
|
// Reset padding and fling
|
||||||
setPadding(0, 0, 0, 0)
|
setPadding(0, 0, 0, 0)
|
||||||
@@ -370,7 +344,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
// Reset scaling
|
// Reset scaling
|
||||||
for (i in 0 until childCount) {
|
for (i in 0 until childCount) {
|
||||||
val child = getChildAt(i)
|
val child = getChildAt(i)
|
||||||
child?.translationX = 0f
|
|
||||||
child?.scaleX = 1f
|
child?.scaleX = 1f
|
||||||
child?.scaleY = 1f
|
child?.scaleY = 1f
|
||||||
child?.alpha = 1f
|
child?.alpha = 1f
|
||||||
|
|||||||
@@ -1245,8 +1245,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_refreshThreadPolicies(JNIEnv* env, jo
|
|||||||
Common::RefreshThreadPolicies();
|
Common::RefreshThreadPolicies();
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_GetDebugKnobAt(JNIEnv* env, jobject jobj, jint index) {
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_getDebugKnobAt(JNIEnv* env, jobject jobj, jint index) {
|
||||||
return static_cast<jboolean>(Settings::GetDebugKnobAt(static_cast<u8>(index)));
|
return static_cast<jboolean>(Settings::getDebugKnobAt(static_cast<u8>(index)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setTurboSpeedLimit(JNIEnv *env, jobject jobj, jboolean enabled) {
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setTurboSpeedLimit(JNIEnv *env, jobject jobj, jboolean enabled) {
|
||||||
|
|||||||
@@ -130,25 +130,6 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setShort(JNIEnv* env, jobject ob
|
|||||||
setting->SetValue(value);
|
setting->SetValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getUnsignedShort(JNIEnv* env, jobject obj,
|
|
||||||
jstring jkey,
|
|
||||||
jboolean needGlobal) {
|
|
||||||
auto setting = getSetting<u16>(env, jkey);
|
|
||||||
if (setting == nullptr) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return static_cast<jint>(setting->GetValue(static_cast<bool>(needGlobal)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setUnsignedShort(JNIEnv* env, jobject obj,
|
|
||||||
jstring jkey, jint value) {
|
|
||||||
auto setting = getSetting<u16>(env, jkey);
|
|
||||||
if (setting == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setting->SetValue(static_cast<u16>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInt(JNIEnv* env, jobject obj, jstring jkey,
|
jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInt(JNIEnv* env, jobject obj, jstring jkey,
|
||||||
jboolean needGlobal) {
|
jboolean needGlobal) {
|
||||||
auto setting = getSetting<int>(env, jkey);
|
auto setting = getSetting<int>(env, jkey);
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item android:state_pressed="true" android:color="?attr/colorControlHighlight" />
|
|
||||||
<item android:state_focused="true" android:color="@android:color/transparent" />
|
|
||||||
<item android:state_selected="true" android:color="@android:color/transparent" />
|
|
||||||
<item android:state_hovered="true" android:color="@android:color/transparent" />
|
|
||||||
<item android:color="@android:color/transparent" />
|
|
||||||
</selector>
|
|
||||||
@@ -10,7 +10,6 @@
|
|||||||
android:clipChildren="true"
|
android:clipChildren="true"
|
||||||
android:layout_margin="0dp"
|
android:layout_margin="0dp"
|
||||||
app:cardBackgroundColor="@color/eden_card_background"
|
app:cardBackgroundColor="@color/eden_card_background"
|
||||||
app:rippleColor="@color/game_card_ripple"
|
|
||||||
app:strokeWidth="1dp"
|
app:strokeWidth="1dp"
|
||||||
app:strokeColor="@color/eden_border">
|
app:strokeColor="@color/eden_border">
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
app:cardCornerRadius="16dp"
|
app:cardCornerRadius="16dp"
|
||||||
app:cardPreventCornerOverlap="true"
|
app:cardPreventCornerOverlap="true"
|
||||||
android:clipChildren="true"
|
android:clipChildren="true"
|
||||||
app:rippleColor="@color/game_card_ripple"
|
|
||||||
android:layout_margin="4dp">
|
android:layout_margin="4dp">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:transitionName="card_game"
|
android:transitionName="card_game"
|
||||||
app:cardCornerRadius="16dp"
|
app:cardCornerRadius="16dp"
|
||||||
app:rippleColor="@color/game_card_ripple"
|
|
||||||
android:foreground="@color/eden_border_gradient_start">
|
android:foreground="@color/eden_border_gradient_start">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:transitionName="card_game_compact"
|
android:transitionName="card_game_compact"
|
||||||
app:cardCornerRadius="16dp"
|
app:cardCornerRadius="16dp"
|
||||||
app:rippleColor="@color/game_card_ripple"
|
|
||||||
android:foreground="@color/eden_border_gradient_start">
|
android:foreground="@color/eden_border_gradient_start">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
app:cardCornerRadius="16dp"
|
app:cardCornerRadius="16dp"
|
||||||
app:cardElevation="0dp"
|
app:cardElevation="0dp"
|
||||||
app:cardBackgroundColor="@android:color/transparent"
|
app:cardBackgroundColor="@android:color/transparent"
|
||||||
app:rippleColor="@color/game_card_ripple"
|
|
||||||
app:strokeWidth="0dp">
|
app:strokeWidth="0dp">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
|||||||
@@ -1107,6 +1107,7 @@
|
|||||||
<string name="theme_mode_light">فاتح</string>
|
<string name="theme_mode_light">فاتح</string>
|
||||||
<string name="theme_mode_dark">داكن</string>
|
<string name="theme_mode_dark">داكن</string>
|
||||||
|
|
||||||
|
<string name="multiplier_none">لا شيء</string>
|
||||||
|
|
||||||
<!-- Black backgrounds theme -->
|
<!-- Black backgrounds theme -->
|
||||||
<string name="use_black_backgrounds">خلفيات سوداء</string>
|
<string name="use_black_backgrounds">خلفيات سوداء</string>
|
||||||
|
|||||||
@@ -991,6 +991,7 @@ Wirklich fortfahren?</string>
|
|||||||
<string name="theme_mode_light">Hell</string>
|
<string name="theme_mode_light">Hell</string>
|
||||||
<string name="theme_mode_dark">Dunkel</string>
|
<string name="theme_mode_dark">Dunkel</string>
|
||||||
|
|
||||||
|
<string name="multiplier_none">Keine</string>
|
||||||
|
|
||||||
<!-- Black backgrounds theme -->
|
<!-- Black backgrounds theme -->
|
||||||
<string name="use_black_backgrounds">Schwarze Hintergründe</string>
|
<string name="use_black_backgrounds">Schwarze Hintergründe</string>
|
||||||
|
|||||||
@@ -1092,6 +1092,7 @@
|
|||||||
<string name="theme_mode_light">Claro</string>
|
<string name="theme_mode_light">Claro</string>
|
||||||
<string name="theme_mode_dark">Oscuro</string>
|
<string name="theme_mode_dark">Oscuro</string>
|
||||||
|
|
||||||
|
<string name="multiplier_none">Nada</string>
|
||||||
|
|
||||||
<!-- Black backgrounds theme -->
|
<!-- Black backgrounds theme -->
|
||||||
<string name="use_black_backgrounds">Fondos oscuros</string>
|
<string name="use_black_backgrounds">Fondos oscuros</string>
|
||||||
|
|||||||
@@ -808,6 +808,9 @@
|
|||||||
<string name="multiplier_x4">x4</string>
|
<string name="multiplier_x4">x4</string>
|
||||||
<string name="multiplier_x8">x8</string>
|
<string name="multiplier_x8">x8</string>
|
||||||
<string name="multiplier_x16">x16</string>
|
<string name="multiplier_x16">x16</string>
|
||||||
|
<string name="multiplier_x32">x32</string>
|
||||||
|
<string name="multiplier_x64">x64</string>
|
||||||
|
<string name="multiplier_none">None</string>
|
||||||
|
|
||||||
<!-- Black backgrounds theme -->
|
<!-- Black backgrounds theme -->
|
||||||
<string name="use_black_backgrounds">پسزمینه مشکی</string>
|
<string name="use_black_backgrounds">پسزمینه مشکی</string>
|
||||||
|
|||||||
@@ -1004,6 +1004,7 @@
|
|||||||
<string name="theme_mode_light">Lumineux</string>
|
<string name="theme_mode_light">Lumineux</string>
|
||||||
<string name="theme_mode_dark">Sombre</string>
|
<string name="theme_mode_dark">Sombre</string>
|
||||||
|
|
||||||
|
<string name="multiplier_none">Aucun</string>
|
||||||
|
|
||||||
<!-- Black backgrounds theme -->
|
<!-- Black backgrounds theme -->
|
||||||
<string name="use_black_backgrounds">Arrière-plan noir</string>
|
<string name="use_black_backgrounds">Arrière-plan noir</string>
|
||||||
|
|||||||
@@ -935,6 +935,7 @@
|
|||||||
<string name="theme_mode_light">Jasny</string>
|
<string name="theme_mode_light">Jasny</string>
|
||||||
<string name="theme_mode_dark">Ciemny</string>
|
<string name="theme_mode_dark">Ciemny</string>
|
||||||
|
|
||||||
|
<string name="multiplier_none">Brak</string>
|
||||||
|
|
||||||
<!-- Black backgrounds theme -->
|
<!-- Black backgrounds theme -->
|
||||||
<string name="use_black_backgrounds">Czarne tła</string>
|
<string name="use_black_backgrounds">Czarne tła</string>
|
||||||
|
|||||||
@@ -891,6 +891,7 @@
|
|||||||
<string name="theme_mode_light">Claro</string>
|
<string name="theme_mode_light">Claro</string>
|
||||||
<string name="theme_mode_dark">Escuro</string>
|
<string name="theme_mode_dark">Escuro</string>
|
||||||
|
|
||||||
|
<string name="multiplier_none">Nenhum</string>
|
||||||
|
|
||||||
<!-- Black backgrounds theme -->
|
<!-- Black backgrounds theme -->
|
||||||
<string name="use_black_backgrounds">Planos de fundo pretos</string>
|
<string name="use_black_backgrounds">Planos de fundo pretos</string>
|
||||||
|
|||||||
@@ -1071,6 +1071,7 @@
|
|||||||
<string name="theme_mode_light">Светлая</string>
|
<string name="theme_mode_light">Светлая</string>
|
||||||
<string name="theme_mode_dark">Темная</string>
|
<string name="theme_mode_dark">Темная</string>
|
||||||
|
|
||||||
|
<string name="multiplier_none">Отключено</string>
|
||||||
|
|
||||||
<!-- Black backgrounds theme -->
|
<!-- Black backgrounds theme -->
|
||||||
<string name="use_black_backgrounds">Чёрный фон</string>
|
<string name="use_black_backgrounds">Чёрный фон</string>
|
||||||
|
|||||||
@@ -1053,6 +1053,7 @@
|
|||||||
<string name="theme_mode_light">Світла</string>
|
<string name="theme_mode_light">Світла</string>
|
||||||
<string name="theme_mode_dark">Темна</string>
|
<string name="theme_mode_dark">Темна</string>
|
||||||
|
|
||||||
|
<string name="multiplier_none">Жодного</string>
|
||||||
|
|
||||||
<!-- Black backgrounds theme -->
|
<!-- Black backgrounds theme -->
|
||||||
<string name="use_black_backgrounds">Чорний фон</string>
|
<string name="use_black_backgrounds">Чорний фон</string>
|
||||||
|
|||||||
@@ -1081,6 +1081,7 @@
|
|||||||
<string name="theme_mode_light">浅色</string>
|
<string name="theme_mode_light">浅色</string>
|
||||||
<string name="theme_mode_dark">深色</string>
|
<string name="theme_mode_dark">深色</string>
|
||||||
|
|
||||||
|
<string name="multiplier_none">无</string>
|
||||||
|
|
||||||
<!-- Black backgrounds theme -->
|
<!-- Black backgrounds theme -->
|
||||||
<string name="use_black_backgrounds">使用黑色背景</string>
|
<string name="use_black_backgrounds">使用黑色背景</string>
|
||||||
|
|||||||
@@ -1006,6 +1006,7 @@
|
|||||||
<string name="theme_mode_light">淺色</string>
|
<string name="theme_mode_light">淺色</string>
|
||||||
<string name="theme_mode_dark">深色</string>
|
<string name="theme_mode_dark">深色</string>
|
||||||
|
|
||||||
|
<string name="multiplier_none">無</string>
|
||||||
|
|
||||||
<!-- Black backgrounds theme -->
|
<!-- Black backgrounds theme -->
|
||||||
<string name="use_black_backgrounds">黑色背景</string>
|
<string name="use_black_backgrounds">黑色背景</string>
|
||||||
|
|||||||
@@ -111,38 +111,43 @@
|
|||||||
<item>1</item>
|
<item>1</item>
|
||||||
</integer-array>
|
</integer-array>
|
||||||
|
|
||||||
|
<!-- VRAM USAGE MODE CHOICES -->
|
||||||
<string-array name="vramUsageMethodNames">
|
<string-array name="vramUsageMethodNames">
|
||||||
<item>@string/vram_usage_conservative</item>
|
<item>@string/vram_usage_conservative</item>
|
||||||
<item>@string/vram_usage_aggressive</item>
|
<item>@string/vram_usage_aggressive</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<!-- VRAM USAGE MODE VALUES -->
|
||||||
<integer-array name="vramUsageMethodValues">
|
<integer-array name="vramUsageMethodValues">
|
||||||
<item>0</item>
|
<item>0</item> <!-- Conservative -->
|
||||||
<item>1</item>
|
<item>1</item> <!-- Aggressive -->
|
||||||
</integer-array>
|
</integer-array>
|
||||||
|
|
||||||
|
<!-- ASTC Decoding Method Choices -->
|
||||||
<string-array name="astcDecodingMethodNames">
|
<string-array name="astcDecodingMethodNames">
|
||||||
<item>@string/accelerate_astc_cpu</item>
|
<item>@string/accelerate_astc_cpu</item>
|
||||||
<item>@string/accelerate_astc_gpu</item>
|
<item>@string/accelerate_astc_gpu</item>
|
||||||
<item>@string/accelerate_astc_async</item>
|
<item>@string/accelerate_astc_async</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<!-- ASTC Decoding Method Values -->
|
||||||
<integer-array name="astcDecodingMethodValues">
|
<integer-array name="astcDecodingMethodValues">
|
||||||
<item>0</item>
|
<item>0</item> <!-- CPU -->
|
||||||
<item>1</item>
|
<item>1</item> <!-- GPU -->
|
||||||
<item>2</item>
|
<item>2</item> <!-- CPU Asynchronously -->
|
||||||
</integer-array>
|
</integer-array>
|
||||||
|
<!-- NVDEC Emulation Choices -->
|
||||||
<string-array name="rendererNvdecNames">
|
<string-array name="rendererNvdecNames">
|
||||||
<item>@string/nvdec_emulation_none</item>
|
<item>@string/nvdec_emulation_none</item> <!-- Off -->
|
||||||
<item>@string/nvdec_emulation_cpu</item>
|
<item>@string/nvdec_emulation_cpu</item> <!-- Cpu -->
|
||||||
<item>@string/nvdec_emulation_gpu</item>
|
<item>@string/nvdec_emulation_gpu</item> <!-- Gpu -->
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<!-- NVDEC Emulation Values -->
|
||||||
<integer-array name="rendererNvdecValues">
|
<integer-array name="rendererNvdecValues">
|
||||||
<item>0</item>
|
<item>3</item> <!-- Off value -->
|
||||||
<item>1</item>
|
<item>1</item> <!-- CPU value -->
|
||||||
<item>2</item>
|
<item>2</item> <!-- GPU value -->
|
||||||
</integer-array>
|
</integer-array>
|
||||||
|
|
||||||
<string-array name="rendererResolutionNames">
|
<string-array name="rendererResolutionNames">
|
||||||
@@ -508,6 +513,9 @@
|
|||||||
<item>@string/multiplier_x4</item>
|
<item>@string/multiplier_x4</item>
|
||||||
<item>@string/multiplier_x8</item>
|
<item>@string/multiplier_x8</item>
|
||||||
<item>@string/multiplier_x16</item>
|
<item>@string/multiplier_x16</item>
|
||||||
|
<item>@string/multiplier_x32</item>
|
||||||
|
<item>@string/multiplier_x64</item>
|
||||||
|
<item>@string/multiplier_none</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<integer-array name="anisoValues">
|
<integer-array name="anisoValues">
|
||||||
<item>0</item>
|
<item>0</item>
|
||||||
@@ -516,6 +524,9 @@
|
|||||||
<item>3</item>
|
<item>3</item>
|
||||||
<item>4</item>
|
<item>4</item>
|
||||||
<item>5</item>
|
<item>5</item>
|
||||||
|
<item>6</item>
|
||||||
|
<item>7</item>
|
||||||
|
<item>8</item>
|
||||||
</integer-array>
|
</integer-array>
|
||||||
|
|
||||||
<string-array name="verticalAlignmentEntries">
|
<string-array name="verticalAlignmentEntries">
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
<integer name="game_columns_grid">2</integer>
|
<integer name="game_columns_grid">2</integer>
|
||||||
<integer name="carousel_max_fling_count">4</integer>
|
<integer name="carousel_max_fling_count">4</integer>
|
||||||
<integer name="carousel_focus_search_repeat_threshold_ms">100</integer>
|
<integer name="carousel_focus_search_repeat_threshold_ms">100</integer>
|
||||||
|
<integer name="carousel_cards_scaling_shape">1</integer>
|
||||||
|
<integer name="carousel_cards_alpha_shape">4</integer>
|
||||||
|
|
||||||
<!-- Default SWITCH landscape layout -->
|
<!-- Default SWITCH landscape layout -->
|
||||||
<integer name="BUTTON_A_X">760</integer>
|
<integer name="BUTTON_A_X">760</integer>
|
||||||
|
|||||||
@@ -111,7 +111,7 @@
|
|||||||
|
|
||||||
<!-- NVDEC Emulation -->
|
<!-- NVDEC Emulation -->
|
||||||
<string name="nvdec_emulation">NVDEC Emulation</string>
|
<string name="nvdec_emulation">NVDEC Emulation</string>
|
||||||
<string name="nvdec_emulation_description">Change to CPU if a crash occurs on cinematics.</string>
|
<string name="nvdec_emulation_description">Select how video decoding (NVDEC) is handled during cutscenes and intros.</string>
|
||||||
<string name="nvdec_emulation_cpu" translatable="false">CPU</string>
|
<string name="nvdec_emulation_cpu" translatable="false">CPU</string>
|
||||||
<string name="nvdec_emulation_gpu" translatable="false">GPU</string>
|
<string name="nvdec_emulation_gpu" translatable="false">GPU</string>
|
||||||
<string name="nvdec_emulation_none">None</string>
|
<string name="nvdec_emulation_none">None</string>
|
||||||
@@ -1246,6 +1246,9 @@
|
|||||||
<string name="multiplier_x4" translatable="false">x4</string>
|
<string name="multiplier_x4" translatable="false">x4</string>
|
||||||
<string name="multiplier_x8" translatable="false">x8</string>
|
<string name="multiplier_x8" translatable="false">x8</string>
|
||||||
<string name="multiplier_x16" translatable="false">x16</string>
|
<string name="multiplier_x16" translatable="false">x16</string>
|
||||||
|
<string name="multiplier_x32" translatable="false">x32</string>
|
||||||
|
<string name="multiplier_x64" translatable="false">x64</string>
|
||||||
|
<string name="multiplier_none">None</string>
|
||||||
|
|
||||||
<!-- Black backgrounds theme -->
|
<!-- Black backgrounds theme -->
|
||||||
<string name="use_black_backgrounds">Black backgrounds</string>
|
<string name="use_black_backgrounds">Black backgrounds</string>
|
||||||
|
|||||||
@@ -19,23 +19,6 @@
|
|||||||
namespace AudioCore::Sink {
|
namespace AudioCore::Sink {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
[[nodiscard]] bool InitializeAudio() {
|
|
||||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
|
||||||
// See https://github.com/PCSX2/pcsx2/pull/12312
|
|
||||||
// "SDL and cubeb backends previously resulted in different names for the output which
|
|
||||||
// caused them be identified as different applications by the OS."
|
|
||||||
//
|
|
||||||
// Keep in sync with cubeb_sink.cpp name.
|
|
||||||
SDL_SetHint("SDL_AUDIO_DEVICE_APP_NAME", "yuzu Latency Getter");
|
|
||||||
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
|
||||||
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_AudioDeviceID FindAudioDeviceByName(const std::string& device_name, bool capture) {
|
SDL_AudioDeviceID FindAudioDeviceByName(const std::string& device_name, bool capture) {
|
||||||
int device_count = 0;
|
int device_count = 0;
|
||||||
SDL_AudioDeviceID* devices = capture ? SDL_GetAudioRecordingDevices(&device_count)
|
SDL_AudioDeviceID* devices = capture ? SDL_GetAudioRecordingDevices(&device_count)
|
||||||
@@ -221,14 +204,20 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
SDLSink::SDLSink(std::string_view target_device_name) {
|
SDLSink::SDLSink(std::string_view target_device_name) {
|
||||||
if (InitializeAudio()) {
|
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||||
if (target_device_name != auto_device_name && !target_device_name.empty()) {
|
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||||
output_device = target_device_name;
|
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
||||||
} else {
|
return;
|
||||||
output_device.clear();
|
|
||||||
}
|
}
|
||||||
device_channels = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target_device_name != auto_device_name && !target_device_name.empty()) {
|
||||||
|
output_device = target_device_name;
|
||||||
|
} else {
|
||||||
|
output_device.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
device_channels = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDLSink::~SDLSink() = default;
|
SDLSink::~SDLSink() = default;
|
||||||
@@ -276,10 +265,15 @@ void SDLSink::SetSystemVolume(f32 volume) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> ListSDLSinkDevices(bool capture) {
|
std::vector<std::string> ListSDLSinkDevices(bool capture) {
|
||||||
if (!InitializeAudio())
|
|
||||||
return {}; //no devices
|
|
||||||
|
|
||||||
std::vector<std::string> device_list;
|
std::vector<std::string> device_list;
|
||||||
|
|
||||||
|
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||||
|
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||||
|
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int device_count = 0;
|
int device_count = 0;
|
||||||
SDL_AudioDeviceID* devices =
|
SDL_AudioDeviceID* devices =
|
||||||
capture ? SDL_GetAudioRecordingDevices(&device_count)
|
capture ? SDL_GetAudioRecordingDevices(&device_count)
|
||||||
@@ -310,8 +304,13 @@ bool IsSDLSuitable() {
|
|||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
// Check SDL can init
|
// Check SDL can init
|
||||||
if (!InitializeAudio()!
|
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||||
return false;
|
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||||
|
LOG_ERROR(Audio_Sink, "SDL failed to init, it is not suitable. Error: {}",
|
||||||
|
SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We can set any latency frequency we want with SDL, so no need to check that.
|
// We can set any latency frequency we want with SDL, so no need to check that.
|
||||||
|
|
||||||
|
|||||||
+45
-56
@@ -39,19 +39,6 @@
|
|||||||
|
|
||||||
namespace Common::Log {
|
namespace Common::Log {
|
||||||
|
|
||||||
/// @brief A log entry. Log entries are store in a structured format to permit more varied output
|
|
||||||
/// formatting on different frontends, as well as facilitating filtering and aggregation.
|
|
||||||
struct Entry {
|
|
||||||
char const* message = nullptr;
|
|
||||||
size_t message_len = 0;
|
|
||||||
std::chrono::microseconds timestamp;
|
|
||||||
Class log_class{};
|
|
||||||
Level log_level{};
|
|
||||||
const char* filename = nullptr;
|
|
||||||
const char* function = nullptr;
|
|
||||||
uint32_t line_num = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
/// @brief Returns the name of the passed log class as a C-string. Subclasses are separated by periods
|
/// @brief Returns the name of the passed log class as a C-string. Subclasses are separated by periods
|
||||||
@@ -83,6 +70,8 @@ const char* GetLevelName(Level log_level) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Some IDEs prefer <file>:<line> instead, so let's just do that :)
|
// Some IDEs prefer <file>:<line> instead, so let's just do that :)
|
||||||
std::string FormatLogMessage(const Entry& entry) noexcept {
|
std::string FormatLogMessage(const Entry& entry) noexcept {
|
||||||
if (!entry.filename) return "";
|
if (!entry.filename) return "";
|
||||||
@@ -90,9 +79,10 @@ std::string FormatLogMessage(const Entry& entry) noexcept {
|
|||||||
auto const time_fractional = uint32_t(entry.timestamp.count() % 1000000);
|
auto const time_fractional = uint32_t(entry.timestamp.count() % 1000000);
|
||||||
auto const class_name = GetLogClassName(entry.log_class);
|
auto const class_name = GetLogClassName(entry.log_class);
|
||||||
auto const level_name = GetLevelName(entry.log_level);
|
auto const level_name = GetLevelName(entry.log_level);
|
||||||
return fmt::format("[{:4d}.{:06d}] {} <{}> {}:{}:{}: {}\n", time_seconds, time_fractional, class_name, level_name, entry.filename, entry.line_num, entry.function, entry.message);
|
return fmt::format("[{:4d}.{:06d}] {} <{}> {}:{}:{}: {}", time_seconds, time_fractional, class_name, level_name, entry.filename, entry.line_num, entry.function, entry.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
template <typename It>
|
template <typename It>
|
||||||
Level GetLevelByName(const It begin, const It end) {
|
Level GetLevelByName(const It begin, const It end) {
|
||||||
for (u32 i = 0; i < u32(Level::Count); ++i) {
|
for (u32 i = 0; i < u32(Level::Count); ++i) {
|
||||||
@@ -137,6 +127,25 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
|||||||
instance.SetClassLevel(log_class, level);
|
instance.SetClassLevel(log_class, level);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} // Anonymous namespace
|
||||||
|
|
||||||
|
void Filter::ParseFilterString(std::string_view filter_view) {
|
||||||
|
auto clause_begin = filter_view.cbegin();
|
||||||
|
while (clause_begin != filter_view.cend()) {
|
||||||
|
auto clause_end = std::find(clause_begin, filter_view.cend(), ' ');
|
||||||
|
// If clause isn't empty
|
||||||
|
if (clause_end != clause_begin) {
|
||||||
|
ParseFilterRule(*this, clause_begin, clause_end);
|
||||||
|
}
|
||||||
|
if (clause_end != filter_view.cend()) {
|
||||||
|
// Skip over the whitespace
|
||||||
|
++clause_end;
|
||||||
|
}
|
||||||
|
clause_begin = clause_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
/// @brief Trims up to and including the last of ../, ..\, src/, src\ in a string
|
/// @brief Trims up to and including the last of ../, ..\, src/, src\ in a string
|
||||||
/// do not be fooled this isn't generating new strings on .rodata :)
|
/// do not be fooled this isn't generating new strings on .rodata :)
|
||||||
@@ -199,7 +208,7 @@ struct ColorConsoleBackend final : public Backend {
|
|||||||
}());
|
}());
|
||||||
SetConsoleTextAttribute(console_handle, color);
|
SetConsoleTextAttribute(console_handle, color);
|
||||||
auto const df = GetDirectFormatArgs(entry);
|
auto const df = GetDirectFormatArgs(entry);
|
||||||
std::fprintf(stdout, CCB_PRINTF_FMT "\n", df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message);
|
std::fprintf(stdout, CCB_PRINTF_FMT "\n", df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Flush() noexcept override {}
|
void Flush() noexcept override {}
|
||||||
@@ -211,24 +220,22 @@ struct ColorConsoleBackend final : public Backend {
|
|||||||
~ColorConsoleBackend() noexcept override {}
|
~ColorConsoleBackend() noexcept override {}
|
||||||
void Write(const Entry& entry) noexcept override {
|
void Write(const Entry& entry) noexcept override {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
|
#define ESC "\x1b"
|
||||||
auto const color_str = [&entry]() -> const char* {
|
auto const color_str = [&entry]() -> const char* {
|
||||||
switch (entry.log_level) {
|
switch (entry.log_level) {
|
||||||
case Level::Debug: return "[0;36m"; // Cyan
|
#define CCB_MAKE_COLOR_FMT(X) ESC X CCB_PRINTF_FMT ESC "[0m\n"
|
||||||
case Level::Info: return "[0;37m"; // Bright gray
|
case Level::Debug: return CCB_MAKE_COLOR_FMT("[0;36m"); // Cyan
|
||||||
case Level::Warning: return "[1;33m"; // Bright yellow
|
case Level::Info: return CCB_MAKE_COLOR_FMT("[0;37m"); // Bright gray
|
||||||
case Level::Error: return "[1;31m"; // Bright red
|
case Level::Warning: return CCB_MAKE_COLOR_FMT("[1;33m"); // Bright yellow
|
||||||
case Level::Critical: return "[1;35m"; // Bright magenta
|
case Level::Error: return CCB_MAKE_COLOR_FMT("[1;31m"); // Bright red
|
||||||
default: return "[1;30m"; // Grey
|
case Level::Critical: return CCB_MAKE_COLOR_FMT("[1;35m"); // Bright magenta
|
||||||
|
default: return CCB_MAKE_COLOR_FMT("[1;30m"); // Grey
|
||||||
|
#undef CCB_MAKE_COLOR_FMT
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
auto const df = GetDirectFormatArgs(entry);
|
auto const df = GetDirectFormatArgs(entry);
|
||||||
// more restrictive, because take for example this simple prelude:
|
std::fprintf(stdout, color_str, df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message.c_str());
|
||||||
// [ 50.872256] Config <Info> common/settings.cpp:142:LogSettings:
|
#undef ESC
|
||||||
char buffer[256];
|
|
||||||
auto result = fmt::format_to_n(buffer, sizeof(buffer) - 1, "\x1b{}[{:4d}.{:06d}] {} <{}> {}:{}:{}: ", color_str, df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message);
|
|
||||||
std::fwrite(buffer, 1, (std::min)(sizeof(buffer) - 1, result.size), stdout);
|
|
||||||
std::fwrite(entry.message, 1, entry.message_len, stdout);
|
|
||||||
std::fwrite("\x1b[0m\n", 1, sizeof("\x1b[0m\n"), stdout);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Flush() noexcept override {}
|
void Flush() noexcept override {}
|
||||||
@@ -239,7 +246,7 @@ struct ColorConsoleBackend final : public Backend {
|
|||||||
#ifndef __OPENORBIS__
|
#ifndef __OPENORBIS__
|
||||||
/// @brief Backend that writes to a file passed into the constructor
|
/// @brief Backend that writes to a file passed into the constructor
|
||||||
struct FileBackend final : public Backend {
|
struct FileBackend final : public Backend {
|
||||||
explicit FileBackend(const std::filesystem::path filename) noexcept {
|
explicit FileBackend(const std::filesystem::path& filename) noexcept {
|
||||||
auto old_filename = filename;
|
auto old_filename = filename;
|
||||||
old_filename += ".old.txt";
|
old_filename += ".old.txt";
|
||||||
// Existence checks are done within the functions themselves.
|
// Existence checks are done within the functions themselves.
|
||||||
@@ -254,7 +261,7 @@ struct FileBackend final : public Backend {
|
|||||||
if (!enabled)
|
if (!enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto message = FormatLogMessage(entry);
|
auto message = FormatLogMessage(entry).append(1, '\n');
|
||||||
#ifndef __ANDROID__
|
#ifndef __ANDROID__
|
||||||
if (Settings::values.censor_username.GetValue()) {
|
if (Settings::values.censor_username.GetValue()) {
|
||||||
// This must be a static otherwise it would get checked on EVERY
|
// This must be a static otherwise it would get checked on EVERY
|
||||||
@@ -262,7 +269,8 @@ struct FileBackend final : public Backend {
|
|||||||
static std::string username = []() -> std::string {
|
static std::string username = []() -> std::string {
|
||||||
// in order of precedence
|
// in order of precedence
|
||||||
// LOGNAME usually works on UNIX, USERNAME on Windows
|
// LOGNAME usually works on UNIX, USERNAME on Windows
|
||||||
// Some UNIX systems suck and don't use LOGNAME so we also need USER :(
|
// Some UNIX systems suck and don't use LOGNAME so we also
|
||||||
|
// need USER :(
|
||||||
for (auto const var : { "LOGNAME", "USERNAME", "USER", })
|
for (auto const var : { "LOGNAME", "USERNAME", "USER", })
|
||||||
if (auto const s = ::getenv(var); s != nullptr)
|
if (auto const s = ::getenv(var); s != nullptr)
|
||||||
return std::string{s};
|
return std::string{s};
|
||||||
@@ -272,7 +280,7 @@ struct FileBackend final : public Backend {
|
|||||||
boost::replace_all(message, username, "user");
|
boost::replace_all(message, username, "user");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
bytes_written += file->WriteSpan(std::span<const char>{message.begin(), message.end()});
|
bytes_written += file->WriteString(message);
|
||||||
|
|
||||||
// Option to log each line rather than 4k buffers
|
// Option to log each line rather than 4k buffers
|
||||||
if (Settings::values.log_flush_line.GetValue())
|
if (Settings::values.log_flush_line.GetValue())
|
||||||
@@ -300,13 +308,14 @@ private:
|
|||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
/// @brief Backend that writes to Visual Studio's output window
|
/// @brief Backend that writes to Visual Studio's output window
|
||||||
struct DebuggerBackend final : public Backend {
|
struct DebuggerBackend final : public Backend {
|
||||||
explicit DebuggerBackend() noexcept = default;
|
explicit DebuggerBackend() noexcept = default;
|
||||||
~DebuggerBackend() noexcept override = default;
|
~DebuggerBackend() noexcept override = default;
|
||||||
void Write(const Entry& entry) noexcept override {
|
void Write(const Entry& entry) noexcept override {
|
||||||
::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry)).c_str());
|
::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
|
||||||
}
|
}
|
||||||
void Flush() noexcept override {}
|
void Flush() noexcept override {}
|
||||||
};
|
};
|
||||||
@@ -329,7 +338,7 @@ struct LogcatBackend : public Backend {
|
|||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
auto const df = GetDirectFormatArgs(entry);
|
auto const df = GetDirectFormatArgs(entry);
|
||||||
__android_log_print(android_log_priority, "YuzuNative", CCB_PRINTF_FMT, df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message);
|
__android_log_print(android_log_priority, "YuzuNative", CCB_PRINTF_FMT, df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message.c_str());
|
||||||
}
|
}
|
||||||
void Flush() noexcept override {}
|
void Flush() noexcept override {}
|
||||||
};
|
};
|
||||||
@@ -368,23 +377,7 @@ struct Impl {
|
|||||||
#endif
|
#endif
|
||||||
std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
|
std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
|
||||||
};
|
};
|
||||||
} // Anonymous namespace
|
} // namespace
|
||||||
|
|
||||||
void Filter::ParseFilterString(std::string_view filter_view) {
|
|
||||||
auto clause_begin = filter_view.cbegin();
|
|
||||||
while (clause_begin < filter_view.cend()) {
|
|
||||||
auto clause_end = std::find(clause_begin, filter_view.cend(), ' ');
|
|
||||||
// If clause isn't empty
|
|
||||||
if (clause_end != clause_begin) {
|
|
||||||
ParseFilterRule(*this, clause_begin, clause_end);
|
|
||||||
}
|
|
||||||
if (clause_end != filter_view.cend()) {
|
|
||||||
// Skip over the whitespace
|
|
||||||
++clause_end;
|
|
||||||
}
|
|
||||||
clause_begin = clause_end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructor shall NOT depend upon Settings() or whatever
|
// Constructor shall NOT depend upon Settings() or whatever
|
||||||
// it's ran at global static ctor() time... so BE CAREFUL MFER!
|
// it's ran at global static ctor() time... so BE CAREFUL MFER!
|
||||||
@@ -426,13 +419,9 @@ void SetColorConsoleBackendEnabled(bool enabled) {
|
|||||||
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, fmt::string_view format, const fmt::format_args& args) {
|
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, fmt::string_view format, const fmt::format_args& args) {
|
||||||
if (logging_instance && logging_instance->filter.CheckMessage(log_class, log_level)) {
|
if (logging_instance && logging_instance->filter.CheckMessage(log_class, log_level)) {
|
||||||
auto const flush = ::Settings::values.log_flush_line.GetValue();
|
auto const flush = ::Settings::values.log_flush_line.GetValue();
|
||||||
char buffer[BUFSIZ];
|
|
||||||
auto result = fmt::vformat_to_n(buffer, sizeof(buffer) - 1, format, args);
|
|
||||||
buffer[(std::min)(result.size, sizeof(buffer) - 1)] = '\0';
|
|
||||||
logging_instance->ForEachBackend([=](Backend& backend) {
|
logging_instance->ForEachBackend([=](Backend& backend) {
|
||||||
backend.Write(Entry{
|
backend.Write(Entry{
|
||||||
.message = buffer,
|
.message = fmt::vformat(format, args),
|
||||||
.message_len = (std::min)(result.size, sizeof(buffer) - 1),
|
|
||||||
.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - logging_instance->time_origin),
|
.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - logging_instance->time_origin),
|
||||||
.log_class = log_class,
|
.log_class = log_class,
|
||||||
.log_level = log_level,
|
.log_level = log_level,
|
||||||
|
|||||||
@@ -140,4 +140,25 @@ void Stop();
|
|||||||
void SetGlobalFilter(const Filter& filter);
|
void SetGlobalFilter(const Filter& filter);
|
||||||
void SetColorConsoleBackendEnabled(bool enabled);
|
void SetColorConsoleBackendEnabled(bool enabled);
|
||||||
|
|
||||||
|
/// @brief A log entry. Log entries are store in a structured format to permit more varied output
|
||||||
|
/// formatting on different frontends, as well as facilitating filtering and aggregation.
|
||||||
|
struct Entry {
|
||||||
|
std::string message;
|
||||||
|
std::chrono::microseconds timestamp;
|
||||||
|
Class log_class{};
|
||||||
|
Level log_level{};
|
||||||
|
const char* filename = nullptr;
|
||||||
|
const char* function = nullptr;
|
||||||
|
unsigned int line_num = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Formats a log entry into the provided text buffer.
|
||||||
|
std::string FormatLogMessage(const Entry& entry) noexcept;
|
||||||
|
|
||||||
|
/// Prints the same message as `PrintMessage`, but colored according to the severity level.
|
||||||
|
void PrintColoredMessage(const Entry& entry) noexcept;
|
||||||
|
|
||||||
|
/// Formats and prints a log entry to the android logcat.
|
||||||
|
void PrintMessageToLogcat(const Entry& entry) noexcept;
|
||||||
|
|
||||||
} // namespace Common::Log
|
} // namespace Common::Log
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ std::optional<std::string> MakeRequest(const std::string& url, const std::string
|
|||||||
response.status);
|
response.status);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (!response.has_header("content-type")) {
|
if (response.headers.find("content-type") == response.headers.end()) {
|
||||||
LOG_ERROR(Common, "GET to {}{} returned no content", url, path);
|
LOG_ERROR(Common, "GET to {}{} returned no content", url, path);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,9 +132,11 @@ void LogSettings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOG_INFO(Config, "Eden Configuration:");
|
|
||||||
|
std::string settings_str{};
|
||||||
for (auto const& e : settings_list)
|
for (auto const& e : settings_list)
|
||||||
LOG_INFO(Config, "{}", e);
|
settings_str += e;
|
||||||
|
LOG_INFO(Config, "Eden Configuration:\n{}", settings_str);
|
||||||
#define LOG_PATH(NAME) \
|
#define LOG_PATH(NAME) \
|
||||||
LOG_INFO(Config, #NAME ": {}", Common::FS::PathToUTF8String(Common::FS::GetEdenPath(Common::FS::EdenPath::NAME)))
|
LOG_INFO(Config, #NAME ": {}", Common::FS::PathToUTF8String(Common::FS::GetEdenPath(Common::FS::EdenPath::NAME)))
|
||||||
LOG_PATH(CacheDir);
|
LOG_PATH(CacheDir);
|
||||||
@@ -146,7 +148,7 @@ void LogSettings() {
|
|||||||
#undef LOG_PATH
|
#undef LOG_PATH
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetDebugKnobAt(u8 i) {
|
bool getDebugKnobAt(u8 i) {
|
||||||
return (values.debug_knobs.GetValue() & (1 << (i & 0xF))) != 0;
|
return (values.debug_knobs.GetValue() & (1 << (i & 0xF))) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -904,7 +904,7 @@ struct Values {
|
|||||||
0,
|
0,
|
||||||
65535,
|
65535,
|
||||||
"debug_knobs",
|
"debug_knobs",
|
||||||
Category::System,
|
Category::Debugging,
|
||||||
Specialization::Countable,
|
Specialization::Countable,
|
||||||
true,
|
true,
|
||||||
true};
|
true};
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt
|
|||||||
GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica,
|
GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica,
|
||||||
Japan, Kwajalein, Libya, Met, Mst, Mst7Mdt, Navajo, Nz, NzChat, Poland, Portugal, Prc, Pst8Pdt,
|
Japan, Kwajalein, Libya, Met, Mst, Mst7Mdt, Navajo, Nz, NzChat, Poland, Portugal, Prc, Pst8Pdt,
|
||||||
Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu);
|
Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu);
|
||||||
ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16);
|
ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16, X32, X64, None);
|
||||||
ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous);
|
ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous);
|
||||||
ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
|
ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
|
||||||
ENUM(FramePacingMode, Target_Auto, Target_30, Target_60, Target_90, Target_120);
|
ENUM(FramePacingMode, Target_Auto, Target_30, Target_60, Target_90, Target_120);
|
||||||
|
|||||||
@@ -7,4 +7,6 @@
|
|||||||
#define STB_IMAGE_IMPLEMENTATION 1
|
#define STB_IMAGE_IMPLEMENTATION 1
|
||||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION 1
|
#define STB_IMAGE_RESIZE_IMPLEMENTATION 1
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION 1
|
#define STB_IMAGE_WRITE_IMPLEMENTATION 1
|
||||||
|
#define STBI_ONLY_JPEG 1
|
||||||
|
|
||||||
#include "common/stb.h"
|
#include "common/stb.h"
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define STBI_ONLY_JPEG 1
|
#define STBI_ONLY_JPEG 1
|
||||||
#define STBI_WRITE_NO_STDIO 1
|
|
||||||
#include <stb_image.h>
|
#include <stb_image.h>
|
||||||
#include <stb_image_resize.h>
|
#include <stb_image_resize.h>
|
||||||
#include <stb_image_write.h>
|
#include <stb_image_write.h>
|
||||||
|
|||||||
@@ -286,7 +286,6 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
|||||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
|
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
|
||||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
|
||||||
break;
|
break;
|
||||||
// Paranoia mode for debugging optimizations
|
// Paranoia mode for debugging optimizations
|
||||||
case Settings::CpuAccuracy::Paranoid:
|
case Settings::CpuAccuracy::Paranoid:
|
||||||
|
|||||||
@@ -338,7 +338,6 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
|||||||
config.unsafe_optimizations = true;
|
config.unsafe_optimizations = true;
|
||||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||||
config.fastmem_address_space_bits = 64;
|
config.fastmem_address_space_bits = 64;
|
||||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
|
||||||
break;
|
break;
|
||||||
// Paranoia mode for debugging optimizations
|
// Paranoia mode for debugging optimizations
|
||||||
case Settings::CpuAccuracy::Paranoid:
|
case Settings::CpuAccuracy::Paranoid:
|
||||||
|
|||||||
@@ -324,7 +324,6 @@ Result IApplicationFunctions::NotifyRunning(Out<bool> out_became_running) {
|
|||||||
|
|
||||||
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
|
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
|
||||||
LOG_WARNING(Service_AM, "(stubbed)");
|
LOG_WARNING(Service_AM, "(stubbed)");
|
||||||
R_UNLESS(out_pseudo_device_id, ResultUnknown);
|
|
||||||
|
|
||||||
// This should be hashed with the device specific hash
|
// This should be hashed with the device specific hash
|
||||||
// for now this will do
|
// for now this will do
|
||||||
|
|||||||
@@ -5,136 +5,33 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <mutex>
|
#include <ctime>
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/kernel/k_event.h"
|
#include "core/hle/kernel/k_event.h"
|
||||||
#include "core/hle/service/cmif_serialization.h"
|
|
||||||
#include "core/hle/service/cmif_types.h"
|
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
#include "core/hle/service/kernel_helpers.h"
|
#include "core/hle/service/kernel_helpers.h"
|
||||||
#include "core/hle/service/nim/nim.h"
|
#include "core/hle/service/nim/nim.h"
|
||||||
#include "core/hle/service/os/event.h"
|
|
||||||
#include "core/hle/service/server_manager.h"
|
#include "core/hle/service/server_manager.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
namespace Service::NIM {
|
namespace Service::NIM {
|
||||||
|
|
||||||
class IShopServiceAsync final : public ServiceFramework<IShopServiceAsync> {
|
class IShopServiceAsync final : public ServiceFramework<IShopServiceAsync> {
|
||||||
public:
|
public:
|
||||||
explicit IShopServiceAsync(Core::System& system_)
|
explicit IShopServiceAsync(Core::System& system_)
|
||||||
: ServiceFramework{system_, "IShopServiceAsync"},
|
: ServiceFramework{system_, "IShopServiceAsync"} {
|
||||||
service_context{system_, "IShopServiceAsync"} {
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, D<&IShopServiceAsync::Cancel>, "Cancel"},
|
{0, nullptr, "Cancel"},
|
||||||
{1, D<&IShopServiceAsync::GetSize>, "GetSize"},
|
{1, nullptr, "GetSize"},
|
||||||
{2, D<&IShopServiceAsync::Read>, "Read"},
|
{2, nullptr, "Read"},
|
||||||
{3, D<&IShopServiceAsync::GetErrorCode>, "GetErrorCode"},
|
{3, nullptr, "GetErrorCode"},
|
||||||
{4, D<&IShopServiceAsync::Request>, "Request"},
|
{4, nullptr, "Request"},
|
||||||
{5, D<&IShopServiceAsync::Prepare>, "Prepare"},
|
{5, nullptr, "Prepare"},
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
|
|
||||||
completion_event = service_context.CreateEvent("IShopServiceAsync:Completion");
|
|
||||||
}
|
|
||||||
|
|
||||||
~IShopServiceAsync() override {
|
|
||||||
CancelImpl();
|
|
||||||
service_context.CloseEvent(completion_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
Kernel::KReadableEvent* GetEvent() const {
|
|
||||||
return &completion_event->GetReadableEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
KernelHelpers::ServiceContext service_context;
|
|
||||||
Kernel::KEvent* completion_event;
|
|
||||||
|
|
||||||
std::jthread worker;
|
|
||||||
std::atomic<u32> error_code{0};
|
|
||||||
|
|
||||||
std::mutex data_mutex;
|
|
||||||
std::vector<u8> download_data;
|
|
||||||
|
|
||||||
void CancelImpl() {
|
|
||||||
worker.request_stop();
|
|
||||||
if (worker.joinable()) {
|
|
||||||
worker.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result Cancel() {
|
|
||||||
LOG_DEBUG(Service_NIM, "called");
|
|
||||||
CancelImpl();
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GetSize(Out<u64> out_size) {
|
|
||||||
LOG_DEBUG(Service_NIM, "called");
|
|
||||||
std::scoped_lock lock{data_mutex};
|
|
||||||
*out_size = download_data.size();
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result Read(Out<u64> out_size, u64 offset, OutBuffer<BufferAttr_HipcAutoSelect> out_buffer) {
|
|
||||||
std::scoped_lock lock{data_mutex};
|
|
||||||
|
|
||||||
u64 actual_read = 0;
|
|
||||||
if (offset < download_data.size()) {
|
|
||||||
actual_read = std::min<u64>(out_buffer.size(), download_data.size() - offset);
|
|
||||||
std::memcpy(out_buffer.data(), download_data.data() + offset, actual_read);
|
|
||||||
}
|
|
||||||
|
|
||||||
*out_size = actual_read;
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GetErrorCode(Out<u32> out_error_code) {
|
|
||||||
LOG_DEBUG(Service_NIM, "called");
|
|
||||||
*out_error_code = error_code.load();
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result Request() {
|
|
||||||
LOG_DEBUG(Service_NIM, "(STUBBED) called");
|
|
||||||
CancelImpl();
|
|
||||||
|
|
||||||
error_code.store(0);
|
|
||||||
completion_event->Clear(system.Kernel());
|
|
||||||
|
|
||||||
{
|
|
||||||
std::scoped_lock lock{data_mutex};
|
|
||||||
download_data.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
worker = std::jthread([this](const std::stop_token& stop_token) {
|
|
||||||
if (stop_token.stop_requested()) {
|
|
||||||
error_code.store(1);
|
|
||||||
} else {
|
|
||||||
std::scoped_lock lock{data_mutex};
|
|
||||||
// Dummy JSON response, else it fails...
|
|
||||||
const std::string dummy_response = "{}";
|
|
||||||
download_data.assign(dummy_response.begin(), dummy_response.end());
|
|
||||||
error_code.store(0);
|
|
||||||
}
|
|
||||||
completion_event->Signal(system.Kernel());
|
|
||||||
});
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result Prepare(InArray<char, BufferAttr_HipcMapAlias> in_path, InArray<char, BufferAttr_HipcMapAlias> in_post) {
|
|
||||||
LOG_DEBUG(Service_NIM, "called");
|
|
||||||
if (!in_path.empty()) {
|
|
||||||
std::string url(in_path.data(), in_path.size());
|
|
||||||
LOG_INFO(Service_NIM, "Preparing request for URL: {}", url);
|
|
||||||
}
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -152,13 +49,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CreateAsyncInterface(HLERequestContext& ctx) {LOG_DEBUG(Service_NIM, "called");
|
void CreateAsyncInterface(HLERequestContext& ctx) {
|
||||||
auto async_interface = std::make_shared<IShopServiceAsync>(system);
|
LOG_WARNING(Service_NIM, "(STUBBED) called");
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 1, 1};
|
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.PushCopyObjects(ctx, async_interface->GetEvent());
|
rb.PushIpcInterface<IShopServiceAsync>(ctx, system);
|
||||||
rb.PushIpcInterface<IShopServiceAsync>(ctx, std::move(async_interface));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,11 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "common/stb.h"
|
#define STBI_ONLY_JPEG 1
|
||||||
|
#include <stb_image.h>
|
||||||
|
#include <stb_image_resize.h>
|
||||||
|
#include <stb_image_write.h>
|
||||||
|
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/file_sys/control_metadata.h"
|
#include "core/file_sys/control_metadata.h"
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
|
|||||||
@@ -105,12 +105,7 @@ void LoopProcess(Core::System& system) {
|
|||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
auto module = std::make_shared<Module>(system);
|
auto module = std::make_shared<Module>(system);
|
||||||
server_manager->RegisterNamedService("nvdrv", std::make_shared<NVDRV>(system, module, "nvdrv"));
|
server_manager->RegisterNamedService("nvdrv", std::make_shared<NVDRV>(system, module, "nvdrv"));
|
||||||
|
server_manager->RegisterNamedService("nvdrv:a", std::make_shared<NVDRV>(system, module, "nvdrv:a"));
|
||||||
const auto NvdrvInterfaceFactoryForApplets = [&, module] {
|
|
||||||
return std::make_shared<NVDRV>(system, module, "nvdrv:a");
|
|
||||||
};
|
|
||||||
|
|
||||||
server_manager->RegisterNamedService("nvdrv:a", NvdrvInterfaceFactoryForApplets);
|
|
||||||
server_manager->RegisterNamedService("nvdrv:s", std::make_shared<NVDRV>(system, module, "nvdrv:s"));
|
server_manager->RegisterNamedService("nvdrv:s", std::make_shared<NVDRV>(system, module, "nvdrv:s"));
|
||||||
server_manager->RegisterNamedService("nvdrv:t", std::make_shared<NVDRV>(system, module, "nvdrv:t"));
|
server_manager->RegisterNamedService("nvdrv:t", std::make_shared<NVDRV>(system, module, "nvdrv:t"));
|
||||||
server_manager->RegisterNamedService("nvmemp", std::make_shared<NVMEMP>(system));
|
server_manager->RegisterNamedService("nvmemp", std::make_shared<NVMEMP>(system));
|
||||||
|
|||||||
@@ -588,12 +588,7 @@ void LoopProcess(Core::System& system) {
|
|||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
auto ro = std::make_shared<RoContext>();
|
auto ro = std::make_shared<RoContext>();
|
||||||
|
server_manager->RegisterNamedService("ldr:ro", std::make_shared<RoInterface>(system, "ldr:ro", ro, NrrKind::User));
|
||||||
const auto RoInterfaceFactoryForUser = [&, ro] {
|
|
||||||
return std::make_shared<RoInterface>(system, "ldr:ro", ro, NrrKind::User);
|
|
||||||
};
|
|
||||||
|
|
||||||
server_manager->RegisterNamedService("ldr:ro", std::move(RoInterfaceFactoryForUser));
|
|
||||||
server_manager->RegisterNamedService("ro:1", std::make_shared<RoInterface>(system, "ro:1", ro, NrrKind::JitPlugin));
|
server_manager->RegisterNamedService("ro:1", std::make_shared<RoInterface>(system, "ro:1", ro, NrrKind::JitPlugin));
|
||||||
server_manager->RegisterNamedService("ro:dmnt", std::make_shared<IDebugMonitorInterface>(system));
|
server_manager->RegisterNamedService("ro:dmnt", std::make_shared<IDebugMonitorInterface>(system));
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
|
|||||||
@@ -114,13 +114,13 @@ std::vector<Network::ScanData> ScanWifiNetworks(std::chrono::milliseconds deadli
|
|||||||
char ifname[IFNAMSIZ] = {0};
|
char ifname[IFNAMSIZ] = {0};
|
||||||
char *args[1] = {ifname};
|
char *args[1] = {ifname};
|
||||||
|
|
||||||
iw_enum_devices(sock, [](int f_skfd, char* f_ifname, char* f_args[], int) -> int {
|
iw_enum_devices(sock, [](int skfd, char* ifname, char* args[], int count) -> int {
|
||||||
iwrange range;
|
iwrange range;
|
||||||
int res = iw_get_range_info(f_skfd, f_ifname, &range);
|
int res = iw_get_range_info(skfd, ifname, &range);
|
||||||
LOG_INFO(Network, "ifname {} returned {} on iw_get_range_info", f_ifname, res);
|
LOG_INFO(Network, "ifname {} returned {} on iw_get_range_info", ifname, res);
|
||||||
if (res >= 0) {
|
if (res >= 0) {
|
||||||
strncpy(f_args[0], f_ifname, IFNAMSIZ - 1);
|
strncpy(args[0], ifname, IFNAMSIZ - 1);
|
||||||
f_args[0][IFNAMSIZ - 1] = 0;
|
args[0][IFNAMSIZ - 1] = 0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging.h"
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "dynarmic/backend/exception_handler.h"
|
#include "dynarmic/backend/exception_handler.h"
|
||||||
#include "dynarmic/common/context.h"
|
#include "dynarmic/common/context.h"
|
||||||
@@ -54,21 +53,23 @@ class SigHandler {
|
|||||||
return e.first <= offset && e.first + e.second.size > offset;
|
return e.first <= offset && e.first + e.second.size > offset;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
static void SigAction(int sig, siginfo_t* info, void* raw_context);
|
||||||
|
|
||||||
|
bool supports_fast_mem = true;
|
||||||
|
void* signal_stack_memory = nullptr;
|
||||||
ankerl::unordered_dense::map<u64, CodeBlockInfo> code_block_infos;
|
ankerl::unordered_dense::map<u64, CodeBlockInfo> code_block_infos;
|
||||||
std::shared_mutex code_block_infos_mutex;
|
std::shared_mutex code_block_infos_mutex;
|
||||||
struct sigaction old_sa_segv;
|
struct sigaction old_sa_segv;
|
||||||
struct sigaction old_sa_bus;
|
struct sigaction old_sa_bus;
|
||||||
std::unique_ptr<uint8_t[]> signal_stack_memory;
|
std::size_t signal_stack_size;
|
||||||
bool supports_fast_mem = true;
|
|
||||||
public:
|
public:
|
||||||
SigHandler() noexcept {
|
SigHandler() noexcept {
|
||||||
auto const stack_size = std::max<size_t>(SIGSTKSZ, 2 * 1024 * 1024);
|
signal_stack_size = std::max<size_t>(SIGSTKSZ, 2 * 1024 * 1024);
|
||||||
signal_stack_memory = std::make_unique<uint8_t[]>(stack_size);
|
signal_stack_memory = mmap(nullptr, signal_stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
|
|
||||||
stack_t signal_stack{};
|
stack_t signal_stack{};
|
||||||
signal_stack.ss_sp = signal_stack_memory.get();
|
signal_stack.ss_sp = signal_stack_memory;
|
||||||
signal_stack.ss_size = stack_size;
|
signal_stack.ss_size = signal_stack_size;
|
||||||
signal_stack.ss_flags = 0;
|
signal_stack.ss_flags = 0;
|
||||||
if (sigaltstack(&signal_stack, nullptr) != 0) {
|
if (sigaltstack(&signal_stack, nullptr) != 0) {
|
||||||
fmt::print(stderr, "dynarmic: POSIX SigHandler: init failure at sigaltstack\n");
|
fmt::print(stderr, "dynarmic: POSIX SigHandler: init failure at sigaltstack\n");
|
||||||
@@ -86,7 +87,7 @@ public:
|
|||||||
supports_fast_mem = false;
|
supports_fast_mem = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#if defined(__APPLE__)
|
#ifdef __APPLE__
|
||||||
if (sigaction(SIGBUS, &sa, &old_sa_bus) != 0) {
|
if (sigaction(SIGBUS, &sa, &old_sa_bus) != 0) {
|
||||||
fmt::print(stderr, "dynarmic: POSIX SigHandler: could not set SIGBUS handler\n");
|
fmt::print(stderr, "dynarmic: POSIX SigHandler: could not set SIGBUS handler\n");
|
||||||
supports_fast_mem = false;
|
supports_fast_mem = false;
|
||||||
@@ -95,6 +96,10 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~SigHandler() noexcept {
|
||||||
|
munmap(signal_stack_memory, signal_stack_size);
|
||||||
|
}
|
||||||
|
|
||||||
void AddCodeBlock(u64 offset, CodeBlockInfo cbi) noexcept {
|
void AddCodeBlock(u64 offset, CodeBlockInfo cbi) noexcept {
|
||||||
std::unique_lock guard(code_block_infos_mutex);
|
std::unique_lock guard(code_block_infos_mutex);
|
||||||
code_block_infos.insert_or_assign(offset, cbi);
|
code_block_infos.insert_or_assign(offset, cbi);
|
||||||
@@ -104,17 +109,14 @@ public:
|
|||||||
code_block_infos.erase(offset);
|
code_block_infos.erase(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline bool SupportsFastmem() const noexcept {
|
bool SupportsFastmem() const noexcept { return supports_fast_mem; }
|
||||||
return supports_fast_mem;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void RegisterHandler();
|
|
||||||
static void SigAction(int sig, siginfo_t* info, void* raw_context);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::mutex handler_lock;
|
||||||
std::optional<SigHandler> sig_handler;
|
std::optional<SigHandler> sig_handler;
|
||||||
|
|
||||||
void SigHandler::RegisterHandler() {
|
void RegisterHandler() {
|
||||||
|
std::lock_guard<std::mutex> guard(handler_lock);
|
||||||
if (!sig_handler) {
|
if (!sig_handler) {
|
||||||
sig_handler.emplace();
|
sig_handler.emplace();
|
||||||
}
|
}
|
||||||
@@ -123,27 +125,51 @@ void SigHandler::RegisterHandler() {
|
|||||||
void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
||||||
DEBUG_ASSERT(sig == SIGSEGV || sig == SIGBUS);
|
DEBUG_ASSERT(sig == SIGSEGV || sig == SIGBUS);
|
||||||
CTX_DECLARE(raw_context);
|
CTX_DECLARE(raw_context);
|
||||||
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
{
|
{
|
||||||
std::shared_lock guard(sig_handler->code_block_infos_mutex);
|
std::shared_lock guard(sig_handler->code_block_infos_mutex);
|
||||||
if (auto const iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) {
|
if (auto const iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) {
|
||||||
FakeCall fc = iter->second.cb(CTX_PC);
|
FakeCall fc = iter->second.cb(CTX_PC);
|
||||||
#if defined(ARCHITECTURE_x86_64)
|
|
||||||
CTX_SP -= sizeof(u64);
|
CTX_SP -= sizeof(u64);
|
||||||
*std::bit_cast<u64*>(CTX_SP) = fc.ret_rip;
|
*std::bit_cast<u64*>(CTX_SP) = fc.ret_rip;
|
||||||
CTX_PC = fc.call_rip;
|
CTX_PC = fc.call_rip;
|
||||||
#elif defined(ARCHITECTURE_arm64)
|
|
||||||
CTX_PC = fc.call_pc;
|
|
||||||
#elif defined(ARCHITECTURE_riscv64)
|
|
||||||
CTX_PC = fc.call_sepc;
|
|
||||||
#elif defined(ARCHITECTURE_loongarch64)
|
|
||||||
CTX_PC = fc.call_pc;
|
|
||||||
#else
|
|
||||||
ASSERT(false);
|
|
||||||
#endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOG_ERROR(Core, "Unhandled {} at {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
fmt::print(stderr, "Unhandled {} at rip {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
||||||
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
|
{
|
||||||
|
std::shared_lock guard(sig_handler->code_block_infos_mutex);
|
||||||
|
if (const auto iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) {
|
||||||
|
FakeCall fc = iter->second.cb(CTX_PC);
|
||||||
|
CTX_PC = fc.call_pc;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
||||||
|
#elif defined(ARCHITECTURE_riscv64)
|
||||||
|
{
|
||||||
|
std::shared_lock guard(sig_handler->code_block_infos_mutex);
|
||||||
|
if (const auto iter = sig_handler->FindCodeBlockInfo(CTX_SEPC); iter != sig_handler->code_block_infos.end()) {
|
||||||
|
FakeCall fc = iter->second.cb(CTX_SEPC);
|
||||||
|
CTX_SEPC = fc.call_sepc;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_SEPC);
|
||||||
|
#elif defined(ARCHITECTURE_loongarch64)
|
||||||
|
{
|
||||||
|
std::shared_lock guard(sig_handler->code_block_infos_mutex);
|
||||||
|
if (const auto iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) {
|
||||||
|
FakeCall fc = iter->second.cb(CTX_PC);
|
||||||
|
CTX_PC = fc.call_pc;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
||||||
|
#else
|
||||||
|
# error "Invalid architecture"
|
||||||
|
#endif
|
||||||
|
|
||||||
struct sigaction* retry_sa = sig == SIGSEGV ? &sig_handler->old_sa_segv : &sig_handler->old_sa_bus;
|
struct sigaction* retry_sa = sig == SIGSEGV ? &sig_handler->old_sa_segv : &sig_handler->old_sa_bus;
|
||||||
if (retry_sa->sa_flags & SA_SIGINFO) {
|
if (retry_sa->sa_flags & SA_SIGINFO) {
|
||||||
@@ -164,10 +190,9 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
|||||||
|
|
||||||
struct ExceptionHandler::Impl final {
|
struct ExceptionHandler::Impl final {
|
||||||
Impl(u64 offset_, u64 size_)
|
Impl(u64 offset_, u64 size_)
|
||||||
: offset(offset_)
|
: offset(offset_)
|
||||||
, size(size_)
|
, size(size_) {
|
||||||
{
|
RegisterHandler();
|
||||||
SigHandler::RegisterHandler();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetCallback(std::function<FakeCall(u64)> cb) {
|
void SetCallback(std::function<FakeCall(u64)> cb) {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#include <variant>
|
|
||||||
#include "dynarmic/backend/loongarch64/emit_loongarch64.h"
|
#include "dynarmic/backend/loongarch64/emit_loongarch64.h"
|
||||||
|
|
||||||
#include "dynarmic/backend/loongarch64/a32_jitstate.h"
|
#include "dynarmic/backend/loongarch64/a32_jitstate.h"
|
||||||
@@ -96,8 +95,7 @@ EmittedBlockInfo EmitLoongArch64(lagoon_assembler_t& as, IR::Block block, const
|
|||||||
|
|
||||||
// TODO: Emit Terminal
|
// TODO: Emit Terminal
|
||||||
const auto term = block.GetTerminal();
|
const auto term = block.GetTerminal();
|
||||||
const IR::Term::LeafTerminal* leaft_term = std::get_if<IR::Term::LeafTerminal>(&term);
|
const IR::Term::LinkBlock* link_block_term = boost::get<IR::Term::LinkBlock>(&term);
|
||||||
const IR::Term::LinkBlock* link_block_term = std::get_if<IR::Term::LinkBlock>(leaft_term);
|
|
||||||
ASSERT(link_block_term);
|
ASSERT(link_block_term);
|
||||||
la_load_immediate64(&as, Xscratch0, link_block_term->next.Value());
|
la_load_immediate64(&as, Xscratch0, link_block_term->next.Value());
|
||||||
la_st_w(&as, Xscratch0, Xstate, static_cast<int32_t>(offsetof(A32JitState, regs) + sizeof(u32) * 15));
|
la_st_w(&as, Xscratch0, Xstate, static_cast<int32_t>(offsetof(A32JitState, regs) + sizeof(u32) * 15));
|
||||||
|
|||||||
@@ -136,14 +136,14 @@
|
|||||||
# endif
|
# endif
|
||||||
#elif defined(ARCHITECTURE_riscv64)
|
#elif defined(ARCHITECTURE_riscv64)
|
||||||
# if defined(__FreeBSD__)
|
# if defined(__FreeBSD__)
|
||||||
# define CTX_PC (mctx.mc_gpregs.gp_sepc)
|
# define CTX_SEPC (mctx.mc_gpregs.gp_sepc)
|
||||||
# define CTX_SP (mctx.mc_gpregs.gp_sp)
|
# define CTX_SP (mctx.mc_gpregs.gp_sp)
|
||||||
# elif defined(__linux__)
|
# elif defined(__linux__)
|
||||||
# define CTX_PC (mctx.__gregs[REG_PC])
|
# define CTX_SEPC (mctx.__gregs[REG_PC])
|
||||||
# define CTX_SP (mctx.__gregs[REG_SP])
|
# define CTX_SP (mctx.__gregs[REG_SP])
|
||||||
# elif defined(__OpenBSD__)
|
# elif defined(__OpenBSD__)
|
||||||
// https://github.com/openbsd/src/blob/master/sys/arch/riscv64/include/signal.h
|
// https://github.com/openbsd/src/blob/master/sys/arch/riscv64/include/signal.h
|
||||||
# define CTX_PC (ucontext->sc_sepc)
|
# define CTX_SEPC (ucontext->sc_sepc)
|
||||||
# define CTX_SP (ucontext->sc_sp)
|
# define CTX_SP (ucontext->sc_sp)
|
||||||
# else
|
# else
|
||||||
# error "unknown platform"
|
# error "unknown platform"
|
||||||
|
|||||||
@@ -522,6 +522,9 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
|
|||||||
PAIR(AnisotropyMode, X4, tr("4x")),
|
PAIR(AnisotropyMode, X4, tr("4x")),
|
||||||
PAIR(AnisotropyMode, X8, tr("8x")),
|
PAIR(AnisotropyMode, X8, tr("8x")),
|
||||||
PAIR(AnisotropyMode, X16, tr("16x")),
|
PAIR(AnisotropyMode, X16, tr("16x")),
|
||||||
|
PAIR(AnisotropyMode, X32, tr("32x")),
|
||||||
|
PAIR(AnisotropyMode, X64, tr("64x")),
|
||||||
|
PAIR(AnisotropyMode, None, tr("None")),
|
||||||
}});
|
}});
|
||||||
translations->insert(
|
translations->insert(
|
||||||
{Settings::EnumMetadata<Settings::Language>::Index(),
|
{Settings::EnumMetadata<Settings::Language>::Index(),
|
||||||
|
|||||||
@@ -74,8 +74,8 @@ static constexpr char DEFAULT_DISCORD_IMAGE[] =
|
|||||||
"https://git.eden-emu.dev/eden-emu/eden/raw/branch/master/dist/qt_themes/default/icons/256x256/"
|
"https://git.eden-emu.dev/eden-emu/eden/raw/branch/master/dist/qt_themes/default/icons/256x256/"
|
||||||
"eden.png";
|
"eden.png";
|
||||||
|
|
||||||
void DiscordImpl::UpdateGameStatus(std::string_view game_url, bool has_boxart) {
|
void DiscordImpl::UpdateGameStatus(bool use_default) {
|
||||||
const std::string url = std::string{has_boxart ? game_url : DEFAULT_DISCORD_IMAGE};
|
const std::string url = use_default ? std::string{DEFAULT_DISCORD_IMAGE} : game_url;
|
||||||
s64 start_time = std::chrono::duration_cast<std::chrono::seconds>(
|
s64 start_time = std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
std::chrono::system_clock::now().time_since_epoch())
|
std::chrono::system_clock::now().time_since_epoch())
|
||||||
.count();
|
.count();
|
||||||
@@ -98,7 +98,7 @@ void DiscordImpl::Update() {
|
|||||||
|
|
||||||
// Used to format Icon URL for yuzu website game compatibility page
|
// Used to format Icon URL for yuzu website game compatibility page
|
||||||
std::string icon_name = GetGameString(game_title);
|
std::string icon_name = GetGameString(game_title);
|
||||||
auto const game_url = fmt::format(
|
game_url = fmt::format(
|
||||||
"https://raw.githubusercontent.com/eden-emulator/boxart/refs/heads/master/img/{}.png",
|
"https://raw.githubusercontent.com/eden-emulator/boxart/refs/heads/master/img/{}.png",
|
||||||
icon_name);
|
icon_name);
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ void DiscordImpl::Update() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto res = client.send(request);
|
auto res = client.send(request);
|
||||||
UpdateGameStatus(game_url, res && res->status == 200);
|
UpdateGameStatus(res && res->status == 200);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
|
||||||
@@ -26,9 +26,11 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::string GetGameString(const std::string& title);
|
std::string GetGameString(const std::string& title);
|
||||||
void UpdateGameStatus(std::string_view game_url, bool use_default);
|
void UpdateGameStatus(bool use_default);
|
||||||
|
|
||||||
|
std::string game_url{};
|
||||||
std::string game_title{};
|
std::string game_title{};
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
@@ -665,8 +665,6 @@ void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU3
|
|||||||
const IR::Value& clamp, const IR::Value& segmentation_mask);
|
const IR::Value& clamp, const IR::Value& segmentation_mask);
|
||||||
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||||
const IR::Value& clamp, const IR::Value& segmentation_mask);
|
const IR::Value& clamp, const IR::Value& segmentation_mask);
|
||||||
void EmitQuadBroadcast(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 lane);
|
|
||||||
void EmitQuadSwap(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 direction);
|
|
||||||
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a, ScalarF32 op_b,
|
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a, ScalarF32 op_b,
|
||||||
ScalarU32 swizzle);
|
ScalarU32 swizzle);
|
||||||
void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a);
|
void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a);
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -100,24 +97,6 @@ void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, Sca
|
|||||||
Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "XOR");
|
Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "XOR");
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitQuadBroadcast(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 lane) {
|
|
||||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
|
||||||
ctx.Add("AND.U RC.x,{}.threadid,~3;"
|
|
||||||
"AND.U RC.y,{},3;"
|
|
||||||
"OR.U RC.x,RC.x,RC.y;"
|
|
||||||
"SHFIDX.U {},{},RC.x,0x1C03;"
|
|
||||||
"MOV.U {}.x,{}.y;",
|
|
||||||
ctx.stage_name, lane, ret, value, ret, ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitQuadSwap(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 direction) {
|
|
||||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
|
||||||
ctx.Add("ADD.U RC.x,{},1;"
|
|
||||||
"SHFXOR.U {},{},RC.x,0x1C03;"
|
|
||||||
"MOV.U {}.x,{}.y;",
|
|
||||||
direction, ret, value, ret, ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a, ScalarF32 op_b,
|
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a, ScalarF32 op_b,
|
||||||
ScalarU32 swizzle) {
|
ScalarU32 swizzle) {
|
||||||
const auto ret{ctx.reg_alloc.Define(inst)};
|
const auto ret{ctx.reg_alloc.Define(inst)};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
@@ -743,10 +743,6 @@ void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
|||||||
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||||
std::string_view index, std::string_view clamp,
|
std::string_view index, std::string_view clamp,
|
||||||
std::string_view segmentation_mask);
|
std::string_view segmentation_mask);
|
||||||
void EmitQuadBroadcast(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
|
||||||
std::string_view lane);
|
|
||||||
void EmitQuadSwap(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
|
||||||
std::string_view direction);
|
|
||||||
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b,
|
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b,
|
||||||
std::string_view swizzle);
|
std::string_view swizzle);
|
||||||
void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a);
|
void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a);
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -203,18 +200,6 @@ void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view val
|
|||||||
ctx.AddU32("{}=shfl_in_bounds?shfl_result:{};", inst, value);
|
ctx.AddU32("{}=shfl_in_bounds?shfl_result:{};", inst, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitQuadBroadcast(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
|
||||||
std::string_view lane) {
|
|
||||||
const auto src_thread_id{fmt::format("(({}&~3)|({}& 3))", THREAD_ID, lane)};
|
|
||||||
ctx.AddU32("{}=readInvocationARB({},{});", inst, value, src_thread_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitQuadSwap(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
|
||||||
std::string_view direction) {
|
|
||||||
const auto src_thread_id{fmt::format("({}^({}+1))", THREAD_ID, direction)};
|
|
||||||
ctx.AddU32("{}=readInvocationARB({},{});", inst, value, src_thread_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b,
|
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b,
|
||||||
std::string_view swizzle) {
|
std::string_view swizzle) {
|
||||||
const auto mask{fmt::format("({}>>((gl_SubGroupInvocationARB&3)<<1))&3", swizzle)};
|
const auto mask{fmt::format("({}>>((gl_SubGroupInvocationARB&3)<<1))&3", swizzle)};
|
||||||
|
|||||||
@@ -322,11 +322,6 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
|
|||||||
if (ctx.runtime_info.force_early_z) {
|
if (ctx.runtime_info.force_early_z) {
|
||||||
ctx.AddExecutionMode(main, spv::ExecutionMode::EarlyFragmentTests);
|
ctx.AddExecutionMode(main, spv::ExecutionMode::EarlyFragmentTests);
|
||||||
}
|
}
|
||||||
if (ctx.profile.support_shader_quad_control && program.info.uses_quad_shuffles) {
|
|
||||||
ctx.AddExtension("SPV_KHR_quad_control");
|
|
||||||
ctx.AddCapability(spv::Capability::QuadControlKHR);
|
|
||||||
ctx.AddExecutionMode(main, spv::ExecutionMode::RequireFullQuadsKHR);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw NotImplementedException("Stage {}", program.stage);
|
throw NotImplementedException("Stage {}", program.stage);
|
||||||
@@ -340,7 +335,7 @@ void SetupDenormControl(const Profile& profile, const IR::Program& program, Emit
|
|||||||
if (info.uses_fp32_denorms_flush && info.uses_fp32_denorms_preserve) {
|
if (info.uses_fp32_denorms_flush && info.uses_fp32_denorms_preserve) {
|
||||||
LOG_DEBUG(Shader_SPIRV, "Fp32 denorm flush and preserve on the same shader");
|
LOG_DEBUG(Shader_SPIRV, "Fp32 denorm flush and preserve on the same shader");
|
||||||
} else if (info.uses_fp32_denorms_flush) {
|
} else if (info.uses_fp32_denorms_flush) {
|
||||||
if (profile.support_fp32_denorm_flush && !profile.has_broken_fp32_denorm_flush) {
|
if (profile.support_fp32_denorm_flush) {
|
||||||
ctx.AddCapability(spv::Capability::DenormFlushToZero);
|
ctx.AddCapability(spv::Capability::DenormFlushToZero);
|
||||||
ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormFlushToZero, 32U);
|
ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormFlushToZero, 32U);
|
||||||
} else {
|
} else {
|
||||||
@@ -448,12 +443,6 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
|
|||||||
ctx.AddCapability(spv::Capability::GroupNonUniformVote);
|
ctx.AddCapability(spv::Capability::GroupNonUniformVote);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (info.uses_quad_shuffles) {
|
|
||||||
if (profile.support_quad_shuffles) {
|
|
||||||
ctx.AddCapability(spv::Capability::GroupNonUniformQuad);
|
|
||||||
}
|
|
||||||
ctx.AddCapability(spv::Capability::GroupNonUniformShuffle);
|
|
||||||
}
|
|
||||||
if (info.uses_int64_bit_atomics && profile.support_int64_atomics) {
|
if (info.uses_int64_bit_atomics && profile.support_int64_atomics) {
|
||||||
ctx.AddCapability(spv::Capability::Int64Atomics);
|
ctx.AddCapability(spv::Capability::Int64Atomics);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
@@ -622,8 +622,6 @@ Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clam
|
|||||||
Id segmentation_mask);
|
Id segmentation_mask);
|
||||||
Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||||
Id segmentation_mask);
|
Id segmentation_mask);
|
||||||
Id EmitQuadBroadcast(EmitContext& ctx, Id value, Id lane);
|
|
||||||
Id EmitQuadSwap(EmitContext& ctx, Id value, Id direction);
|
|
||||||
Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle);
|
Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle);
|
||||||
Id EmitDPdxFine(EmitContext& ctx, Id op_a);
|
Id EmitDPdxFine(EmitContext& ctx, Id op_a);
|
||||||
Id EmitDPdyFine(EmitContext& ctx, Id op_a);
|
Id EmitDPdyFine(EmitContext& ctx, Id op_a);
|
||||||
|
|||||||
@@ -260,21 +260,6 @@ Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id
|
|||||||
return SelectValue(ctx, in_range, value, src_thread_id);
|
return SelectValue(ctx, in_range, value, src_thread_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitQuadBroadcast(EmitContext& ctx, Id value, Id lane) {
|
|
||||||
if (ctx.profile.support_quad_shuffles) {
|
|
||||||
return ctx.OpGroupNonUniformQuadBroadcast(ctx.U32[1], SubgroupScope(ctx), value, lane);
|
|
||||||
}
|
|
||||||
const Id base{ctx.OpBitwiseAnd(ctx.U32[1], GetThreadId(ctx), ctx.Const(~3u))};
|
|
||||||
const Id local_lane{ctx.OpBitwiseAnd(ctx.U32[1], lane, ctx.Const(3u))};
|
|
||||||
const Id src_thread_id{ctx.OpBitwiseOr(ctx.U32[1], base, local_lane)};
|
|
||||||
return ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
Id EmitQuadSwap(EmitContext& ctx, Id value, Id direction) {
|
|
||||||
const Id xor_mask{ctx.OpIAdd(ctx.U32[1], direction, ctx.Const(1u))};
|
|
||||||
return ctx.OpGroupNonUniformShuffleXor(ctx.U32[1], SubgroupScope(ctx), value, xor_mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) {
|
Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) {
|
||||||
const Id three{ctx.Const(3U)};
|
const Id three{ctx.Const(3U)};
|
||||||
Id mask{GetThreadId(ctx)};
|
Id mask{GetThreadId(ctx)};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
@@ -2100,14 +2100,6 @@ U32 IREmitter::ShuffleButterfly(const IR::U32& value, const IR::U32& index, cons
|
|||||||
return Inst<U32>(Opcode::ShuffleButterfly, value, index, clamp, seg_mask);
|
return Inst<U32>(Opcode::ShuffleButterfly, value, index, clamp, seg_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
U32 IREmitter::QuadBroadcast(const IR::U32& value, const IR::U32& lane) {
|
|
||||||
return Inst<U32>(Opcode::QuadBroadcast, value, lane);
|
|
||||||
}
|
|
||||||
|
|
||||||
U32 IREmitter::QuadSwap(const IR::U32& value, const IR::U32& direction) {
|
|
||||||
return Inst<U32>(Opcode::QuadSwap, value, direction);
|
|
||||||
}
|
|
||||||
|
|
||||||
F32 IREmitter::FSwizzleAdd(const F32& a, const F32& b, const U32& swizzle, FpControl control) {
|
F32 IREmitter::FSwizzleAdd(const F32& a, const F32& b, const U32& swizzle, FpControl control) {
|
||||||
return Inst<F32>(Opcode::FSwizzleAdd, Flags{control}, a, b, swizzle);
|
return Inst<F32>(Opcode::FSwizzleAdd, Flags{control}, a, b, swizzle);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
@@ -394,8 +394,6 @@ public:
|
|||||||
const IR::U32& seg_mask);
|
const IR::U32& seg_mask);
|
||||||
[[nodiscard]] U32 ShuffleButterfly(const IR::U32& value, const IR::U32& index,
|
[[nodiscard]] U32 ShuffleButterfly(const IR::U32& value, const IR::U32& index,
|
||||||
const IR::U32& clamp, const IR::U32& seg_mask);
|
const IR::U32& clamp, const IR::U32& seg_mask);
|
||||||
[[nodiscard]] U32 QuadBroadcast(const IR::U32& value, const IR::U32& lane);
|
|
||||||
[[nodiscard]] U32 QuadSwap(const IR::U32& value, const IR::U32& direction);
|
|
||||||
[[nodiscard]] F32 FSwizzleAdd(const F32& a, const F32& b, const U32& swizzle,
|
[[nodiscard]] F32 FSwizzleAdd(const F32& a, const F32& b, const U32& swizzle,
|
||||||
FpControl control = {});
|
FpControl control = {});
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Shader::IR {
|
|||||||
|
|
||||||
namespace Detail {
|
namespace Detail {
|
||||||
|
|
||||||
OpcodeMeta META_TABLE[534] = {
|
OpcodeMeta META_TABLE[532] = {
|
||||||
#define OPCODE(name_token, type_token, ...) \
|
#define OPCODE(name_token, type_token, ...) \
|
||||||
{ \
|
{ \
|
||||||
.name{#name_token}, \
|
.name{#name_token}, \
|
||||||
@@ -21,7 +21,7 @@ OpcodeMeta META_TABLE[534] = {
|
|||||||
#undef OPCODE
|
#undef OPCODE
|
||||||
};
|
};
|
||||||
|
|
||||||
u8 NUM_ARGS[534] = {
|
u8 NUM_ARGS[532] = {
|
||||||
#define OPCODE(name_token, type_token, ...) u8(CalculateNumArgsOf(Opcode::name_token)),
|
#define OPCODE(name_token, type_token, ...) u8(CalculateNumArgsOf(Opcode::name_token)),
|
||||||
#include "opcodes.inc"
|
#include "opcodes.inc"
|
||||||
#undef OPCODE
|
#undef OPCODE
|
||||||
|
|||||||
@@ -57,12 +57,12 @@ static constexpr Type F64x2{Type::F64x2};
|
|||||||
static constexpr Type F64x3{Type::F64x3};
|
static constexpr Type F64x3{Type::F64x3};
|
||||||
static constexpr Type F64x4{Type::F64x4};
|
static constexpr Type F64x4{Type::F64x4};
|
||||||
|
|
||||||
extern OpcodeMeta META_TABLE[534];
|
extern OpcodeMeta META_TABLE[532];
|
||||||
constexpr size_t CalculateNumArgsOf(Opcode op) noexcept {
|
constexpr size_t CalculateNumArgsOf(Opcode op) noexcept {
|
||||||
const auto& arg_types = META_TABLE[size_t(op)].arg_types;
|
const auto& arg_types = META_TABLE[size_t(op)].arg_types;
|
||||||
return size_t(std::distance(arg_types.begin(), std::ranges::find(arg_types, Type::Void)));
|
return size_t(std::distance(arg_types.begin(), std::ranges::find(arg_types, Type::Void)));
|
||||||
}
|
}
|
||||||
extern u8 NUM_ARGS[534];
|
extern u8 NUM_ARGS[532];
|
||||||
} // namespace Detail
|
} // namespace Detail
|
||||||
|
|
||||||
/// Get return type of an opcode
|
/// Get return type of an opcode
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -582,8 +579,6 @@ OPCODE(ShuffleIndex, U32, U32,
|
|||||||
OPCODE(ShuffleUp, U32, U32, U32, U32, U32, )
|
OPCODE(ShuffleUp, U32, U32, U32, U32, U32, )
|
||||||
OPCODE(ShuffleDown, U32, U32, U32, U32, U32, )
|
OPCODE(ShuffleDown, U32, U32, U32, U32, U32, )
|
||||||
OPCODE(ShuffleButterfly, U32, U32, U32, U32, U32, )
|
OPCODE(ShuffleButterfly, U32, U32, U32, U32, U32, )
|
||||||
OPCODE(QuadBroadcast, U32, U32, U32, )
|
|
||||||
OPCODE(QuadSwap, U32, U32, U32, )
|
|
||||||
OPCODE(FSwizzleAdd, F32, F32, F32, U32, )
|
OPCODE(FSwizzleAdd, F32, F32, F32, U32, )
|
||||||
OPCODE(DPdxFine, F32, F32, )
|
OPCODE(DPdxFine, F32, F32, )
|
||||||
OPCODE(DPdyFine, F32, F32, )
|
OPCODE(DPdyFine, F32, F32, )
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
@@ -36,10 +36,7 @@ enum class ShuffleMode : u64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr u32 QUAD_MASK = (28u << 8) | 3u;
|
void Shuffle(TranslatorVisitor& v, u64 insn, const IR::U32& index, const IR::U32& mask) {
|
||||||
|
|
||||||
void Shuffle(TranslatorVisitor& v, u64 insn, const IR::U32& index, const IR::U32& mask,
|
|
||||||
bool index_is_imm, u32 index_imm, bool mask_is_imm, u32 mask_imm) {
|
|
||||||
union {
|
union {
|
||||||
u64 insn;
|
u64 insn;
|
||||||
BitField<0, 8, IR::Reg> dest_reg;
|
BitField<0, 8, IR::Reg> dest_reg;
|
||||||
@@ -48,21 +45,6 @@ void Shuffle(TranslatorVisitor& v, u64 insn, const IR::U32& index, const IR::U32
|
|||||||
BitField<48, 3, IR::Pred> pred;
|
BitField<48, 3, IR::Pred> pred;
|
||||||
} const shfl{insn};
|
} const shfl{insn};
|
||||||
|
|
||||||
const bool is_quad_candidate{mask_is_imm && mask_imm == QUAD_MASK && index_is_imm &&
|
|
||||||
v.env.ShaderStage() == Stage::Fragment};
|
|
||||||
if (is_quad_candidate) {
|
|
||||||
if (shfl.mode == ShuffleMode::IDX && index_imm <= 3) {
|
|
||||||
v.X(shfl.dest_reg, v.ir.QuadBroadcast(v.X(shfl.src_reg), v.ir.Imm32(index_imm)));
|
|
||||||
v.ir.SetPred(shfl.pred, v.ir.Imm1(true));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (shfl.mode == ShuffleMode::BFLY && index_imm >= 1 && index_imm <= 3) {
|
|
||||||
v.X(shfl.dest_reg, v.ir.QuadSwap(v.X(shfl.src_reg), v.ir.Imm32(index_imm - 1)));
|
|
||||||
v.ir.SetPred(shfl.pred, v.ir.Imm1(true));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const IR::U32 result{ShuffleOperation(v.ir, v.X(shfl.src_reg), index, mask, shfl.mode)};
|
const IR::U32 result{ShuffleOperation(v.ir, v.X(shfl.src_reg), index, mask, shfl.mode)};
|
||||||
v.ir.SetPred(shfl.pred, v.ir.GetInBoundsFromOp(result));
|
v.ir.SetPred(shfl.pred, v.ir.GetInBoundsFromOp(result));
|
||||||
v.X(shfl.dest_reg, result);
|
v.X(shfl.dest_reg, result);
|
||||||
@@ -77,14 +59,11 @@ void TranslatorVisitor::SHFL(u64 insn) {
|
|||||||
BitField<29, 1, u64> src_b_flag;
|
BitField<29, 1, u64> src_b_flag;
|
||||||
BitField<34, 13, u64> src_b_imm;
|
BitField<34, 13, u64> src_b_imm;
|
||||||
} const flags{insn};
|
} const flags{insn};
|
||||||
const bool index_is_imm{flags.src_a_flag != 0};
|
const IR::U32 src_a{flags.src_a_flag != 0 ? ir.Imm32(static_cast<u32>(flags.src_a_imm))
|
||||||
const bool mask_is_imm{flags.src_b_flag != 0};
|
: GetReg20(insn)};
|
||||||
const IR::U32 src_a{index_is_imm ? ir.Imm32(static_cast<u32>(flags.src_a_imm))
|
const IR::U32 src_b{flags.src_b_flag != 0 ? ir.Imm32(static_cast<u32>(flags.src_b_imm))
|
||||||
: GetReg20(insn)};
|
: GetReg39(insn)};
|
||||||
const IR::U32 src_b{mask_is_imm ? ir.Imm32(static_cast<u32>(flags.src_b_imm))
|
Shuffle(*this, insn, src_a, src_b);
|
||||||
: GetReg39(insn)};
|
|
||||||
Shuffle(*this, insn, src_a, src_b, index_is_imm, static_cast<u32>(flags.src_a_imm),
|
|
||||||
mask_is_imm, static_cast<u32>(flags.src_b_imm));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Shader::Maxwell
|
} // namespace Shader::Maxwell
|
||||||
|
|||||||
@@ -498,10 +498,6 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
|||||||
case IR::Opcode::ShuffleButterfly:
|
case IR::Opcode::ShuffleButterfly:
|
||||||
info.uses_subgroup_shuffles = true;
|
info.uses_subgroup_shuffles = true;
|
||||||
break;
|
break;
|
||||||
case IR::Opcode::QuadBroadcast:
|
|
||||||
case IR::Opcode::QuadSwap:
|
|
||||||
info.uses_quad_shuffles = true;
|
|
||||||
break;
|
|
||||||
case IR::Opcode::GetCbufU8:
|
case IR::Opcode::GetCbufU8:
|
||||||
case IR::Opcode::GetCbufS8:
|
case IR::Opcode::GetCbufS8:
|
||||||
case IR::Opcode::GetCbufU16:
|
case IR::Opcode::GetCbufU16:
|
||||||
|
|||||||
@@ -37,8 +37,6 @@ struct Profile {
|
|||||||
bool support_explicit_workgroup_layout{};
|
bool support_explicit_workgroup_layout{};
|
||||||
bool support_workgroup_layout_8bit_access{};
|
bool support_workgroup_layout_8bit_access{};
|
||||||
bool support_workgroup_layout_16bit_access{};
|
bool support_workgroup_layout_16bit_access{};
|
||||||
bool support_shader_quad_control{};
|
|
||||||
bool support_quad_shuffles{};
|
|
||||||
bool support_vote{};
|
bool support_vote{};
|
||||||
u32 supported_subgroup_stages{0x7F};
|
u32 supported_subgroup_stages{0x7F};
|
||||||
bool support_viewport_index_layer_non_geometry{};
|
bool support_viewport_index_layer_non_geometry{};
|
||||||
@@ -88,8 +86,6 @@ struct Profile {
|
|||||||
bool has_broken_signed_operations{};
|
bool has_broken_signed_operations{};
|
||||||
/// Float controls break when fp16 is enabled
|
/// Float controls break when fp16 is enabled
|
||||||
bool has_broken_fp16_float_controls{};
|
bool has_broken_fp16_float_controls{};
|
||||||
/// Declaring fp32 denorm flush to zero miscompiles on some drivers
|
|
||||||
bool has_broken_fp32_denorm_flush{};
|
|
||||||
/// Dynamic vec4 indexing is broken on some OpenGL drivers
|
/// Dynamic vec4 indexing is broken on some OpenGL drivers
|
||||||
bool has_gl_component_indexing_bug{};
|
bool has_gl_component_indexing_bug{};
|
||||||
/// The precise type qualifier is broken in the fragment stage of some drivers
|
/// The precise type qualifier is broken in the fragment stage of some drivers
|
||||||
|
|||||||
@@ -252,7 +252,6 @@ struct Info {
|
|||||||
bool uses_is_helper_invocation{};
|
bool uses_is_helper_invocation{};
|
||||||
bool uses_subgroup_invocation_id{};
|
bool uses_subgroup_invocation_id{};
|
||||||
bool uses_subgroup_shuffles{};
|
bool uses_subgroup_shuffles{};
|
||||||
bool uses_quad_shuffles{};
|
|
||||||
std::array<bool, 30> uses_patches{};
|
std::array<bool, 30> uses_patches{};
|
||||||
|
|
||||||
std::array<Interpolation, 32> interpolation{};
|
std::array<Interpolation, 32> interpolation{};
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ set(SHADER_FILES
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp
|
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth.frag
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp
|
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp
|
||||||
@@ -31,12 +30,8 @@ set(SHADER_FILES
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_float_to_depth.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/convert_float_to_depth.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa.comp
|
${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa.comp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa_depth.frag
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa_depth_stencil.frag
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.comp
|
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.comp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth.frag
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth_stencil.frag
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_s8d24_to_abgr8.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/convert_s8d24_to_abgr8.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/full_screen_triangle.vert
|
${CMAKE_CURRENT_SOURCE_DIR}/full_screen_triangle.vert
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/fxaa.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/fxaa.frag
|
||||||
@@ -168,44 +163,6 @@ foreach(SOURCE_FILE IN ITEMS ${SHADER_FILES})
|
|||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
# Integer variants of the MSAA conversion shaders. They only differ from the float
|
|
||||||
# source in the sampler and output types, so they are generated from it via defines.
|
|
||||||
set(SHADER_TYPE_VARIANTS
|
|
||||||
"convert_msaa_to_non_msaa.frag|sint|isampler2DMS|ivec4"
|
|
||||||
"convert_msaa_to_non_msaa.frag|uint|usampler2DMS|uvec4"
|
|
||||||
"convert_non_msaa_to_msaa.frag|sint|isampler2D|ivec4"
|
|
||||||
"convert_non_msaa_to_msaa.frag|uint|usampler2D|uvec4"
|
|
||||||
)
|
|
||||||
|
|
||||||
foreach(VARIANT IN ITEMS ${SHADER_TYPE_VARIANTS})
|
|
||||||
string(REPLACE "|" ";" VARIANT_PARTS ${VARIANT})
|
|
||||||
list(GET VARIANT_PARTS 0 VARIANT_FILENAME)
|
|
||||||
list(GET VARIANT_PARTS 1 VARIANT_SUFFIX)
|
|
||||||
list(GET VARIANT_PARTS 2 VARIANT_SAMPLER)
|
|
||||||
list(GET VARIANT_PARTS 3 VARIANT_TEXEL)
|
|
||||||
|
|
||||||
set(VARIANT_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${VARIANT_FILENAME})
|
|
||||||
get_filename_component(VARIANT_STEM ${VARIANT_FILENAME} NAME_WE)
|
|
||||||
get_filename_component(VARIANT_EXT ${VARIANT_FILENAME} EXT)
|
|
||||||
string(REPLACE "." "" VARIANT_EXT ${VARIANT_EXT})
|
|
||||||
set(VARIANT_NAME ${VARIANT_STEM}_${VARIANT_SUFFIX}_${VARIANT_EXT})
|
|
||||||
|
|
||||||
string(TOUPPER ${VARIANT_NAME}_SPV VARIANT_VARIABLE_NAME)
|
|
||||||
set(VARIANT_HEADER_FILE ${SHADER_DIR}/${VARIANT_NAME}_spv.h)
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT
|
|
||||||
${VARIANT_HEADER_FILE}
|
|
||||||
COMMAND
|
|
||||||
${GLSLANGVALIDATOR} -V ${QUIET_FLAG} -I"${FIDELITYFX_INCLUDE_DIR}" ${GLSL_FLAGS}
|
|
||||||
-DSAMPLER_TYPE=${VARIANT_SAMPLER} -DTEXEL_TYPE=${VARIANT_TEXEL}
|
|
||||||
--variable-name ${VARIANT_VARIABLE_NAME} -o ${VARIANT_HEADER_FILE} ${VARIANT_SOURCE}
|
|
||||||
--target-env ${SPIR_V_VERSION}
|
|
||||||
MAIN_DEPENDENCY
|
|
||||||
${VARIANT_SOURCE}
|
|
||||||
)
|
|
||||||
set(SHADER_HEADERS ${SHADER_HEADERS} ${VARIANT_HEADER_FILE})
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
foreach(FILEPATH IN ITEMS ${FIDELITYFX_FILES})
|
foreach(FILEPATH IN ITEMS ${FIDELITYFX_FILES})
|
||||||
get_filename_component(FILENAME ${FILEPATH} NAME)
|
get_filename_component(FILENAME ${FILEPATH} NAME)
|
||||||
string(REPLACE "." "_" HEADER_NAME ${FILENAME})
|
string(REPLACE "." "_" HEADER_NAME ${FILENAME})
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#version 450 core
|
|
||||||
|
|
||||||
layout(binding = 0) uniform sampler2D depth_tex;
|
|
||||||
|
|
||||||
layout(location = 0) in vec2 texcoord;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_FragDepth = textureLod(depth_tex, texcoord, 0).r;
|
|
||||||
}
|
|
||||||
@@ -8,5 +8,5 @@ layout(binding = 0) uniform sampler2DMS depth_tex;
|
|||||||
layout(location = 0) in vec2 texcoord;
|
layout(location = 0) in vec2 texcoord;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), gl_SampleID).r;
|
gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), 0).r;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ layout(binding = 1) uniform usampler2DMS stencil_tex;
|
|||||||
layout(location = 0) in vec2 texcoord;
|
layout(location = 0) in vec2 texcoord;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), gl_SampleID).r;
|
gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), 0).r;
|
||||||
gl_FragStencilRefARB = int(texelFetch(stencil_tex, ivec2(texcoord), gl_SampleID).r);
|
gl_FragStencilRefARB = int(texelFetch(stencil_tex, ivec2(texcoord), 0).r);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,7 @@
|
|||||||
|
|
||||||
#version 450 core
|
#version 450 core
|
||||||
|
|
||||||
#ifndef SAMPLER_TYPE
|
layout(binding = 0) uniform sampler2DMS msaa_in;
|
||||||
#define SAMPLER_TYPE sampler2DMS
|
|
||||||
#endif
|
|
||||||
#ifndef TEXEL_TYPE
|
|
||||||
#define TEXEL_TYPE vec4
|
|
||||||
#endif
|
|
||||||
|
|
||||||
layout(binding = 0) uniform SAMPLER_TYPE msaa_in;
|
|
||||||
|
|
||||||
layout(push_constant) uniform PushConstants {
|
layout(push_constant) uniform PushConstants {
|
||||||
ivec2 dst_offset;
|
ivec2 dst_offset;
|
||||||
@@ -18,7 +11,7 @@ layout(push_constant) uniform PushConstants {
|
|||||||
ivec2 scale;
|
ivec2 scale;
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(location = 0) out TEXEL_TYPE frag_color;
|
layout(location = 0) out vec4 frag_color;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
const ivec2 coord = ivec2(gl_FragCoord.xy) - dst_offset + src_offset;
|
const ivec2 coord = ivec2(gl_FragCoord.xy) - dst_offset + src_offset;
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#version 450 core
|
|
||||||
|
|
||||||
layout(binding = 0) uniform sampler2DMS msaa_in;
|
|
||||||
|
|
||||||
layout(push_constant) uniform PushConstants {
|
|
||||||
ivec2 dst_offset;
|
|
||||||
ivec2 src_offset;
|
|
||||||
ivec2 scale;
|
|
||||||
};
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
const ivec2 coord = ivec2(gl_FragCoord.xy) - dst_offset + src_offset;
|
|
||||||
const ivec2 msaa_coord = coord / scale;
|
|
||||||
const ivec2 sample_offset = coord % scale;
|
|
||||||
const int sample_id = sample_offset.x + scale.x * sample_offset.y;
|
|
||||||
gl_FragDepth = texelFetch(msaa_in, msaa_coord, sample_id).r;
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#version 450 core
|
|
||||||
#extension GL_ARB_shader_stencil_export : require
|
|
||||||
|
|
||||||
layout(binding = 0) uniform sampler2DMS depth_tex;
|
|
||||||
layout(binding = 1) uniform usampler2DMS stencil_tex;
|
|
||||||
|
|
||||||
layout(push_constant) uniform PushConstants {
|
|
||||||
ivec2 dst_offset;
|
|
||||||
ivec2 src_offset;
|
|
||||||
ivec2 scale;
|
|
||||||
};
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
const ivec2 coord = ivec2(gl_FragCoord.xy) - dst_offset + src_offset;
|
|
||||||
const ivec2 msaa_coord = coord / scale;
|
|
||||||
const ivec2 sample_offset = coord % scale;
|
|
||||||
const int sample_id = sample_offset.x + scale.x * sample_offset.y;
|
|
||||||
gl_FragDepth = texelFetch(depth_tex, msaa_coord, sample_id).r;
|
|
||||||
gl_FragStencilRefARB = int(texelFetch(stencil_tex, msaa_coord, sample_id).r);
|
|
||||||
}
|
|
||||||
@@ -3,14 +3,7 @@
|
|||||||
|
|
||||||
#version 450 core
|
#version 450 core
|
||||||
|
|
||||||
#ifndef SAMPLER_TYPE
|
layout(binding = 0) uniform sampler2D img_in;
|
||||||
#define SAMPLER_TYPE sampler2D
|
|
||||||
#endif
|
|
||||||
#ifndef TEXEL_TYPE
|
|
||||||
#define TEXEL_TYPE vec4
|
|
||||||
#endif
|
|
||||||
|
|
||||||
layout(binding = 0) uniform SAMPLER_TYPE img_in;
|
|
||||||
|
|
||||||
layout(push_constant) uniform PushConstants {
|
layout(push_constant) uniform PushConstants {
|
||||||
ivec2 dst_offset;
|
ivec2 dst_offset;
|
||||||
@@ -18,7 +11,7 @@ layout(push_constant) uniform PushConstants {
|
|||||||
ivec2 scale;
|
ivec2 scale;
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(location = 0) out TEXEL_TYPE frag_color;
|
layout(location = 0) out vec4 frag_color;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset;
|
const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset;
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#version 450 core
|
|
||||||
|
|
||||||
layout(binding = 0) uniform sampler2D img_in;
|
|
||||||
|
|
||||||
layout(push_constant) uniform PushConstants {
|
|
||||||
ivec2 dst_offset;
|
|
||||||
ivec2 src_offset;
|
|
||||||
ivec2 scale;
|
|
||||||
};
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset;
|
|
||||||
const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x);
|
|
||||||
const ivec2 coord = msaa_coord * scale + sample_offset + src_offset;
|
|
||||||
gl_FragDepth = texelFetch(img_in, coord, 0).r;
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#version 450 core
|
|
||||||
#extension GL_ARB_shader_stencil_export : require
|
|
||||||
|
|
||||||
layout(binding = 0) uniform sampler2D depth_tex;
|
|
||||||
layout(binding = 1) uniform usampler2D stencil_tex;
|
|
||||||
|
|
||||||
layout(push_constant) uniform PushConstants {
|
|
||||||
ivec2 dst_offset;
|
|
||||||
ivec2 src_offset;
|
|
||||||
ivec2 scale;
|
|
||||||
};
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset;
|
|
||||||
const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x);
|
|
||||||
const ivec2 coord = msaa_coord * scale + sample_offset + src_offset;
|
|
||||||
gl_FragDepth = texelFetch(depth_tex, coord, 0).r;
|
|
||||||
gl_FragStencilRefARB = int(texelFetch(stencil_tex, coord, 0).r);
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,6 @@ layout(push_constant) uniform constants {
|
|||||||
vec2 scale;
|
vec2 scale;
|
||||||
vec2 size;
|
vec2 size;
|
||||||
vec2 resize_factor;
|
vec2 resize_factor;
|
||||||
vec2 crop_offset;
|
|
||||||
float edge_sharpness;
|
float edge_sharpness;
|
||||||
};
|
};
|
||||||
layout(location = 0) out highp vec2 texcoord;
|
layout(location = 0) out highp vec2 texcoord;
|
||||||
@@ -16,5 +15,5 @@ void main() {
|
|||||||
float x = float((gl_VertexIndex & 1) << 2);
|
float x = float((gl_VertexIndex & 1) << 2);
|
||||||
float y = float((gl_VertexIndex & 2) << 1);
|
float y = float((gl_VertexIndex & 2) << 1);
|
||||||
gl_Position = vec4(x - 1.0f, y - 1.0f, 0.0, 1.0f) * vec4(sign(resize_factor), 1.f, 1.f);
|
gl_Position = vec4(x - 1.0f, y - 1.0f, 0.0, 1.0f) * vec4(sign(resize_factor), 1.f, 1.f);
|
||||||
texcoord = crop_offset + vec2(x, y) * abs(resize_factor) * 0.5;
|
texcoord = vec2(x, y) * abs(resize_factor) * 0.5;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ layout(push_constant) uniform constants {
|
|||||||
vec2 scale;
|
vec2 scale;
|
||||||
vec2 size;
|
vec2 size;
|
||||||
vec2 resize_factor;
|
vec2 resize_factor;
|
||||||
vec2 crop_offset;
|
|
||||||
float edge_sharpness;
|
float edge_sharpness;
|
||||||
};
|
};
|
||||||
layout(set = 0, binding = 0) uniform sampler2D sampler0;
|
layout(set = 0, binding = 0) uniform sampler2D sampler0;
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
layout( push_constant ) uniform constants {
|
layout( push_constant ) uniform constants {
|
||||||
vec4 ViewportInfo[1];
|
vec4 ViewportInfo[1];
|
||||||
vec2 ResizeFactor;
|
vec2 ResizeFactor;
|
||||||
vec2 CropOffset;
|
|
||||||
float EdgeSharpness;
|
float EdgeSharpness;
|
||||||
};
|
};
|
||||||
layout(set = 0, binding = 0) uniform sampler2D ps0;
|
layout(set = 0, binding = 0) uniform sampler2D ps0;
|
||||||
|
|||||||
@@ -231,7 +231,6 @@ ShaderCache::ShaderCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
|||||||
.has_broken_unsigned_image_offsets = true,
|
.has_broken_unsigned_image_offsets = true,
|
||||||
.has_broken_signed_operations = true,
|
.has_broken_signed_operations = true,
|
||||||
.has_broken_fp16_float_controls = false,
|
.has_broken_fp16_float_controls = false,
|
||||||
.has_broken_fp32_denorm_flush = false,
|
|
||||||
.has_gl_component_indexing_bug = device.HasComponentIndexingBug(),
|
.has_gl_component_indexing_bug = device.HasComponentIndexingBug(),
|
||||||
.has_gl_precise_bug = device.HasPreciseBug(),
|
.has_gl_precise_bug = device.HasPreciseBug(),
|
||||||
.has_gl_cbuf_ftou_bug = device.HasCbufFtouBug(),
|
.has_gl_cbuf_ftou_bug = device.HasCbufFtouBug(),
|
||||||
|
|||||||
@@ -75,8 +75,6 @@ public:
|
|||||||
|
|
||||||
void Finish();
|
void Finish();
|
||||||
|
|
||||||
void FlushDeferredClear() {}
|
|
||||||
|
|
||||||
StagingBufferMap UploadStagingBuffer(size_t size, bool deferred = false);
|
StagingBufferMap UploadStagingBuffer(size_t size, bool deferred = false);
|
||||||
|
|
||||||
StagingBufferMap DownloadStagingBuffer(size_t size, bool deferred = false);
|
StagingBufferMap DownloadStagingBuffer(size_t size, bool deferred = false);
|
||||||
@@ -372,7 +370,6 @@ struct TextureCacheParams {
|
|||||||
static constexpr bool HAS_EMULATED_COPIES = true;
|
static constexpr bool HAS_EMULATED_COPIES = true;
|
||||||
static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
|
static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
|
||||||
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
|
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
|
||||||
static constexpr bool HAS_MSAA_DOWNLOADS = false;
|
|
||||||
|
|
||||||
using Runtime = OpenGL::TextureCacheRuntime;
|
using Runtime = OpenGL::TextureCacheRuntime;
|
||||||
using Image = OpenGL::Image;
|
using Image = OpenGL::Image;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "video_core/host_shaders/blit_color_float_frag_spv.h"
|
#include "video_core/host_shaders/blit_color_float_frag_spv.h"
|
||||||
#include "video_core/host_shaders/blit_color_msaa_frag_spv.h"
|
#include "video_core/host_shaders/blit_color_msaa_frag_spv.h"
|
||||||
#include "video_core/host_shaders/blit_depth_frag_spv.h"
|
|
||||||
#include "video_core/host_shaders/blit_depth_msaa_frag_spv.h"
|
#include "video_core/host_shaders/blit_depth_msaa_frag_spv.h"
|
||||||
#include "video_core/host_shaders/blit_depth_stencil_msaa_frag_spv.h"
|
#include "video_core/host_shaders/blit_depth_stencil_msaa_frag_spv.h"
|
||||||
#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h"
|
#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h"
|
||||||
@@ -22,15 +21,7 @@
|
|||||||
#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
|
#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
|
||||||
#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h"
|
#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h"
|
||||||
#include "video_core/host_shaders/convert_msaa_to_non_msaa_frag_spv.h"
|
#include "video_core/host_shaders/convert_msaa_to_non_msaa_frag_spv.h"
|
||||||
#include "video_core/host_shaders/convert_msaa_to_non_msaa_depth_frag_spv.h"
|
|
||||||
#include "video_core/host_shaders/convert_msaa_to_non_msaa_depth_stencil_frag_spv.h"
|
|
||||||
#include "video_core/host_shaders/convert_msaa_to_non_msaa_sint_frag_spv.h"
|
|
||||||
#include "video_core/host_shaders/convert_msaa_to_non_msaa_uint_frag_spv.h"
|
|
||||||
#include "video_core/host_shaders/convert_non_msaa_to_msaa_frag_spv.h"
|
#include "video_core/host_shaders/convert_non_msaa_to_msaa_frag_spv.h"
|
||||||
#include "video_core/host_shaders/convert_non_msaa_to_msaa_sint_frag_spv.h"
|
|
||||||
#include "video_core/host_shaders/convert_non_msaa_to_msaa_uint_frag_spv.h"
|
|
||||||
#include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_frag_spv.h"
|
|
||||||
#include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_stencil_frag_spv.h"
|
|
||||||
#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h"
|
#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h"
|
||||||
#include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
|
#include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
|
||||||
#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
|
#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
|
||||||
@@ -527,19 +518,8 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] MSAACopyFormatClass FormatClass(VideoCore::Surface::PixelFormat format) {
|
|
||||||
if (!VideoCore::Surface::IsPixelFormatInteger(format)) {
|
|
||||||
return MSAACopyFormatClass::Float;
|
|
||||||
}
|
|
||||||
if (VideoCore::Surface::IsPixelFormatSignedInteger(format)) {
|
|
||||||
return MSAACopyFormatClass::SignedInteger;
|
|
||||||
}
|
|
||||||
return MSAACopyFormatClass::UnsignedInteger;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] vk::ImageView MakeMSAACopyView(const vk::Device& device, VkImage image,
|
[[nodiscard]] vk::ImageView MakeMSAACopyView(const vk::Device& device, VkImage image,
|
||||||
VkFormat format, u32 base_level, u32 base_layer,
|
VkFormat format, u32 base_level) {
|
||||||
VkImageAspectFlags aspect_mask) {
|
|
||||||
return device.CreateImageView(VkImageViewCreateInfo{
|
return device.CreateImageView(VkImageViewCreateInfo{
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
@@ -554,10 +534,10 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
|
|||||||
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
|
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||||
},
|
},
|
||||||
.subresourceRange{
|
.subresourceRange{
|
||||||
.aspectMask = aspect_mask,
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
.baseMipLevel = base_level,
|
.baseMipLevel = base_level,
|
||||||
.levelCount = 1,
|
.levelCount = 1,
|
||||||
.baseArrayLayer = base_layer,
|
.baseArrayLayer = 0,
|
||||||
.layerCount = 1,
|
.layerCount = 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -606,17 +586,12 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
|
|||||||
msaa_copy_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
|
msaa_copy_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
|
||||||
one_texture_set_layout.address(),
|
one_texture_set_layout.address(),
|
||||||
PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(MSAACopyPushConstants)>))),
|
PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(MSAACopyPushConstants)>))),
|
||||||
msaa_copy_depth_stencil_pipeline_layout(
|
|
||||||
device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
|
|
||||||
two_textures_set_layout.address(),
|
|
||||||
PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(MSAACopyPushConstants)>))),
|
|
||||||
full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)),
|
full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)),
|
||||||
blit_color_to_color_frag(BuildShader(device, BLIT_COLOR_FLOAT_FRAG_SPV)),
|
blit_color_to_color_frag(BuildShader(device, BLIT_COLOR_FLOAT_FRAG_SPV)),
|
||||||
blit_color_msaa_frag(BuildShader(device, BLIT_COLOR_MSAA_FRAG_SPV)),
|
blit_color_msaa_frag(BuildShader(device, BLIT_COLOR_MSAA_FRAG_SPV)),
|
||||||
blit_depth_stencil_frag(device.IsExtShaderStencilExportSupported()
|
blit_depth_stencil_frag(device.IsExtShaderStencilExportSupported()
|
||||||
? BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)
|
? BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)
|
||||||
: vk::ShaderModule{}),
|
: vk::ShaderModule{}),
|
||||||
blit_depth_frag(BuildShader(device, BLIT_DEPTH_FRAG_SPV)),
|
|
||||||
blit_depth_msaa_frag(BuildShader(device, BLIT_DEPTH_MSAA_FRAG_SPV)),
|
blit_depth_msaa_frag(BuildShader(device, BLIT_DEPTH_MSAA_FRAG_SPV)),
|
||||||
blit_depth_stencil_msaa_frag(device.IsExtShaderStencilExportSupported()
|
blit_depth_stencil_msaa_frag(device.IsExtShaderStencilExportSupported()
|
||||||
? BuildShader(device, BLIT_DEPTH_STENCIL_MSAA_FRAG_SPV)
|
? BuildShader(device, BLIT_DEPTH_STENCIL_MSAA_FRAG_SPV)
|
||||||
@@ -634,25 +609,7 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
|
|||||||
convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)),
|
convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)),
|
||||||
convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)),
|
convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)),
|
||||||
convert_msaa_to_non_msaa_frag(BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_FRAG_SPV)),
|
convert_msaa_to_non_msaa_frag(BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_FRAG_SPV)),
|
||||||
convert_msaa_to_non_msaa_sint_frag(
|
|
||||||
BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_SINT_FRAG_SPV)),
|
|
||||||
convert_msaa_to_non_msaa_uint_frag(
|
|
||||||
BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_UINT_FRAG_SPV)),
|
|
||||||
convert_msaa_to_non_msaa_depth_frag(
|
|
||||||
BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_DEPTH_FRAG_SPV)),
|
|
||||||
convert_msaa_to_non_msaa_depth_stencil_frag(
|
|
||||||
BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_DEPTH_STENCIL_FRAG_SPV)),
|
|
||||||
convert_non_msaa_to_msaa_frag(BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_FRAG_SPV)),
|
convert_non_msaa_to_msaa_frag(BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_FRAG_SPV)),
|
||||||
convert_non_msaa_to_msaa_sint_frag(
|
|
||||||
BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_SINT_FRAG_SPV)),
|
|
||||||
convert_non_msaa_to_msaa_uint_frag(
|
|
||||||
BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_UINT_FRAG_SPV)),
|
|
||||||
convert_non_msaa_to_msaa_depth_frag(
|
|
||||||
BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_FRAG_SPV)),
|
|
||||||
convert_non_msaa_to_msaa_depth_stencil_frag(
|
|
||||||
device.IsExtShaderStencilExportSupported()
|
|
||||||
? BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_STENCIL_FRAG_SPV)
|
|
||||||
: vk::ShaderModule{}),
|
|
||||||
linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)),
|
linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)),
|
||||||
nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {}
|
nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {}
|
||||||
|
|
||||||
@@ -667,13 +624,24 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, const ImageV
|
|||||||
.renderpass = dst_framebuffer->RenderPass(),
|
.renderpass = dst_framebuffer->RenderPass(),
|
||||||
.operation = operation,
|
.operation = operation,
|
||||||
};
|
};
|
||||||
VkSampler sampler = *nearest_sampler;
|
const VkPipelineLayout layout = *one_texture_pipeline_layout;
|
||||||
if (is_linear) {
|
const VkSampler sampler = is_linear ? *linear_sampler : *nearest_sampler;
|
||||||
sampler = *linear_sampler;
|
const VkPipeline pipeline = FindOrEmplaceColorPipeline(key);
|
||||||
}
|
const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
|
||||||
BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
|
|
||||||
FindOrEmplaceColorPipeline(key), sampler,
|
RecordShaderReadBarrier(scheduler, src_image_view);
|
||||||
src_image_view.Handle(Shader::TextureType::Color2D), VK_NULL_HANDLE, false);
|
scheduler.RequestRenderpass(dst_framebuffer);
|
||||||
|
scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler,
|
||||||
|
src_view](vk::CommandBuffer cmdbuf) {
|
||||||
|
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
|
||||||
|
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
|
||||||
|
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||||
|
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
|
||||||
|
nullptr);
|
||||||
|
BindBlitState(cmdbuf, layout, dst_region, src_region);
|
||||||
|
cmdbuf.Draw(3, 1, 0, 0);
|
||||||
|
});
|
||||||
|
scheduler.InvalidateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view,
|
void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view,
|
||||||
@@ -702,29 +670,24 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlitImageHelper::BlitImpl(const Framebuffer* dst_framebuffer,
|
void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer,
|
||||||
const ImageView& src_image_view, const Region2D& dst_region,
|
const ImageView& src_image_view, const Region2D& dst_region,
|
||||||
const Region2D& src_region, VkPipeline pipeline, VkSampler sampler,
|
const Region2D& src_region) {
|
||||||
VkImageView src_view, VkImageView src_stencil_view,
|
const BlitMSAAPipelineKey key{
|
||||||
bool blit_stencil) {
|
.renderpass = dst_framebuffer->RenderPass(),
|
||||||
VkPipelineLayout layout = *one_texture_pipeline_layout;
|
.samples = dst_framebuffer->Samples(),
|
||||||
if (blit_stencil) {
|
};
|
||||||
layout = *two_textures_pipeline_layout;
|
const VkPipelineLayout layout = *one_texture_pipeline_layout;
|
||||||
}
|
const VkSampler sampler = *nearest_sampler;
|
||||||
|
const VkPipeline pipeline = FindOrEmplaceBlitColorMSAAPipeline(key);
|
||||||
|
const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
|
||||||
|
|
||||||
RecordShaderReadBarrier(scheduler, src_image_view);
|
RecordShaderReadBarrier(scheduler, src_image_view);
|
||||||
scheduler.RequestRenderpass(dst_framebuffer);
|
scheduler.RequestRenderpass(dst_framebuffer);
|
||||||
scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler, src_view,
|
scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler,
|
||||||
src_stencil_view, blit_stencil](vk::CommandBuffer cmdbuf) {
|
src_view](vk::CommandBuffer cmdbuf) {
|
||||||
VkDescriptorSet descriptor_set = VK_NULL_HANDLE;
|
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
|
||||||
if (blit_stencil) {
|
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
|
||||||
descriptor_set = two_textures_descriptor_allocator.Commit();
|
|
||||||
UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_view,
|
|
||||||
src_stencil_view);
|
|
||||||
} else {
|
|
||||||
descriptor_set = one_texture_descriptor_allocator.Commit();
|
|
||||||
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
|
|
||||||
}
|
|
||||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
|
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
|
||||||
nullptr);
|
nullptr);
|
||||||
@@ -734,56 +697,41 @@ void BlitImageHelper::BlitImpl(const Framebuffer* dst_framebuffer,
|
|||||||
scheduler.InvalidateState();
|
scheduler.InvalidateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer,
|
|
||||||
const ImageView& src_image_view, const Region2D& dst_region,
|
|
||||||
const Region2D& src_region) {
|
|
||||||
const BlitMSAAPipelineKey key{
|
|
||||||
.renderpass = dst_framebuffer->RenderPass(),
|
|
||||||
.samples = dst_framebuffer->Samples(),
|
|
||||||
};
|
|
||||||
BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
|
|
||||||
FindOrEmplaceBlitColorMSAAPipeline(key), *nearest_sampler,
|
|
||||||
src_image_view.Handle(Shader::TextureType::Color2D), VK_NULL_HANDLE, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BlitImageHelper::BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer,
|
|
||||||
ImageView& src_image_view, const Region2D& dst_region,
|
|
||||||
const Region2D& src_region) {
|
|
||||||
const bool blit_stencil =
|
|
||||||
dst_framebuffer->HasAspectStencilBit() && device.IsExtShaderStencilExportSupported();
|
|
||||||
const BlitMSAAPipelineKey key{
|
|
||||||
.renderpass = dst_framebuffer->RenderPass(),
|
|
||||||
.samples = dst_framebuffer->Samples(),
|
|
||||||
};
|
|
||||||
VkImageView src_stencil_view = VK_NULL_HANDLE;
|
|
||||||
if (blit_stencil) {
|
|
||||||
src_stencil_view = src_image_view.StencilView();
|
|
||||||
}
|
|
||||||
BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
|
|
||||||
FindOrEmplaceBlitDepthStencilMSAAPipeline(key, blit_stencil), *nearest_sampler,
|
|
||||||
src_image_view.DepthView(), src_stencil_view, blit_stencil);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BlitImageHelper::BlitDepth(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
|
|
||||||
const Region2D& dst_region, const Region2D& src_region) {
|
|
||||||
BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
|
|
||||||
FindOrEmplaceBlitDepthPipeline(dst_framebuffer->RenderPass()), *nearest_sampler,
|
|
||||||
src_image_view.DepthView(), VK_NULL_HANDLE, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BlitImageHelper::ResolveDepthStencil(const Framebuffer* dst_framebuffer,
|
void BlitImageHelper::ResolveDepthStencil(const Framebuffer* dst_framebuffer,
|
||||||
ImageView& src_image_view, const Region2D& dst_region,
|
ImageView& src_image_view, const Region2D& dst_region,
|
||||||
const Region2D& src_region) {
|
const Region2D& src_region) {
|
||||||
const bool resolve_stencil =
|
const bool resolve_stencil =
|
||||||
dst_framebuffer->HasAspectStencilBit() && device.IsExtShaderStencilExportSupported();
|
dst_framebuffer->HasAspectStencilBit() && device.IsExtShaderStencilExportSupported();
|
||||||
VkImageView src_stencil_view = VK_NULL_HANDLE;
|
const VkPipeline pipeline =
|
||||||
if (resolve_stencil) {
|
FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer->RenderPass(), resolve_stencil);
|
||||||
src_stencil_view = src_image_view.StencilView();
|
const VkPipelineLayout layout =
|
||||||
}
|
resolve_stencil ? *two_textures_pipeline_layout : *one_texture_pipeline_layout;
|
||||||
BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
|
const VkSampler sampler = *nearest_sampler;
|
||||||
FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer->RenderPass(),
|
const VkImageView src_depth_view = src_image_view.DepthView();
|
||||||
resolve_stencil),
|
const VkImageView src_stencil_view =
|
||||||
*nearest_sampler, src_image_view.DepthView(), src_stencil_view, resolve_stencil);
|
resolve_stencil ? src_image_view.StencilView() : VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
RecordShaderReadBarrier(scheduler, src_image_view);
|
||||||
|
scheduler.RequestRenderpass(dst_framebuffer);
|
||||||
|
scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler, src_depth_view,
|
||||||
|
src_stencil_view, resolve_stencil](vk::CommandBuffer cmdbuf) {
|
||||||
|
if (resolve_stencil) {
|
||||||
|
const VkDescriptorSet descriptor_set = two_textures_descriptor_allocator.Commit();
|
||||||
|
UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_depth_view,
|
||||||
|
src_stencil_view);
|
||||||
|
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
|
||||||
|
nullptr);
|
||||||
|
} else {
|
||||||
|
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
|
||||||
|
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_depth_view);
|
||||||
|
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
|
||||||
|
nullptr);
|
||||||
|
}
|
||||||
|
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||||
|
BindBlitState(cmdbuf, layout, dst_region, src_region);
|
||||||
|
cmdbuf.Draw(3, 1, 0, 0);
|
||||||
|
});
|
||||||
|
scheduler.InvalidateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
|
void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
|
||||||
@@ -791,23 +739,35 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
|
|||||||
const Region2D& dst_region, const Region2D& src_region,
|
const Region2D& dst_region, const Region2D& src_region,
|
||||||
Tegra::Engines::Fermi2D::Filter filter,
|
Tegra::Engines::Fermi2D::Filter filter,
|
||||||
Tegra::Engines::Fermi2D::Operation operation) {
|
Tegra::Engines::Fermi2D::Operation operation) {
|
||||||
|
if (!device.IsExtShaderStencilExportSupported()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
ASSERT(filter == Tegra::Engines::Fermi2D::Filter::Point);
|
ASSERT(filter == Tegra::Engines::Fermi2D::Filter::Point);
|
||||||
ASSERT(operation == Tegra::Engines::Fermi2D::Operation::SrcCopy);
|
ASSERT(operation == Tegra::Engines::Fermi2D::Operation::SrcCopy);
|
||||||
const bool blit_stencil = device.IsExtShaderStencilExportSupported();
|
|
||||||
const BlitImagePipelineKey key{
|
const BlitImagePipelineKey key{
|
||||||
.renderpass = dst_framebuffer->RenderPass(),
|
.renderpass = dst_framebuffer->RenderPass(),
|
||||||
.operation = operation,
|
.operation = operation,
|
||||||
};
|
};
|
||||||
VkPipeline pipeline{};
|
const VkPipelineLayout layout = *two_textures_pipeline_layout;
|
||||||
VkImageView src_stencil_view = VK_NULL_HANDLE;
|
const VkSampler sampler = *nearest_sampler;
|
||||||
if (blit_stencil) {
|
const VkPipeline pipeline = FindOrEmplaceDepthStencilPipeline(key);
|
||||||
pipeline = FindOrEmplaceDepthStencilPipeline(key);
|
const VkImageView src_depth_view = src_image_view.DepthView();
|
||||||
src_stencil_view = src_image_view.StencilView();
|
const VkImageView src_stencil_view = src_image_view.StencilView();
|
||||||
} else {
|
|
||||||
pipeline = FindOrEmplaceBlitDepthPipeline(key.renderpass);
|
RecordShaderReadBarrier(scheduler, src_image_view);
|
||||||
}
|
scheduler.RequestRenderpass(dst_framebuffer);
|
||||||
BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, pipeline, *nearest_sampler,
|
scheduler.Record([dst_region, src_region, pipeline, layout, sampler, src_depth_view,
|
||||||
src_image_view.DepthView(), src_stencil_view, blit_stencil);
|
src_stencil_view, this](vk::CommandBuffer cmdbuf) {
|
||||||
|
const VkDescriptorSet descriptor_set = two_textures_descriptor_allocator.Commit();
|
||||||
|
UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_depth_view,
|
||||||
|
src_stencil_view);
|
||||||
|
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||||
|
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
|
||||||
|
nullptr);
|
||||||
|
BindBlitState(cmdbuf, layout, dst_region, src_region);
|
||||||
|
cmdbuf.Draw(3, 1, 0, 0);
|
||||||
|
});
|
||||||
|
scheduler.InvalidateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlitImageHelper::ConvertD32ToR32(const Framebuffer* dst_framebuffer,
|
void BlitImageHelper::ConvertD32ToR32(const Framebuffer* dst_framebuffer,
|
||||||
@@ -922,183 +882,19 @@ void BlitImageHelper::ClearDepthStencil(const Framebuffer* dst_framebuffer, bool
|
|||||||
scheduler.InvalidateState();
|
scheduler.InvalidateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline,
|
|
||||||
VkPipelineLayout layout, VkImage dst_image,
|
|
||||||
VkFormat dst_vk_format, VkImage src_image,
|
|
||||||
VkFormat src_vk_format, s32 scale_x, s32 scale_y,
|
|
||||||
std::span<const VideoCommon::ImageCopy> copies,
|
|
||||||
const MSAACopyAspectInfo& aspect_info, bool copy_stencil) {
|
|
||||||
while (!msaa_copy_resources.empty() && scheduler.IsFree(msaa_copy_resources.front().tick)) {
|
|
||||||
msaa_copy_resources.pop_front();
|
|
||||||
}
|
|
||||||
const VkSampler sampler = *nearest_sampler;
|
|
||||||
for (const VideoCommon::ImageCopy& copy : copies) {
|
|
||||||
const s32 num_layers = (std::min)(copy.src_subresource.num_layers,
|
|
||||||
copy.dst_subresource.num_layers);
|
|
||||||
for (s32 layer = 0; layer < num_layers; ++layer) {
|
|
||||||
const u32 src_level = static_cast<u32>(copy.src_subresource.base_level);
|
|
||||||
const u32 src_layer = static_cast<u32>(copy.src_subresource.base_layer + layer);
|
|
||||||
vk::ImageView src_view =
|
|
||||||
MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, src_level,
|
|
||||||
src_layer, aspect_info.src_view_aspect);
|
|
||||||
vk::ImageView src_stencil_view;
|
|
||||||
if (copy_stencil) {
|
|
||||||
src_stencil_view =
|
|
||||||
MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, src_level,
|
|
||||||
src_layer, VK_IMAGE_ASPECT_STENCIL_BIT);
|
|
||||||
}
|
|
||||||
vk::ImageView dst_view =
|
|
||||||
MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format,
|
|
||||||
static_cast<u32>(copy.dst_subresource.base_level),
|
|
||||||
static_cast<u32>(copy.dst_subresource.base_layer + layer),
|
|
||||||
aspect_info.attachment_aspect);
|
|
||||||
const VkOffset2D dst_offset{copy.dst_offset.x, copy.dst_offset.y};
|
|
||||||
const VkExtent2D dst_extent{copy.extent.width, copy.extent.height};
|
|
||||||
const VkRect2D render_area{
|
|
||||||
.offset = dst_offset,
|
|
||||||
.extent = dst_extent,
|
|
||||||
};
|
|
||||||
vk::Framebuffer framebuffer = device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.renderPass = renderpass,
|
|
||||||
.attachmentCount = 1,
|
|
||||||
.pAttachments = dst_view.address(),
|
|
||||||
.width = static_cast<u32>(dst_offset.x) + dst_extent.width,
|
|
||||||
.height = static_cast<u32>(dst_offset.y) + dst_extent.height,
|
|
||||||
.layers = 1,
|
|
||||||
});
|
|
||||||
const MSAACopyPushConstants push_constants{
|
|
||||||
.dst_offset = {dst_offset.x, dst_offset.y},
|
|
||||||
.src_offset = {copy.src_offset.x, copy.src_offset.y},
|
|
||||||
.scale = {scale_x, scale_y},
|
|
||||||
};
|
|
||||||
VkImageView src_stencil_handle = VK_NULL_HANDLE;
|
|
||||||
if (copy_stencil) {
|
|
||||||
src_stencil_handle = *src_stencil_view;
|
|
||||||
}
|
|
||||||
scheduler.RequestOutsideRenderPassOperationContext();
|
|
||||||
scheduler.Record([this, pipeline, layout, sampler, renderpass,
|
|
||||||
framebuffer_handle = *framebuffer, src_view_handle = *src_view,
|
|
||||||
src_stencil_handle, src = src_image, dst = dst_image, render_area,
|
|
||||||
aspect_info, push_constants](vk::CommandBuffer cmdbuf) {
|
|
||||||
const VkImageSubresourceRange barrier_range{
|
|
||||||
.aspectMask = aspect_info.barrier_aspect,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
|
||||||
};
|
|
||||||
const std::array pre_barriers{
|
|
||||||
VkImageMemoryBarrier{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.srcAccessMask = aspect_info.pre_src_access,
|
|
||||||
.dstAccessMask = aspect_info.pre_src_dst_access,
|
|
||||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.image = src,
|
|
||||||
.subresourceRange = barrier_range,
|
|
||||||
},
|
|
||||||
VkImageMemoryBarrier{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.srcAccessMask = aspect_info.pre_src_access,
|
|
||||||
.dstAccessMask = aspect_info.pre_dst_dst_access,
|
|
||||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.image = dst,
|
|
||||||
.subresourceRange = barrier_range,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
cmdbuf.PipelineBarrier(aspect_info.pre_src_stages, aspect_info.pre_dst_stages, 0,
|
|
||||||
nullptr, nullptr, pre_barriers);
|
|
||||||
const VkRenderPassBeginInfo renderpass_bi{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.renderPass = renderpass,
|
|
||||||
.framebuffer = framebuffer_handle,
|
|
||||||
.renderArea = render_area,
|
|
||||||
.clearValueCount = 0,
|
|
||||||
.pClearValues = nullptr,
|
|
||||||
};
|
|
||||||
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
|
|
||||||
VkDescriptorSet descriptor_set = VK_NULL_HANDLE;
|
|
||||||
if (src_stencil_handle != VK_NULL_HANDLE) {
|
|
||||||
descriptor_set = two_textures_descriptor_allocator.Commit();
|
|
||||||
UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_view_handle,
|
|
||||||
src_stencil_handle);
|
|
||||||
} else {
|
|
||||||
descriptor_set = one_texture_descriptor_allocator.Commit();
|
|
||||||
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view_handle);
|
|
||||||
}
|
|
||||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
|
||||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
|
|
||||||
nullptr);
|
|
||||||
const VkViewport viewport{
|
|
||||||
.x = static_cast<float>(render_area.offset.x),
|
|
||||||
.y = static_cast<float>(render_area.offset.y),
|
|
||||||
.width = static_cast<float>(render_area.extent.width),
|
|
||||||
.height = static_cast<float>(render_area.extent.height),
|
|
||||||
.minDepth = 0.0f,
|
|
||||||
.maxDepth = 1.0f,
|
|
||||||
};
|
|
||||||
cmdbuf.SetViewport(0, viewport);
|
|
||||||
cmdbuf.SetScissor(0, render_area);
|
|
||||||
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, push_constants);
|
|
||||||
cmdbuf.Draw(3, 1, 0, 0);
|
|
||||||
cmdbuf.EndRenderPass();
|
|
||||||
const VkImageMemoryBarrier post_barrier{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.srcAccessMask = aspect_info.post_src_access,
|
|
||||||
.dstAccessMask = aspect_info.post_dst_access,
|
|
||||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.image = dst,
|
|
||||||
.subresourceRange = barrier_range,
|
|
||||||
};
|
|
||||||
cmdbuf.PipelineBarrier(aspect_info.post_src_stages, aspect_info.post_dst_stages, 0,
|
|
||||||
post_barrier);
|
|
||||||
});
|
|
||||||
msaa_copy_resources.push_back(MSAACopyResources{
|
|
||||||
.tick = scheduler.CurrentTick(),
|
|
||||||
.src_view = std::move(src_view),
|
|
||||||
.dst_view = std::move(dst_view),
|
|
||||||
.framebuffer = std::move(framebuffer),
|
|
||||||
});
|
|
||||||
if (copy_stencil) {
|
|
||||||
msaa_copy_resources.push_back(MSAACopyResources{
|
|
||||||
.tick = scheduler.CurrentTick(),
|
|
||||||
.src_view = std::move(src_stencil_view),
|
|
||||||
.dst_view = vk::ImageView{},
|
|
||||||
.framebuffer = vk::Framebuffer{},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scheduler.InvalidateState();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_image,
|
void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_image,
|
||||||
VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
|
VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
|
||||||
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
|
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
|
||||||
std::span<const VideoCommon::ImageCopy> copies,
|
std::span<const VideoCommon::ImageCopy> copies,
|
||||||
bool msaa_to_non_msaa) {
|
bool msaa_to_non_msaa) {
|
||||||
|
while (!msaa_copy_resources.empty() && scheduler.IsFree(msaa_copy_resources.front().tick)) {
|
||||||
|
msaa_copy_resources.pop_front();
|
||||||
|
}
|
||||||
const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(static_cast<int>(num_samples));
|
const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(static_cast<int>(num_samples));
|
||||||
const s32 scale_x = 1 << samples_x;
|
const s32 scale_x = 1 << samples_x;
|
||||||
const s32 scale_y = 1 << samples_y;
|
const s32 scale_y = 1 << samples_y;
|
||||||
VkSampleCountFlagBits samples = SampleCountFlag(num_samples);
|
const VkSampleCountFlagBits samples =
|
||||||
if (msaa_to_non_msaa) {
|
msaa_to_non_msaa ? VK_SAMPLE_COUNT_1_BIT : SampleCountFlag(num_samples);
|
||||||
samples = VK_SAMPLE_COUNT_1_BIT;
|
|
||||||
}
|
|
||||||
RenderPassKey renderpass_key{};
|
RenderPassKey renderpass_key{};
|
||||||
renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid);
|
renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid);
|
||||||
renderpass_key.color_formats[0] = dst_format;
|
renderpass_key.color_formats[0] = dst_format;
|
||||||
@@ -1109,35 +905,149 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i
|
|||||||
.renderpass = renderpass,
|
.renderpass = renderpass,
|
||||||
.samples = samples,
|
.samples = samples,
|
||||||
.msaa_to_non_msaa = msaa_to_non_msaa,
|
.msaa_to_non_msaa = msaa_to_non_msaa,
|
||||||
.format_class = FormatClass(dst_format),
|
|
||||||
};
|
|
||||||
const MSAACopyAspectInfo aspect_info{
|
|
||||||
.src_view_aspect = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
||||||
.attachment_aspect = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
||||||
.barrier_aspect = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
||||||
.pre_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT |
|
|
||||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
||||||
.pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT,
|
|
||||||
.pre_dst_dst_access =
|
|
||||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
|
||||||
.pre_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
|
||||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
|
|
||||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
||||||
.pre_dst_stages =
|
|
||||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
||||||
.post_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
|
||||||
.post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT,
|
|
||||||
.post_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
||||||
.post_dst_stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
|
||||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
||||||
};
|
};
|
||||||
|
const VkPipeline pipeline = FindOrEmplaceMSAACopyPipeline(key);
|
||||||
|
const VkPipelineLayout layout = *msaa_copy_pipeline_layout;
|
||||||
|
const VkSampler sampler = *nearest_sampler;
|
||||||
const VkFormat src_vk_format =
|
const VkFormat src_vk_format =
|
||||||
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format;
|
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format;
|
||||||
const VkFormat dst_vk_format =
|
const VkFormat dst_vk_format =
|
||||||
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, dst_format).format;
|
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, dst_format).format;
|
||||||
CopyMSAAImpl(renderpass, FindOrEmplaceMSAACopyPipeline(key), *msaa_copy_pipeline_layout,
|
for (const VideoCommon::ImageCopy& copy : copies) {
|
||||||
dst_image, dst_vk_format, src_image, src_vk_format, scale_x, scale_y, copies,
|
ASSERT(copy.src_subresource.base_layer == 0);
|
||||||
aspect_info, false);
|
ASSERT(copy.src_subresource.num_layers == 1);
|
||||||
|
ASSERT(copy.dst_subresource.base_layer == 0);
|
||||||
|
ASSERT(copy.dst_subresource.num_layers == 1);
|
||||||
|
vk::ImageView src_view =
|
||||||
|
MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format,
|
||||||
|
static_cast<u32>(copy.src_subresource.base_level));
|
||||||
|
vk::ImageView dst_view =
|
||||||
|
MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format,
|
||||||
|
static_cast<u32>(copy.dst_subresource.base_level));
|
||||||
|
const VkOffset2D dst_offset{copy.dst_offset.x, copy.dst_offset.y};
|
||||||
|
const VkExtent2D dst_extent{copy.extent.width, copy.extent.height};
|
||||||
|
const VkRect2D render_area{
|
||||||
|
.offset = dst_offset,
|
||||||
|
.extent = dst_extent,
|
||||||
|
};
|
||||||
|
vk::Framebuffer framebuffer = device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.flags = 0,
|
||||||
|
.renderPass = renderpass,
|
||||||
|
.attachmentCount = 1,
|
||||||
|
.pAttachments = dst_view.address(),
|
||||||
|
.width = static_cast<u32>(dst_offset.x) + dst_extent.width,
|
||||||
|
.height = static_cast<u32>(dst_offset.y) + dst_extent.height,
|
||||||
|
.layers = 1,
|
||||||
|
});
|
||||||
|
const MSAACopyPushConstants push_constants{
|
||||||
|
.dst_offset = {dst_offset.x, dst_offset.y},
|
||||||
|
.src_offset = {copy.src_offset.x, copy.src_offset.y},
|
||||||
|
.scale = {scale_x, scale_y},
|
||||||
|
};
|
||||||
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
|
scheduler.Record([this, pipeline, layout, sampler, renderpass,
|
||||||
|
framebuffer_handle = *framebuffer, src_view_handle = *src_view,
|
||||||
|
src = src_image, dst = dst_image, render_area,
|
||||||
|
push_constants](vk::CommandBuffer cmdbuf) {
|
||||||
|
constexpr VkImageSubresourceRange color_range{
|
||||||
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||||
|
};
|
||||||
|
const std::array pre_barriers{
|
||||||
|
VkImageMemoryBarrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||||
|
VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||||
|
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = src,
|
||||||
|
.subresourceRange = color_range,
|
||||||
|
},
|
||||||
|
VkImageMemoryBarrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||||
|
VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
|
||||||
|
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||||
|
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = dst,
|
||||||
|
.subresourceRange = color_range,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||||
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
|
||||||
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||||
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||||
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||||
|
0, nullptr, nullptr, pre_barriers);
|
||||||
|
const VkRenderPassBeginInfo renderpass_bi{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.renderPass = renderpass,
|
||||||
|
.framebuffer = framebuffer_handle,
|
||||||
|
.renderArea = render_area,
|
||||||
|
.clearValueCount = 0,
|
||||||
|
.pClearValues = nullptr,
|
||||||
|
};
|
||||||
|
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
|
||||||
|
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
|
||||||
|
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view_handle);
|
||||||
|
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||||
|
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
|
||||||
|
nullptr);
|
||||||
|
const VkViewport viewport{
|
||||||
|
.x = static_cast<float>(render_area.offset.x),
|
||||||
|
.y = static_cast<float>(render_area.offset.y),
|
||||||
|
.width = static_cast<float>(render_area.extent.width),
|
||||||
|
.height = static_cast<float>(render_area.extent.height),
|
||||||
|
.minDepth = 0.0f,
|
||||||
|
.maxDepth = 1.0f,
|
||||||
|
};
|
||||||
|
cmdbuf.SetViewport(0, viewport);
|
||||||
|
cmdbuf.SetScissor(0, render_area);
|
||||||
|
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, push_constants);
|
||||||
|
cmdbuf.Draw(3, 1, 0, 0);
|
||||||
|
cmdbuf.EndRenderPass();
|
||||||
|
const VkImageMemoryBarrier post_barrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT,
|
||||||
|
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = dst,
|
||||||
|
.subresourceRange = color_range,
|
||||||
|
};
|
||||||
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||||
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||||
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
|
||||||
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
0, post_barrier);
|
||||||
|
});
|
||||||
|
msaa_copy_resources.push_back(MSAACopyResources{
|
||||||
|
.tick = scheduler.CurrentTick(),
|
||||||
|
.src_view = std::move(src_view),
|
||||||
|
.dst_view = std::move(dst_view),
|
||||||
|
.framebuffer = std::move(framebuffer),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
scheduler.InvalidateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||||
@@ -1469,87 +1379,6 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip
|
|||||||
return *blit_msaa_color_pipelines.back();
|
return *blit_msaa_color_pipelines.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthStencilMSAAPipeline(
|
|
||||||
const BlitMSAAPipelineKey& key, bool blit_stencil) {
|
|
||||||
auto& keys = blit_stencil ? blit_msaa_depth_stencil_keys : blit_msaa_depth_keys;
|
|
||||||
auto& pipelines = blit_stencil ? blit_msaa_depth_stencil_pipelines : blit_msaa_depth_pipelines;
|
|
||||||
const auto it = std::ranges::find(keys, key);
|
|
||||||
if (it != keys.end()) {
|
|
||||||
return *pipelines[std::distance(keys.begin(), it)];
|
|
||||||
}
|
|
||||||
keys.push_back(key);
|
|
||||||
const std::array stages =
|
|
||||||
MakeStages(*full_screen_vert,
|
|
||||||
blit_stencil ? *blit_depth_stencil_msaa_frag : *blit_depth_msaa_frag);
|
|
||||||
const VkPipelineMultisampleStateCreateInfo multisample_ci{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.rasterizationSamples = key.samples,
|
|
||||||
.sampleShadingEnable = VK_TRUE,
|
|
||||||
.minSampleShading = 1.0f,
|
|
||||||
.pSampleMask = nullptr,
|
|
||||||
.alphaToCoverageEnable = VK_FALSE,
|
|
||||||
.alphaToOneEnable = VK_FALSE,
|
|
||||||
};
|
|
||||||
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
|
|
||||||
pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
|
|
||||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.stageCount = static_cast<u32>(stages.size()),
|
|
||||||
.pStages = stages.data(),
|
|
||||||
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
|
||||||
.pInputAssemblyState = &input_assembly_ci,
|
|
||||||
.pTessellationState = nullptr,
|
|
||||||
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
|
||||||
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
|
||||||
.pMultisampleState = &multisample_ci,
|
|
||||||
.pDepthStencilState = blit_stencil ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO
|
|
||||||
: &PIPELINE_DEPTH_ONLY_STATE_CREATE_INFO,
|
|
||||||
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
|
|
||||||
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
|
||||||
.layout = blit_stencil ? *two_textures_pipeline_layout : *one_texture_pipeline_layout,
|
|
||||||
.renderPass = key.renderpass,
|
|
||||||
.subpass = 0,
|
|
||||||
.basePipelineHandle = VK_NULL_HANDLE,
|
|
||||||
.basePipelineIndex = 0,
|
|
||||||
}));
|
|
||||||
return *pipelines.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthPipeline(VkRenderPass renderpass) {
|
|
||||||
const auto it = std::ranges::find(blit_depth_keys, renderpass);
|
|
||||||
if (it != blit_depth_keys.end()) {
|
|
||||||
return *blit_depth_pipelines[std::distance(blit_depth_keys.begin(), it)];
|
|
||||||
}
|
|
||||||
blit_depth_keys.push_back(renderpass);
|
|
||||||
const std::array stages = MakeStages(*full_screen_vert, *blit_depth_frag);
|
|
||||||
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
|
|
||||||
blit_depth_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
|
|
||||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.stageCount = static_cast<u32>(stages.size()),
|
|
||||||
.pStages = stages.data(),
|
|
||||||
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
|
||||||
.pInputAssemblyState = &input_assembly_ci,
|
|
||||||
.pTessellationState = nullptr,
|
|
||||||
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
|
||||||
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
|
||||||
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
|
||||||
.pDepthStencilState = &PIPELINE_DEPTH_ONLY_STATE_CREATE_INFO,
|
|
||||||
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
|
|
||||||
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
|
||||||
.layout = *one_texture_pipeline_layout,
|
|
||||||
.renderPass = renderpass,
|
|
||||||
.subpass = 0,
|
|
||||||
.basePipelineHandle = VK_NULL_HANDLE,
|
|
||||||
.basePipelineIndex = 0,
|
|
||||||
}));
|
|
||||||
return *blit_depth_pipelines.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass,
|
VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass,
|
||||||
bool resolve_stencil) {
|
bool resolve_stencil) {
|
||||||
auto& keys = resolve_stencil ? resolve_depth_stencil_keys : resolve_depth_keys;
|
auto& keys = resolve_stencil ? resolve_depth_stencil_keys : resolve_depth_keys;
|
||||||
@@ -1588,83 +1417,15 @@ VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPas
|
|||||||
return *pipelines.back();
|
return *pipelines.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage dst_image,
|
|
||||||
VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
|
|
||||||
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
|
|
||||||
std::span<const VideoCommon::ImageCopy> copies,
|
|
||||||
bool copy_stencil, bool msaa_to_non_msaa) {
|
|
||||||
const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(static_cast<int>(num_samples));
|
|
||||||
const s32 scale_x = 1 << samples_x;
|
|
||||||
const s32 scale_y = 1 << samples_y;
|
|
||||||
VkSampleCountFlagBits samples = SampleCountFlag(num_samples);
|
|
||||||
if (msaa_to_non_msaa) {
|
|
||||||
samples = VK_SAMPLE_COUNT_1_BIT;
|
|
||||||
}
|
|
||||||
RenderPassKey renderpass_key{};
|
|
||||||
renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid);
|
|
||||||
renderpass_key.depth_format = dst_format;
|
|
||||||
renderpass_key.samples = samples;
|
|
||||||
const VkRenderPass renderpass = render_pass_cache.Get(renderpass_key);
|
|
||||||
const MSAACopyPipelineKey key{
|
|
||||||
.renderpass = renderpass,
|
|
||||||
.samples = samples,
|
|
||||||
.msaa_to_non_msaa = msaa_to_non_msaa,
|
|
||||||
.format_class = MSAACopyFormatClass::Float,
|
|
||||||
};
|
|
||||||
VkImageAspectFlags attachment_aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
||||||
if (VideoCore::Surface::GetFormatType(dst_format) ==
|
|
||||||
VideoCore::Surface::SurfaceType::DepthStencil) {
|
|
||||||
attachment_aspect |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
||||||
}
|
|
||||||
VkPipelineLayout layout = *msaa_copy_pipeline_layout;
|
|
||||||
if (copy_stencil) {
|
|
||||||
layout = *msaa_copy_depth_stencil_pipeline_layout;
|
|
||||||
}
|
|
||||||
const MSAACopyAspectInfo aspect_info{
|
|
||||||
.src_view_aspect = VK_IMAGE_ASPECT_DEPTH_BIT,
|
|
||||||
.attachment_aspect = attachment_aspect,
|
|
||||||
.barrier_aspect = attachment_aspect,
|
|
||||||
.pre_src_access =
|
|
||||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
||||||
.pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT,
|
|
||||||
.pre_dst_dst_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
|
|
||||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
|
||||||
.pre_src_stages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
|
|
||||||
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
|
||||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
||||||
.pre_dst_stages =
|
|
||||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
|
|
||||||
.post_src_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
|
||||||
.post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT |
|
|
||||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT,
|
|
||||||
.post_src_stages = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
|
||||||
.post_dst_stages = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER,
|
|
||||||
};
|
|
||||||
const VkFormat src_vk_format =
|
|
||||||
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format;
|
|
||||||
const VkFormat dst_vk_format =
|
|
||||||
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, dst_format).format;
|
|
||||||
CopyMSAAImpl(renderpass, FindOrEmplaceMSAACopyDepthPipeline(key, copy_stencil), layout,
|
|
||||||
dst_image, dst_vk_format, src_image, src_vk_format, scale_x, scale_y, copies,
|
|
||||||
aspect_info, copy_stencil);
|
|
||||||
}
|
|
||||||
|
|
||||||
VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key) {
|
VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key) {
|
||||||
const auto it = std::ranges::find(msaa_copy_keys, key);
|
const auto it = std::ranges::find(msaa_copy_keys, key);
|
||||||
if (it != msaa_copy_keys.end()) {
|
if (it != msaa_copy_keys.end()) {
|
||||||
return *msaa_copy_pipelines[std::distance(msaa_copy_keys.begin(), it)];
|
return *msaa_copy_pipelines[std::distance(msaa_copy_keys.begin(), it)];
|
||||||
}
|
}
|
||||||
msaa_copy_keys.push_back(key);
|
msaa_copy_keys.push_back(key);
|
||||||
VkShaderModule frag_module = key.msaa_to_non_msaa ? *convert_msaa_to_non_msaa_frag
|
const std::array stages = MakeStages(*clear_color_vert, key.msaa_to_non_msaa
|
||||||
: *convert_non_msaa_to_msaa_frag;
|
? *convert_msaa_to_non_msaa_frag
|
||||||
if (key.format_class == MSAACopyFormatClass::SignedInteger) {
|
: *convert_non_msaa_to_msaa_frag);
|
||||||
frag_module = key.msaa_to_non_msaa ? *convert_msaa_to_non_msaa_sint_frag
|
|
||||||
: *convert_non_msaa_to_msaa_sint_frag;
|
|
||||||
} else if (key.format_class == MSAACopyFormatClass::UnsignedInteger) {
|
|
||||||
frag_module = key.msaa_to_non_msaa ? *convert_msaa_to_non_msaa_uint_frag
|
|
||||||
: *convert_non_msaa_to_msaa_uint_frag;
|
|
||||||
}
|
|
||||||
const std::array stages = MakeStages(*clear_color_vert, frag_module);
|
|
||||||
const VkPipelineMultisampleStateCreateInfo multisample_ci{
|
const VkPipelineMultisampleStateCreateInfo multisample_ci{
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
@@ -1701,85 +1462,6 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipeline
|
|||||||
return *msaa_copy_pipelines.back();
|
return *msaa_copy_pipelines.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPipelineKey& key,
|
|
||||||
bool copy_stencil) {
|
|
||||||
auto& keys = copy_stencil ? msaa_copy_depth_stencil_keys : msaa_copy_depth_keys;
|
|
||||||
auto& pipelines = copy_stencil ? msaa_copy_depth_stencil_pipelines : msaa_copy_depth_pipelines;
|
|
||||||
const auto it = std::ranges::find(keys, key);
|
|
||||||
if (it != keys.end()) {
|
|
||||||
return *pipelines[std::distance(keys.begin(), it)];
|
|
||||||
}
|
|
||||||
keys.push_back(key);
|
|
||||||
VkShaderModule frag_module;
|
|
||||||
if (key.msaa_to_non_msaa) {
|
|
||||||
frag_module = copy_stencil ? *convert_msaa_to_non_msaa_depth_stencil_frag
|
|
||||||
: *convert_msaa_to_non_msaa_depth_frag;
|
|
||||||
} else {
|
|
||||||
frag_module = copy_stencil ? *convert_non_msaa_to_msaa_depth_stencil_frag
|
|
||||||
: *convert_non_msaa_to_msaa_depth_frag;
|
|
||||||
}
|
|
||||||
const std::array stages = MakeStages(*clear_color_vert, frag_module);
|
|
||||||
const VkPipelineMultisampleStateCreateInfo multisample_ci{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.rasterizationSamples = key.samples,
|
|
||||||
.sampleShadingEnable = key.msaa_to_non_msaa ? VK_FALSE : VK_TRUE,
|
|
||||||
.minSampleShading = key.msaa_to_non_msaa ? 0.0f : 1.0f,
|
|
||||||
.pSampleMask = nullptr,
|
|
||||||
.alphaToCoverageEnable = VK_FALSE,
|
|
||||||
.alphaToOneEnable = VK_FALSE,
|
|
||||||
};
|
|
||||||
static constexpr VkStencilOpState REPLACE_STENCIL_OP{
|
|
||||||
.failOp = VK_STENCIL_OP_REPLACE,
|
|
||||||
.passOp = VK_STENCIL_OP_REPLACE,
|
|
||||||
.depthFailOp = VK_STENCIL_OP_REPLACE,
|
|
||||||
.compareOp = VK_COMPARE_OP_ALWAYS,
|
|
||||||
.compareMask = 0xFF,
|
|
||||||
.writeMask = 0xFF,
|
|
||||||
.reference = 0,
|
|
||||||
};
|
|
||||||
const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.depthTestEnable = VK_TRUE,
|
|
||||||
.depthWriteEnable = VK_TRUE,
|
|
||||||
.depthCompareOp = VK_COMPARE_OP_ALWAYS,
|
|
||||||
.depthBoundsTestEnable = VK_FALSE,
|
|
||||||
.stencilTestEnable = copy_stencil ? VK_TRUE : VK_FALSE,
|
|
||||||
.front = copy_stencil ? REPLACE_STENCIL_OP : VkStencilOpState{},
|
|
||||||
.back = copy_stencil ? REPLACE_STENCIL_OP : VkStencilOpState{},
|
|
||||||
.minDepthBounds = 0.0f,
|
|
||||||
.maxDepthBounds = 0.0f,
|
|
||||||
};
|
|
||||||
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci =
|
|
||||||
GetPipelineInputAssemblyStateCreateInfo(device);
|
|
||||||
pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
|
|
||||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.stageCount = static_cast<u32>(stages.size()),
|
|
||||||
.pStages = stages.data(),
|
|
||||||
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
|
||||||
.pInputAssemblyState = &input_assembly_ci,
|
|
||||||
.pTessellationState = nullptr,
|
|
||||||
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
|
||||||
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
|
||||||
.pMultisampleState = &multisample_ci,
|
|
||||||
.pDepthStencilState = &depth_stencil_ci,
|
|
||||||
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
|
|
||||||
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
|
||||||
.layout = copy_stencil ? *msaa_copy_depth_stencil_pipeline_layout
|
|
||||||
: *msaa_copy_pipeline_layout,
|
|
||||||
.renderPass = key.renderpass,
|
|
||||||
.subpass = 0,
|
|
||||||
.basePipelineHandle = VK_NULL_HANDLE,
|
|
||||||
.basePipelineIndex = 0,
|
|
||||||
}));
|
|
||||||
return *pipelines.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
|
void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
|
||||||
ConvertPipeline(pipeline, renderpass, false);
|
ConvertPipeline(pipeline, renderpass, false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,19 +45,12 @@ struct BlitDepthStencilPipelineKey {
|
|||||||
u32 stencil_ref;
|
u32 stencil_ref;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class MSAACopyFormatClass : u32 {
|
|
||||||
Float,
|
|
||||||
SignedInteger,
|
|
||||||
UnsignedInteger,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MSAACopyPipelineKey {
|
struct MSAACopyPipelineKey {
|
||||||
constexpr auto operator<=>(const MSAACopyPipelineKey&) const noexcept = default;
|
constexpr auto operator<=>(const MSAACopyPipelineKey&) const noexcept = default;
|
||||||
|
|
||||||
VkRenderPass renderpass;
|
VkRenderPass renderpass;
|
||||||
VkSampleCountFlagBits samples;
|
VkSampleCountFlagBits samples;
|
||||||
bool msaa_to_non_msaa;
|
bool msaa_to_non_msaa;
|
||||||
MSAACopyFormatClass format_class;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BlitMSAAPipelineKey {
|
struct BlitMSAAPipelineKey {
|
||||||
@@ -85,12 +78,6 @@ public:
|
|||||||
void BlitColorMSAA(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
|
void BlitColorMSAA(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
|
||||||
const Region2D& dst_region, const Region2D& src_region);
|
const Region2D& dst_region, const Region2D& src_region);
|
||||||
|
|
||||||
void BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
|
|
||||||
const Region2D& dst_region, const Region2D& src_region);
|
|
||||||
|
|
||||||
void BlitDepth(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
|
|
||||||
const Region2D& dst_region, const Region2D& src_region);
|
|
||||||
|
|
||||||
void ResolveDepthStencil(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
|
void ResolveDepthStencil(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
|
||||||
const Region2D& dst_region, const Region2D& src_region);
|
const Region2D& dst_region, const Region2D& src_region);
|
||||||
|
|
||||||
@@ -129,39 +116,7 @@ public:
|
|||||||
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
|
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
|
||||||
std::span<const VideoCommon::ImageCopy> copies, bool msaa_to_non_msaa);
|
std::span<const VideoCommon::ImageCopy> copies, bool msaa_to_non_msaa);
|
||||||
|
|
||||||
void CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage dst_image,
|
|
||||||
VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
|
|
||||||
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
|
|
||||||
std::span<const VideoCommon::ImageCopy> copies, bool copy_stencil,
|
|
||||||
bool msaa_to_non_msaa);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct MSAACopyAspectInfo {
|
|
||||||
VkImageAspectFlags src_view_aspect;
|
|
||||||
VkImageAspectFlags attachment_aspect;
|
|
||||||
VkImageAspectFlags barrier_aspect;
|
|
||||||
VkAccessFlags pre_src_access;
|
|
||||||
VkAccessFlags pre_src_dst_access;
|
|
||||||
VkAccessFlags pre_dst_dst_access;
|
|
||||||
VkPipelineStageFlags pre_src_stages;
|
|
||||||
VkPipelineStageFlags pre_dst_stages;
|
|
||||||
VkAccessFlags post_src_access;
|
|
||||||
VkAccessFlags post_dst_access;
|
|
||||||
VkPipelineStageFlags post_src_stages;
|
|
||||||
VkPipelineStageFlags post_dst_stages;
|
|
||||||
};
|
|
||||||
|
|
||||||
void BlitImpl(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
|
|
||||||
const Region2D& dst_region, const Region2D& src_region, VkPipeline pipeline,
|
|
||||||
VkSampler sampler, VkImageView src_view, VkImageView src_stencil_view,
|
|
||||||
bool blit_stencil);
|
|
||||||
|
|
||||||
void CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, VkPipelineLayout layout,
|
|
||||||
VkImage dst_image, VkFormat dst_vk_format, VkImage src_image,
|
|
||||||
VkFormat src_vk_format, s32 scale_x, s32 scale_y,
|
|
||||||
std::span<const VideoCommon::ImageCopy> copies,
|
|
||||||
const MSAACopyAspectInfo& aspect_info, bool copy_stencil);
|
|
||||||
|
|
||||||
void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||||
const ImageView& src_image_view);
|
const ImageView& src_image_view);
|
||||||
|
|
||||||
@@ -176,13 +131,7 @@ private:
|
|||||||
[[nodiscard]] VkPipeline FindOrEmplaceClearStencilPipeline(
|
[[nodiscard]] VkPipeline FindOrEmplaceClearStencilPipeline(
|
||||||
const BlitDepthStencilPipelineKey& key);
|
const BlitDepthStencilPipelineKey& key);
|
||||||
[[nodiscard]] VkPipeline FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key);
|
[[nodiscard]] VkPipeline FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key);
|
||||||
|
|
||||||
[[nodiscard]] VkPipeline FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPipelineKey& key,
|
|
||||||
bool copy_stencil);
|
|
||||||
[[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key);
|
[[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key);
|
||||||
[[nodiscard]] VkPipeline FindOrEmplaceBlitDepthStencilMSAAPipeline(
|
|
||||||
const BlitMSAAPipelineKey& key, bool blit_stencil);
|
|
||||||
[[nodiscard]] VkPipeline FindOrEmplaceBlitDepthPipeline(VkRenderPass renderpass);
|
|
||||||
[[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass,
|
[[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass,
|
||||||
bool resolve_stencil);
|
bool resolve_stencil);
|
||||||
|
|
||||||
@@ -213,12 +162,10 @@ private:
|
|||||||
vk::PipelineLayout two_textures_pipeline_layout;
|
vk::PipelineLayout two_textures_pipeline_layout;
|
||||||
vk::PipelineLayout clear_color_pipeline_layout;
|
vk::PipelineLayout clear_color_pipeline_layout;
|
||||||
vk::PipelineLayout msaa_copy_pipeline_layout;
|
vk::PipelineLayout msaa_copy_pipeline_layout;
|
||||||
vk::PipelineLayout msaa_copy_depth_stencil_pipeline_layout;
|
|
||||||
vk::ShaderModule full_screen_vert;
|
vk::ShaderModule full_screen_vert;
|
||||||
vk::ShaderModule blit_color_to_color_frag;
|
vk::ShaderModule blit_color_to_color_frag;
|
||||||
vk::ShaderModule blit_color_msaa_frag;
|
vk::ShaderModule blit_color_msaa_frag;
|
||||||
vk::ShaderModule blit_depth_stencil_frag;
|
vk::ShaderModule blit_depth_stencil_frag;
|
||||||
vk::ShaderModule blit_depth_frag;
|
|
||||||
vk::ShaderModule blit_depth_msaa_frag;
|
vk::ShaderModule blit_depth_msaa_frag;
|
||||||
vk::ShaderModule blit_depth_stencil_msaa_frag;
|
vk::ShaderModule blit_depth_stencil_msaa_frag;
|
||||||
vk::ShaderModule clear_color_vert;
|
vk::ShaderModule clear_color_vert;
|
||||||
@@ -232,15 +179,7 @@ private:
|
|||||||
vk::ShaderModule convert_d24s8_to_abgr8_frag;
|
vk::ShaderModule convert_d24s8_to_abgr8_frag;
|
||||||
vk::ShaderModule convert_s8d24_to_abgr8_frag;
|
vk::ShaderModule convert_s8d24_to_abgr8_frag;
|
||||||
vk::ShaderModule convert_msaa_to_non_msaa_frag;
|
vk::ShaderModule convert_msaa_to_non_msaa_frag;
|
||||||
vk::ShaderModule convert_msaa_to_non_msaa_sint_frag;
|
|
||||||
vk::ShaderModule convert_msaa_to_non_msaa_uint_frag;
|
|
||||||
vk::ShaderModule convert_msaa_to_non_msaa_depth_frag;
|
|
||||||
vk::ShaderModule convert_msaa_to_non_msaa_depth_stencil_frag;
|
|
||||||
vk::ShaderModule convert_non_msaa_to_msaa_frag;
|
vk::ShaderModule convert_non_msaa_to_msaa_frag;
|
||||||
vk::ShaderModule convert_non_msaa_to_msaa_sint_frag;
|
|
||||||
vk::ShaderModule convert_non_msaa_to_msaa_uint_frag;
|
|
||||||
vk::ShaderModule convert_non_msaa_to_msaa_depth_frag;
|
|
||||||
vk::ShaderModule convert_non_msaa_to_msaa_depth_stencil_frag;
|
|
||||||
vk::Sampler linear_sampler;
|
vk::Sampler linear_sampler;
|
||||||
vk::Sampler nearest_sampler;
|
vk::Sampler nearest_sampler;
|
||||||
|
|
||||||
@@ -254,18 +193,8 @@ private:
|
|||||||
std::vector<vk::Pipeline> clear_stencil_pipelines;
|
std::vector<vk::Pipeline> clear_stencil_pipelines;
|
||||||
std::vector<MSAACopyPipelineKey> msaa_copy_keys;
|
std::vector<MSAACopyPipelineKey> msaa_copy_keys;
|
||||||
std::vector<vk::Pipeline> msaa_copy_pipelines;
|
std::vector<vk::Pipeline> msaa_copy_pipelines;
|
||||||
std::vector<MSAACopyPipelineKey> msaa_copy_depth_keys;
|
|
||||||
std::vector<vk::Pipeline> msaa_copy_depth_pipelines;
|
|
||||||
std::vector<MSAACopyPipelineKey> msaa_copy_depth_stencil_keys;
|
|
||||||
std::vector<vk::Pipeline> msaa_copy_depth_stencil_pipelines;
|
|
||||||
std::vector<BlitMSAAPipelineKey> blit_msaa_color_keys;
|
std::vector<BlitMSAAPipelineKey> blit_msaa_color_keys;
|
||||||
std::vector<vk::Pipeline> blit_msaa_color_pipelines;
|
std::vector<vk::Pipeline> blit_msaa_color_pipelines;
|
||||||
std::vector<VkRenderPass> blit_depth_keys;
|
|
||||||
std::vector<vk::Pipeline> blit_depth_pipelines;
|
|
||||||
std::vector<BlitMSAAPipelineKey> blit_msaa_depth_keys;
|
|
||||||
std::vector<vk::Pipeline> blit_msaa_depth_pipelines;
|
|
||||||
std::vector<BlitMSAAPipelineKey> blit_msaa_depth_stencil_keys;
|
|
||||||
std::vector<vk::Pipeline> blit_msaa_depth_stencil_pipelines;
|
|
||||||
std::vector<VkRenderPass> resolve_depth_keys;
|
std::vector<VkRenderPass> resolve_depth_keys;
|
||||||
std::vector<vk::Pipeline> resolve_depth_pipelines;
|
std::vector<vk::Pipeline> resolve_depth_pipelines;
|
||||||
std::vector<VkRenderPass> resolve_depth_stencil_keys;
|
std::vector<VkRenderPass> resolve_depth_stencil_keys;
|
||||||
|
|||||||
@@ -370,9 +370,20 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
|
|||||||
const VkImageView null_image_view{texture_cache.GetImageView(VideoCommon::NULL_IMAGE_VIEW_ID).Handle(desc.type)};
|
const VkImageView null_image_view{texture_cache.GetImageView(VideoCommon::NULL_IMAGE_VIEW_ID).Handle(desc.type)};
|
||||||
if (null_image_view != VK_NULL_HANDLE) vk_image_view = null_image_view;
|
if (null_image_view != VK_NULL_HANDLE) vk_image_view = null_image_view;
|
||||||
}
|
}
|
||||||
Sampler& sampler{texture_cache.GetSampler(sampler_id)};
|
const Sampler& sampler{texture_cache.GetSampler(sampler_id)};
|
||||||
guest_descriptor_queue.AddSampledImage(vk_image_view,
|
const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
|
||||||
sampler.HandleFor(image_view, desc.is_depth));
|
!image_view.SupportsAnisotropy()};
|
||||||
|
VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy()
|
||||||
|
: sampler.Handle()};
|
||||||
|
if (sampler.HasLinearFiltering() &&
|
||||||
|
VideoCore::Surface::IsPixelFormatInteger(image_view.format)) {
|
||||||
|
vk_sampler = sampler.HandleWithNearestFilter();
|
||||||
|
}
|
||||||
|
if (desc.is_depth && sampler.HasDepthComparison() &&
|
||||||
|
!image_view.SupportsDepthComparison()) {
|
||||||
|
vk_sampler = sampler.HandleWithoutDepthComparison();
|
||||||
|
}
|
||||||
|
guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler);
|
||||||
const bool element_rescaled{texture_cache.IsRescaling(image_view)};
|
const bool element_rescaled{texture_cache.IsRescaling(image_view)};
|
||||||
is_rescaled |= element_rescaled;
|
is_rescaled |= element_rescaled;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/div_ceil.h"
|
#include "common/div_ceil.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
@@ -19,7 +17,7 @@
|
|||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
using PushConstants = std::array<u32, 4 + 2 + 2 + 1>;
|
using PushConstants = std::array<u32, 4 + 2 + 1>;
|
||||||
|
|
||||||
SGSR::SGSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, VkExtent2D extent, bool edge_dir)
|
SGSR::SGSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, VkExtent2D extent, bool edge_dir)
|
||||||
: m_memory_allocator{memory_allocator}
|
: m_memory_allocator{memory_allocator}
|
||||||
@@ -102,28 +100,26 @@ VkImageView SGSR::Draw(const Device& device, Scheduler& scheduler, size_t image_
|
|||||||
|
|
||||||
const f32 input_image_width = f32(input_image_extent.width);
|
const f32 input_image_width = f32(input_image_extent.width);
|
||||||
const f32 input_image_height = f32(input_image_extent.height);
|
const f32 input_image_height = f32(input_image_extent.height);
|
||||||
const f32 crop_width = (crop_rect.right - crop_rect.left) * input_image_width;
|
const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width;
|
||||||
const f32 crop_height = (crop_rect.bottom - crop_rect.top) * input_image_height;
|
const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height;
|
||||||
static constexpr f32 EDGE_SHARPNESS_MAX = 2.0f;
|
// expected [0, 2]
|
||||||
const f32 edge_sharpness =
|
const f32 sharpening = f32(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
|
||||||
EDGE_SHARPNESS_MAX - f32(Settings::values.fsr_sharpening_slider.GetValue()) / 200.0f;
|
|
||||||
|
|
||||||
|
// p = (tex * viewport) / input = [0,n] (normalized texcoords)
|
||||||
|
// p * input = [0,1024], [0,768]
|
||||||
// layout( push_constant ) uniform constants {
|
// layout( push_constant ) uniform constants {
|
||||||
// highp vec4 ViewportInfo[1];
|
// highp vec4 ViewportInfo[1];
|
||||||
// highp vec2 ResizeFactor;
|
// highp vec2 ResizeFactor;
|
||||||
// highp vec2 CropOffset;
|
|
||||||
// highp float EdgeSharpness;
|
// highp float EdgeSharpness;
|
||||||
// };
|
// };
|
||||||
PushConstants viewport_con{};
|
PushConstants viewport_con{};
|
||||||
viewport_con[0] = std::bit_cast<u32>(1.f / input_image_width);
|
viewport_con[0] = std::bit_cast<u32>(std::abs(1.f / viewport_width));
|
||||||
viewport_con[1] = std::bit_cast<u32>(1.f / input_image_height);
|
viewport_con[1] = std::bit_cast<u32>(std::abs(1.f / viewport_height));
|
||||||
viewport_con[2] = std::bit_cast<u32>(input_image_width);
|
viewport_con[2] = std::bit_cast<u32>(std::abs(viewport_width));
|
||||||
viewport_con[3] = std::bit_cast<u32>(input_image_height);
|
viewport_con[3] = std::bit_cast<u32>(std::abs(viewport_height));
|
||||||
viewport_con[4] = std::bit_cast<u32>(crop_width / input_image_width);
|
viewport_con[4] = std::bit_cast<u32>(viewport_width / input_image_width);
|
||||||
viewport_con[5] = std::bit_cast<u32>(crop_height / input_image_height);
|
viewport_con[5] = std::bit_cast<u32>(viewport_height / input_image_height);
|
||||||
viewport_con[6] = std::bit_cast<u32>((std::min)(crop_rect.left, crop_rect.right));
|
viewport_con[6] = std::bit_cast<u32>(sharpening);
|
||||||
viewport_con[7] = std::bit_cast<u32>((std::min)(crop_rect.top, crop_rect.bottom));
|
|
||||||
viewport_con[8] = std::bit_cast<u32>(edge_sharpness);
|
|
||||||
|
|
||||||
UploadImages(device, scheduler);
|
UploadImages(device, scheduler);
|
||||||
UpdateDescriptorSets(device, source_image_view, image_index);
|
UpdateDescriptorSets(device, source_image_view, image_index);
|
||||||
|
|||||||
@@ -404,8 +404,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
|||||||
device.IsWorkgroupMemoryExplicitLayout8BitAccessSupported(),
|
device.IsWorkgroupMemoryExplicitLayout8BitAccessSupported(),
|
||||||
.support_workgroup_layout_16bit_access =
|
.support_workgroup_layout_16bit_access =
|
||||||
device.IsWorkgroupMemoryExplicitLayout16BitAccessSupported(),
|
device.IsWorkgroupMemoryExplicitLayout16BitAccessSupported(),
|
||||||
.support_shader_quad_control = device.IsKhrShaderQuadControlSupported(),
|
|
||||||
.support_quad_shuffles = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_QUAD_BIT),
|
|
||||||
.support_vote = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_VOTE_BIT),
|
.support_vote = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_VOTE_BIT),
|
||||||
.supported_subgroup_stages = supported_subgroup_stages,
|
.supported_subgroup_stages = supported_subgroup_stages,
|
||||||
.support_viewport_index_layer_non_geometry =
|
.support_viewport_index_layer_non_geometry =
|
||||||
@@ -446,7 +444,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
|||||||
.has_broken_unsigned_image_offsets = false,
|
.has_broken_unsigned_image_offsets = false,
|
||||||
.has_broken_signed_operations = false,
|
.has_broken_signed_operations = false,
|
||||||
.has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY,
|
.has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY,
|
||||||
.has_broken_fp32_denorm_flush = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
|
|
||||||
.ignore_nan_fp_comparisons = false,
|
.ignore_nan_fp_comparisons = false,
|
||||||
.has_broken_spirv_subgroup_mask_vector_extract_dynamic = false,
|
.has_broken_spirv_subgroup_mask_vector_extract_dynamic = false,
|
||||||
.has_broken_robust =
|
.has_broken_robust =
|
||||||
|
|||||||
@@ -424,12 +424,24 @@ void RasterizerVulkan::Clear(u32 layer_count) {
|
|||||||
const bool ds_deferrable =
|
const bool ds_deferrable =
|
||||||
!ds_used || ((!framebuffer->HasAspectDepthBit() || use_depth) &&
|
!ds_used || ((!framebuffer->HasAspectDepthBit() || use_depth) &&
|
||||||
(!framebuffer->HasAspectStencilBit() || use_stencil) && !stencil_partial);
|
(!framebuffer->HasAspectStencilBit() || use_stencil) && !stencil_partial);
|
||||||
|
const bool can_defer_clear = ENABLE_DEFERRED_CLEAR && !regs.clear_control.use_scissor &&
|
||||||
|
regs.clear_surface.layer == 0 &&
|
||||||
|
!scheduler.IsRenderPassActive() &&
|
||||||
|
(!use_color || color_full_channels) && ds_deferrable;
|
||||||
|
if (!can_defer_clear) {
|
||||||
|
scheduler.RequestRenderpass(framebuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
query_cache.NotifySegment(true);
|
||||||
|
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable);
|
||||||
u32 up_scale = 1;
|
u32 up_scale = 1;
|
||||||
u32 down_shift = 0;
|
u32 down_shift = 0;
|
||||||
if (texture_cache.IsRescaling()) {
|
if (texture_cache.IsRescaling()) {
|
||||||
up_scale = Settings::values.resolution_info.up_scale;
|
up_scale = Settings::values.resolution_info.up_scale;
|
||||||
down_shift = Settings::values.resolution_info.down_shift;
|
down_shift = Settings::values.resolution_info.down_shift;
|
||||||
}
|
}
|
||||||
|
UpdateViewportsState(regs);
|
||||||
|
|
||||||
VkRect2D default_scissor{};
|
VkRect2D default_scissor{};
|
||||||
default_scissor.offset.x = 0;
|
default_scissor.offset.x = 0;
|
||||||
default_scissor.offset.y = 0;
|
default_scissor.offset.y = 0;
|
||||||
@@ -485,22 +497,6 @@ void RasterizerVulkan::Clear(u32 layer_count) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool clear_covers_render_area =
|
|
||||||
clear_rect.rect.offset.x == 0 && clear_rect.rect.offset.y == 0 &&
|
|
||||||
clear_rect.rect.extent.width >= render_area.width &&
|
|
||||||
clear_rect.rect.extent.height >= render_area.height;
|
|
||||||
const bool can_defer_clear = ENABLE_DEFERRED_CLEAR && (!regs.clear_control.use_scissor || clear_covers_render_area) &&
|
|
||||||
regs.clear_surface.layer == 0 &&
|
|
||||||
!scheduler.IsRenderPassActive() &&
|
|
||||||
(!use_color || color_full_channels) && ds_deferrable;
|
|
||||||
if (!can_defer_clear) {
|
|
||||||
scheduler.RequestRenderpass(framebuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
query_cache.NotifySegment(true);
|
|
||||||
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable);
|
|
||||||
UpdateViewportsState(regs);
|
|
||||||
|
|
||||||
const u32 color_attachment = regs.clear_surface.RT;
|
const u32 color_attachment = regs.clear_surface.RT;
|
||||||
if (use_color && framebuffer->HasAspectColorBit(color_attachment)) {
|
if (use_color && framebuffer->HasAspectColorBit(color_attachment)) {
|
||||||
const auto format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(regs.rt[color_attachment].format);
|
const auto format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(regs.rt[color_attachment].format);
|
||||||
|
|||||||
@@ -65,72 +65,8 @@ using VideoCore::Surface::SurfaceType;
|
|||||||
.finalLayout = VK_IMAGE_LAYOUT_GENERAL,
|
.finalLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ResolveAspects {
|
|
||||||
bool depth;
|
|
||||||
bool stencil;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ResolveModes {
|
|
||||||
VkResolveModeFlagBits depth;
|
|
||||||
VkResolveModeFlagBits stencil;
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr ResolveAspects GetResolveAspects(PixelFormat format) {
|
|
||||||
const SurfaceType surface_type = GetSurfaceType(format);
|
|
||||||
return ResolveAspects{
|
|
||||||
.depth = surface_type == SurfaceType::Depth ||
|
|
||||||
surface_type == SurfaceType::DepthStencil,
|
|
||||||
.stencil = surface_type == SurfaceType::Stencil ||
|
|
||||||
surface_type == SurfaceType::DepthStencil,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ResolveModes PickResolveModes(const Device& device, PixelFormat format) {
|
|
||||||
constexpr VkResolveModeFlagBits mode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
|
|
||||||
|
|
||||||
const ResolveAspects aspects = GetResolveAspects(format);
|
|
||||||
const bool depth_mode_supported = (device.GetDepthResolveModes() & mode) != 0;
|
|
||||||
const bool stencil_mode_supported = (device.GetStencilResolveModes() & mode) != 0;
|
|
||||||
|
|
||||||
ResolveModes modes{
|
|
||||||
.depth = VK_RESOLVE_MODE_NONE,
|
|
||||||
.stencil = VK_RESOLVE_MODE_NONE,
|
|
||||||
};
|
|
||||||
if (aspects.depth && depth_mode_supported) {
|
|
||||||
modes.depth = mode;
|
|
||||||
}
|
|
||||||
if (aspects.stencil && stencil_mode_supported) {
|
|
||||||
modes.stencil = mode;
|
|
||||||
}
|
|
||||||
if (modes.depth == modes.stencil || device.SupportsIndependentResolveNone()) {
|
|
||||||
return modes;
|
|
||||||
}
|
|
||||||
if (modes.depth != VK_RESOLVE_MODE_NONE && stencil_mode_supported) {
|
|
||||||
modes.stencil = mode;
|
|
||||||
} else if (modes.stencil != VK_RESOLVE_MODE_NONE && depth_mode_supported) {
|
|
||||||
modes.depth = mode;
|
|
||||||
}
|
|
||||||
return modes;
|
|
||||||
}
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
bool SupportsDepthStencilResolve(const Device& device, PixelFormat depth_format) {
|
|
||||||
if (depth_format == PixelFormat::Invalid || !device.IsKhrDepthStencilResolveSupported()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const ResolveAspects aspects = GetResolveAspects(depth_format);
|
|
||||||
if (!aspects.depth && !aspects.stencil) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const ResolveModes modes = PickResolveModes(device, depth_format);
|
|
||||||
if ((aspects.depth && modes.depth == VK_RESOLVE_MODE_NONE) ||
|
|
||||||
(aspects.stencil && modes.stencil == VK_RESOLVE_MODE_NONE)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return modes.depth == modes.stencil || device.SupportsIndependentResolveNone();
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {}
|
RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {}
|
||||||
|
|
||||||
VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
||||||
@@ -139,9 +75,7 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
|||||||
if (!is_new) {
|
if (!is_new) {
|
||||||
return *pair->second;
|
return *pair->second;
|
||||||
}
|
}
|
||||||
static constexpr size_t MAX_ATTACHMENTS =
|
boost::container::static_vector<VkAttachmentDescription, 9> descriptions;
|
||||||
2 * std::tuple_size_v<decltype(RenderPassKey::color_formats)> + 2;
|
|
||||||
boost::container::static_vector<VkAttachmentDescription, MAX_ATTACHMENTS> descriptions;
|
|
||||||
std::array<VkAttachmentReference, 8> references{};
|
std::array<VkAttachmentReference, 8> references{};
|
||||||
u32 num_attachments{};
|
u32 num_attachments{};
|
||||||
u32 num_colors{};
|
u32 num_colors{};
|
||||||
@@ -175,11 +109,8 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
|||||||
const VkAttachmentLoadOp depth_load_op = key.depth_stencil_clear
|
const VkAttachmentLoadOp depth_load_op = key.depth_stencil_clear
|
||||||
? VK_ATTACHMENT_LOAD_OP_CLEAR
|
? VK_ATTACHMENT_LOAD_OP_CLEAR
|
||||||
: VK_ATTACHMENT_LOAD_OP_LOAD;
|
: VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||||
const VkAttachmentStoreOp depth_store_op = key.depth_stencil_discard
|
|
||||||
? VK_ATTACHMENT_STORE_OP_DONT_CARE
|
|
||||||
: VK_ATTACHMENT_STORE_OP_STORE;
|
|
||||||
descriptions.push_back(AttachmentDescription(*device, key.depth_format, key.samples,
|
descriptions.push_back(AttachmentDescription(*device, key.depth_format, key.samples,
|
||||||
depth_load_op, depth_store_op));
|
depth_load_op, VK_ATTACHMENT_STORE_OP_STORE));
|
||||||
}
|
}
|
||||||
std::array<VkAttachmentReference, 8> resolve_references{};
|
std::array<VkAttachmentReference, 8> resolve_references{};
|
||||||
const bool do_resolve_color =
|
const bool do_resolve_color =
|
||||||
@@ -202,21 +133,6 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const bool do_resolve_depth_stencil = key.resolve_depth_stencil && has_depth &&
|
|
||||||
key.samples != VK_SAMPLE_COUNT_1_BIT &&
|
|
||||||
SupportsDepthStencilResolve(*device, key.depth_format);
|
|
||||||
VkAttachmentReference depth_resolve_reference{};
|
|
||||||
if (do_resolve_depth_stencil) {
|
|
||||||
depth_resolve_reference = VkAttachmentReference{
|
|
||||||
.attachment = static_cast<u32>(descriptions.size()),
|
|
||||||
.layout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
};
|
|
||||||
VkAttachmentDescription resolve_desc =
|
|
||||||
AttachmentDescription(*device, key.depth_format, VK_SAMPLE_COUNT_1_BIT,
|
|
||||||
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE);
|
|
||||||
resolve_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
||||||
descriptions.push_back(resolve_desc);
|
|
||||||
}
|
|
||||||
const VkSubpassDescription subpass{
|
const VkSubpassDescription subpass{
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||||
@@ -241,92 +157,6 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
|||||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||||
.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT
|
.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT
|
||||||
};
|
};
|
||||||
|
|
||||||
if (device->IsKhrCreateRenderPass2Supported()) {
|
|
||||||
boost::container::static_vector<VkAttachmentDescription2, MAX_ATTACHMENTS> descriptions2;
|
|
||||||
for (const VkAttachmentDescription& description : descriptions) {
|
|
||||||
descriptions2.push_back(VkAttachmentDescription2{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = description.flags,
|
|
||||||
.format = description.format,
|
|
||||||
.samples = description.samples,
|
|
||||||
.loadOp = description.loadOp,
|
|
||||||
.storeOp = description.storeOp,
|
|
||||||
.stencilLoadOp = description.stencilLoadOp,
|
|
||||||
.stencilStoreOp = description.stencilStoreOp,
|
|
||||||
.initialLayout = description.initialLayout,
|
|
||||||
.finalLayout = description.finalLayout,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const auto promote = [](const VkAttachmentReference& reference) {
|
|
||||||
return VkAttachmentReference2{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.attachment = reference.attachment,
|
|
||||||
.layout = reference.layout,
|
|
||||||
.aspectMask = 0,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
std::array<VkAttachmentReference2, 8> references2{};
|
|
||||||
std::array<VkAttachmentReference2, 8> resolve_references2{};
|
|
||||||
for (size_t index = 0; index < references.size(); ++index) {
|
|
||||||
references2[index] = promote(references[index]);
|
|
||||||
resolve_references2[index] = promote(resolve_references[index]);
|
|
||||||
}
|
|
||||||
const VkAttachmentReference2 depth_reference2 = promote(depth_reference);
|
|
||||||
const VkAttachmentReference2 depth_resolve_reference2 = promote(depth_resolve_reference);
|
|
||||||
const ResolveModes resolve_modes = PickResolveModes(*device, key.depth_format);
|
|
||||||
const VkSubpassDescriptionDepthStencilResolve depth_stencil_resolve{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.depthResolveMode = resolve_modes.depth,
|
|
||||||
.stencilResolveMode = resolve_modes.stencil,
|
|
||||||
.pDepthStencilResolveAttachment = &depth_resolve_reference2,
|
|
||||||
};
|
|
||||||
const VkSubpassDescription2 subpass2{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2,
|
|
||||||
.pNext = do_resolve_depth_stencil ? &depth_stencil_resolve : nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
||||||
.viewMask = 0,
|
|
||||||
.inputAttachmentCount = 0,
|
|
||||||
.pInputAttachments = nullptr,
|
|
||||||
.colorAttachmentCount = num_attachments,
|
|
||||||
.pColorAttachments = references2.data(),
|
|
||||||
.pResolveAttachments = do_resolve_color ? resolve_references2.data() : nullptr,
|
|
||||||
.pDepthStencilAttachment = has_depth ? &depth_reference2 : nullptr,
|
|
||||||
.preserveAttachmentCount = 0,
|
|
||||||
.pPreserveAttachments = nullptr,
|
|
||||||
};
|
|
||||||
const VkSubpassDependency2 dependency2{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.srcSubpass = dependency.srcSubpass,
|
|
||||||
.dstSubpass = dependency.dstSubpass,
|
|
||||||
.srcStageMask = dependency.srcStageMask,
|
|
||||||
.dstStageMask = dependency.dstStageMask,
|
|
||||||
.srcAccessMask = dependency.srcAccessMask,
|
|
||||||
.dstAccessMask = dependency.dstAccessMask,
|
|
||||||
.dependencyFlags = dependency.dependencyFlags,
|
|
||||||
.viewOffset = 0,
|
|
||||||
};
|
|
||||||
pair->second = device->GetLogical().CreateRenderPass2({
|
|
||||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.attachmentCount = static_cast<u32>(descriptions2.size()),
|
|
||||||
.pAttachments = descriptions2.empty() ? nullptr : descriptions2.data(),
|
|
||||||
.subpassCount = 1,
|
|
||||||
.pSubpasses = &subpass2,
|
|
||||||
.dependencyCount = 1,
|
|
||||||
.pDependencies = &dependency2,
|
|
||||||
.correlatedViewMaskCount = 0,
|
|
||||||
.pCorrelatedViewMasks = nullptr,
|
|
||||||
});
|
|
||||||
return *pair->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
pair->second = device->GetLogical().CreateRenderPass({
|
pair->second = device->GetLogical().CreateRenderPass({
|
||||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <ankerl/unordered_dense.h>
|
#include <ankerl/unordered_dense.h>
|
||||||
|
|
||||||
#include "common/container_hash.h"
|
|
||||||
#include "video_core/surface.h"
|
#include "video_core/surface.h"
|
||||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||||
|
|
||||||
@@ -22,11 +21,9 @@ struct RenderPassKey {
|
|||||||
VideoCore::Surface::PixelFormat depth_format;
|
VideoCore::Surface::PixelFormat depth_format;
|
||||||
VkSampleCountFlagBits samples;
|
VkSampleCountFlagBits samples;
|
||||||
bool resolve_color;
|
bool resolve_color;
|
||||||
bool resolve_depth_stencil;
|
|
||||||
u32 color_clear_mask;
|
u32 color_clear_mask;
|
||||||
bool depth_stencil_clear;
|
bool depth_stencil_clear;
|
||||||
u32 color_discard_mask;
|
u32 color_discard_mask;
|
||||||
bool depth_stencil_discard;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
@@ -34,28 +31,17 @@ struct RenderPassKey {
|
|||||||
namespace std {
|
namespace std {
|
||||||
template <>
|
template <>
|
||||||
struct hash<Vulkan::RenderPassKey> {
|
struct hash<Vulkan::RenderPassKey> {
|
||||||
static_assert(std::tuple_size_v<decltype(Vulkan::RenderPassKey::color_formats)> <= 8);
|
|
||||||
static_assert(static_cast<u32>(VideoCore::Surface::PixelFormat::Invalid) <= 0xFF);
|
|
||||||
static_assert(static_cast<u32>(VideoCore::Surface::PixelFormat::Max) <= 0xFF);
|
|
||||||
static_assert(VK_SAMPLE_COUNT_64_BIT <= 0xFF);
|
|
||||||
|
|
||||||
[[nodiscard]] size_t operator()(const Vulkan::RenderPassKey& key) const noexcept {
|
[[nodiscard]] size_t operator()(const Vulkan::RenderPassKey& key) const noexcept {
|
||||||
u64 formats = 0;
|
size_t value = static_cast<size_t>(key.depth_format) << 48;
|
||||||
for (size_t index = 0; index < key.color_formats.size(); ++index) {
|
value ^= static_cast<size_t>(key.samples) << 52;
|
||||||
formats |= static_cast<u64>(key.color_formats[index]) << (index * 8);
|
value ^= static_cast<size_t>(key.resolve_color) << 63;
|
||||||
|
value ^= static_cast<size_t>(key.color_clear_mask) << 54;
|
||||||
|
value ^= static_cast<size_t>(key.depth_stencil_clear) << 62;
|
||||||
|
value ^= static_cast<size_t>(key.color_discard_mask) << 24;
|
||||||
|
for (size_t i = 0; i < key.color_formats.size(); ++i) {
|
||||||
|
value ^= static_cast<size_t>(key.color_formats[i]) << (i * 6);
|
||||||
}
|
}
|
||||||
const u64 state = static_cast<u64>(key.depth_format) |
|
return value;
|
||||||
(static_cast<u64>(key.samples) << 8) |
|
|
||||||
(static_cast<u64>(key.color_clear_mask) << 16) |
|
|
||||||
(static_cast<u64>(key.color_discard_mask) << 24) |
|
|
||||||
(static_cast<u64>(key.resolve_color) << 32) |
|
|
||||||
(static_cast<u64>(key.depth_stencil_clear) << 33) |
|
|
||||||
(static_cast<u64>(key.resolve_depth_stencil) << 34) |
|
|
||||||
(static_cast<u64>(key.depth_stencil_discard) << 35);
|
|
||||||
size_t seed = 0;
|
|
||||||
Common::HashCombine(seed, formats);
|
|
||||||
Common::HashCombine(seed, state);
|
|
||||||
return seed;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace std
|
} // namespace std
|
||||||
@@ -64,9 +50,6 @@ namespace Vulkan {
|
|||||||
|
|
||||||
class Device;
|
class Device;
|
||||||
|
|
||||||
[[nodiscard]] bool SupportsDepthStencilResolve(const Device& device,
|
|
||||||
VideoCore::Surface::PixelFormat depth_format);
|
|
||||||
|
|
||||||
class RenderPassCache {
|
class RenderPassCache {
|
||||||
public:
|
public:
|
||||||
explicit RenderPassCache(const Device& device_);
|
explicit RenderPassCache(const Device& device_);
|
||||||
|
|||||||
@@ -132,7 +132,6 @@ void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass
|
|||||||
num_renderpass_images = framebuffer->NumImages();
|
num_renderpass_images = framebuffer->NumImages();
|
||||||
renderpass_images = framebuffer->Images();
|
renderpass_images = framebuffer->Images();
|
||||||
renderpass_image_ranges = framebuffer->ImageRanges();
|
renderpass_image_ranges = framebuffer->ImageRanges();
|
||||||
framebuffer->MarkResolveShadowsUpToDate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::RealizeDeferredClear() {
|
void Scheduler::RealizeDeferredClear() {
|
||||||
@@ -156,10 +155,8 @@ void Scheduler::RealizeDeferredClear() {
|
|||||||
}
|
}
|
||||||
const u32 color_discard_mask =
|
const u32 color_discard_mask =
|
||||||
dc.framebuffer->DiscardsMsaaColor() ? dc.color_clear_mask : 0u;
|
dc.framebuffer->DiscardsMsaaColor() ? dc.color_clear_mask : 0u;
|
||||||
const bool depth_stencil_discard =
|
|
||||||
dc.depth_stencil && dc.framebuffer->DiscardsMsaaDepthStencil();
|
|
||||||
const VkRenderPass renderpass = dc.framebuffer->RenderPassVariant(
|
const VkRenderPass renderpass = dc.framebuffer->RenderPassVariant(
|
||||||
dc.color_clear_mask, dc.depth_stencil, color_discard_mask, depth_stencil_discard);
|
dc.color_clear_mask, dc.depth_stencil, color_discard_mask);
|
||||||
EndRenderPass();
|
EndRenderPass();
|
||||||
BeginRenderPassImpl(dc.framebuffer, renderpass, clear_values.data(), count);
|
BeginRenderPassImpl(dc.framebuffer, renderpass, clear_values.data(), count);
|
||||||
}
|
}
|
||||||
@@ -193,14 +190,6 @@ bool Scheduler::DeferDepthStencilClear(const Framebuffer* framebuffer, const VkC
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::FlushDeferredClear() {
|
|
||||||
if (deferred_clear.framebuffer == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
RealizeDeferredClear();
|
|
||||||
EndRenderPass();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) {
|
void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) {
|
||||||
if (deferred_clear.framebuffer == framebuffer) {
|
if (deferred_clear.framebuffer == framebuffer) {
|
||||||
RealizeDeferredClear();
|
RealizeDeferredClear();
|
||||||
|
|||||||
@@ -65,9 +65,6 @@ public:
|
|||||||
/// Defers a full depth/stencil clear so it becomes the next render pass.
|
/// Defers a full depth/stencil clear so it becomes the next render pass.
|
||||||
bool DeferDepthStencilClear(const Framebuffer* framebuffer, const VkClearValue& value);
|
bool DeferDepthStencilClear(const Framebuffer* framebuffer, const VkClearValue& value);
|
||||||
|
|
||||||
/// Realizes any pending deferred clear before its framebuffer can be moved or freed.
|
|
||||||
void FlushDeferredClear();
|
|
||||||
|
|
||||||
/// Requests the current execution context to be able to execute operations only allowed outside
|
/// Requests the current execution context to be able to execute operations only allowed outside
|
||||||
/// of a renderpass.
|
/// of a renderpass.
|
||||||
void RequestOutsideRenderPassOperationContext();
|
void RequestOutsideRenderPassOperationContext();
|
||||||
|
|||||||
@@ -33,10 +33,10 @@ constexpr VkDeviceSize MAX_ALIGNMENT = 256;
|
|||||||
// Windows ones however, can intake bigger buffers and generally do not OOM.
|
// Windows ones however, can intake bigger buffers and generally do not OOM.
|
||||||
// - GTX 960 on Windows will not OOM with 256mib
|
// - GTX 960 on Windows will not OOM with 256mib
|
||||||
// - GT 1030 on ^NIX will OOM with 256mib
|
// - GT 1030 on ^NIX will OOM with 256mib
|
||||||
#if defined(__FreeBSD__)
|
#if defined(_WIN32) || defined(__ANDROID__)
|
||||||
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 128_MiB;
|
|
||||||
#else
|
|
||||||
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 256_MiB;
|
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 256_MiB;
|
||||||
|
#else
|
||||||
|
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 128_MiB;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
size_t GetStreamBufferSize(const Device& device) {
|
size_t GetStreamBufferSize(const Device& device) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user