mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-29 09:58:05 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 22ec7e06e4 | |||
| 119291dc77 | |||
| f7dbff2157 | |||
| b859d7fdf4 | |||
| f635827fa6 | |||
| 243453c172 | |||
| 39158b67a7 | |||
| 50cb8fd1c9 | |||
| faaf1bac64 | |||
| 0295dc5fff |
+24
-8
@@ -43,6 +43,7 @@ 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};
|
||||||
```
|
```
|
||||||
@@ -67,6 +68,7 @@ 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,
|
||||||
@@ -91,6 +93,7 @@ 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"),
|
||||||
```
|
```
|
||||||
@@ -106,6 +109,7 @@ 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(
|
||||||
@@ -123,6 +127,7 @@ 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)
|
||||||
```
|
```
|
||||||
@@ -137,6 +142,7 @@ 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>
|
||||||
@@ -150,6 +156,7 @@ 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();
|
||||||
|
|
||||||
@@ -196,25 +203,31 @@ 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.
|
||||||
@@ -247,6 +260,7 @@ 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.
|
||||||
|
|
||||||
@@ -259,6 +273,7 @@ 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
|
||||||
@@ -270,6 +285,7 @@ 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
|
||||||
@@ -282,12 +298,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
|
||||||
}
|
}
|
||||||
@@ -299,7 +315,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);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -309,7 +325,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"
|
||||||
|
|||||||
+1
-2
@@ -11,8 +11,7 @@ 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
@@ -0,0 +1,30 @@
|
|||||||
|
// 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)
|
||||||
|
}
|
||||||
+2
-1
@@ -20,6 +20,7 @@ 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
|
||||||
@@ -1034,7 +1035,7 @@ abstract class SettingsItem(
|
|||||||
)
|
)
|
||||||
put(
|
put(
|
||||||
SpinBoxSetting(
|
SpinBoxSetting(
|
||||||
ShortSetting.DEBUG_KNOBS,
|
UShortSetting.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,
|
||||||
|
|||||||
+2
-1
@@ -25,6 +25,7 @@ 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
|
||||||
@@ -1326,7 +1327,7 @@ class SettingsFragmentPresenter(
|
|||||||
|
|
||||||
add(HeaderSetting(R.string.general))
|
add(HeaderSetting(R.string.general))
|
||||||
|
|
||||||
add(ShortSetting.DEBUG_KNOBS.key)
|
add(UShortSetting.DEBUG_KNOBS.key)
|
||||||
add(StringSetting.PROGRAM_ARGS.key)
|
add(StringSetting.PROGRAM_ARGS.key)
|
||||||
|
|
||||||
if (!NativeConfig.isPerGameConfigLoaded()) {
|
if (!NativeConfig.isPerGameConfigLoaded()) {
|
||||||
|
|||||||
@@ -80,6 +80,12 @@ 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
|
||||||
|
|
||||||
|
|||||||
@@ -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,6 +130,25 @@ 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);
|
||||||
|
|||||||
@@ -19,6 +19,23 @@
|
|||||||
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)
|
||||||
@@ -204,20 +221,14 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
SDLSink::SDLSink(std::string_view target_device_name) {
|
SDLSink::SDLSink(std::string_view target_device_name) {
|
||||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
if (InitializeAudio()) {
|
||||||
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
if (target_device_name != auto_device_name && !target_device_name.empty()) {
|
||||||
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
output_device = target_device_name;
|
||||||
return;
|
} else {
|
||||||
|
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;
|
||||||
@@ -265,15 +276,10 @@ 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)
|
||||||
@@ -304,13 +310,8 @@ bool IsSDLSuitable() {
|
|||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
// Check SDL can init
|
// Check SDL can init
|
||||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
if (!InitializeAudio()!
|
||||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
return false;
|
||||||
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.
|
||||||
|
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ struct ColorConsoleBackend final : public Backend {
|
|||||||
auto const df = GetDirectFormatArgs(entry);
|
auto const df = GetDirectFormatArgs(entry);
|
||||||
// more restrictive, because take for example this simple prelude:
|
// more restrictive, because take for example this simple prelude:
|
||||||
// [ 50.872256] Config <Info> common/settings.cpp:142:LogSettings:
|
// [ 50.872256] Config <Info> common/settings.cpp:142:LogSettings:
|
||||||
char buffer[128];
|
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);
|
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(buffer, 1, (std::min)(sizeof(buffer) - 1, result.size), stdout);
|
||||||
std::fwrite(entry.message, 1, entry.message_len, stdout);
|
std::fwrite(entry.message, 1, entry.message_len, stdout);
|
||||||
@@ -425,14 +425,14 @@ 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();
|
||||||
char buffer[BUFSIZ];
|
char buffer[BUFSIZ];
|
||||||
auto result = fmt::vformat_to_n(buffer, sizeof(buffer) - 1, format, args);
|
auto result = fmt::vformat_to_n(buffer, sizeof(buffer) - 1, format, args);
|
||||||
buffer[result.size] = '\0';
|
buffer[(std::min)(result.size, sizeof(buffer) - 1)] = '\0';
|
||||||
auto const flush = ::Settings::values.log_flush_line.GetValue();
|
|
||||||
logging_instance->ForEachBackend([=](Backend& backend) {
|
logging_instance->ForEachBackend([=](Backend& backend) {
|
||||||
backend.Write(Entry{
|
backend.Write(Entry{
|
||||||
.message = buffer,
|
.message = buffer,
|
||||||
.message_len = (std::min)(sizeof(buffer) - 1, result.size),
|
.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,
|
||||||
|
|||||||
@@ -132,11 +132,9 @@ void LogSettings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LOG_INFO(Config, "Eden Configuration:");
|
||||||
std::string settings_str{};
|
|
||||||
for (auto const& e : settings_list)
|
for (auto const& e : settings_list)
|
||||||
settings_str += e;
|
LOG_INFO(Config, "{}", 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);
|
||||||
@@ -148,7 +146,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::Debugging,
|
Category::System,
|
||||||
Specialization::Countable,
|
Specialization::Countable,
|
||||||
true,
|
true,
|
||||||
true};
|
true};
|
||||||
|
|||||||
@@ -7,6 +7,4 @@
|
|||||||
#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,6 +7,7 @@
|
|||||||
#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>
|
||||||
|
|||||||
@@ -324,6 +324,7 @@ 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
|
||||||
|
|||||||
@@ -9,11 +9,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#define STBI_ONLY_JPEG 1
|
#include "common/stb.h"
|
||||||
#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"
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#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"
|
||||||
@@ -53,23 +54,21 @@ 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::size_t signal_stack_size;
|
std::unique_ptr<uint8_t[]> signal_stack_memory;
|
||||||
|
bool supports_fast_mem = true;
|
||||||
public:
|
public:
|
||||||
SigHandler() noexcept {
|
SigHandler() noexcept {
|
||||||
signal_stack_size = std::max<size_t>(SIGSTKSZ, 2 * 1024 * 1024);
|
auto const stack_size = std::max<size_t>(SIGSTKSZ, 2 * 1024 * 1024);
|
||||||
signal_stack_memory = mmap(nullptr, signal_stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
signal_stack_memory = std::make_unique<uint8_t[]>(stack_size);
|
||||||
|
|
||||||
stack_t signal_stack{};
|
stack_t signal_stack{};
|
||||||
signal_stack.ss_sp = signal_stack_memory;
|
signal_stack.ss_sp = signal_stack_memory.get();
|
||||||
signal_stack.ss_size = signal_stack_size;
|
signal_stack.ss_size = 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");
|
||||||
@@ -87,7 +86,7 @@ public:
|
|||||||
supports_fast_mem = false;
|
supports_fast_mem = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#ifdef __APPLE__
|
#if defined(__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;
|
||||||
@@ -96,10 +95,6 @@ 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);
|
||||||
@@ -109,14 +104,17 @@ public:
|
|||||||
code_block_infos.erase(offset);
|
code_block_infos.erase(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SupportsFastmem() const noexcept { return supports_fast_mem; }
|
[[nodiscard]] inline bool SupportsFastmem() const noexcept {
|
||||||
|
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 RegisterHandler() {
|
void SigHandler::RegisterHandler() {
|
||||||
std::lock_guard<std::mutex> guard(handler_lock);
|
|
||||||
if (!sig_handler) {
|
if (!sig_handler) {
|
||||||
sig_handler.emplace();
|
sig_handler.emplace();
|
||||||
}
|
}
|
||||||
@@ -125,51 +123,27 @@ void 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;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt::print(stderr, "Unhandled {} at rip {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
|
||||||
#elif defined(ARCHITECTURE_arm64)
|
#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;
|
CTX_PC = fc.call_pc;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
|
||||||
#elif defined(ARCHITECTURE_riscv64)
|
#elif defined(ARCHITECTURE_riscv64)
|
||||||
{
|
CTX_PC = fc.call_sepc;
|
||||||
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)
|
#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;
|
CTX_PC = fc.call_pc;
|
||||||
|
#else
|
||||||
|
ASSERT(false);
|
||||||
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
LOG_ERROR(Core, "Unhandled {} at {:#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) {
|
||||||
@@ -190,9 +164,10 @@ 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,6 +1,7 @@
|
|||||||
// 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"
|
||||||
@@ -95,7 +96,8 @@ 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::LinkBlock* link_block_term = boost::get<IR::Term::LinkBlock>(&term);
|
const IR::Term::LeafTerminal* leaft_term = std::get_if<IR::Term::LeafTerminal>(&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_SEPC (mctx.mc_gpregs.gp_sepc)
|
# define CTX_PC (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_SEPC (mctx.__gregs[REG_PC])
|
# define CTX_PC (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_SEPC (ucontext->sc_sepc)
|
# define CTX_PC (ucontext->sc_sepc)
|
||||||
# define CTX_SP (ucontext->sc_sp)
|
# define CTX_SP (ucontext->sc_sp)
|
||||||
# else
|
# else
|
||||||
# error "unknown platform"
|
# error "unknown platform"
|
||||||
|
|||||||
@@ -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(bool use_default) {
|
void DiscordImpl::UpdateGameStatus(std::string_view game_url, bool has_boxart) {
|
||||||
const std::string url = use_default ? std::string{DEFAULT_DISCORD_IMAGE} : game_url;
|
const std::string url = std::string{has_boxart ? game_url : DEFAULT_DISCORD_IMAGE};
|
||||||
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);
|
||||||
game_url = fmt::format(
|
auto const 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(res && res->status == 200);
|
UpdateGameStatus(game_url, res && res->status == 200);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
|
||||||
@@ -26,11 +26,9 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::string GetGameString(const std::string& title);
|
std::string GetGameString(const std::string& title);
|
||||||
void UpdateGameStatus(bool use_default);
|
void UpdateGameStatus(std::string_view game_url, 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 2025 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
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
@@ -665,6 +665,8 @@ 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,3 +1,6 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
@@ -97,6 +100,24 @@ 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 2025 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
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
@@ -743,6 +743,10 @@ 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,3 +1,6 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
@@ -200,6 +203,18 @@ 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,6 +322,11 @@ 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);
|
||||||
@@ -443,6 +448,12 @@ 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 2025 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
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
@@ -622,6 +622,8 @@ 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,6 +260,21 @@ 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 2025 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
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
@@ -2100,6 +2100,14 @@ 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 2025 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
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
@@ -394,6 +394,8 @@ 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[532] = {
|
OpcodeMeta META_TABLE[534] = {
|
||||||
#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[532] = {
|
|||||||
#undef OPCODE
|
#undef OPCODE
|
||||||
};
|
};
|
||||||
|
|
||||||
u8 NUM_ARGS[532] = {
|
u8 NUM_ARGS[534] = {
|
||||||
#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[532];
|
extern OpcodeMeta META_TABLE[534];
|
||||||
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[532];
|
extern u8 NUM_ARGS[534];
|
||||||
} // namespace Detail
|
} // namespace Detail
|
||||||
|
|
||||||
/// Get return type of an opcode
|
/// Get return type of an opcode
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
@@ -579,6 +582,8 @@ 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 2025 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
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
@@ -36,7 +36,10 @@ enum class ShuffleMode : u64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shuffle(TranslatorVisitor& v, u64 insn, const IR::U32& index, const IR::U32& mask) {
|
constexpr u32 QUAD_MASK = (28u << 8) | 3u;
|
||||||
|
|
||||||
|
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;
|
||||||
@@ -45,6 +48,21 @@ 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);
|
||||||
@@ -59,11 +77,14 @@ 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 IR::U32 src_a{flags.src_a_flag != 0 ? ir.Imm32(static_cast<u32>(flags.src_a_imm))
|
const bool index_is_imm{flags.src_a_flag != 0};
|
||||||
: GetReg20(insn)};
|
const bool mask_is_imm{flags.src_b_flag != 0};
|
||||||
const IR::U32 src_b{flags.src_b_flag != 0 ? ir.Imm32(static_cast<u32>(flags.src_b_imm))
|
const IR::U32 src_a{index_is_imm ? ir.Imm32(static_cast<u32>(flags.src_a_imm))
|
||||||
: GetReg39(insn)};
|
: GetReg20(insn)};
|
||||||
Shuffle(*this, insn, src_a, src_b);
|
const IR::U32 src_b{mask_is_imm ? ir.Imm32(static_cast<u32>(flags.src_b_imm))
|
||||||
|
: 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,6 +498,10 @@ 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,6 +37,8 @@ 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{};
|
||||||
|
|||||||
@@ -252,6 +252,7 @@ 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{};
|
||||||
|
|||||||
@@ -370,20 +370,9 @@ 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;
|
||||||
}
|
}
|
||||||
const Sampler& sampler{texture_cache.GetSampler(sampler_id)};
|
Sampler& sampler{texture_cache.GetSampler(sampler_id)};
|
||||||
const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
|
guest_descriptor_queue.AddSampledImage(vk_image_view,
|
||||||
!image_view.SupportsAnisotropy()};
|
sampler.HandleFor(image_view, desc.is_depth));
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -404,6 +404,8 @@ 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 =
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ using VideoCore::Surface::BytesPerBlock;
|
|||||||
using VideoCore::Surface::HasAlpha;
|
using VideoCore::Surface::HasAlpha;
|
||||||
using VideoCore::Surface::IsPixelFormatASTC;
|
using VideoCore::Surface::IsPixelFormatASTC;
|
||||||
using VideoCore::Surface::IsPixelFormatInteger;
|
using VideoCore::Surface::IsPixelFormatInteger;
|
||||||
|
using VideoCore::Surface::IsPixelFormatSRGB;
|
||||||
using VideoCore::Surface::SurfaceType;
|
using VideoCore::Surface::SurfaceType;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -59,6 +60,17 @@ constexpr bool ENABLE_MSAA_RESOLVE_CONSUME = true;
|
|||||||
constexpr bool ENABLE_MSAA_COLOR_DISCARD = true;
|
constexpr bool ENABLE_MSAA_COLOR_DISCARD = true;
|
||||||
constexpr bool ENABLE_MSAA_DEPTH_STENCIL_DISCARD = true;
|
constexpr bool ENABLE_MSAA_DEPTH_STENCIL_DISCARD = true;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr bool NeedsExplicitBorderColorFormat(VkFormat format) {
|
||||||
|
switch (format) {
|
||||||
|
case VK_FORMAT_B4G4R4A4_UNORM_PACK16:
|
||||||
|
case VK_FORMAT_B5G6R5_UNORM_PACK16:
|
||||||
|
case VK_FORMAT_B5G5R5A1_UNORM_PACK16:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||||
if (color == std::array<float, 4>{0, 0, 0, 0}) {
|
if (color == std::array<float, 4>{0, 0, 0, 0}) {
|
||||||
return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
|
return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
|
||||||
@@ -1802,10 +1814,6 @@ bool TextureCacheRuntime::CanReportMemoryUsage() const {
|
|||||||
return device.CanReportMemoryUsage();
|
return device.CanReportMemoryUsage();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<size_t> TextureCacheRuntime::GetSamplerHeapBudget() const {
|
|
||||||
return device.GetSamplerHeapBudget();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextureCacheRuntime::FlushDeferredClear() {
|
void TextureCacheRuntime::FlushDeferredClear() {
|
||||||
scheduler.FlushDeferredClear();
|
scheduler.FlushDeferredClear();
|
||||||
}
|
}
|
||||||
@@ -2517,9 +2525,23 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
|||||||
supports_depth_comparison =
|
supports_depth_comparison =
|
||||||
(properties3.optimalTilingFeatures &
|
(properties3.optimalTilingFeatures &
|
||||||
VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT) != 0;
|
VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT) != 0;
|
||||||
|
supports_minmax_filter = (properties3.optimalTilingFeatures &
|
||||||
|
VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_MINMAX_BIT) != 0;
|
||||||
} else {
|
} else {
|
||||||
supports_depth_comparison = true;
|
supports_depth_comparison = true;
|
||||||
|
supports_minmax_filter =
|
||||||
|
(device->GetPhysical().GetFormatProperties(format_info.format).optimalTilingFeatures &
|
||||||
|
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT) != 0;
|
||||||
}
|
}
|
||||||
|
requires_border_color_format = NeedsExplicitBorderColorFormat(format_info.format);
|
||||||
|
swizzle_mapping = VkComponentMapping{
|
||||||
|
.r = ComponentSwizzle(swizzle[0]),
|
||||||
|
.g = ComponentSwizzle(swizzle[1]),
|
||||||
|
.b = ComponentSwizzle(swizzle[2]),
|
||||||
|
.a = ComponentSwizzle(swizzle[3]),
|
||||||
|
};
|
||||||
|
has_identity_swizzle = swizzle[0] == SwizzleSource::R && swizzle[1] == SwizzleSource::G &&
|
||||||
|
swizzle[2] == SwizzleSource::B && swizzle[3] == SwizzleSource::A;
|
||||||
const VkImageUsageFlags requested_view_usage = ImageUsageFlags(format_info, format);
|
const VkImageUsageFlags requested_view_usage = ImageUsageFlags(format_info, format);
|
||||||
const VkImageUsageFlags image_usage = image.UsageFlags();
|
const VkImageUsageFlags image_usage = image.UsageFlags();
|
||||||
const VkImageUsageFlags clamped_view_usage = requested_view_usage & image_usage;
|
const VkImageUsageFlags clamped_view_usage = requested_view_usage & image_usage;
|
||||||
@@ -2544,12 +2566,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
|||||||
.image = image.Handle(),
|
.image = image.Handle(),
|
||||||
.viewType = VkImageViewType{},
|
.viewType = VkImageViewType{},
|
||||||
.format = format_info.format,
|
.format = format_info.format,
|
||||||
.components{
|
.components = swizzle_mapping,
|
||||||
.r = ComponentSwizzle(swizzle[0]),
|
|
||||||
.g = ComponentSwizzle(swizzle[1]),
|
|
||||||
.b = ComponentSwizzle(swizzle[2]),
|
|
||||||
.a = ComponentSwizzle(swizzle[3]),
|
|
||||||
},
|
|
||||||
.subresourceRange = MakeSubresourceRange(aspect_mask, info.range),
|
.subresourceRange = MakeSubresourceRange(aspect_mask, info.range),
|
||||||
};
|
};
|
||||||
const auto create = [&](TextureType tex_type, std::optional<u32> num_layers) {
|
const auto create = [&](TextureType tex_type, std::optional<u32> num_layers) {
|
||||||
@@ -2718,92 +2735,223 @@ vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CustomBorderColorBudget::~CustomBorderColorBudget() {
|
||||||
|
Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomBorderColorBudget::CustomBorderColorBudget(CustomBorderColorBudget&& rhs) noexcept
|
||||||
|
: device_ptr{std::exchange(rhs.device_ptr, nullptr)}, held{std::exchange(rhs.held, 0)} {}
|
||||||
|
|
||||||
|
CustomBorderColorBudget& CustomBorderColorBudget::operator=(
|
||||||
|
CustomBorderColorBudget&& rhs) noexcept {
|
||||||
|
if (this != &rhs) {
|
||||||
|
Release();
|
||||||
|
device_ptr = std::exchange(rhs.device_ptr, nullptr);
|
||||||
|
held = std::exchange(rhs.held, 0);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CustomBorderColorBudget::TryAcquire(const Device& device, size_t count) {
|
||||||
|
if (!device.TryReserveCustomBorderColorSamplers(count)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
device_ptr = &device;
|
||||||
|
held += count;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomBorderColorBudget::Release() noexcept {
|
||||||
|
if (device_ptr != nullptr) {
|
||||||
|
device_ptr->ReleaseCustomBorderColorSamplers(held);
|
||||||
|
}
|
||||||
|
device_ptr = nullptr;
|
||||||
|
held = 0;
|
||||||
|
}
|
||||||
|
|
||||||
Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& tsc) {
|
Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& tsc) {
|
||||||
const auto& device = runtime.device;
|
const auto& device = runtime.device;
|
||||||
const bool has_custom_border_extension = runtime.device.IsExtCustomBorderColorSupported();
|
device_ptr = &device;
|
||||||
const bool has_format_undefined =
|
border_color = tsc.BorderColor();
|
||||||
has_custom_border_extension && runtime.device.IsCustomBorderColorWithoutFormatSupported();
|
srgb_border_color = tsc.SrgbBorderColor();
|
||||||
const bool has_custom_border_colors =
|
|
||||||
has_format_undefined && runtime.device.IsCustomBorderColorsSupported();
|
|
||||||
const auto color = tsc.BorderColor();
|
|
||||||
|
|
||||||
|
const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f);
|
||||||
|
default_anisotropy = static_cast<f32>(1U << tsc.max_anisotropy);
|
||||||
|
|
||||||
|
const VkFilter mag_filter{MaxwellToVK::Sampler::Filter(tsc.mag_filter)};
|
||||||
|
const VkFilter min_filter{MaxwellToVK::Sampler::Filter(tsc.min_filter)};
|
||||||
|
const VkSamplerMipmapMode mipmap_mode{MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter)};
|
||||||
|
|
||||||
|
const VkSamplerAddressMode wrap_u{
|
||||||
|
MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter)};
|
||||||
|
const VkSamplerAddressMode wrap_v{
|
||||||
|
MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter)};
|
||||||
|
const VkSamplerAddressMode wrap_p{
|
||||||
|
MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter)};
|
||||||
|
const bool samples_border = wrap_u == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER ||
|
||||||
|
wrap_v == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER ||
|
||||||
|
wrap_p == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
||||||
|
|
||||||
|
reduction_mode = MaxwellToVK::SamplerReduction(tsc.reduction_filter);
|
||||||
|
|
||||||
|
has_added_anisotropy = max_anisotropy > default_anisotropy;
|
||||||
|
has_linear_filtering = mag_filter == VK_FILTER_LINEAR || min_filter == VK_FILTER_LINEAR ||
|
||||||
|
mipmap_mode == VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||||
|
has_depth_comparison = tsc.depth_compare_enabled != 0;
|
||||||
|
has_minmax_reduction = reduction_mode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT;
|
||||||
|
has_srgb_border_color = tsc.srgb_conversion != 0 && srgb_border_color != border_color;
|
||||||
|
if (has_minmax_reduction && !device.IsExtSamplerFilterMinmaxSupported()) {
|
||||||
|
LOG_WARNING(Render_Vulkan, "VK_EXT_sampler_filter_minmax is required");
|
||||||
|
has_minmax_reduction = false;
|
||||||
|
}
|
||||||
|
has_custom_border_colors = samples_border && device.IsCustomBorderColorUsable();
|
||||||
|
needs_swizzle_mapping = has_custom_border_colors && device.NeedsBorderColorSwizzleMapping();
|
||||||
|
|
||||||
|
if (has_custom_border_colors && GPU::Logging::IsActive()) {
|
||||||
|
GPU::Logging::GPULogger::GetInstance().LogExtensionUsage(
|
||||||
|
"VK_EXT_custom_border_color", "Sampler::Sampler");
|
||||||
|
}
|
||||||
|
if (device.IsExtBorderColorSwizzleSupported() && GPU::Logging::IsActive()) {
|
||||||
|
GPU::Logging::GPULogger::GetInstance().LogExtensionUsage(
|
||||||
|
"VK_EXT_border_color_swizzle", "Sampler::Sampler");
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 min_lod = 0.0f;
|
||||||
|
f32 max_lod = 0.25f;
|
||||||
|
if (tsc.mipmap_filter != TextureMipmapFilter::None) {
|
||||||
|
min_lod = tsc.MinLod();
|
||||||
|
max_lod = tsc.MaxLod();
|
||||||
|
}
|
||||||
|
base_ci = VkSamplerCreateInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.flags = 0,
|
||||||
|
.magFilter = mag_filter,
|
||||||
|
.minFilter = min_filter,
|
||||||
|
.mipmapMode = mipmap_mode,
|
||||||
|
.addressModeU = wrap_u,
|
||||||
|
.addressModeV = wrap_v,
|
||||||
|
.addressModeW = wrap_p,
|
||||||
|
.mipLodBias = tsc.LodBias(),
|
||||||
|
.anisotropyEnable = static_cast<VkBool32>(max_anisotropy > 1.0f),
|
||||||
|
.maxAnisotropy = max_anisotropy,
|
||||||
|
.compareEnable = static_cast<VkBool32>(tsc.depth_compare_enabled),
|
||||||
|
.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
|
||||||
|
.minLod = min_lod,
|
||||||
|
.maxLod = max_lod,
|
||||||
|
.borderColor = VK_BORDER_COLOR_FLOAT_CUSTOM_EXT,
|
||||||
|
.unnormalizedCoordinates = VK_FALSE,
|
||||||
|
};
|
||||||
|
variants.reserve(1);
|
||||||
|
Emplace(VariantKey{});
|
||||||
|
}
|
||||||
|
|
||||||
|
Sampler::VariantKey Sampler::MakeKey(const ImageView& image_view, bool is_depth) const noexcept {
|
||||||
|
VariantKey key{};
|
||||||
|
key.reduce_anisotropy = has_added_anisotropy && !image_view.SupportsAnisotropy();
|
||||||
|
key.force_nearest = has_linear_filtering && IsPixelFormatInteger(image_view.format);
|
||||||
|
key.drop_depth_comparison =
|
||||||
|
is_depth && has_depth_comparison && !image_view.SupportsDepthComparison();
|
||||||
|
key.drop_reduction = has_minmax_reduction && !image_view.SupportsMinmaxFilter();
|
||||||
|
key.drop_custom_border = has_custom_border_colors && image_view.RequiresBorderColorFormat();
|
||||||
|
key.srgb_border = has_srgb_border_color && IsPixelFormatSRGB(image_view.format);
|
||||||
|
if (needs_swizzle_mapping && !key.drop_custom_border && !image_view.HasIdentitySwizzle()) {
|
||||||
|
const VkComponentMapping& mapping = image_view.Swizzle();
|
||||||
|
key.swizzle = {mapping.r, mapping.g, mapping.b, mapping.a};
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSampler Sampler::Find(const VariantKey& key) const noexcept {
|
||||||
|
const auto it = std::ranges::find(variants, key, &Variant::key);
|
||||||
|
if (it == variants.end()) {
|
||||||
|
return VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
return *it->sampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSampler Sampler::Emplace(VariantKey key) {
|
||||||
|
bool custom_border = has_custom_border_colors && !key.drop_custom_border;
|
||||||
|
if (custom_border && !custom_border_color_budget.TryAcquire(*device_ptr, 1)) {
|
||||||
|
static bool warned_budget = false;
|
||||||
|
if (!warned_budget) {
|
||||||
|
warned_budget = true;
|
||||||
|
}
|
||||||
|
custom_border = false;
|
||||||
|
key.drop_custom_border = true;
|
||||||
|
key.swizzle = {};
|
||||||
|
if (const VkSampler existing = Find(key); existing != VK_NULL_HANDLE) {
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<float, 4> color = border_color;
|
||||||
|
if (key.srgb_border) {
|
||||||
|
color = srgb_border_color;
|
||||||
|
}
|
||||||
const VkSamplerCustomBorderColorCreateInfoEXT border_ci{
|
const VkSamplerCustomBorderColorCreateInfoEXT border_ci{
|
||||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT,
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.customBorderColor = std::bit_cast<VkClearColorValue>(color),
|
.customBorderColor = std::bit_cast<VkClearColorValue>(color),
|
||||||
.format = VK_FORMAT_UNDEFINED,
|
.format = VK_FORMAT_UNDEFINED,
|
||||||
};
|
};
|
||||||
const void* pnext = nullptr;
|
const VkSamplerBorderColorComponentMappingCreateInfoEXT mapping_ci{
|
||||||
if (has_custom_border_colors) {
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_BORDER_COLOR_COMPONENT_MAPPING_CREATE_INFO_EXT,
|
||||||
pnext = &border_ci;
|
.pNext = &border_ci,
|
||||||
if (GPU::Logging::IsActive()) {
|
.components{key.swizzle[0], key.swizzle[1], key.swizzle[2], key.swizzle[3]},
|
||||||
GPU::Logging::GPULogger::GetInstance().LogExtensionUsage(
|
.srgb = VK_FALSE,
|
||||||
"VK_EXT_custom_border_color", "Sampler::Sampler");
|
};
|
||||||
|
const void* chain = nullptr;
|
||||||
|
if (custom_border) {
|
||||||
|
chain = &border_ci;
|
||||||
|
if (key.HasSwizzle()) {
|
||||||
|
chain = &mapping_ci;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (device.IsExtBorderColorSwizzleSupported() && GPU::Logging::IsActive()) {
|
|
||||||
GPU::Logging::GPULogger::GetInstance().LogExtensionUsage(
|
|
||||||
"VK_EXT_border_color_swizzle", "Sampler::Sampler");
|
|
||||||
}
|
|
||||||
const VkSamplerReductionModeCreateInfoEXT reduction_ci{
|
const VkSamplerReductionModeCreateInfoEXT reduction_ci{
|
||||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT,
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT,
|
||||||
.pNext = pnext,
|
.pNext = chain,
|
||||||
.reductionMode = MaxwellToVK::SamplerReduction(tsc.reduction_filter),
|
.reductionMode = reduction_mode,
|
||||||
};
|
};
|
||||||
if (runtime.device.IsExtSamplerFilterMinmaxSupported()) {
|
if (has_minmax_reduction && !key.drop_reduction) {
|
||||||
pnext = &reduction_ci;
|
chain = &reduction_ci;
|
||||||
} else if (reduction_ci.reductionMode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT) {
|
|
||||||
LOG_WARNING(Render_Vulkan, "VK_EXT_sampler_filter_minmax is required");
|
|
||||||
}
|
}
|
||||||
// Some games have samplers with garbage. Sanitize them here.
|
|
||||||
const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f);
|
|
||||||
|
|
||||||
const VkFilter mag_filter{MaxwellToVK::Sampler::Filter(tsc.mag_filter)};
|
VkSamplerCreateInfo create_info = base_ci;
|
||||||
const VkFilter min_filter{MaxwellToVK::Sampler::Filter(tsc.min_filter)};
|
create_info.pNext = chain;
|
||||||
const VkSamplerMipmapMode mipmap_mode{MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter)};
|
if (key.force_nearest) {
|
||||||
const bool has_linear_filtering{mag_filter == VK_FILTER_LINEAR ||
|
create_info.magFilter = VK_FILTER_NEAREST;
|
||||||
min_filter == VK_FILTER_LINEAR ||
|
create_info.minFilter = VK_FILTER_NEAREST;
|
||||||
mipmap_mode == VK_SAMPLER_MIPMAP_MODE_LINEAR};
|
create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||||
|
create_info.anisotropyEnable = VK_FALSE;
|
||||||
const auto create_sampler = [&](const f32 anisotropy, bool force_nearest,
|
create_info.maxAnisotropy = 1.0f;
|
||||||
bool disable_compare = false) {
|
} else if (key.reduce_anisotropy) {
|
||||||
return device.GetLogical().CreateSampler(VkSamplerCreateInfo{
|
create_info.anisotropyEnable = static_cast<VkBool32>(default_anisotropy > 1.0f);
|
||||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
create_info.maxAnisotropy = default_anisotropy;
|
||||||
.pNext = pnext,
|
|
||||||
.flags = 0,
|
|
||||||
.magFilter = force_nearest ? VK_FILTER_NEAREST : mag_filter,
|
|
||||||
.minFilter = force_nearest ? VK_FILTER_NEAREST : min_filter,
|
|
||||||
.mipmapMode = force_nearest ? VK_SAMPLER_MIPMAP_MODE_NEAREST : mipmap_mode,
|
|
||||||
.addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter),
|
|
||||||
.addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter),
|
|
||||||
.addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter),
|
|
||||||
.mipLodBias = tsc.LodBias(),
|
|
||||||
.anisotropyEnable =
|
|
||||||
static_cast<VkBool32>(!force_nearest && anisotropy > 1.0f ? VK_TRUE : VK_FALSE),
|
|
||||||
.maxAnisotropy = force_nearest ? 1.0f : anisotropy,
|
|
||||||
.compareEnable = disable_compare ? VK_FALSE
|
|
||||||
: static_cast<VkBool32>(tsc.depth_compare_enabled),
|
|
||||||
.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
|
|
||||||
.minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(),
|
|
||||||
.maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(),
|
|
||||||
.borderColor = has_custom_border_colors ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT
|
|
||||||
: ConvertBorderColor(color),
|
|
||||||
.unnormalizedCoordinates = VK_FALSE,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
sampler = create_sampler(max_anisotropy, false);
|
|
||||||
|
|
||||||
const f32 max_anisotropy_default = static_cast<f32>(1U << tsc.max_anisotropy);
|
|
||||||
if (max_anisotropy > max_anisotropy_default) {
|
|
||||||
sampler_default_anisotropy = create_sampler(max_anisotropy_default, false);
|
|
||||||
}
|
}
|
||||||
if (has_linear_filtering) {
|
if (key.drop_depth_comparison) {
|
||||||
sampler_nearest = create_sampler(1.0f, true);
|
create_info.compareEnable = VK_FALSE;
|
||||||
}
|
}
|
||||||
if (tsc.depth_compare_enabled) {
|
if (!custom_border) {
|
||||||
sampler_noncompare = create_sampler(max_anisotropy, false, true);
|
create_info.borderColor = ConvertBorderColor(color);
|
||||||
}
|
}
|
||||||
|
variants.push_back(Variant{
|
||||||
|
.key = key,
|
||||||
|
.sampler = device_ptr->GetLogical().CreateSampler(create_info),
|
||||||
|
});
|
||||||
|
return *variants.back().sampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSampler Sampler::HandleFor(const ImageView& image_view, bool is_depth) {
|
||||||
|
VariantKey key = MakeKey(image_view, is_depth);
|
||||||
|
if (variants.size() >= MAX_VARIANTS) {
|
||||||
|
key.srgb_border = false;
|
||||||
|
key.swizzle = {};
|
||||||
|
}
|
||||||
|
if (const VkSampler existing = Find(key); existing != VK_NULL_HANDLE) {
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
return Emplace(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM_RT> color_buffers,
|
Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM_RT> color_buffers,
|
||||||
|
|||||||
@@ -68,8 +68,6 @@ public:
|
|||||||
|
|
||||||
bool CanReportMemoryUsage() const;
|
bool CanReportMemoryUsage() const;
|
||||||
|
|
||||||
std::optional<size_t> GetSamplerHeapBudget() const;
|
|
||||||
|
|
||||||
bool CanDownloadMsaa(const VideoCommon::ImageInfo& info) const;
|
bool CanDownloadMsaa(const VideoCommon::ImageInfo& info) const;
|
||||||
|
|
||||||
[[nodiscard]] VkImage AcquireMsaaScratchImage(const VkImageCreateInfo& image_ci);
|
[[nodiscard]] VkImage AcquireMsaaScratchImage(const VkImageCreateInfo& image_ci);
|
||||||
@@ -446,6 +444,22 @@ public:
|
|||||||
return supports_depth_comparison;
|
return supports_depth_comparison;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool RequiresBorderColorFormat() const noexcept {
|
||||||
|
return requires_border_color_format;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool SupportsMinmaxFilter() const noexcept {
|
||||||
|
return supports_minmax_filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const VkComponentMapping& Swizzle() const noexcept {
|
||||||
|
return swizzle_mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool HasIdentitySwizzle() const noexcept {
|
||||||
|
return has_identity_swizzle;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] GPUVAddr GpuAddr() const noexcept {
|
[[nodiscard]] GPUVAddr GpuAddr() const noexcept {
|
||||||
return gpu_addr;
|
return gpu_addr;
|
||||||
}
|
}
|
||||||
@@ -478,48 +492,91 @@ private:
|
|||||||
VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
|
VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
u32 buffer_size = 0;
|
u32 buffer_size = 0;
|
||||||
|
|
||||||
|
VkComponentMapping swizzle_mapping{};
|
||||||
|
|
||||||
bool supports_depth_comparison = false;
|
bool supports_depth_comparison = false;
|
||||||
|
bool requires_border_color_format = false;
|
||||||
|
bool supports_minmax_filter = false;
|
||||||
|
bool has_identity_swizzle = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ImageAlloc : public VideoCommon::ImageAllocBase {};
|
class ImageAlloc : public VideoCommon::ImageAllocBase {};
|
||||||
|
|
||||||
|
class CustomBorderColorBudget {
|
||||||
|
public:
|
||||||
|
CustomBorderColorBudget() = default;
|
||||||
|
~CustomBorderColorBudget();
|
||||||
|
|
||||||
|
CustomBorderColorBudget(const CustomBorderColorBudget&) = delete;
|
||||||
|
CustomBorderColorBudget& operator=(const CustomBorderColorBudget&) = delete;
|
||||||
|
|
||||||
|
CustomBorderColorBudget(CustomBorderColorBudget&& rhs) noexcept;
|
||||||
|
CustomBorderColorBudget& operator=(CustomBorderColorBudget&& rhs) noexcept;
|
||||||
|
|
||||||
|
bool TryAcquire(const Device& device, size_t count);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Release() noexcept;
|
||||||
|
|
||||||
|
const Device* device_ptr = nullptr;
|
||||||
|
size_t held = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class Sampler {
|
class Sampler {
|
||||||
public:
|
public:
|
||||||
explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&);
|
explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&);
|
||||||
|
|
||||||
[[nodiscard]] VkSampler Handle() const noexcept {
|
[[nodiscard]] VkSampler Handle() const noexcept {
|
||||||
return *sampler;
|
return *variants.front().sampler;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] VkSampler HandleWithDefaultAnisotropy() const noexcept {
|
[[nodiscard]] VkSampler HandleFor(const ImageView& image_view, bool is_depth);
|
||||||
return *sampler_default_anisotropy;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool HasAddedAnisotropy() const noexcept {
|
|
||||||
return static_cast<bool>(sampler_default_anisotropy);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] VkSampler HandleWithNearestFilter() const noexcept {
|
|
||||||
return *sampler_nearest;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool HasLinearFiltering() const noexcept {
|
|
||||||
return static_cast<bool>(sampler_nearest);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] VkSampler HandleWithoutDepthComparison() const noexcept {
|
|
||||||
return *sampler_noncompare;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool HasDepthComparison() const noexcept {
|
|
||||||
return static_cast<bool>(sampler_noncompare);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
vk::Sampler sampler;
|
struct VariantKey {
|
||||||
vk::Sampler sampler_default_anisotropy;
|
bool reduce_anisotropy;
|
||||||
vk::Sampler sampler_nearest;
|
bool force_nearest;
|
||||||
vk::Sampler sampler_noncompare;
|
bool drop_depth_comparison;
|
||||||
|
bool drop_reduction;
|
||||||
|
bool drop_custom_border;
|
||||||
|
bool srgb_border;
|
||||||
|
std::array<VkComponentSwizzle, 4> swizzle;
|
||||||
|
|
||||||
|
bool operator==(const VariantKey&) const noexcept = default;
|
||||||
|
|
||||||
|
[[nodiscard]] bool HasSwizzle() const noexcept {
|
||||||
|
return swizzle != std::array<VkComponentSwizzle, 4>{};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Variant {
|
||||||
|
VariantKey key;
|
||||||
|
vk::Sampler sampler;
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr size_t MAX_VARIANTS = 32;
|
||||||
|
|
||||||
|
[[nodiscard]] VariantKey MakeKey(const ImageView& image_view, bool is_depth) const noexcept;
|
||||||
|
[[nodiscard]] VkSampler Find(const VariantKey& key) const noexcept;
|
||||||
|
VkSampler Emplace(VariantKey key);
|
||||||
|
|
||||||
|
CustomBorderColorBudget custom_border_color_budget;
|
||||||
|
std::vector<Variant> variants;
|
||||||
|
|
||||||
|
const Device* device_ptr{nullptr};
|
||||||
|
VkSamplerCreateInfo base_ci{};
|
||||||
|
VkSamplerReductionModeEXT reduction_mode{VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT};
|
||||||
|
std::array<float, 4> border_color{};
|
||||||
|
std::array<float, 4> srgb_border_color{};
|
||||||
|
f32 default_anisotropy{1.0f};
|
||||||
|
|
||||||
|
bool has_added_anisotropy{};
|
||||||
|
bool has_linear_filtering{};
|
||||||
|
bool has_depth_comparison{};
|
||||||
|
bool has_minmax_reduction{};
|
||||||
|
bool has_custom_border_colors{};
|
||||||
|
bool has_srgb_border_color{};
|
||||||
|
bool needs_swizzle_mapping{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TextureCacheParams {
|
struct TextureCacheParams {
|
||||||
|
|||||||
@@ -1896,67 +1896,10 @@ SamplerId TextureCache<P>::FindSampler(const TSCEntry& config, bool compute) {
|
|||||||
const auto [pair, is_new] = channel_state->samplers.try_emplace(config);
|
const auto [pair, is_new] = channel_state->samplers.try_emplace(config);
|
||||||
if (is_new) {
|
if (is_new) {
|
||||||
pair->second = slot_samplers.insert(runtime, config);
|
pair->second = slot_samplers.insert(runtime, config);
|
||||||
EnforceSamplerBudget();
|
|
||||||
}
|
}
|
||||||
return pair->second;
|
return pair->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
|
||||||
std::optional<size_t> TextureCache<P>::QuerySamplerBudget() const {
|
|
||||||
if constexpr (requires { runtime.GetSamplerHeapBudget(); }) {
|
|
||||||
return runtime.GetSamplerHeapBudget();
|
|
||||||
} else {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class P>
|
|
||||||
void TextureCache<P>::EnforceSamplerBudget() {
|
|
||||||
if (auto const budget = QuerySamplerBudget(); budget) {
|
|
||||||
if (slot_samplers.size() < *budget) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!channel_state) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (last_sampler_gc_frame == frame_tick) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
last_sampler_gc_frame = frame_tick;
|
|
||||||
TrimInactiveSamplers(*budget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class P>
|
|
||||||
void TextureCache<P>::TrimInactiveSamplers(size_t budget) {
|
|
||||||
if (channel_state->samplers.size() > 0) {
|
|
||||||
constexpr size_t SAMPLER_GC_SLACK = 1024;
|
|
||||||
ankerl::unordered_dense::set<SamplerId> active_sampler_ids;
|
|
||||||
for (auto const& e : channel_state->sampler_ids)
|
|
||||||
active_sampler_ids.insert(e.second);
|
|
||||||
// Elements in the map must be necesarily valid
|
|
||||||
size_t removed = 0;
|
|
||||||
for (auto it = channel_state->samplers.begin(); it != channel_state->samplers.end();) {
|
|
||||||
const SamplerId sampler_id = it->second;
|
|
||||||
if (!sampler_id || sampler_id == CORRUPT_ID) {
|
|
||||||
it = channel_state->samplers.erase(it);
|
|
||||||
} else if (std::ranges::find(active_sampler_ids, sampler_id) != active_sampler_ids.end()) {
|
|
||||||
++it;
|
|
||||||
} else {
|
|
||||||
slot_samplers.erase(sampler_id);
|
|
||||||
it = channel_state->samplers.erase(it);
|
|
||||||
++removed;
|
|
||||||
if (slot_samplers.size() + SAMPLER_GC_SLACK <= budget) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (removed != 0) {
|
|
||||||
LOG_WARNING(HW_GPU, "Sampler cache exceeded {} entries on this driver; reclaimed {} inactive samplers", budget, removed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
ImageViewId TextureCache<P>::FindColorBuffer(size_t index) {
|
ImageViewId TextureCache<P>::FindColorBuffer(size_t index) {
|
||||||
const auto& regs = maxwell3d->regs;
|
const auto& regs = maxwell3d->regs;
|
||||||
|
|||||||
@@ -420,9 +420,6 @@ private:
|
|||||||
|
|
||||||
void QueueAsyncDecode(Image& image, ImageId image_id);
|
void QueueAsyncDecode(Image& image, ImageId image_id);
|
||||||
void TickAsyncDecode();
|
void TickAsyncDecode();
|
||||||
void EnforceSamplerBudget();
|
|
||||||
void TrimInactiveSamplers(size_t budget);
|
|
||||||
std::optional<size_t> QuerySamplerBudget() const;
|
|
||||||
|
|
||||||
void QueueAsyncUnswizzle(Image& image, ImageId image_id);
|
void QueueAsyncUnswizzle(Image& image, ImageId image_id);
|
||||||
void TickAsyncUnswizzle();
|
void TickAsyncUnswizzle();
|
||||||
@@ -509,7 +506,6 @@ private:
|
|||||||
|
|
||||||
u64 modification_tick = 0;
|
u64 modification_tick = 0;
|
||||||
u64 frame_tick = 0;
|
u64 frame_tick = 0;
|
||||||
u64 last_sampler_gc_frame = (std::numeric_limits<u64>::max)();
|
|
||||||
|
|
||||||
Common::ThreadWorker texture_decode_worker{1, "TextureDecoder", {},
|
Common::ThreadWorker texture_decode_worker{1, "TextureDecoder", {},
|
||||||
Common::ThreadPlacement::Efficiency};
|
Common::ThreadPlacement::Efficiency};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#include "common/cityhash.h"
|
#include "common/cityhash.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
@@ -17,53 +18,25 @@ namespace Tegra::Texture {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
[[maybe_unused]] constexpr std::array<float, 256> SRGB_CONVERSION_LUT = {
|
float SrgbToLinear(u32 value) {
|
||||||
0.000000f, 0.000000f, 0.000000f, 0.000012f, 0.000021f, 0.000033f, 0.000046f, 0.000062f,
|
const float encoded = static_cast<float>(value) / 255.0f;
|
||||||
0.000081f, 0.000102f, 0.000125f, 0.000151f, 0.000181f, 0.000214f, 0.000251f, 0.000293f,
|
if (encoded <= 0.04045f) {
|
||||||
0.000338f, 0.000388f, 0.000443f, 0.000503f, 0.000568f, 0.000639f, 0.000715f, 0.000798f,
|
return encoded / 12.92f;
|
||||||
0.000887f, 0.000983f, 0.001085f, 0.001195f, 0.001312f, 0.001437f, 0.001569f, 0.001710f,
|
}
|
||||||
0.001860f, 0.002019f, 0.002186f, 0.002364f, 0.002551f, 0.002748f, 0.002955f, 0.003174f,
|
return std::pow((encoded + 0.055f) / 1.055f, 2.4f);
|
||||||
0.003403f, 0.003643f, 0.003896f, 0.004160f, 0.004436f, 0.004725f, 0.005028f, 0.005343f,
|
}
|
||||||
0.005672f, 0.006015f, 0.006372f, 0.006744f, 0.007130f, 0.007533f, 0.007950f, 0.008384f,
|
|
||||||
0.008834f, 0.009301f, 0.009785f, 0.010286f, 0.010805f, 0.011342f, 0.011898f, 0.012472f,
|
|
||||||
0.013066f, 0.013680f, 0.014313f, 0.014967f, 0.015641f, 0.016337f, 0.017054f, 0.017793f,
|
|
||||||
0.018554f, 0.019337f, 0.020144f, 0.020974f, 0.021828f, 0.022706f, 0.023609f, 0.024536f,
|
|
||||||
0.025489f, 0.026468f, 0.027473f, 0.028504f, 0.029563f, 0.030649f, 0.031762f, 0.032904f,
|
|
||||||
0.034074f, 0.035274f, 0.036503f, 0.037762f, 0.039050f, 0.040370f, 0.041721f, 0.043103f,
|
|
||||||
0.044518f, 0.045964f, 0.047444f, 0.048956f, 0.050503f, 0.052083f, 0.053699f, 0.055349f,
|
|
||||||
0.057034f, 0.058755f, 0.060513f, 0.062307f, 0.064139f, 0.066008f, 0.067915f, 0.069861f,
|
|
||||||
0.071845f, 0.073869f, 0.075933f, 0.078037f, 0.080182f, 0.082369f, 0.084597f, 0.086867f,
|
|
||||||
0.089180f, 0.091535f, 0.093935f, 0.096378f, 0.098866f, 0.101398f, 0.103977f, 0.106601f,
|
|
||||||
0.109271f, 0.111988f, 0.114753f, 0.117565f, 0.120426f, 0.123335f, 0.126293f, 0.129301f,
|
|
||||||
0.132360f, 0.135469f, 0.138629f, 0.141841f, 0.145105f, 0.148421f, 0.151791f, 0.155214f,
|
|
||||||
0.158691f, 0.162224f, 0.165810f, 0.169453f, 0.173152f, 0.176907f, 0.180720f, 0.184589f,
|
|
||||||
0.188517f, 0.192504f, 0.196549f, 0.200655f, 0.204820f, 0.209046f, 0.213334f, 0.217682f,
|
|
||||||
0.222093f, 0.226567f, 0.231104f, 0.235704f, 0.240369f, 0.245099f, 0.249894f, 0.254754f,
|
|
||||||
0.259681f, 0.264674f, 0.269736f, 0.274864f, 0.280062f, 0.285328f, 0.290664f, 0.296070f,
|
|
||||||
0.301546f, 0.307094f, 0.312713f, 0.318404f, 0.324168f, 0.330006f, 0.335916f, 0.341902f,
|
|
||||||
0.347962f, 0.354097f, 0.360309f, 0.366597f, 0.372961f, 0.379403f, 0.385924f, 0.392524f,
|
|
||||||
0.399202f, 0.405960f, 0.412798f, 0.419718f, 0.426719f, 0.433802f, 0.440967f, 0.448216f,
|
|
||||||
0.455548f, 0.462965f, 0.470465f, 0.478052f, 0.485725f, 0.493484f, 0.501329f, 0.509263f,
|
|
||||||
0.517285f, 0.525396f, 0.533595f, 0.541885f, 0.550265f, 0.558736f, 0.567299f, 0.575954f,
|
|
||||||
0.584702f, 0.593542f, 0.602477f, 0.611507f, 0.620632f, 0.629852f, 0.639168f, 0.648581f,
|
|
||||||
0.658092f, 0.667700f, 0.677408f, 0.687214f, 0.697120f, 0.707127f, 0.717234f, 0.727443f,
|
|
||||||
0.737753f, 0.748167f, 0.758685f, 0.769305f, 0.780031f, 0.790861f, 0.801798f, 0.812839f,
|
|
||||||
0.823989f, 0.835246f, 0.846611f, 0.858085f, 0.869668f, 0.881360f, 0.893164f, 0.905078f,
|
|
||||||
0.917104f, 0.929242f, 0.941493f, 0.953859f, 0.966338f, 1.000000f, 1.000000f, 1.000000f,
|
|
||||||
};
|
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
std::array<float, 4> TSCEntry::BorderColor() const noexcept {
|
std::array<float, 4> TSCEntry::BorderColor() const noexcept {
|
||||||
// TODO: Handle SRGB correctly. Using this breaks shadows in some games (Xenoblade).
|
|
||||||
// if (!srgb_conversion) {
|
|
||||||
// return border_color;
|
|
||||||
//}
|
|
||||||
// return {SRGB_CONVERSION_LUT[srgb_border_color_r], SRGB_CONVERSION_LUT[srgb_border_color_g],
|
|
||||||
// SRGB_CONVERSION_LUT[srgb_border_color_b], border_color[3]};
|
|
||||||
return border_color;
|
return border_color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::array<float, 4> TSCEntry::SrgbBorderColor() const noexcept {
|
||||||
|
return {SrgbToLinear(srgb_border_color_r), SrgbToLinear(srgb_border_color_g),
|
||||||
|
SrgbToLinear(srgb_border_color_b), border_color[3]};
|
||||||
|
}
|
||||||
|
|
||||||
float TSCEntry::MaxAnisotropy() const noexcept {
|
float TSCEntry::MaxAnisotropy() const noexcept {
|
||||||
const bool is_suitable_mipmap_filter = mipmap_filter != TextureMipmapFilter::None;
|
const bool is_suitable_mipmap_filter = mipmap_filter != TextureMipmapFilter::None;
|
||||||
const bool has_regular_lods = min_lod_clamp == 0 && max_lod_clamp >= 256;
|
const bool has_regular_lods = min_lod_clamp == 0 && max_lod_clamp >= 256;
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -378,6 +381,8 @@ struct TSCEntry {
|
|||||||
|
|
||||||
std::array<float, 4> BorderColor() const noexcept;
|
std::array<float, 4> BorderColor() const noexcept;
|
||||||
|
|
||||||
|
std::array<float, 4> SrgbBorderColor() const noexcept;
|
||||||
|
|
||||||
float MaxAnisotropy() const noexcept;
|
float MaxAnisotropy() const noexcept;
|
||||||
|
|
||||||
float MinLod() const {
|
float MinLod() const {
|
||||||
|
|||||||
@@ -508,12 +508,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
|||||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers require scaled vertex format emulation.");
|
LOG_WARNING(Render_Vulkan, "Qualcomm drivers require scaled vertex format emulation.");
|
||||||
has_broken_descriptor_aliasing = true;
|
has_broken_descriptor_aliasing = true;
|
||||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken descriptor aliasing.");
|
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken descriptor aliasing.");
|
||||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken custom border color.");
|
|
||||||
RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color,
|
|
||||||
VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
|
|
||||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken border color swizzle.");
|
|
||||||
RemoveExtensionFeature(extensions.border_color_swizzle, features.border_color_swizzle,
|
|
||||||
VK_EXT_BORDER_COLOR_SWIZZLE_EXTENSION_NAME);
|
|
||||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken color write enable.");
|
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken color write enable.");
|
||||||
RemoveExtensionFeature(extensions.color_write_enable, features.color_write_enable,
|
RemoveExtensionFeature(extensions.color_write_enable, features.color_write_enable,
|
||||||
VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME);
|
VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME);
|
||||||
@@ -617,21 +611,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_qualcomm) {
|
|
||||||
const size_t sampler_limit = properties.properties.limits.maxSamplerAllocationCount;
|
|
||||||
if (sampler_limit > 0) {
|
|
||||||
constexpr size_t MIN_SAMPLER_BUDGET = 1024U;
|
|
||||||
const size_t reserved = sampler_limit / 4U;
|
|
||||||
const size_t derived_budget =
|
|
||||||
(std::max)(MIN_SAMPLER_BUDGET, sampler_limit - reserved);
|
|
||||||
sampler_heap_budget = derived_budget;
|
|
||||||
LOG_WARNING(Render_Vulkan,
|
|
||||||
"Qualcomm driver reports max {} samplers; reserving {} (25%) and "
|
|
||||||
"allowing Eden to use {} (75%) to avoid heap exhaustion",
|
|
||||||
sampler_limit, reserved, sampler_heap_budget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extensions.sampler_filter_minmax && is_amd) {
|
if (extensions.sampler_filter_minmax && is_amd) {
|
||||||
// Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
|
// Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
|
||||||
if (!features.shader_float16_int8.shaderFloat16) {
|
if (!features.shader_float16_int8.shaderFloat16) {
|
||||||
@@ -1144,6 +1123,11 @@ bool Device::GetSuitability(bool requires_swapchain) {
|
|||||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_PROPERTIES_KHR;
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_PROPERTIES_KHR;
|
||||||
SetNext(next, properties.maintenance5);
|
SetNext(next, properties.maintenance5);
|
||||||
}
|
}
|
||||||
|
if (extensions.custom_border_color) {
|
||||||
|
properties.custom_border_color.sType =
|
||||||
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT;
|
||||||
|
SetNext(next, properties.custom_border_color);
|
||||||
|
}
|
||||||
|
|
||||||
// Perform the property fetch.
|
// Perform the property fetch.
|
||||||
physical.GetProperties2(properties2);
|
physical.GetProperties2(properties2);
|
||||||
@@ -1240,9 +1224,7 @@ void Device::RemoveUnsuitableExtensions() {
|
|||||||
// VK_EXT_border_color_swizzle
|
// VK_EXT_border_color_swizzle
|
||||||
if (extensions.border_color_swizzle) {
|
if (extensions.border_color_swizzle) {
|
||||||
extensions.border_color_swizzle =
|
extensions.border_color_swizzle =
|
||||||
extensions.custom_border_color &&
|
extensions.custom_border_color && features.border_color_swizzle.borderColorSwizzle;
|
||||||
features.border_color_swizzle.borderColorSwizzle &&
|
|
||||||
features.border_color_swizzle.borderColorSwizzleFromImage;
|
|
||||||
}
|
}
|
||||||
RemoveExtensionFeatureIfUnsuitable(extensions.border_color_swizzle,
|
RemoveExtensionFeatureIfUnsuitable(extensions.border_color_swizzle,
|
||||||
features.border_color_swizzle,
|
features.border_color_swizzle,
|
||||||
@@ -1404,6 +1386,11 @@ void Device::RemoveUnsuitableExtensions() {
|
|||||||
VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
|
VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VK_KHR_shader_quad_control
|
||||||
|
extensions.shader_quad_control = features.shader_quad_control.shaderQuadControl;
|
||||||
|
RemoveExtensionFeatureIfUnsuitable(extensions.shader_quad_control, features.shader_quad_control,
|
||||||
|
VK_KHR_SHADER_QUAD_CONTROL_EXTENSION_NAME);
|
||||||
|
|
||||||
// VK_KHR_workgroup_memory_explicit_layout
|
// VK_KHR_workgroup_memory_explicit_layout
|
||||||
extensions.workgroup_memory_explicit_layout =
|
extensions.workgroup_memory_explicit_layout =
|
||||||
features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout &&
|
features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout &&
|
||||||
@@ -1488,11 +1475,26 @@ void Device::SetupFamilies(VkSurfaceKHR surface) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<size_t> Device::GetSamplerHeapBudget() const {
|
bool Device::TryReserveCustomBorderColorSamplers(size_t count) const {
|
||||||
if (sampler_heap_budget == 0) {
|
const size_t limit = properties.custom_border_color.maxCustomBorderColorSamplers;
|
||||||
return std::nullopt;
|
if (limit == 0) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return sampler_heap_budget;
|
size_t used = custom_border_color_samplers_used.load(std::memory_order_relaxed);
|
||||||
|
while (used + count <= limit) {
|
||||||
|
if (custom_border_color_samplers_used.compare_exchange_weak(
|
||||||
|
used, used + count, std::memory_order_relaxed, std::memory_order_relaxed)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::ReleaseCustomBorderColorSamplers(size_t count) const {
|
||||||
|
if (count == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
custom_border_color_samplers_used.fetch_sub(count, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 Device::GetDeviceMemoryUsage() const {
|
u64 Device::GetDeviceMemoryUsage() const {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <span>
|
#include <span>
|
||||||
@@ -74,6 +75,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
|
|||||||
FEATURE(KHR, Maintenance6, MAINTENANCE_6, maintenance6) \
|
FEATURE(KHR, Maintenance6, MAINTENANCE_6, maintenance6) \
|
||||||
FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \
|
FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \
|
||||||
pipeline_executable_properties) \
|
pipeline_executable_properties) \
|
||||||
|
FEATURE(KHR, ShaderQuadControl, SHADER_QUAD_CONTROL, shader_quad_control) \
|
||||||
FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \
|
FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \
|
||||||
workgroup_memory_explicit_layout)
|
workgroup_memory_explicit_layout)
|
||||||
|
|
||||||
@@ -585,6 +587,11 @@ FN_MAX_LIMIT_LIST
|
|||||||
features.features.shaderInt16;
|
features.features.shaderInt16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the device supports VK_KHR_shader_quad_control.
|
||||||
|
bool IsKhrShaderQuadControlSupported() const {
|
||||||
|
return extensions.shader_quad_control && features.shader_quad_control.shaderQuadControl;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if the device supports VK_KHR_image_format_list.
|
/// Returns true if the device supports VK_KHR_image_format_list.
|
||||||
bool IsKhrImageFormatListSupported() const {
|
bool IsKhrImageFormatListSupported() const {
|
||||||
return extensions.image_format_list || instance_version >= VK_API_VERSION_1_2;
|
return extensions.image_format_list || instance_version >= VK_API_VERSION_1_2;
|
||||||
@@ -699,20 +706,18 @@ FN_MAX_LIMIT_LIST
|
|||||||
return features.transform_feedback.geometryStreams;
|
return features.transform_feedback.geometryStreams;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the device supports VK_EXT_custom_border_color.
|
/// Returns true if custom border colors can be created without a format.
|
||||||
bool IsExtCustomBorderColorSupported() const {
|
bool IsCustomBorderColorUsable() const {
|
||||||
return extensions.custom_border_color;
|
return extensions.custom_border_color &&
|
||||||
|
features.custom_border_color.customBorderColors &&
|
||||||
|
features.custom_border_color.customBorderColorWithoutFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if customBorderColors feature is available.
|
/// Takes budget for samplers carrying a custom border color, false when exhausted.
|
||||||
bool IsCustomBorderColorsSupported() const {
|
bool TryReserveCustomBorderColorSamplers(size_t count) const;
|
||||||
return features.custom_border_color.customBorderColors;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if customBorderColorWithoutFormat feature is available.
|
/// Gives back budget taken by TryReserveCustomBorderColorSamplers.
|
||||||
bool IsCustomBorderColorWithoutFormatSupported() const {
|
void ReleaseCustomBorderColorSamplers(size_t count) const;
|
||||||
return features.custom_border_color.customBorderColorWithoutFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the device supports VK_EXT_color_write_enable.
|
/// Returns true if the device supports VK_EXT_color_write_enable.
|
||||||
bool IsExtColorWriteEnableSupported() const {
|
bool IsExtColorWriteEnableSupported() const {
|
||||||
@@ -724,6 +729,12 @@ FN_MAX_LIMIT_LIST
|
|||||||
return extensions.border_color_swizzle;
|
return extensions.border_color_swizzle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if samplers must be carried with border color swizzle mapping.
|
||||||
|
bool NeedsBorderColorSwizzleMapping() const {
|
||||||
|
return extensions.border_color_swizzle &&
|
||||||
|
!features.border_color_swizzle.borderColorSwizzleFromImage;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if borderColorSwizzleFromImage is available.
|
/// Returns true if borderColorSwizzleFromImage is available.
|
||||||
bool IsBorderColorSwizzleFromImageSupported() const {
|
bool IsBorderColorSwizzleFromImageSupported() const {
|
||||||
return features.border_color_swizzle.borderColorSwizzleFromImage;
|
return features.border_color_swizzle.borderColorSwizzleFromImage;
|
||||||
@@ -919,8 +930,6 @@ FN_MAX_LIMIT_LIST
|
|||||||
return has_broken_parallel_compiling;
|
return has_broken_parallel_compiling;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<size_t> GetSamplerHeapBudget() const;
|
|
||||||
|
|
||||||
/// Returns the vendor name reported from Vulkan.
|
/// Returns the vendor name reported from Vulkan.
|
||||||
std::string_view GetVendorName() const {
|
std::string_view GetVendorName() const {
|
||||||
return properties.driver.driverName;
|
return properties.driver.driverName;
|
||||||
@@ -1173,6 +1182,7 @@ private:
|
|||||||
VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
|
VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
|
||||||
VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{};
|
VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{};
|
||||||
VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{};
|
VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{};
|
||||||
|
VkPhysicalDeviceCustomBorderColorPropertiesEXT custom_border_color{};
|
||||||
|
|
||||||
VkPhysicalDeviceProperties properties{};
|
VkPhysicalDeviceProperties properties{};
|
||||||
};
|
};
|
||||||
@@ -1211,7 +1221,7 @@ private:
|
|||||||
bool dynamic_state3_alpha_to_coverage{};
|
bool dynamic_state3_alpha_to_coverage{};
|
||||||
bool dynamic_state3_alpha_to_one{};
|
bool dynamic_state3_alpha_to_one{};
|
||||||
bool supports_conditional_barriers{}; ///< Allows barriers in conditional control flow.
|
bool supports_conditional_barriers{}; ///< Allows barriers in conditional control flow.
|
||||||
size_t sampler_heap_budget{}; ///< Sampler budget for buggy drivers (0 = unlimited).
|
mutable std::atomic<size_t> custom_border_color_samplers_used{};
|
||||||
u64 device_access_memory{}; ///< Total size of device local memory in bytes.
|
u64 device_access_memory{}; ///< Total size of device local memory in bytes.
|
||||||
u32 sets_per_pool{}; ///< Sets per Description Pool
|
u32 sets_per_pool{}; ///< Sets per Description Pool
|
||||||
NvidiaArchitecture nvidia_arch{NvidiaArchitecture::Arch_AmpereOrNewer};
|
NvidiaArchitecture nvidia_arch{NvidiaArchitecture::Arch_AmpereOrNewer};
|
||||||
|
|||||||
Reference in New Issue
Block a user