mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-29 09:58:05 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 22ec7e06e4 | |||
| 119291dc77 | |||
| f7dbff2157 | |||
| b859d7fdf4 | |||
| f635827fa6 | |||
| 243453c172 | |||
| 39158b67a7 | |||
| 50cb8fd1c9 |
+27
-11
@@ -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);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -308,13 +324,13 @@ bool UseOptimizedPath() {
|
|||||||
```cpp
|
```cpp
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Experimental implementation
|
// Experimental implementation
|
||||||
ExperimentalImplementation();
|
ExperimentalImplementation();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ object NativeLibrary {
|
|||||||
|
|
||||||
external fun refreshThreadPolicies()
|
external fun refreshThreadPolicies()
|
||||||
|
|
||||||
external fun getDebugKnobAt(index: Int): Boolean
|
external fun GetDebugKnobAt(index: Int): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the current speed limit to the configured turbo speed.
|
* Set the current speed limit to the configured turbo speed.
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ object Settings {
|
|||||||
fun getPlayerString(player: Int): String =
|
fun getPlayerString(player: Int): String =
|
||||||
YuzuApplication.appContext.getString(R.string.preferences_player, player)
|
YuzuApplication.appContext.getString(R.string.preferences_player, player)
|
||||||
|
|
||||||
fun getDebugKnobAt(index: Int): Boolean {
|
fun GetDebugKnobAt(index: Int): Boolean {
|
||||||
return org.yuzu.yuzu_emu.NativeLibrary.getDebugKnobAt(index)
|
return org.yuzu.yuzu_emu.NativeLibrary.GetDebugKnobAt(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
|
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
|
||||||
|
|||||||
+2
-3
@@ -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)
|
||||||
@@ -29,4 +28,4 @@ enum class ShortSetting(override val key: String) : AbstractShortSetting {
|
|||||||
override fun getValueAsString(needsGlobal: Boolean): String = getShort(needsGlobal).toString()
|
override fun getValueAsString(needsGlobal: Boolean): String = getShort(needsGlobal).toString()
|
||||||
|
|
||||||
override fun reset() = NativeConfig.setShort(key, defaultValue)
|
override fun reset() = NativeConfig.setShort(key, defaultValue)
|
||||||
}
|
}
|
||||||
|
|||||||
+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.
|
||||||
|
|
||||||
|
|||||||
@@ -146,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{};
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ namespace VideoCommon {
|
|||||||
|
|
||||||
enum class BufferFlagBits {
|
enum class BufferFlagBits {
|
||||||
Picked = 1 << 0,
|
Picked = 1 << 0,
|
||||||
|
CachedWrites = 1 << 1,
|
||||||
|
PreemtiveDownload = 1 << 2,
|
||||||
};
|
};
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(BufferFlagBits)
|
DECLARE_ENUM_FLAG_OPERATORS(BufferFlagBits)
|
||||||
|
|
||||||
@@ -56,6 +58,15 @@ public:
|
|||||||
flags |= BufferFlagBits::Picked;
|
flags |= BufferFlagBits::Picked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MarkPreemtiveDownload() noexcept {
|
||||||
|
flags |= BufferFlagBits::PreemtiveDownload;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unmark buffer as picked
|
||||||
|
void Unpick() noexcept {
|
||||||
|
flags &= ~BufferFlagBits::Picked;
|
||||||
|
}
|
||||||
|
|
||||||
/// Increases the likeliness of this being a stream buffer
|
/// Increases the likeliness of this being a stream buffer
|
||||||
void IncreaseStreamScore(int score) noexcept {
|
void IncreaseStreamScore(int score) noexcept {
|
||||||
stream_score += score;
|
stream_score += score;
|
||||||
@@ -76,6 +87,15 @@ public:
|
|||||||
return True(flags & BufferFlagBits::Picked);
|
return True(flags & BufferFlagBits::Picked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true when the buffer has pending cached writes
|
||||||
|
[[nodiscard]] bool HasCachedWrites() const noexcept {
|
||||||
|
return True(flags & BufferFlagBits::CachedWrites);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPreemtiveDownload() const noexcept {
|
||||||
|
return True(flags & BufferFlagBits::PreemtiveDownload);
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the base CPU address of the buffer
|
/// Returns the base CPU address of the buffer
|
||||||
[[nodiscard]] VAddr CpuAddr() const noexcept {
|
[[nodiscard]] VAddr CpuAddr() const noexcept {
|
||||||
return cpu_addr;
|
return cpu_addr;
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <limits>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
|
||||||
@@ -122,6 +121,25 @@ void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
|
|||||||
memory_tracker.MarkRegionAsCpuModified(device_addr, size);
|
memory_tracker.MarkRegionAsCpuModified(device_addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class P>
|
||||||
|
void BufferCache<P>::CachedWriteMemory(DAddr device_addr, u64 size) {
|
||||||
|
const bool is_dirty = IsRegionRegistered(device_addr, size);
|
||||||
|
if (!is_dirty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DAddr aligned_start = Common::AlignDown(device_addr, DEVICE_PAGESIZE);
|
||||||
|
DAddr aligned_end = Common::AlignUp(device_addr + size, DEVICE_PAGESIZE);
|
||||||
|
if (!IsRegionGpuModified(aligned_start, aligned_end - aligned_start)) {
|
||||||
|
WriteMemory(device_addr, size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp_buffer.resize_destructive(size);
|
||||||
|
device_memory.ReadBlockUnsafe(device_addr, tmp_buffer.data(), size);
|
||||||
|
|
||||||
|
InlineMemoryImplementation(device_addr, size, tmp_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
bool BufferCache<P>::OnCPUWrite(DAddr device_addr, u64 size) {
|
bool BufferCache<P>::OnCPUWrite(DAddr device_addr, u64 size) {
|
||||||
const bool is_dirty = IsRegionRegistered(device_addr, size);
|
const bool is_dirty = IsRegionRegistered(device_addr, size);
|
||||||
@@ -404,7 +422,7 @@ void BufferCache<P>::UnbindGraphicsStorageBuffers(size_t stage) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index,
|
bool BufferCache<P>::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index,
|
||||||
u32 cbuf_offset, bool is_written) {
|
u32 cbuf_offset, bool is_written) {
|
||||||
const bool already_enabled =
|
const bool already_enabled =
|
||||||
((channel_state->enabled_storage_buffers[stage] >> ssbo_index) & 1U) != 0;
|
((channel_state->enabled_storage_buffers[stage] >> ssbo_index) & 1U) != 0;
|
||||||
@@ -415,7 +433,7 @@ void BufferCache<P>::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index,
|
|||||||
LOG_WARNING(HW_GPU,
|
LOG_WARNING(HW_GPU,
|
||||||
"Skipping graphics storage buffer {} due to driver limit {}",
|
"Skipping graphics storage buffer {} due to driver limit {}",
|
||||||
ssbo_index, max_bindings);
|
ssbo_index, max_bindings);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -431,6 +449,7 @@ void BufferCache<P>::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index,
|
|||||||
const GPUVAddr ssbo_addr = cbufs.const_buffers[cbuf_index].address + cbuf_offset;
|
const GPUVAddr ssbo_addr = cbufs.const_buffers[cbuf_index].address + cbuf_offset;
|
||||||
channel_state->storage_buffers[stage][ssbo_index] =
|
channel_state->storage_buffers[stage][ssbo_index] =
|
||||||
StorageBufferBinding(ssbo_addr, cbuf_index, is_written);
|
StorageBufferBinding(ssbo_addr, cbuf_index, is_written);
|
||||||
|
return (channel_state->storage_buffers[stage][ssbo_index].buffer_id != NULL_BUFFER_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
@@ -743,6 +762,16 @@ void BufferCache<P>::BindHostIndexBuffer() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class P>
|
||||||
|
void BufferCache<P>::BindHostVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size,
|
||||||
|
u32 stride) {
|
||||||
|
if constexpr (IS_OPENGL) {
|
||||||
|
runtime.BindVertexBuffer(index, buffer, offset, size, stride);
|
||||||
|
} else {
|
||||||
|
runtime.BindVertexBuffer(index, buffer.Handle(), offset, size, stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
Binding& BufferCache<P>::VertexBufferSlot(u32 index) {
|
Binding& BufferCache<P>::VertexBufferSlot(u32 index) {
|
||||||
ASSERT(index < NUM_VERTEX_BUFFERS);
|
ASSERT(index < NUM_VERTEX_BUFFERS);
|
||||||
@@ -1222,14 +1251,9 @@ void BufferCache<P>::UpdateIndexBuffer() {
|
|||||||
const GPUVAddr gpu_addr_begin = index_buffer_ref.StartAddress();
|
const GPUVAddr gpu_addr_begin = index_buffer_ref.StartAddress();
|
||||||
const GPUVAddr gpu_addr_end = index_buffer_ref.EndAddress();
|
const GPUVAddr gpu_addr_end = index_buffer_ref.EndAddress();
|
||||||
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
|
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
|
||||||
u64 address_size = 0;
|
const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
|
||||||
if (gpu_addr_end > gpu_addr_begin) {
|
const u32 draw_size = (index_buffer_ref.count + index_buffer_ref.first) * u32(index_buffer_ref.FormatSizeInBytes());
|
||||||
address_size = (std::min)(gpu_addr_end - gpu_addr_begin,
|
const u32 size = (std::min)(address_size, draw_size);
|
||||||
u64{(std::numeric_limits<u32>::max)()});
|
|
||||||
}
|
|
||||||
const u64 draw_size = (u64{index_buffer_ref.count} + u64{index_buffer_ref.first}) *
|
|
||||||
u64{index_buffer_ref.FormatSizeInBytes()};
|
|
||||||
const u32 size = static_cast<u32>((std::min)(address_size, draw_size));
|
|
||||||
if (size == 0 || !device_addr) {
|
if (size == 0 || !device_addr) {
|
||||||
channel_state->index_buffer = NULL_BINDING;
|
channel_state->index_buffer = NULL_BINDING;
|
||||||
return;
|
return;
|
||||||
@@ -1244,20 +1268,6 @@ void BufferCache<P>::UpdateIndexBuffer() {
|
|||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::UpdateVertexBuffers() {
|
void BufferCache<P>::UpdateVertexBuffers() {
|
||||||
auto& flags = maxwell3d->dirty.flags;
|
auto& flags = maxwell3d->dirty.flags;
|
||||||
const u32 base_instance = maxwell3d->draw_manager.draw_state.base_instance;
|
|
||||||
if (draw_instance_count != last_draw_instance_count ||
|
|
||||||
base_instance != last_draw_base_instance) {
|
|
||||||
last_draw_instance_count = draw_instance_count;
|
|
||||||
last_draw_base_instance = base_instance;
|
|
||||||
const auto& instances = maxwell3d->regs.vertex_stream_instances;
|
|
||||||
for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
|
|
||||||
if (!instances.IsInstancingEnabled(index)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
flags[Dirty::VertexBuffer0 + index] = true;
|
|
||||||
flags[Dirty::VertexBuffers] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!maxwell3d->dirty.flags[Dirty::VertexBuffers]) {
|
if (!maxwell3d->dirty.flags[Dirty::VertexBuffers]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1278,37 +1288,15 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
|
|||||||
const GPUVAddr gpu_addr_begin = array.Address();
|
const GPUVAddr gpu_addr_begin = array.Address();
|
||||||
const GPUVAddr gpu_addr_end = limit.Address() + 1;
|
const GPUVAddr gpu_addr_end = limit.Address() + 1;
|
||||||
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
|
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
|
||||||
if (array.enable == 0 || !device_addr || gpu_addr_end <= gpu_addr_begin) {
|
const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
|
||||||
|
u32 size = address_size; // TODO: Analyze stride and number of vertices
|
||||||
|
if (array.enable == 0 || size == 0 || !device_addr) {
|
||||||
channel_state->vertex_buffers[index] = NULL_BINDING;
|
channel_state->vertex_buffers[index] = NULL_BINDING;
|
||||||
UpdateVertexBufferSlot(index, NULL_BINDING);
|
UpdateVertexBufferSlot(index, NULL_BINDING);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// TODO: Analyze stride and number of vertices
|
if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end) || size >= 64_MiB) {
|
||||||
constexpr u64 implausible_size = 64_MiB;
|
size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size));
|
||||||
u64 address_size = gpu_addr_end - gpu_addr_begin;
|
|
||||||
if (address_size > u64{(std::numeric_limits<u32>::max)()}) {
|
|
||||||
address_size = implausible_size;
|
|
||||||
}
|
|
||||||
const bool is_instanced = maxwell3d->regs.vertex_stream_instances.IsInstancingEnabled(index);
|
|
||||||
const u64 stride = static_cast<u64>(array.stride);
|
|
||||||
if (is_instanced && stride != 0 && draw_instance_count != 0) {
|
|
||||||
const u64 base_instance = static_cast<u64>(maxwell3d->draw_manager.draw_state.base_instance);
|
|
||||||
u64 elements = base_instance + 1;
|
|
||||||
if (array.frequency != 0) {
|
|
||||||
elements = (base_instance + static_cast<u64>(draw_instance_count) - 1) /
|
|
||||||
static_cast<u64>(array.frequency) +
|
|
||||||
1;
|
|
||||||
}
|
|
||||||
address_size = (std::min)(address_size, (elements + 1) * stride);
|
|
||||||
}
|
|
||||||
if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end) || address_size >= implausible_size) {
|
|
||||||
address_size = gpu_memory->MaxContinuousRange(gpu_addr_begin, address_size);
|
|
||||||
}
|
|
||||||
const u32 size = static_cast<u32>(address_size);
|
|
||||||
if (size == 0) {
|
|
||||||
channel_state->vertex_buffers[index] = NULL_BINDING;
|
|
||||||
UpdateVertexBufferSlot(index, NULL_BINDING);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
const BufferId buffer_id = FindBuffer(*device_addr, size);
|
const BufferId buffer_id = FindBuffer(*device_addr, size);
|
||||||
const Binding binding{
|
const Binding binding{
|
||||||
@@ -1590,10 +1578,9 @@ template <class P>
|
|||||||
BufferId BufferCache<P>::CreateBuffer(DAddr device_addr, u32 wanted_size) {
|
BufferId BufferCache<P>::CreateBuffer(DAddr device_addr, u32 wanted_size) {
|
||||||
DAddr device_addr_end = Common::AlignUp(device_addr + wanted_size, CACHING_PAGESIZE);
|
DAddr device_addr_end = Common::AlignUp(device_addr + wanted_size, CACHING_PAGESIZE);
|
||||||
device_addr = Common::AlignDown(device_addr, CACHING_PAGESIZE);
|
device_addr = Common::AlignDown(device_addr, CACHING_PAGESIZE);
|
||||||
constexpr u64 max_buffer_size = u64{(std::numeric_limits<u32>::max)()};
|
wanted_size = static_cast<u32>(device_addr_end - device_addr);
|
||||||
wanted_size = static_cast<u32>((std::min)(device_addr_end - device_addr, max_buffer_size));
|
|
||||||
const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size);
|
const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size);
|
||||||
const u32 size = static_cast<u32>((std::min)(overlap.end - overlap.begin, max_buffer_size));
|
const u32 size = static_cast<u32>(overlap.end - overlap.begin);
|
||||||
const BufferId new_buffer_id = slot_buffers.insert(runtime, overlap.begin, size);
|
const BufferId new_buffer_id = slot_buffers.insert(runtime, overlap.begin, size);
|
||||||
auto& new_buffer = slot_buffers[new_buffer_id];
|
auto& new_buffer = slot_buffers[new_buffer_id];
|
||||||
const size_t size_bytes = new_buffer.SizeBytes();
|
const size_t size_bytes = new_buffer.SizeBytes();
|
||||||
|
|||||||
@@ -217,6 +217,8 @@ public:
|
|||||||
|
|
||||||
void WriteMemory(DAddr device_addr, u64 size);
|
void WriteMemory(DAddr device_addr, u64 size);
|
||||||
|
|
||||||
|
void CachedWriteMemory(DAddr device_addr, u64 size);
|
||||||
|
|
||||||
bool OnCPUWrite(DAddr device_addr, u64 size);
|
bool OnCPUWrite(DAddr device_addr, u64 size);
|
||||||
|
|
||||||
void DownloadMemory(DAddr device_addr, u64 size);
|
void DownloadMemory(DAddr device_addr, u64 size);
|
||||||
@@ -246,7 +248,7 @@ public:
|
|||||||
|
|
||||||
void UnbindGraphicsStorageBuffers(size_t stage);
|
void UnbindGraphicsStorageBuffers(size_t stage);
|
||||||
|
|
||||||
void BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset,
|
bool BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset,
|
||||||
bool is_written);
|
bool is_written);
|
||||||
|
|
||||||
void UnbindGraphicsTextureBuffers(size_t stage);
|
void UnbindGraphicsTextureBuffers(size_t stage);
|
||||||
@@ -307,10 +309,6 @@ public:
|
|||||||
current_draw_indirect = current_draw_indirect_;
|
current_draw_indirect = current_draw_indirect_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetDrawInstanceCount(u32 draw_instance_count_) {
|
|
||||||
draw_instance_count = draw_instance_count_;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectCount();
|
[[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectCount();
|
||||||
|
|
||||||
[[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectBuffer();
|
[[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectBuffer();
|
||||||
@@ -378,6 +376,8 @@ private:
|
|||||||
|
|
||||||
void BindHostTransformFeedbackBuffers();
|
void BindHostTransformFeedbackBuffers();
|
||||||
|
|
||||||
|
void BindHostVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride);
|
||||||
|
|
||||||
void BindHostComputeUniformBuffers();
|
void BindHostComputeUniformBuffers();
|
||||||
|
|
||||||
void BindHostComputeStorageBuffers();
|
void BindHostComputeStorageBuffers();
|
||||||
@@ -484,10 +484,6 @@ private:
|
|||||||
|
|
||||||
const Tegra::Engines::Maxwell3D::DrawManager::IndirectParams* current_draw_indirect{};
|
const Tegra::Engines::Maxwell3D::DrawManager::IndirectParams* current_draw_indirect{};
|
||||||
|
|
||||||
u32 draw_instance_count = 0;
|
|
||||||
u32 last_draw_instance_count = 0;
|
|
||||||
u32 last_draw_base_instance = 0;
|
|
||||||
|
|
||||||
u32 last_index_count = 0;
|
u32 last_index_count = 0;
|
||||||
|
|
||||||
u32 enabled_vertex_buffers_mask = 0;
|
u32 enabled_vertex_buffers_mask = 0;
|
||||||
|
|||||||
@@ -206,40 +206,6 @@ foreach(VARIANT IN ITEMS ${SHADER_TYPE_VARIANTS})
|
|||||||
set(SHADER_HEADERS ${SHADER_HEADERS} ${VARIANT_HEADER_FILE})
|
set(SHADER_HEADERS ${SHADER_HEADERS} ${VARIANT_HEADER_FILE})
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
set(SHADER_DEFINE_VARIANTS
|
|
||||||
"block_linear_unswizzle_2d.comp|nonarrow|HAS_EXTENDED_TYPES=0"
|
|
||||||
"pitch_unswizzle.comp|nonarrow|HAS_EXTENDED_TYPES=0"
|
|
||||||
"block_linear_unswizzle_3d.comp|nonarrow|HAS_EXTENDED_TYPES=0"
|
|
||||||
)
|
|
||||||
|
|
||||||
foreach(VARIANT IN ITEMS ${SHADER_DEFINE_VARIANTS})
|
|
||||||
string(REPLACE "|" ";" VARIANT_PARTS ${VARIANT})
|
|
||||||
list(GET VARIANT_PARTS 0 VARIANT_FILENAME)
|
|
||||||
list(GET VARIANT_PARTS 1 VARIANT_SUFFIX)
|
|
||||||
list(GET VARIANT_PARTS 2 VARIANT_DEFINE)
|
|
||||||
|
|
||||||
set(VARIANT_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${VARIANT_FILENAME})
|
|
||||||
get_filename_component(VARIANT_STEM ${VARIANT_FILENAME} NAME_WE)
|
|
||||||
get_filename_component(VARIANT_EXT ${VARIANT_FILENAME} EXT)
|
|
||||||
string(REPLACE "." "" VARIANT_EXT ${VARIANT_EXT})
|
|
||||||
set(VARIANT_NAME ${VARIANT_STEM}_${VARIANT_SUFFIX}_${VARIANT_EXT})
|
|
||||||
|
|
||||||
string(TOUPPER ${VARIANT_NAME}_SPV VARIANT_VARIABLE_NAME)
|
|
||||||
set(VARIANT_HEADER_FILE ${SHADER_DIR}/${VARIANT_NAME}_spv.h)
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT
|
|
||||||
${VARIANT_HEADER_FILE}
|
|
||||||
COMMAND
|
|
||||||
${GLSLANGVALIDATOR} -V ${QUIET_FLAG} -I"${FIDELITYFX_INCLUDE_DIR}" ${GLSL_FLAGS}
|
|
||||||
-D${VARIANT_DEFINE}
|
|
||||||
--variable-name ${VARIANT_VARIABLE_NAME} -o ${VARIANT_HEADER_FILE} ${VARIANT_SOURCE}
|
|
||||||
--target-env ${SPIR_V_VERSION}
|
|
||||||
MAIN_DEPENDENCY
|
|
||||||
${VARIANT_SOURCE}
|
|
||||||
)
|
|
||||||
set(SHADER_HEADERS ${SHADER_HEADERS} ${VARIANT_HEADER_FILE})
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
foreach(FILEPATH IN ITEMS ${FIDELITYFX_FILES})
|
foreach(FILEPATH IN ITEMS ${FIDELITYFX_FILES})
|
||||||
get_filename_component(FILENAME ${FILEPATH} NAME)
|
get_filename_component(FILENAME ${FILEPATH} NAME)
|
||||||
string(REPLACE "." "_" HEADER_NAME ${FILENAME})
|
string(REPLACE "." "_" HEADER_NAME ${FILENAME})
|
||||||
|
|||||||
@@ -5,13 +5,9 @@
|
|||||||
|
|
||||||
#ifdef VULKAN
|
#ifdef VULKAN
|
||||||
|
|
||||||
#ifndef HAS_EXTENDED_TYPES
|
|
||||||
#define HAS_EXTENDED_TYPES 1
|
|
||||||
#endif
|
|
||||||
#if HAS_EXTENDED_TYPES
|
|
||||||
#extension GL_EXT_shader_16bit_storage : require
|
#extension GL_EXT_shader_16bit_storage : require
|
||||||
#extension GL_EXT_shader_8bit_storage : require
|
#extension GL_EXT_shader_8bit_storage : require
|
||||||
#endif
|
#define HAS_EXTENDED_TYPES 1
|
||||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||||
#define END_PUSH_CONSTANTS };
|
#define END_PUSH_CONSTANTS };
|
||||||
#define UNIFORM(n)
|
#define UNIFORM(n)
|
||||||
|
|||||||
@@ -5,13 +5,9 @@
|
|||||||
|
|
||||||
#ifdef VULKAN
|
#ifdef VULKAN
|
||||||
|
|
||||||
#ifndef HAS_EXTENDED_TYPES
|
|
||||||
#define HAS_EXTENDED_TYPES 1
|
|
||||||
#endif
|
|
||||||
#if HAS_EXTENDED_TYPES
|
|
||||||
#extension GL_EXT_shader_16bit_storage : require
|
#extension GL_EXT_shader_16bit_storage : require
|
||||||
#extension GL_EXT_shader_8bit_storage : require
|
#extension GL_EXT_shader_8bit_storage : require
|
||||||
#endif
|
#define HAS_EXTENDED_TYPES 1
|
||||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||||
#define END_PUSH_CONSTANTS };
|
#define END_PUSH_CONSTANTS };
|
||||||
#define UNIFORM(n)
|
#define UNIFORM(n)
|
||||||
|
|||||||
@@ -5,13 +5,9 @@
|
|||||||
|
|
||||||
#ifdef VULKAN
|
#ifdef VULKAN
|
||||||
|
|
||||||
#ifndef HAS_EXTENDED_TYPES
|
|
||||||
#define HAS_EXTENDED_TYPES 1
|
|
||||||
#endif
|
|
||||||
#if HAS_EXTENDED_TYPES
|
|
||||||
#extension GL_EXT_shader_16bit_storage : require
|
#extension GL_EXT_shader_16bit_storage : require
|
||||||
#extension GL_EXT_shader_8bit_storage : require
|
#extension GL_EXT_shader_8bit_storage : require
|
||||||
#endif
|
#define HAS_EXTENDED_TYPES 1
|
||||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||||
#define END_PUSH_CONSTANTS };
|
#define END_PUSH_CONSTANTS };
|
||||||
#define UNIFORM(n)
|
#define UNIFORM(n)
|
||||||
|
|||||||
@@ -226,6 +226,22 @@ void BufferCacheRuntime::BindIndexBuffer(Buffer& buffer, u32 offset, u32 size) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BufferCacheRuntime::BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size,
|
||||||
|
u32 stride) {
|
||||||
|
if (index >= max_attributes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (has_unified_vertex_buffers) {
|
||||||
|
buffer.MakeResident(GL_READ_ONLY);
|
||||||
|
glBindVertexBuffer(index, 0, 0, static_cast<GLsizei>(stride));
|
||||||
|
glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, index,
|
||||||
|
buffer.HostGpuAddr() + offset, static_cast<GLsizeiptr>(size));
|
||||||
|
} else {
|
||||||
|
glBindVertexBuffer(index, buffer.Handle(), static_cast<GLintptr>(offset),
|
||||||
|
static_cast<GLsizei>(stride));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
|
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
|
||||||
// TODO: Should HostBindings provide the correct runtime types to avoid these transforms?
|
// TODO: Should HostBindings provide the correct runtime types to avoid these transforms?
|
||||||
std::array<GLuint, 32> buffer_handles;
|
std::array<GLuint, 32> buffer_handles;
|
||||||
|
|||||||
@@ -99,6 +99,8 @@ public:
|
|||||||
|
|
||||||
void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size);
|
void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size);
|
||||||
|
|
||||||
|
void BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride);
|
||||||
|
|
||||||
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
|
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
|
||||||
|
|
||||||
void BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size);
|
void BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size);
|
||||||
|
|||||||
@@ -259,7 +259,6 @@ void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
|
void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
|
||||||
buffer_cache.SetDrawInstanceCount(instance_count);
|
|
||||||
PrepareDraw(is_indexed, [this, is_indexed, instance_count](GLenum primitive_mode) {
|
PrepareDraw(is_indexed, [this, is_indexed, instance_count](GLenum primitive_mode) {
|
||||||
const auto& draw_state = maxwell3d->draw_manager.draw_state;
|
const auto& draw_state = maxwell3d->draw_manager.draw_state;
|
||||||
const GLuint base_instance = GLuint(draw_state.base_instance);
|
const GLuint base_instance = GLuint(draw_state.base_instance);
|
||||||
@@ -305,7 +304,6 @@ void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
|
|||||||
void RasterizerOpenGL::DrawIndirect() {
|
void RasterizerOpenGL::DrawIndirect() {
|
||||||
const auto& params = maxwell3d->draw_manager.indirect_state;
|
const auto& params = maxwell3d->draw_manager.indirect_state;
|
||||||
buffer_cache.SetDrawIndirect(¶ms);
|
buffer_cache.SetDrawIndirect(¶ms);
|
||||||
buffer_cache.SetDrawInstanceCount(0);
|
|
||||||
PrepareDraw(params.is_indexed, [this, ¶ms](GLenum primitive_mode) {
|
PrepareDraw(params.is_indexed, [this, ¶ms](GLenum primitive_mode) {
|
||||||
if (params.is_byte_count) {
|
if (params.is_byte_count) {
|
||||||
const GPUVAddr tfb_object_base_addr = params.indirect_start_address - 4U;
|
const GPUVAddr tfb_object_base_addr = params.indirect_start_address - 4U;
|
||||||
|
|||||||
@@ -585,6 +585,29 @@ void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 fir
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride) {
|
||||||
|
if (index >= device.GetMaxVertexInputBindings()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (device.IsExtExtendedDynamicStateSupported()) {
|
||||||
|
scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) {
|
||||||
|
const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0;
|
||||||
|
const VkDeviceSize vk_size = buffer != VK_NULL_HANDLE ? size : VK_WHOLE_SIZE;
|
||||||
|
const VkDeviceSize vk_stride = stride;
|
||||||
|
cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, &vk_stride);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (!device.HasNullDescriptor() && buffer == VK_NULL_HANDLE) {
|
||||||
|
ReserveNullBuffer();
|
||||||
|
buffer = *null_buffer;
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
scheduler.Record([index, buffer, offset](vk::CommandBuffer cmdbuf) {
|
||||||
|
cmdbuf.BindVertexBuffer(index, buffer, offset);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
|
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
|
||||||
boost::container::static_vector<VkBuffer, VideoCommon::NUM_VERTEX_BUFFERS> buffer_handles(bindings.buffers.size());
|
boost::container::static_vector<VkBuffer, VideoCommon::NUM_VERTEX_BUFFERS> buffer_handles(bindings.buffers.size());
|
||||||
for (u32 i = 0; i < bindings.buffers.size(); ++i) {
|
for (u32 i = 0; i < bindings.buffers.size(); ++i) {
|
||||||
|
|||||||
@@ -138,6 +138,8 @@ public:
|
|||||||
|
|
||||||
void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count);
|
void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count);
|
||||||
|
|
||||||
|
void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride);
|
||||||
|
|
||||||
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
|
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
|
||||||
|
|
||||||
void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size);
|
void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size);
|
||||||
|
|||||||
@@ -22,13 +22,7 @@
|
|||||||
#include "video_core/host_shaders/resolve_conditional_render_comp_spv.h"
|
#include "video_core/host_shaders/resolve_conditional_render_comp_spv.h"
|
||||||
#include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h"
|
#include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h"
|
||||||
#include "video_core/host_shaders/vulkan_uint8_comp_spv.h"
|
#include "video_core/host_shaders/vulkan_uint8_comp_spv.h"
|
||||||
#include "video_core/host_shaders/block_linear_unswizzle_2d_comp_spv.h"
|
|
||||||
#include "video_core/host_shaders/block_linear_unswizzle_2d_nonarrow_comp_spv.h"
|
|
||||||
#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h"
|
#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h"
|
||||||
#include "video_core/host_shaders/block_linear_unswizzle_3d_comp_spv.h"
|
|
||||||
#include "video_core/host_shaders/block_linear_unswizzle_3d_nonarrow_comp_spv.h"
|
|
||||||
#include "video_core/host_shaders/pitch_unswizzle_comp_spv.h"
|
|
||||||
#include "video_core/host_shaders/pitch_unswizzle_nonarrow_comp_spv.h"
|
|
||||||
#include "video_core/renderer_vulkan/vk_compute_pass.h"
|
#include "video_core/renderer_vulkan/vk_compute_pass.h"
|
||||||
#include "video_core/surface.h"
|
#include "video_core/surface.h"
|
||||||
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||||
@@ -878,291 +872,4 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
constexpr u32 UNSWIZZLE_BINDING_INPUT_BUFFER = 0;
|
|
||||||
constexpr u32 UNSWIZZLE_BINDING_OUTPUT_IMAGE = 1;
|
|
||||||
constexpr size_t UNSWIZZLE_NUM_BINDINGS = 2;
|
|
||||||
|
|
||||||
constexpr std::array<VkDescriptorSetLayoutBinding, UNSWIZZLE_NUM_BINDINGS>
|
|
||||||
UNSWIZZLE_DESCRIPTOR_SET_BINDINGS{{
|
|
||||||
{
|
|
||||||
.binding = UNSWIZZLE_BINDING_INPUT_BUFFER,
|
|
||||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
|
||||||
.pImmutableSamplers = nullptr,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.binding = UNSWIZZLE_BINDING_OUTPUT_IMAGE,
|
|
||||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
|
||||||
.pImmutableSamplers = nullptr,
|
|
||||||
},
|
|
||||||
}};
|
|
||||||
|
|
||||||
constexpr std::array<VkDescriptorUpdateTemplateEntry, UNSWIZZLE_NUM_BINDINGS>
|
|
||||||
UNSWIZZLE_DESCRIPTOR_UPDATE_TEMPLATE{{
|
|
||||||
{
|
|
||||||
.dstBinding = UNSWIZZLE_BINDING_INPUT_BUFFER,
|
|
||||||
.dstArrayElement = 0,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
|
||||||
.offset = UNSWIZZLE_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
|
||||||
.stride = sizeof(DescriptorUpdateEntry),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.dstBinding = UNSWIZZLE_BINDING_OUTPUT_IMAGE,
|
|
||||||
.dstArrayElement = 0,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
|
||||||
.offset = UNSWIZZLE_BINDING_OUTPUT_IMAGE * sizeof(DescriptorUpdateEntry),
|
|
||||||
.stride = sizeof(DescriptorUpdateEntry),
|
|
||||||
},
|
|
||||||
}};
|
|
||||||
|
|
||||||
constexpr DescriptorBankInfo UNSWIZZLE_BANK_INFO{
|
|
||||||
.uniform_buffers = 0,
|
|
||||||
.storage_buffers = 1,
|
|
||||||
.texture_buffers = 0,
|
|
||||||
.image_buffers = 0,
|
|
||||||
.textures = 0,
|
|
||||||
.images = 1,
|
|
||||||
.score = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
[[nodiscard]] std::span<const u32> UnswizzleSpv(const Device& device,
|
|
||||||
std::span<const u32> extended,
|
|
||||||
std::span<const u32> narrow) {
|
|
||||||
if (device.IsStorageBuffer8BitAccessSupported() &&
|
|
||||||
device.IsStorageBuffer16BitAccessSupported()) {
|
|
||||||
return extended;
|
|
||||||
}
|
|
||||||
return narrow;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PitchUnswizzlePushConstants {
|
|
||||||
alignas(8) std::array<u32, 2> origin;
|
|
||||||
alignas(8) std::array<s32, 2> destination;
|
|
||||||
u32 bytes_per_block;
|
|
||||||
u32 pitch;
|
|
||||||
};
|
|
||||||
|
|
||||||
void RecordUnswizzleEntryBarrier(Scheduler& scheduler, VkPipeline vk_pipeline, VkImage vk_image,
|
|
||||||
VkImageAspectFlags aspect_mask, bool is_initialized) {
|
|
||||||
scheduler.Record([vk_pipeline, vk_image, aspect_mask,
|
|
||||||
is_initialized](vk::CommandBuffer cmdbuf) {
|
|
||||||
VkAccessFlags src_access = VK_ACCESS_NONE;
|
|
||||||
VkImageLayout old_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
||||||
if (is_initialized) {
|
|
||||||
src_access = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT |
|
|
||||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
|
||||||
old_layout = VK_IMAGE_LAYOUT_GENERAL;
|
|
||||||
}
|
|
||||||
const VkImageMemoryBarrier image_barrier{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.srcAccessMask = src_access,
|
|
||||||
.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
|
||||||
.oldLayout = old_layout,
|
|
||||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.image = vk_image,
|
|
||||||
.subresourceRange{
|
|
||||||
.aspectMask = aspect_mask,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
VkPipelineStageFlags src_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
|
||||||
if (is_initialized) {
|
|
||||||
src_stage = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER;
|
|
||||||
}
|
|
||||||
cmdbuf.PipelineBarrier(src_stage, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier);
|
|
||||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void RecordUnswizzleExitBarrier(Scheduler& scheduler, VkImage vk_image,
|
|
||||||
VkImageAspectFlags aspect_mask) {
|
|
||||||
scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) {
|
|
||||||
const VkImageMemoryBarrier image_barrier{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
|
||||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT |
|
|
||||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT,
|
|
||||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.image = vk_image,
|
|
||||||
.subresourceRange{
|
|
||||||
.aspectMask = aspect_mask,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
|
||||||
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, 0, image_barrier);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Anonymous namespace
|
|
||||||
|
|
||||||
BlockLinearUnswizzle2DPass::BlockLinearUnswizzle2DPass(
|
|
||||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
|
||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
|
||||||
: ComputePass(device_, scheduler_, descriptor_pool_, UNSWIZZLE_DESCRIPTOR_SET_BINDINGS,
|
|
||||||
UNSWIZZLE_DESCRIPTOR_UPDATE_TEMPLATE, UNSWIZZLE_BANK_INFO,
|
|
||||||
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(
|
|
||||||
VideoCommon::Accelerated::BlockLinearSwizzle2DParams)>,
|
|
||||||
UnswizzleSpv(device_, BLOCK_LINEAR_UNSWIZZLE_2D_COMP_SPV,
|
|
||||||
BLOCK_LINEAR_UNSWIZZLE_2D_NONARROW_COMP_SPV)),
|
|
||||||
scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
|
||||||
|
|
||||||
BlockLinearUnswizzle2DPass::~BlockLinearUnswizzle2DPass() = default;
|
|
||||||
|
|
||||||
void BlockLinearUnswizzle2DPass::Unswizzle(
|
|
||||||
Image& image, const StagingBufferRef& map,
|
|
||||||
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
|
||||||
using namespace VideoCommon::Accelerated;
|
|
||||||
scheduler.RequestOutsideRenderPassOperationContext();
|
|
||||||
const VkPipeline vk_pipeline = *pipeline;
|
|
||||||
const VkImageAspectFlags aspect_mask = image.AspectMask();
|
|
||||||
const VkImage vk_image = image.Handle();
|
|
||||||
const bool is_initialized = image.ExchangeInitialization();
|
|
||||||
RecordUnswizzleEntryBarrier(scheduler, vk_pipeline, vk_image, aspect_mask, is_initialized);
|
|
||||||
|
|
||||||
const u32 num_layers = static_cast<u32>(image.info.resources.layers);
|
|
||||||
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
|
|
||||||
const size_t input_offset = swizzle.buffer_offset + map.offset;
|
|
||||||
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U);
|
|
||||||
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U);
|
|
||||||
|
|
||||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
|
||||||
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
|
|
||||||
image.guest_size_bytes - swizzle.buffer_offset);
|
|
||||||
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
|
|
||||||
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
|
||||||
|
|
||||||
const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info);
|
|
||||||
scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_layers, params,
|
|
||||||
descriptor_data](vk::CommandBuffer cmdbuf) {
|
|
||||||
const VkDescriptorSet set = descriptor_allocator.Commit();
|
|
||||||
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
|
||||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
|
||||||
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params);
|
|
||||||
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, num_layers);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
RecordUnswizzleExitBarrier(scheduler, vk_image, aspect_mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockLinearUnswizzleImage3DPass::BlockLinearUnswizzleImage3DPass(
|
|
||||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
|
||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
|
||||||
: ComputePass(device_, scheduler_, descriptor_pool_, UNSWIZZLE_DESCRIPTOR_SET_BINDINGS,
|
|
||||||
UNSWIZZLE_DESCRIPTOR_UPDATE_TEMPLATE, UNSWIZZLE_BANK_INFO,
|
|
||||||
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearSwizzle3DParams)>,
|
|
||||||
UnswizzleSpv(device_, BLOCK_LINEAR_UNSWIZZLE_3D_COMP_SPV,
|
|
||||||
BLOCK_LINEAR_UNSWIZZLE_3D_NONARROW_COMP_SPV)),
|
|
||||||
scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
|
||||||
|
|
||||||
BlockLinearUnswizzleImage3DPass::~BlockLinearUnswizzleImage3DPass() = default;
|
|
||||||
|
|
||||||
void BlockLinearUnswizzleImage3DPass::Unswizzle(
|
|
||||||
Image& image, const StagingBufferRef& map,
|
|
||||||
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
|
||||||
using namespace VideoCommon::Accelerated;
|
|
||||||
scheduler.RequestOutsideRenderPassOperationContext();
|
|
||||||
const VkPipeline vk_pipeline = *pipeline;
|
|
||||||
const VkImageAspectFlags aspect_mask = image.AspectMask();
|
|
||||||
const VkImage vk_image = image.Handle();
|
|
||||||
const bool is_initialized = image.ExchangeInitialization();
|
|
||||||
RecordUnswizzleEntryBarrier(scheduler, vk_pipeline, vk_image, aspect_mask, is_initialized);
|
|
||||||
|
|
||||||
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
|
|
||||||
const size_t input_offset = swizzle.buffer_offset + map.offset;
|
|
||||||
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 16U);
|
|
||||||
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U);
|
|
||||||
const u32 num_dispatches_z = Common::DivCeil(swizzle.num_tiles.depth, 8U);
|
|
||||||
|
|
||||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
|
||||||
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
|
|
||||||
image.guest_size_bytes - swizzle.buffer_offset);
|
|
||||||
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
|
|
||||||
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
|
||||||
|
|
||||||
const auto params = MakeBlockLinearSwizzle3DParams(swizzle, image.info);
|
|
||||||
scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_dispatches_z, params,
|
|
||||||
descriptor_data](vk::CommandBuffer cmdbuf) {
|
|
||||||
const VkDescriptorSet set = descriptor_allocator.Commit();
|
|
||||||
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
|
||||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
|
||||||
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params);
|
|
||||||
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, num_dispatches_z);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
RecordUnswizzleExitBarrier(scheduler, vk_image, aspect_mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
PitchUnswizzlePass::PitchUnswizzlePass(
|
|
||||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
|
||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
|
||||||
: ComputePass(device_, scheduler_, descriptor_pool_, UNSWIZZLE_DESCRIPTOR_SET_BINDINGS,
|
|
||||||
UNSWIZZLE_DESCRIPTOR_UPDATE_TEMPLATE, UNSWIZZLE_BANK_INFO,
|
|
||||||
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(PitchUnswizzlePushConstants)>,
|
|
||||||
UnswizzleSpv(device_, PITCH_UNSWIZZLE_COMP_SPV,
|
|
||||||
PITCH_UNSWIZZLE_NONARROW_COMP_SPV)),
|
|
||||||
scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
|
||||||
|
|
||||||
PitchUnswizzlePass::~PitchUnswizzlePass() = default;
|
|
||||||
|
|
||||||
void PitchUnswizzlePass::Unswizzle(Image& image, const StagingBufferRef& map,
|
|
||||||
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
|
||||||
scheduler.RequestOutsideRenderPassOperationContext();
|
|
||||||
const VkPipeline vk_pipeline = *pipeline;
|
|
||||||
const VkImageAspectFlags aspect_mask = image.AspectMask();
|
|
||||||
const VkImage vk_image = image.Handle();
|
|
||||||
const bool is_initialized = image.ExchangeInitialization();
|
|
||||||
RecordUnswizzleEntryBarrier(scheduler, vk_pipeline, vk_image, aspect_mask, is_initialized);
|
|
||||||
|
|
||||||
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(image.info.format);
|
|
||||||
const u32 pitch = image.info.pitch;
|
|
||||||
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
|
|
||||||
const size_t input_offset = swizzle.buffer_offset + map.offset;
|
|
||||||
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U);
|
|
||||||
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U);
|
|
||||||
|
|
||||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
|
||||||
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
|
|
||||||
image.guest_size_bytes - swizzle.buffer_offset);
|
|
||||||
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
|
|
||||||
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
|
||||||
|
|
||||||
const PitchUnswizzlePushConstants params{
|
|
||||||
.origin{0, 0},
|
|
||||||
.destination{0, 0},
|
|
||||||
.bytes_per_block = bytes_per_block,
|
|
||||||
.pitch = pitch,
|
|
||||||
};
|
|
||||||
scheduler.Record([this, num_dispatches_x, num_dispatches_y, params,
|
|
||||||
descriptor_data](vk::CommandBuffer cmdbuf) {
|
|
||||||
const VkDescriptorSet set = descriptor_allocator.Commit();
|
|
||||||
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
|
||||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
|
||||||
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params);
|
|
||||||
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
RecordUnswizzleExitBarrier(scheduler, vk_image, aspect_mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|||||||
@@ -164,49 +164,4 @@ private:
|
|||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BlockLinearUnswizzle2DPass final : public ComputePass {
|
|
||||||
public:
|
|
||||||
explicit BlockLinearUnswizzle2DPass(
|
|
||||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
|
||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
|
||||||
~BlockLinearUnswizzle2DPass();
|
|
||||||
|
|
||||||
void Unswizzle(Image& image, const StagingBufferRef& map,
|
|
||||||
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Scheduler& scheduler;
|
|
||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BlockLinearUnswizzleImage3DPass final : public ComputePass {
|
|
||||||
public:
|
|
||||||
explicit BlockLinearUnswizzleImage3DPass(
|
|
||||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
|
||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
|
||||||
~BlockLinearUnswizzleImage3DPass();
|
|
||||||
|
|
||||||
void Unswizzle(Image& image, const StagingBufferRef& map,
|
|
||||||
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Scheduler& scheduler;
|
|
||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
|
||||||
};
|
|
||||||
|
|
||||||
class PitchUnswizzlePass final : public ComputePass {
|
|
||||||
public:
|
|
||||||
explicit PitchUnswizzlePass(const Device& device_, Scheduler& scheduler_,
|
|
||||||
DescriptorPool& descriptor_pool_,
|
|
||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
|
||||||
~PitchUnswizzlePass();
|
|
||||||
|
|
||||||
void Unswizzle(Image& image, const StagingBufferRef& map,
|
|
||||||
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Scheduler& scheduler;
|
|
||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|||||||
@@ -694,11 +694,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
|||||||
const size_t num_vertex_arrays = (std::min)(
|
const size_t num_vertex_arrays = (std::min)(
|
||||||
Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings()));
|
Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings()));
|
||||||
for (size_t index = 0; index < num_vertex_arrays; ++index) {
|
for (size_t index = 0; index < num_vertex_arrays; ++index) {
|
||||||
const bool instanced = ((key.state.enabled_divisors >> index) & 1) != 0;
|
const bool instanced = key.state.binding_divisors[index] != 0;
|
||||||
auto rate = VK_VERTEX_INPUT_RATE_VERTEX;
|
const auto rate =
|
||||||
if (instanced) {
|
instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
|
||||||
rate = VK_VERTEX_INPUT_RATE_INSTANCE;
|
|
||||||
}
|
|
||||||
vertex_bindings.push_back({
|
vertex_bindings.push_back({
|
||||||
.binding = static_cast<u32>(index),
|
.binding = static_cast<u32>(index),
|
||||||
.stride = key.state.vertex_strides[index],
|
.stride = key.state.vertex_strides[index],
|
||||||
@@ -707,7 +705,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
|||||||
if (instanced) {
|
if (instanced) {
|
||||||
vertex_binding_divisors.push_back({
|
vertex_binding_divisors.push_back({
|
||||||
.binding = static_cast<u32>(index),
|
.binding = static_cast<u32>(index),
|
||||||
.divisor = device.GetVertexAttribDivisor(key.state.binding_divisors[index]),
|
.divisor = key.state.binding_divisors[index],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 =
|
||||||
|
|||||||
@@ -260,7 +260,6 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
|
void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
|
||||||
buffer_cache.SetDrawInstanceCount(instance_count);
|
|
||||||
PrepareDraw(is_indexed, [this, is_indexed, instance_count] {
|
PrepareDraw(is_indexed, [this, is_indexed, instance_count] {
|
||||||
const auto& draw_state = maxwell3d->draw_manager.draw_state;
|
const auto& draw_state = maxwell3d->draw_manager.draw_state;
|
||||||
const u32 num_instances{instance_count};
|
const u32 num_instances{instance_count};
|
||||||
@@ -296,7 +295,6 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
|
|||||||
void RasterizerVulkan::DrawIndirect() {
|
void RasterizerVulkan::DrawIndirect() {
|
||||||
const auto& params = maxwell3d->draw_manager.indirect_state;
|
const auto& params = maxwell3d->draw_manager.indirect_state;
|
||||||
buffer_cache.SetDrawIndirect(¶ms);
|
buffer_cache.SetDrawIndirect(¶ms);
|
||||||
buffer_cache.SetDrawInstanceCount(0);
|
|
||||||
PrepareDraw(params.is_indexed, [this, ¶ms] {
|
PrepareDraw(params.is_indexed, [this, ¶ms] {
|
||||||
const auto indirect_buffer = buffer_cache.GetDrawIndirectBuffer();
|
const auto indirect_buffer = buffer_cache.GetDrawIndirectBuffer();
|
||||||
const auto& buffer = indirect_buffer.first;
|
const auto& buffer = indirect_buffer.first;
|
||||||
@@ -1919,19 +1917,13 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
|
|||||||
for (u32 binding = 0; binding < max_bindings; ++binding) {
|
for (u32 binding = 0; binding < max_bindings; ++binding) {
|
||||||
const auto& input_binding{regs.vertex_streams[binding]};
|
const auto& input_binding{regs.vertex_streams[binding]};
|
||||||
const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)};
|
const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)};
|
||||||
auto input_rate = VK_VERTEX_INPUT_RATE_VERTEX;
|
|
||||||
u32 divisor = 1;
|
|
||||||
if (is_instanced) {
|
|
||||||
input_rate = VK_VERTEX_INPUT_RATE_INSTANCE;
|
|
||||||
divisor = device.GetVertexAttribDivisor(input_binding.frequency);
|
|
||||||
}
|
|
||||||
bindings.push_back({
|
bindings.push_back({
|
||||||
.sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT,
|
.sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.binding = binding,
|
.binding = binding,
|
||||||
.stride = input_binding.stride,
|
.stride = input_binding.stride,
|
||||||
.inputRate = input_rate,
|
.inputRate = is_instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX,
|
||||||
.divisor = divisor,
|
.divisor = is_instanced ? input_binding.frequency : 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -449,9 +449,7 @@ void Scheduler::EndRenderPass()
|
|||||||
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
|
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
|
||||||
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
|
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
|
||||||
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
|
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
|
||||||
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT
|
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
||||||
| VK_ACCESS_TRANSFER_READ_BIT
|
|
||||||
| VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
||||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
@@ -462,7 +460,7 @@ void Scheduler::EndRenderPass()
|
|||||||
}
|
}
|
||||||
cmdbuf.EndRenderPass();
|
cmdbuf.EndRenderPass();
|
||||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
||||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER,
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
|
||||||
0, nullptr, nullptr, vk::Span(barriers.data(), num_images));
|
0, nullptr, nullptr, vk::Span(barriers.data(), num_images));
|
||||||
if (has_transform_feedback) {
|
if (has_transform_feedback) {
|
||||||
static constexpr VkMemoryBarrier XFB_OUTPUT_BARRIER{
|
static constexpr VkMemoryBarrier XFB_OUTPUT_BARRIER{
|
||||||
|
|||||||
@@ -160,55 +160,6 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
|||||||
info.size.depth == 1;
|
info.size.depth == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] PixelFormat UnswizzleViewFormat(u32 bytes_per_block) {
|
|
||||||
switch (bytes_per_block) {
|
|
||||||
case 1:
|
|
||||||
return PixelFormat::R8_UINT;
|
|
||||||
case 2:
|
|
||||||
return PixelFormat::R16_UINT;
|
|
||||||
case 4:
|
|
||||||
return PixelFormat::R32_UINT;
|
|
||||||
case 8:
|
|
||||||
return PixelFormat::R32G32_UINT;
|
|
||||||
case 16:
|
|
||||||
return PixelFormat::R32G32B32A32_UINT;
|
|
||||||
default:
|
|
||||||
return PixelFormat::Invalid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr u32 UNSWIZZLE_WORKGROUP_INVOCATIONS = 32 * 32;
|
|
||||||
|
|
||||||
[[nodiscard]] bool SupportsAcceleratedUnswizzleDevice(const Device& device) {
|
|
||||||
return device.IsKhrImageFormatListSupported() &&
|
|
||||||
device.GetMaxComputeWorkGroupInvocations() >= UNSWIZZLE_WORKGROUP_INVOCATIONS;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool SupportsAcceleratedUnswizzle(const Device& device, const ImageInfo& info) {
|
|
||||||
if (!SupportsAcceleratedUnswizzleDevice(device)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (info.num_samples > 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (info.type != ImageType::e2D && info.type != ImageType::e3D &&
|
|
||||||
info.type != ImageType::Linear) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const PixelFormat view_format =
|
|
||||||
UnswizzleViewFormat(VideoCore::Surface::BytesPerBlock(info.format));
|
|
||||||
if (view_format == PixelFormat::Invalid) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!VideoCore::Surface::IsViewCompatible(info.format, view_format, false, true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto host_format =
|
|
||||||
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, view_format);
|
|
||||||
return device.IsFormatSupported(host_format.format, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT,
|
|
||||||
FormatType::Optimal);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info,
|
[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info,
|
||||||
std::optional<VkFormat> format_override = {}) {
|
std::optional<VkFormat> format_override = {}) {
|
||||||
auto format_info =
|
auto format_info =
|
||||||
@@ -297,18 +248,8 @@ constexpr u32 UNSWIZZLE_WORKGROUP_INVOCATIONS = 32 * 32;
|
|||||||
return allocator.CreateImage(image_ci);
|
return allocator.CreateImage(image_ci);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] VkImageViewType StorageViewType(ImageType type) {
|
|
||||||
if (type == ImageType::e3D) {
|
|
||||||
return VK_IMAGE_VIEW_TYPE_3D;
|
|
||||||
}
|
|
||||||
if (type == ImageType::Linear) {
|
|
||||||
return VK_IMAGE_VIEW_TYPE_2D;
|
|
||||||
}
|
|
||||||
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] vk::ImageView MakeStorageView(const vk::Device& device, u32 level, VkImage image,
|
[[nodiscard]] vk::ImageView MakeStorageView(const vk::Device& device, u32 level, VkImage image,
|
||||||
VkFormat format, VkImageViewType view_type) {
|
VkFormat format) {
|
||||||
static constexpr VkImageViewUsageCreateInfo storage_image_view_usage_create_info{
|
static constexpr VkImageViewUsageCreateInfo storage_image_view_usage_create_info{
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
@@ -319,7 +260,7 @@ constexpr u32 UNSWIZZLE_WORKGROUP_INVOCATIONS = 32 * 32;
|
|||||||
.pNext = &storage_image_view_usage_create_info,
|
.pNext = &storage_image_view_usage_create_info,
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.image = image,
|
.image = image,
|
||||||
.viewType = view_type,
|
.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY,
|
||||||
.format = format,
|
.format = format,
|
||||||
.components{
|
.components{
|
||||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||||
@@ -717,11 +658,18 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
|
|||||||
.subresourceRange = subresource_range,
|
.subresourceRange = subresource_range,
|
||||||
};
|
};
|
||||||
|
|
||||||
cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
||||||
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||||
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
||||||
read_barrier);
|
read_barrier);
|
||||||
cmdbuf.CopyBufferToImage(src_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copies);
|
cmdbuf.CopyBufferToImage(src_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copies);
|
||||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0,
|
// TODO: Move this to another API
|
||||||
nullptr, nullptr, write_barrier);
|
cmdbuf.PipelineBarrier(
|
||||||
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
||||||
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
|
||||||
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||||
|
0, nullptr, nullptr, write_barrier);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] VkImageBlit MakeImageBlit(const Region2D& dst_region, const Region2D& src_region,
|
[[nodiscard]] VkImageBlit MakeImageBlit(const Region2D& dst_region, const Region2D& src_region,
|
||||||
@@ -1020,14 +968,6 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
|
|||||||
bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
|
bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
|
||||||
staging_buffer_pool, compute_pass_descriptor_queue);
|
staging_buffer_pool, compute_pass_descriptor_queue);
|
||||||
}
|
}
|
||||||
if (SupportsAcceleratedUnswizzleDevice(device)) {
|
|
||||||
bl_unswizzle_2d_pass.emplace(device, scheduler, descriptor_pool,
|
|
||||||
compute_pass_descriptor_queue);
|
|
||||||
bl_unswizzle_image_3d_pass.emplace(device, scheduler, descriptor_pool,
|
|
||||||
compute_pass_descriptor_queue);
|
|
||||||
pitch_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
|
|
||||||
compute_pass_descriptor_queue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureCacheRuntime::Finish() {
|
void TextureCacheRuntime::Finish() {
|
||||||
@@ -1962,12 +1902,16 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
|
|||||||
if (runtime->device.HasDebuggingToolAttached()) {
|
if (runtime->device.HasDebuggingToolAttached()) {
|
||||||
original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str());
|
original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str());
|
||||||
}
|
}
|
||||||
if (False(flags & VideoCommon::ImageFlagBits::Converted) &&
|
|
||||||
SupportsAcceleratedUnswizzle(runtime->device, info)) {
|
|
||||||
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
|
|
||||||
}
|
|
||||||
current_image = &Image::original_image;
|
current_image = &Image::original_image;
|
||||||
storage_image_views.resize(info.resources.levels);
|
storage_image_views.resize(info.resources.levels);
|
||||||
|
if (WillUseAcceleratedAstcDecode(runtime->device, info)) {
|
||||||
|
const auto& device = runtime->device.GetLogical();
|
||||||
|
const VkFormat storage_format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||||
|
for (s32 level = 0; level < info.resources.levels; ++level) {
|
||||||
|
storage_image_views[level] =
|
||||||
|
MakeStorageView(device, level, *original_image, storage_format);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::Image(const VideoCommon::NullImageParams& params) : VideoCommon::ImageBase{params} {}
|
Image::Image(const VideoCommon::NullImageParams& params) : VideoCommon::ImageBase{params} {}
|
||||||
@@ -2091,7 +2035,7 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
|
|||||||
temp_vk_image, info.format, info.num_samples,
|
temp_vk_image, info.format, info.num_samples,
|
||||||
{image_copies.data(), image_copies.size()}, false);
|
{image_copies.data(), image_copies.size()}, false);
|
||||||
}
|
}
|
||||||
InitializationFor(current_image) = true;
|
initialized = true;
|
||||||
runtime->ReleaseMsaaScratchImage(temp_vk_image);
|
runtime->ReleaseMsaaScratchImage(temp_vk_image);
|
||||||
|
|
||||||
if (is_rescaled) {
|
if (is_rescaled) {
|
||||||
@@ -2112,7 +2056,7 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
|
|||||||
const VkBuffer src_buffer = buffer;
|
const VkBuffer src_buffer = buffer;
|
||||||
const VkImage vk_image = *original_image;
|
const VkImage vk_image = *original_image;
|
||||||
const VkImageAspectFlags vk_aspect_mask = aspect_mask;
|
const VkImageAspectFlags vk_aspect_mask = aspect_mask;
|
||||||
const bool was_initialized = std::exchange(InitializationFor(&Image::original_image), true);
|
const bool was_initialized = std::exchange(initialized, true);
|
||||||
|
|
||||||
scheduler->Record([src_buffer, vk_image, vk_aspect_mask, was_initialized,
|
scheduler->Record([src_buffer, vk_image, vk_aspect_mask, was_initialized,
|
||||||
vk_copies](vk::CommandBuffer cmdbuf) {
|
vk_copies](vk::CommandBuffer cmdbuf) {
|
||||||
@@ -2377,46 +2321,16 @@ void Image::DownloadMemory(const StagingBufferRef& map, std::span<const BufferIm
|
|||||||
DownloadMemory(buffers, offsets, copies);
|
DownloadMemory(buffers, offsets, copies);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<vk::ImageView>& Image::StorageViewsFor(vk::Image Image::*image) {
|
|
||||||
if (image == &Image::scaled_image) {
|
|
||||||
if (scaled_storage_image_views.empty()) {
|
|
||||||
scaled_storage_image_views.resize(info.resources.levels);
|
|
||||||
}
|
|
||||||
return scaled_storage_image_views;
|
|
||||||
}
|
|
||||||
return storage_image_views;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool& Image::InitializationFor(vk::Image Image::*image) noexcept {
|
|
||||||
if (image == &Image::scaled_image) {
|
|
||||||
return scaled_initialized;
|
|
||||||
}
|
|
||||||
return original_initialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkImageView Image::StorageImageView(s32 level) noexcept {
|
VkImageView Image::StorageImageView(s32 level) noexcept {
|
||||||
const bool astc_decode = WillUseAcceleratedAstcDecode(runtime->device, info);
|
auto& view = storage_image_views[level];
|
||||||
const bool unswizzle_upload =
|
|
||||||
!astc_decode && True(flags & ImageFlagBits::AcceleratedUpload);
|
|
||||||
vk::Image Image::*target = current_image;
|
|
||||||
if (astc_decode || unswizzle_upload) {
|
|
||||||
target = &Image::original_image;
|
|
||||||
}
|
|
||||||
auto& view = StorageViewsFor(target)[level];
|
|
||||||
if (!view) {
|
if (!view) {
|
||||||
auto format_info =
|
auto format_info =
|
||||||
MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format);
|
MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format);
|
||||||
if (astc_decode) {
|
if (WillUseAcceleratedAstcDecode(runtime->device, info)) {
|
||||||
format_info.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
format_info.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||||
}
|
}
|
||||||
if (unswizzle_upload) {
|
view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*current_image),
|
||||||
const PixelFormat view_format =
|
format_info.format);
|
||||||
UnswizzleViewFormat(VideoCore::Surface::BytesPerBlock(info.format));
|
|
||||||
format_info = MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, false,
|
|
||||||
view_format);
|
|
||||||
}
|
|
||||||
view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*target),
|
|
||||||
format_info.format, StorageViewType(info.type));
|
|
||||||
}
|
}
|
||||||
return *view;
|
return *view;
|
||||||
}
|
}
|
||||||
@@ -2456,7 +2370,6 @@ bool Image::ScaleUp(bool ignore) {
|
|||||||
}
|
}
|
||||||
if (NeedsScaleHelper()) {
|
if (NeedsScaleHelper()) {
|
||||||
if (!BlitScaleHelper(true)) {
|
if (!BlitScaleHelper(true)) {
|
||||||
flags &= ~ImageFlagBits::Rescaled;
|
|
||||||
current_image = &Image::original_image;
|
current_image = &Image::original_image;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2646,10 +2559,6 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
|||||||
if (device->IsExtAstcDecodeModeSupported() && IsLdrAstcFormat(format_info.format)) {
|
if (device->IsExtAstcDecodeModeSupported() && IsLdrAstcFormat(format_info.format)) {
|
||||||
view_next = &astc_decode_mode;
|
view_next = &astc_decode_mode;
|
||||||
}
|
}
|
||||||
auto subresource_range = MakeSubresourceRange(aspect_mask, info.range);
|
|
||||||
if (True(flags & VideoCommon::ImageViewFlagBits::Slice)) {
|
|
||||||
subresource_range.levelCount = 1;
|
|
||||||
}
|
|
||||||
const VkImageViewCreateInfo create_info{
|
const VkImageViewCreateInfo create_info{
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||||
.pNext = view_next,
|
.pNext = view_next,
|
||||||
@@ -2658,7 +2567,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
|||||||
.viewType = VkImageViewType{},
|
.viewType = VkImageViewType{},
|
||||||
.format = format_info.format,
|
.format = format_info.format,
|
||||||
.components = swizzle_mapping,
|
.components = swizzle_mapping,
|
||||||
.subresourceRange = subresource_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) {
|
||||||
VkImageViewCreateInfo ci{create_info};
|
VkImageViewCreateInfo ci{create_info};
|
||||||
@@ -3232,19 +3141,6 @@ void TextureCacheRuntime::AccelerateImageUpload(
|
|||||||
return astc_decoder_pass->Assemble(image, map, swizzles);
|
return astc_decoder_pass->Assemble(image, map, swizzles);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bl_unswizzle_2d_pass && image.info.type == ImageType::e2D) {
|
|
||||||
return bl_unswizzle_2d_pass->Unswizzle(image, map, swizzles);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bl_unswizzle_image_3d_pass && image.info.type == ImageType::e3D &&
|
|
||||||
!IsPixelFormatBCn(image.info.format)) {
|
|
||||||
return bl_unswizzle_image_3d_pass->Unswizzle(image, map, swizzles);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pitch_unswizzle_pass && image.info.type == ImageType::Linear) {
|
|
||||||
return pitch_unswizzle_pass->Unswizzle(image, map, swizzles);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) {
|
if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) {
|
||||||
if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) {
|
if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) {
|
||||||
ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture");
|
ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture");
|
||||||
|
|||||||
@@ -159,9 +159,6 @@ public:
|
|||||||
std::optional<ASTCDecoderPass> astc_decoder_pass;
|
std::optional<ASTCDecoderPass> astc_decoder_pass;
|
||||||
|
|
||||||
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
|
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
|
||||||
std::optional<BlockLinearUnswizzle2DPass> bl_unswizzle_2d_pass;
|
|
||||||
std::optional<BlockLinearUnswizzleImage3DPass> bl_unswizzle_image_3d_pass;
|
|
||||||
std::optional<PitchUnswizzlePass> pitch_unswizzle_pass;
|
|
||||||
const Settings::ResolutionScalingInfo& resolution;
|
const Settings::ResolutionScalingInfo& resolution;
|
||||||
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
|
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
|
||||||
|
|
||||||
@@ -353,7 +350,7 @@ public:
|
|||||||
|
|
||||||
/// Returns true when the image is already initialized and mark it as initialized
|
/// Returns true when the image is already initialized and mark it as initialized
|
||||||
[[nodiscard]] bool ExchangeInitialization() noexcept {
|
[[nodiscard]] bool ExchangeInitialization() noexcept {
|
||||||
return std::exchange(InitializationFor(current_image), true);
|
return std::exchange(initialized, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
VkImageView StorageImageView(s32 level) noexcept;
|
VkImageView StorageImageView(s32 level) noexcept;
|
||||||
@@ -373,10 +370,6 @@ private:
|
|||||||
|
|
||||||
bool NeedsScaleHelper() const;
|
bool NeedsScaleHelper() const;
|
||||||
|
|
||||||
std::vector<vk::ImageView>& StorageViewsFor(vk::Image Image::*image);
|
|
||||||
|
|
||||||
bool& InitializationFor(vk::Image Image::*image) noexcept;
|
|
||||||
|
|
||||||
Scheduler* scheduler{};
|
Scheduler* scheduler{};
|
||||||
TextureCacheRuntime* runtime{};
|
TextureCacheRuntime* runtime{};
|
||||||
|
|
||||||
@@ -394,10 +387,8 @@ private:
|
|||||||
vk::Image Image::*current_image{};
|
vk::Image Image::*current_image{};
|
||||||
|
|
||||||
std::vector<vk::ImageView> storage_image_views;
|
std::vector<vk::ImageView> storage_image_views;
|
||||||
std::vector<vk::ImageView> scaled_storage_image_views;
|
|
||||||
VkImageAspectFlags aspect_mask = 0;
|
VkImageAspectFlags aspect_mask = 0;
|
||||||
bool original_initialized = false;
|
bool initialized = false;
|
||||||
bool scaled_initialized = false;
|
|
||||||
|
|
||||||
std::optional<Framebuffer> scale_framebuffer;
|
std::optional<Framebuffer> scale_framebuffer;
|
||||||
std::optional<Framebuffer> normal_framebuffer;
|
std::optional<Framebuffer> normal_framebuffer;
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -26,8 +23,8 @@ struct BlockLinearSwizzle2DParams {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct BlockLinearSwizzle3DParams {
|
struct BlockLinearSwizzle3DParams {
|
||||||
alignas(16) std::array<u32, 3> origin;
|
std::array<u32, 3> origin;
|
||||||
alignas(16) std::array<s32, 3> destination;
|
std::array<s32, 3> destination;
|
||||||
u32 bytes_per_block_log2;
|
u32 bytes_per_block_log2;
|
||||||
u32 slice_size;
|
u32 slice_size;
|
||||||
u32 block_size;
|
u32 block_size;
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
#include "video_core/engines/kepler_compute.h"
|
#include "video_core/engines/kepler_compute.h"
|
||||||
#include "video_core/guest_memory.h"
|
#include "video_core/guest_memory.h"
|
||||||
#include "video_core/host1x/gpu_device_memory_manager.h"
|
#include "video_core/host1x/gpu_device_memory_manager.h"
|
||||||
#include "video_core/texture_cache/accelerated_swizzle.h"
|
|
||||||
#include "video_core/texture_cache/image_view_base.h"
|
#include "video_core/texture_cache/image_view_base.h"
|
||||||
#include "video_core/texture_cache/samples_helper.h"
|
#include "video_core/texture_cache/samples_helper.h"
|
||||||
#include "video_core/texture_cache/texture_cache_base.h"
|
#include "video_core/texture_cache/texture_cache_base.h"
|
||||||
@@ -280,11 +279,11 @@ void TextureCache<P>::CheckFeedbackLoop(std::span<const ImageViewInOut> views) {
|
|||||||
|
|
||||||
const ImageId view_image_id = slot_image_views[view.id].image_id;
|
const ImageId view_image_id = slot_image_views[view.id].image_id;
|
||||||
{
|
{
|
||||||
bool is_feedback = false;
|
bool is_continue = false;
|
||||||
for (size_t i = 0; i < 8; ++i)
|
for (size_t i = 0; i < 8; ++i)
|
||||||
is_feedback |= (rt_active_mask & (1u << i)) && view_image_id == rt_image_id[i];
|
is_continue |= (rt_active_mask & (1u << i)) && view_image_id == rt_image_id[i];
|
||||||
if (is_feedback)
|
if (is_continue)
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
if (depth_active && view_image_id == rt_depth_image_id) {
|
if (depth_active && view_image_id == rt_depth_image_id) {
|
||||||
return true;
|
return true;
|
||||||
@@ -627,25 +626,14 @@ void TextureCache<P>::DownloadMemory(DAddr cpu_addr, size_t size) {
|
|||||||
std::ranges::sort(images, [this](ImageId lhs, ImageId rhs) {
|
std::ranges::sort(images, [this](ImageId lhs, ImageId rhs) {
|
||||||
return slot_images[lhs].modification_tick < slot_images[rhs].modification_tick;
|
return slot_images[lhs].modification_tick < slot_images[rhs].modification_tick;
|
||||||
});
|
});
|
||||||
size_t total_size_bytes = 0;
|
|
||||||
for (const ImageId image_id : images) {
|
|
||||||
total_size_bytes += slot_images[image_id].unswizzled_size_bytes;
|
|
||||||
}
|
|
||||||
auto download_map = runtime.DownloadStagingBuffer(total_size_bytes);
|
|
||||||
for (const ImageId image_id : images) {
|
for (const ImageId image_id : images) {
|
||||||
Image& image = slot_images[image_id];
|
Image& image = slot_images[image_id];
|
||||||
|
auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes);
|
||||||
const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info));
|
const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info));
|
||||||
image.DownloadMemory(download_map, copies);
|
image.DownloadMemory(map, copies);
|
||||||
download_map.offset += image.unswizzled_size_bytes;
|
runtime.Finish();
|
||||||
}
|
SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span,
|
||||||
runtime.Finish();
|
|
||||||
std::span<u8> download_span = download_map.mapped_span;
|
|
||||||
for (const ImageId image_id : images) {
|
|
||||||
const ImageBase& image = slot_images[image_id];
|
|
||||||
const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info));
|
|
||||||
SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span,
|
|
||||||
swizzle_data_buffer);
|
swizzle_data_buffer);
|
||||||
download_span = download_span.subspan(image.unswizzled_size_bytes);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1134,12 +1122,6 @@ void TextureCache<P>::RefreshContents(Image& image, ImageId image_id) {
|
|||||||
|
|
||||||
TrackImage(image, image_id);
|
TrackImage(image, image_id);
|
||||||
|
|
||||||
if (image.info.rescaleable &&
|
|
||||||
IsRegionGpuModified(image.cpu_addr, image.guest_size_bytes)) {
|
|
||||||
runtime.TransitionImageLayout(image);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (image.info.num_samples > 1 && !runtime.CanUploadMSAA()) {
|
if (image.info.num_samples > 1 && !runtime.CanUploadMSAA()) {
|
||||||
LOG_WARNING(HW_GPU, "MSAA image uploads are not implemented");
|
LOG_WARNING(HW_GPU, "MSAA image uploads are not implemented");
|
||||||
runtime.TransitionImageLayout(image);
|
runtime.TransitionImageLayout(image);
|
||||||
@@ -1175,7 +1157,8 @@ void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging)
|
|||||||
const GPUVAddr gpu_addr = image.gpu_addr;
|
const GPUVAddr gpu_addr = image.gpu_addr;
|
||||||
|
|
||||||
if (True(image.flags & ImageFlagBits::AcceleratedUpload)) {
|
if (True(image.flags & ImageFlagBits::AcceleratedUpload)) {
|
||||||
gpu_memory->ReadBlockUnsafe(gpu_addr, mapped_span.data(), image.guest_size_bytes);
|
gpu_memory->ReadBlock(gpu_addr, mapped_span.data(), mapped_span.size_bytes(),
|
||||||
|
VideoCommon::CacheType::NoTextureCache);
|
||||||
const auto uploads = FullUploadSwizzles(image.info);
|
const auto uploads = FullUploadSwizzles(image.info);
|
||||||
runtime.AccelerateImageUpload(image, staging, FixSmallVectorADL(uploads), 0, 0);
|
runtime.AccelerateImageUpload(image, staging, FixSmallVectorADL(uploads), 0, 0);
|
||||||
return;
|
return;
|
||||||
@@ -1282,9 +1265,6 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
|
|||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
bool TextureCache<P>::ImageCanRescale(ImageBase& image) {
|
bool TextureCache<P>::ImageCanRescale(ImageBase& image) {
|
||||||
if (!Settings::values.resolution_info.active) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!image.info.rescaleable) {
|
if (!image.info.rescaleable) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1372,12 +1352,12 @@ void TextureCache<P>::QueueAsyncDecode(Image& image, ImageId image_id) {
|
|||||||
decode->image_id = image_id;
|
decode->image_id = image_id;
|
||||||
async_decodes.push_back(std::move(decode));
|
async_decodes.push_back(std::move(decode));
|
||||||
|
|
||||||
Common::ScratchBuffer<u8> local_unswizzle_data_buffer(image.unswizzled_size_bytes);
|
std::vector<u8> local_unswizzle_data_buffer(image.unswizzled_size_bytes, 0);
|
||||||
Tegra::Memory::GpuGuestMemory<u8, Tegra::Memory::GuestMemoryFlags::UnsafeRead> swizzle_data(*gpu_memory, image.gpu_addr, image.guest_size_bytes, &swizzle_data_buffer);
|
Tegra::Memory::GpuGuestMemory<u8, Tegra::Memory::GuestMemoryFlags::UnsafeRead> swizzle_data(*gpu_memory, image.gpu_addr, image.guest_size_bytes, &swizzle_data_buffer);
|
||||||
auto copies = UnswizzleImage(*gpu_memory, image.gpu_addr, image.info, swizzle_data, local_unswizzle_data_buffer);
|
auto copies = UnswizzleImage(*gpu_memory, image.gpu_addr, image.info, swizzle_data, local_unswizzle_data_buffer);
|
||||||
const size_t out_size = MapSizeBytes(image);
|
const size_t out_size = MapSizeBytes(image);
|
||||||
|
|
||||||
auto func = [out_size, copies = std::move(copies), info = image.info,
|
auto func = [out_size, copies, info = image.info,
|
||||||
input = std::move(local_unswizzle_data_buffer),
|
input = std::move(local_unswizzle_data_buffer),
|
||||||
async_decode = decode_ptr]() mutable {
|
async_decode = decode_ptr]() mutable {
|
||||||
async_decode->decoded_data.resize_destructive(out_size);
|
async_decode->decoded_data.resize_destructive(out_size);
|
||||||
@@ -1446,16 +1426,18 @@ void TextureCache<P>::TickAsyncUnswizzle() {
|
|||||||
Image& image = slot_images[task.image_id];
|
Image& image = slot_images[task.image_id];
|
||||||
|
|
||||||
if (!task.initialized) {
|
if (!task.initialized) {
|
||||||
task.total_size = image.guest_size_bytes;
|
task.total_size = MapSizeBytes(image);
|
||||||
task.staging_buffer = runtime.UploadStagingBuffer(task.total_size, true);
|
task.staging_buffer = runtime.UploadStagingBuffer(task.total_size, true);
|
||||||
|
|
||||||
const auto layout = FullUploadSwizzles(task.info);
|
const auto& info = image.info;
|
||||||
const auto params =
|
const u32 bytes_per_block = BytesPerBlock(info.format);
|
||||||
VideoCommon::Accelerated::MakeBlockLinearSwizzle3DParams(layout.front(), task.info);
|
const u32 width_blocks = Common::DivCeil(info.size.width, 4u);
|
||||||
task.bytes_per_slice = params.slice_size;
|
const u32 height_blocks = Common::DivCeil(info.size.height, 4u);
|
||||||
task.chunked = task.info.block.depth == 0;
|
|
||||||
|
const u32 stride = width_blocks * bytes_per_block;
|
||||||
|
const u32 aligned_height = height_blocks;
|
||||||
|
task.bytes_per_slice = static_cast<size_t>(stride) * aligned_height;
|
||||||
task.last_submitted_offset = 0;
|
task.last_submitted_offset = 0;
|
||||||
task.slices_submitted = 0;
|
|
||||||
task.initialized = true;
|
task.initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1470,39 +1452,31 @@ void TextureCache<P>::TickAsyncUnswizzle() {
|
|||||||
if (copy_amount == 0) copy_amount = task.bytes_per_slice;
|
if (copy_amount == 0) copy_amount = task.bytes_per_slice;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpu_memory->ReadBlockUnsafe(image.gpu_addr + task.current_offset,
|
gpu_memory->ReadBlock(image.gpu_addr + task.current_offset,
|
||||||
task.staging_buffer.mapped_span.data() + task.current_offset,
|
task.staging_buffer.mapped_span.data() + task.current_offset,
|
||||||
copy_amount);
|
copy_amount);
|
||||||
task.current_offset += copy_amount;
|
task.current_offset += copy_amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool is_final_batch = task.current_offset >= task.total_size;
|
const bool is_final_batch = task.current_offset >= task.total_size;
|
||||||
|
const size_t bytes_ready = task.current_offset - task.last_submitted_offset;
|
||||||
|
const u32 complete_slices = static_cast<u32>(bytes_ready / task.bytes_per_slice);
|
||||||
|
|
||||||
if (task.chunked) {
|
if (complete_slices >= swizzle_slices_per_batch || (is_final_batch && complete_slices > 0)) {
|
||||||
const size_t bytes_ready = task.current_offset - task.last_submitted_offset;
|
const u32 z_start = static_cast<u32>(task.last_submitted_offset / task.bytes_per_slice);
|
||||||
const u32 complete_slices = static_cast<u32>(bytes_ready / task.bytes_per_slice);
|
const u32 slices_to_process = (std::min)(complete_slices, swizzle_slices_per_batch);
|
||||||
|
const u32 z_count = (std::min)(slices_to_process, image.info.size.depth - z_start);
|
||||||
|
|
||||||
if (complete_slices >= swizzle_slices_per_batch || (is_final_batch && complete_slices > 0)) {
|
if (z_count > 0) {
|
||||||
const u32 z_start = task.slices_submitted;
|
const auto uploads = FullUploadSwizzles(task.info);
|
||||||
const u32 slices_to_process = (std::min)(complete_slices, swizzle_slices_per_batch);
|
runtime.AccelerateImageUpload(image, task.staging_buffer, FixSmallVectorADL(uploads), z_start, z_count);
|
||||||
const u32 z_count = (std::min)(slices_to_process, image.info.size.depth - z_start);
|
task.last_submitted_offset += (static_cast<size_t>(z_count) * task.bytes_per_slice);
|
||||||
|
|
||||||
if (z_count > 0) {
|
|
||||||
const auto uploads = FullUploadSwizzles(task.info);
|
|
||||||
runtime.AccelerateImageUpload(image, task.staging_buffer,
|
|
||||||
FixSmallVectorADL(uploads), z_start, z_count);
|
|
||||||
task.last_submitted_offset += static_cast<size_t>(z_count) * task.bytes_per_slice;
|
|
||||||
task.slices_submitted += z_count;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (is_final_batch && task.slices_submitted == 0) {
|
|
||||||
const auto uploads = FullUploadSwizzles(task.info);
|
|
||||||
runtime.AccelerateImageUpload(image, task.staging_buffer, FixSmallVectorADL(uploads), 0,
|
|
||||||
image.info.size.depth);
|
|
||||||
task.slices_submitted = image.info.size.depth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool all_slices_submitted = task.slices_submitted >= image.info.size.depth;
|
// Check if complete
|
||||||
|
const u32 slices_submitted = static_cast<u32>(task.last_submitted_offset / task.bytes_per_slice);
|
||||||
|
const bool all_slices_submitted = slices_submitted >= image.info.size.depth;
|
||||||
|
|
||||||
if (is_final_batch && all_slices_submitted) {
|
if (is_final_batch && all_slices_submitted) {
|
||||||
runtime.FreeDeferredStagingBuffer(task.staging_buffer);
|
runtime.FreeDeferredStagingBuffer(task.staging_buffer);
|
||||||
|
|||||||
@@ -138,8 +138,6 @@ class TextureCache : public VideoCommon::ChannelSetupCaches<TextureCacheChannelI
|
|||||||
AsyncBuffer staging_buffer;
|
AsyncBuffer staging_buffer;
|
||||||
size_t last_submitted_offset = 0;
|
size_t last_submitted_offset = 0;
|
||||||
size_t bytes_per_slice;
|
size_t bytes_per_slice;
|
||||||
u32 slices_submitted = 0;
|
|
||||||
bool chunked = false;
|
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1118,11 +1118,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
|
|||||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT;
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT;
|
||||||
SetNext(next, properties.transform_feedback);
|
SetNext(next, properties.transform_feedback);
|
||||||
}
|
}
|
||||||
if (extensions.vertex_attribute_divisor) {
|
|
||||||
properties.vertex_attribute_divisor.sType =
|
|
||||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT;
|
|
||||||
SetNext(next, properties.vertex_attribute_divisor);
|
|
||||||
}
|
|
||||||
if (extensions.maintenance5) {
|
if (extensions.maintenance5) {
|
||||||
properties.maintenance5.sType =
|
properties.maintenance5.sType =
|
||||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_PROPERTIES_KHR;
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_PROPERTIES_KHR;
|
||||||
@@ -1391,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 &&
|
||||||
|
|||||||
@@ -70,12 +70,12 @@ VK_DEFINE_HANDLE(VmaAllocator)
|
|||||||
FEATURE(EXT, ProvokingVertex, PROVOKING_VERTEX, provoking_vertex) \
|
FEATURE(EXT, ProvokingVertex, PROVOKING_VERTEX, provoking_vertex) \
|
||||||
FEATURE(EXT, Robustness2, ROBUSTNESS_2, robustness2) \
|
FEATURE(EXT, Robustness2, ROBUSTNESS_2, robustness2) \
|
||||||
FEATURE(EXT, TransformFeedback, TRANSFORM_FEEDBACK, transform_feedback) \
|
FEATURE(EXT, TransformFeedback, TRANSFORM_FEEDBACK, transform_feedback) \
|
||||||
FEATURE(EXT, VertexAttributeDivisor, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \
|
|
||||||
FEATURE(EXT, VertexInputDynamicState, VERTEX_INPUT_DYNAMIC_STATE, vertex_input_dynamic_state) \
|
FEATURE(EXT, VertexInputDynamicState, VERTEX_INPUT_DYNAMIC_STATE, vertex_input_dynamic_state) \
|
||||||
FEATURE(KHR, Maintenance5, MAINTENANCE_5, maintenance5) \
|
FEATURE(KHR, Maintenance5, MAINTENANCE_5, maintenance5) \
|
||||||
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)
|
||||||
|
|
||||||
@@ -92,6 +92,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
|
|||||||
EXTENSION(EXT, SHADER_STENCIL_EXPORT, shader_stencil_export) \
|
EXTENSION(EXT, SHADER_STENCIL_EXPORT, shader_stencil_export) \
|
||||||
EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \
|
EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \
|
||||||
EXTENSION(EXT, TOOLING_INFO, tooling_info) \
|
EXTENSION(EXT, TOOLING_INFO, tooling_info) \
|
||||||
|
EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \
|
||||||
EXTENSION(KHR, CREATE_RENDERPASS_2, create_renderpass2) \
|
EXTENSION(KHR, CREATE_RENDERPASS_2, create_renderpass2) \
|
||||||
EXTENSION(KHR, DEPTH_STENCIL_RESOLVE, depth_stencil_resolve) \
|
EXTENSION(KHR, DEPTH_STENCIL_RESOLVE, depth_stencil_resolve) \
|
||||||
EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \
|
EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \
|
||||||
@@ -354,7 +355,6 @@ public:
|
|||||||
|
|
||||||
#define FN_MAX_LIMIT_LIST \
|
#define FN_MAX_LIMIT_LIST \
|
||||||
FN_MAX_LIMIT_ELEM(ComputeSharedMemorySize) \
|
FN_MAX_LIMIT_ELEM(ComputeSharedMemorySize) \
|
||||||
FN_MAX_LIMIT_ELEM(ComputeWorkGroupInvocations) \
|
|
||||||
FN_MAX_LIMIT_ELEM(PerStageDescriptorSampledImages) \
|
FN_MAX_LIMIT_ELEM(PerStageDescriptorSampledImages) \
|
||||||
FN_MAX_LIMIT_ELEM(PerStageResources) \
|
FN_MAX_LIMIT_ELEM(PerStageResources) \
|
||||||
FN_MAX_LIMIT_ELEM(DescriptorSetSamplers) \
|
FN_MAX_LIMIT_ELEM(DescriptorSetSamplers) \
|
||||||
@@ -587,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;
|
||||||
@@ -680,32 +685,6 @@ FN_MAX_LIMIT_LIST
|
|||||||
return features.host_query_reset.hostQueryReset != VK_FALSE;
|
return features.host_query_reset.hostQueryReset != VK_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetMaxVertexAttribDivisor() const {
|
|
||||||
const u32 reported = properties.vertex_attribute_divisor.maxVertexAttribDivisor;
|
|
||||||
if (reported == 0) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return reported;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsVertexAttributeInstanceRateZeroDivisorSupported() const {
|
|
||||||
return features.vertex_attribute_divisor.vertexAttributeInstanceRateZeroDivisor == VK_TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetVertexAttribDivisor(u32 frequency) const {
|
|
||||||
const u32 max_divisor = GetMaxVertexAttribDivisor();
|
|
||||||
if (frequency == 0) {
|
|
||||||
if (IsVertexAttributeInstanceRateZeroDivisorSupported()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return max_divisor;
|
|
||||||
}
|
|
||||||
if (frequency > max_divisor) {
|
|
||||||
return max_divisor;
|
|
||||||
}
|
|
||||||
return frequency;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the device supports VK_EXT_transform_feedback.
|
/// Returns true if the device supports VK_EXT_transform_feedback.
|
||||||
bool IsExtTransformFeedbackSupported() const {
|
bool IsExtTransformFeedbackSupported() const {
|
||||||
return extensions.transform_feedback;
|
return extensions.transform_feedback;
|
||||||
@@ -1201,7 +1180,6 @@ private:
|
|||||||
VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptor_buffer{};
|
VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptor_buffer{};
|
||||||
VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{};
|
VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{};
|
||||||
VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
|
VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
|
||||||
VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT vertex_attribute_divisor{};
|
|
||||||
VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{};
|
VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{};
|
||||||
VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{};
|
VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{};
|
||||||
VkPhysicalDeviceCustomBorderColorPropertiesEXT custom_border_color{};
|
VkPhysicalDeviceCustomBorderColorPropertiesEXT custom_border_color{};
|
||||||
|
|||||||
Reference in New Issue
Block a user