Compare commits
12 Commits
stuffmadef
...
static-lin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c1cf35080 | ||
|
|
fe13539d72 | ||
|
|
be218cc020 | ||
|
|
c3cbe2d4d0 | ||
|
|
79b162a37c | ||
|
|
f3fbb3812f | ||
|
|
d8caa74233 | ||
|
|
17fe74ef11 | ||
|
|
73713737c6 | ||
|
|
61f3ce643c | ||
|
|
f7f6a4cde4 | ||
|
|
65fa1a37e2 |
8
.github/ISSUE_TEMPLATE/config.yml
vendored
8
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,8 +1,4 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: yuzu Discord
|
- name: Eden Discord
|
||||||
url: https://discord.com/invite/u77vRWY
|
url: https://discord.gg/HstXbPch7X
|
||||||
about: If you are experiencing an issue with yuzu, and you need tech support, or if you have a general question, try asking in the official yuzu Discord linked here. Piracy is not allowed.
|
|
||||||
- name: Community forums
|
|
||||||
url: https://community.citra-emu.org
|
|
||||||
about: This is an alternative place for tech support, however helpers there are not as active.
|
|
||||||
|
|||||||
@@ -125,14 +125,16 @@ if (YUZU_STATIC_BUILD)
|
|||||||
set(Boost_USE_STATIC_LIBS ON)
|
set(Boost_USE_STATIC_LIBS ON)
|
||||||
set(BUILD_SHARED_LIBS OFF)
|
set(BUILD_SHARED_LIBS OFF)
|
||||||
|
|
||||||
## find .a libs first (static, usually)
|
if (NOT PLATFORM_LINUX)
|
||||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
|
## find .a libs first (static, usually)
|
||||||
|
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
|
||||||
|
|
||||||
## some libraries define a Library::Name_static alternative ##
|
## some libraries define a Library::Name_static alternative ##
|
||||||
set(YUZU_STATIC_SUFFIX _static)
|
set(YUZU_STATIC_SUFFIX _static)
|
||||||
|
|
||||||
## some libraries use CMAKE_IMPORT_LIBRARY_SUFFIX e.g. Harfbuzz ##
|
## some libraries use CMAKE_IMPORT_LIBRARY_SUFFIX e.g. Harfbuzz ##
|
||||||
set(CMAKE_IMPORT_LIBRARY_SUFFIX ".a")
|
set(CMAKE_IMPORT_LIBRARY_SUFFIX ".a")
|
||||||
|
endif()
|
||||||
|
|
||||||
if (MINGW)
|
if (MINGW)
|
||||||
# simple hook to reject dynamic libs
|
# simple hook to reject dynamic libs
|
||||||
@@ -175,6 +177,15 @@ if (YUZU_STATIC_BUILD)
|
|||||||
set(SPIRV-Tools_FORCE_BUNDLED ON)
|
set(SPIRV-Tools_FORCE_BUNDLED ON)
|
||||||
set(SPIRV-Headers_FORCE_BUNDLED ON)
|
set(SPIRV-Headers_FORCE_BUNDLED ON)
|
||||||
set(zstd_FORCE_BUNDLED ON)
|
set(zstd_FORCE_BUNDLED ON)
|
||||||
|
elseif(PLATFORM_LINUX)
|
||||||
|
# Most distros don't package static libs :(
|
||||||
|
set(YUZU_USE_CPM ON)
|
||||||
|
set(CPMUTIL_FORCE_BUNDLED ON)
|
||||||
|
|
||||||
|
set(YUZU_USE_BUNDLED_FFMPEG ON)
|
||||||
|
set(YUZU_USE_BUNDLED_SDL2 ON)
|
||||||
|
set(YUZU_USE_BUNDLED_OPENSSL ON)
|
||||||
|
set(YUZU_USE_BUNDLED_SIRIT ON)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -802,6 +813,16 @@ if (YUZU_TESTS OR DYNARMIC_TESTS)
|
|||||||
find_package(Catch2)
|
find_package(Catch2)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Qt expects this target
|
||||||
|
if (YUZU_STATIC_BUILD AND PLATFORM_LINUX)
|
||||||
|
get_target_property(RDOC_TARGET RenderDoc::API ALIASED_TARGET)
|
||||||
|
if (RDOC_TARGET)
|
||||||
|
add_library(RenderDoc::RenderDoc ALIAS ${RDOC_TARGET})
|
||||||
|
else()
|
||||||
|
add_library(RenderDoc::RenderDoc ALIAS RenderDoc::API)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
if (ENABLE_QT)
|
if (ENABLE_QT)
|
||||||
if (YUZU_USE_BUNDLED_QT)
|
if (YUZU_USE_BUNDLED_QT)
|
||||||
download_qt(6.8.3)
|
download_qt(6.8.3)
|
||||||
|
|||||||
@@ -16,13 +16,15 @@ function(static_qt_link target)
|
|||||||
# NB: yes, we have to put them here twice. I have no idea why
|
# NB: yes, we have to put them here twice. I have no idea why
|
||||||
|
|
||||||
# libtiff.a
|
# libtiff.a
|
||||||
extra_libs(tiff jbig bz2 lzma deflate jpeg tiff)
|
if (MINGW)
|
||||||
|
extra_libs(tiff jbig bz2 lzma deflate jpeg tiff)
|
||||||
|
|
||||||
# libfreetype.a
|
# libfreetype.a
|
||||||
extra_libs(freetype bz2 Lerc brotlidec brotlicommon freetype)
|
extra_libs(freetype bz2 Lerc brotlidec brotlicommon freetype)
|
||||||
|
|
||||||
# libharfbuzz.a
|
# libharfbuzz.a
|
||||||
extra_libs(harfbuzz graphite2)
|
extra_libs(harfbuzz graphite2)
|
||||||
|
endif()
|
||||||
|
|
||||||
# sijfjkfnjkdfjsbjsbsdfhvbdf
|
# sijfjkfnjkdfjsbjsbsdfhvbdf
|
||||||
if (ENABLE_OPENSSL)
|
if (ENABLE_OPENSSL)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<!--
|
# Contributing
|
||||||
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
|
||||||
SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
**The Contributor's Guide has moved to [the yuzu wiki](https://github.com/yuzu-emu/yuzu/wiki/Contributing).**
|
You want to contribute? Please consult [the development guide](./docs/Development.md).
|
||||||
|
|
||||||
|
Don't forget to [get a git account](./docs/SIGNUP.md) - not a requirement per se but it's highly recommended.
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ It is written in C++ with portability in mind, and we actively maintain builds f
|
|||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://discord.gg/kXAmGCXBGD">
|
<a href="https://discord.gg/HstXbPch7X">
|
||||||
<img src="https://img.shields.io/discord/1367654015269339267?color=5865F2&label=Eden&logo=discord&logoColor=white"
|
<img src="https://img.shields.io/discord/1367654015269339267?color=5865F2&label=Eden&logo=discord&logoColor=white"
|
||||||
alt="Discord">
|
alt="Discord">
|
||||||
</a>
|
</a>
|
||||||
@@ -52,8 +52,8 @@ Check out our [website](https://eden-emu.dev) for the latest news on exciting fe
|
|||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
Most of the development happens on our Git server. It is also where [our central repository](https://git.eden-emu.dev/eden-emu/eden) is hosted. For development discussions, please join us on [Discord](https://discord.gg/kXAmGCXBGD) or [Revolt](https://rvlt.gg/qKgFEAbH).
|
Most of the development happens on our Git server. It is also where [our central repository](https://git.eden-emu.dev/eden-emu/eden) is hosted. For development discussions, please join us on [Discord](https://discord.gg/HstXbPch7X) or [Revolt](https://rvlt.gg/qKgFEAbH).
|
||||||
You can also follow us on [X (Twitter)](https://x.com/edenemuofficial) for updates and announcements.
|
You can also follow us on [X (Twitter)](https://nitter.poast.org/edenemuofficial) for updates and announcements.
|
||||||
|
|
||||||
If you would like to contribute, we are open to new developers and pull requests. Please ensure that your work is of a high standard and properly documented. You can also contact any of the developers on Discord or Revolt to learn more about the current state of the emulator.
|
If you would like to contribute, we are open to new developers and pull requests. Please ensure that your work is of a high standard and properly documented. You can also contact any of the developers on Discord or Revolt to learn more about the current state of the emulator.
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ Any donations received will go towards things such as:
|
|||||||
* Additional hardware (e.g. GPUs as needed to improve rendering support, other peripherals to add support for, etc.)
|
* Additional hardware (e.g. GPUs as needed to improve rendering support, other peripherals to add support for, etc.)
|
||||||
* CI Infrastructure
|
* CI Infrastructure
|
||||||
|
|
||||||
If you would prefer to support us in a different way, please join our [Discord](https://discord.gg/edenemu) and talk to Camille or any of our other developers.
|
If you would prefer to support us in a different way, please join our [Discord](https://discord.gg/HstXbPch7X) and talk to Camille or any of our other developers.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ else()
|
|||||||
add_compile_definitions(QT_STATICPLUGIN)
|
add_compile_definitions(QT_STATICPLUGIN)
|
||||||
|
|
||||||
# macos doesn't even let you make static executables... wtf?
|
# macos doesn't even let you make static executables... wtf?
|
||||||
if (NOT APPLE)
|
if (NOT APPLE AND NOT PLATFORM_LINUX)
|
||||||
add_compile_options(-static)
|
add_compile_options(-static)
|
||||||
if (YUZU_STATIC_BUILD)
|
if (YUZU_STATIC_BUILD)
|
||||||
# yuzu-cmd requires us to explicitly link libpthread, libgcc, and libstdc++ as static
|
# yuzu-cmd requires us to explicitly link libpthread, libgcc, and libstdc++ as static
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ android {
|
|||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionName = getGitVersion()
|
versionName = getGitVersion()
|
||||||
|
|
||||||
versionCode = autoVersion
|
versionCode = autoVersion
|
||||||
|
|
||||||
ndk {
|
ndk {
|
||||||
@@ -69,9 +68,6 @@ android {
|
|||||||
abiFilters += listOf("arm64-v8a")
|
abiFilters += listOf("arm64-v8a")
|
||||||
}
|
}
|
||||||
|
|
||||||
buildConfigField("String", "GIT_HASH", "\"${getGitHash()}\"")
|
|
||||||
buildConfigField("String", "BRANCH", "\"${getBranch()}\"")
|
|
||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
val extraCMakeArgs =
|
val extraCMakeArgs =
|
||||||
@@ -92,14 +88,14 @@ android {
|
|||||||
"-DYUZU_TESTS=OFF",
|
"-DYUZU_TESTS=OFF",
|
||||||
"-DDYNARMIC_TESTS=OFF",
|
"-DDYNARMIC_TESTS=OFF",
|
||||||
*extraCMakeArgs.toTypedArray()
|
*extraCMakeArgs.toTypedArray()
|
||||||
))
|
)
|
||||||
|
)
|
||||||
|
|
||||||
abiFilters("arm64-v8a")
|
abiFilters("arm64-v8a")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val keystoreFile = System.getenv("ANDROID_KEYSTORE_FILE")
|
val keystoreFile = System.getenv("ANDROID_KEYSTORE_FILE")
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
if (keystoreFile != null) {
|
if (keystoreFile != null) {
|
||||||
@@ -162,7 +158,39 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is really annoying but idk any other ways to fix this behavior
|
flavorDimensions.add("version")
|
||||||
|
productFlavors {
|
||||||
|
create("mainline") {
|
||||||
|
dimension = "version"
|
||||||
|
resValue("string", "app_name_suffixed", "Eden")
|
||||||
|
}
|
||||||
|
|
||||||
|
create("genshinSpoof") {
|
||||||
|
dimension = "version"
|
||||||
|
resValue("string", "app_name_suffixed", "Eden Optimized")
|
||||||
|
applicationId = "com.miHoYo.Yuanshen"
|
||||||
|
}
|
||||||
|
|
||||||
|
create("legacy") {
|
||||||
|
dimension = "version"
|
||||||
|
resValue("string", "app_name_suffixed", "Eden Legacy")
|
||||||
|
applicationId = "dev.legacy.eden_emulator"
|
||||||
|
|
||||||
|
externalNativeBuild {
|
||||||
|
cmake {
|
||||||
|
arguments.add("-DYUZU_LEGACY=ON")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
getByName("legacy") {
|
||||||
|
res.srcDirs("src/main/legacy")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is really annoying but idk any other ways to fix this behavior
|
||||||
applicationVariants.all {
|
applicationVariants.all {
|
||||||
val variant = this
|
val variant = this
|
||||||
when {
|
when {
|
||||||
@@ -187,40 +215,6 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
|
||||||
flavorDimensions.add("version")
|
|
||||||
productFlavors {
|
|
||||||
create("mainline") {
|
|
||||||
dimension = "version"
|
|
||||||
resValue("string", "app_name_suffixed", "Eden")
|
|
||||||
}
|
|
||||||
|
|
||||||
create("genshinSpoof") {
|
|
||||||
dimension = "version"
|
|
||||||
resValue("string", "app_name_suffixed", "Eden Optimized")
|
|
||||||
applicationId = "com.miHoYo.Yuanshen"
|
|
||||||
}
|
|
||||||
|
|
||||||
create("legacy") {
|
|
||||||
dimension = "version"
|
|
||||||
resValue("string", "app_name_suffixed", "Eden Legacy")
|
|
||||||
applicationId = "dev.legacy.eden_emulator"
|
|
||||||
|
|
||||||
externalNativeBuild {
|
|
||||||
cmake {
|
|
||||||
arguments.add("-DYUZU_LEGACY=ON")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
getByName("legacy") {
|
|
||||||
res.srcDirs("src/main/legacy")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
version = "3.22.1"
|
version = "3.22.1"
|
||||||
@@ -284,7 +278,6 @@ dependencies {
|
|||||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.17.2")
|
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.17.2")
|
||||||
implementation("androidx.window:window:1.3.0")
|
implementation("androidx.window:window:1.3.0")
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.2.1")
|
|
||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
implementation("org.commonmark:commonmark:0.22.0")
|
implementation("org.commonmark:commonmark:0.22.0")
|
||||||
implementation("androidx.navigation:navigation-fragment-ktx:2.8.9")
|
implementation("androidx.navigation:navigation-fragment-ktx:2.8.9")
|
||||||
@@ -302,7 +295,9 @@ fun runGitCommand(command: List<String>): String {
|
|||||||
.directory(project.rootDir)
|
.directory(project.rootDir)
|
||||||
.redirectOutput(ProcessBuilder.Redirect.PIPE)
|
.redirectOutput(ProcessBuilder.Redirect.PIPE)
|
||||||
.redirectError(ProcessBuilder.Redirect.PIPE)
|
.redirectError(ProcessBuilder.Redirect.PIPE)
|
||||||
.start().inputStream.bufferedReader().use { it.readText() }
|
.start()
|
||||||
|
.inputStream.bufferedReader()
|
||||||
|
.use { it.readText() }
|
||||||
.trim()
|
.trim()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error("Cannot find git")
|
logger.error("Cannot find git")
|
||||||
@@ -326,9 +321,3 @@ fun getGitVersion(): String {
|
|||||||
}
|
}
|
||||||
return versionName.ifEmpty { "0.0" }
|
return versionName.ifEmpty { "0.0" }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getGitHash(): String =
|
|
||||||
runGitCommand(listOf("git", "rev-parse", "--short", "HEAD")).ifEmpty { "dummy-hash" }
|
|
||||||
|
|
||||||
fun getBranch(): String =
|
|
||||||
runGitCommand(listOf("git", "rev-parse", "--abbrev-ref", "HEAD")).ifEmpty { "dummy-hash" }
|
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="org.yuzu.yuzu_emu.YuzuApplication"
|
android:name="org.yuzu.yuzu_emu.YuzuApplication"
|
||||||
@@ -110,5 +109,15 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.provider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/file_paths" />
|
||||||
|
</provider>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -222,11 +222,21 @@ object NativeLibrary {
|
|||||||
*/
|
*/
|
||||||
external fun getUpdateUrl(version: String): String
|
external fun getUpdateUrl(version: String): String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the URL to download the APK for the given version
|
||||||
|
*/
|
||||||
|
external fun getUpdateApkUrl(version: String, packageId: String): String
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the update checker is enabled through CMAKE options.
|
* Returns whether the update checker is enabled through CMAKE options.
|
||||||
*/
|
*/
|
||||||
external fun isUpdateCheckerEnabled(): Boolean
|
external fun isUpdateCheckerEnabled(): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the build version generated by CMake (BUILD_VERSION).
|
||||||
|
*/
|
||||||
|
external fun getBuildVersion(): String
|
||||||
|
|
||||||
enum class CoreError {
|
enum class CoreError {
|
||||||
ErrorSystemFiles,
|
ErrorSystemFiles,
|
||||||
ErrorSavestate,
|
ErrorSavestate,
|
||||||
|
|||||||
@@ -235,10 +235,13 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||||
|
val isPhysicalKeyboard = event.source and InputDevice.SOURCE_KEYBOARD == InputDevice.SOURCE_KEYBOARD &&
|
||||||
|
event.device?.isVirtual == false
|
||||||
|
|
||||||
if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK &&
|
if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK &&
|
||||||
event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD &&
|
event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD &&
|
||||||
event.source and InputDevice.SOURCE_KEYBOARD != InputDevice.SOURCE_KEYBOARD &&
|
event.source and InputDevice.SOURCE_MOUSE != InputDevice.SOURCE_MOUSE &&
|
||||||
event.source and InputDevice.SOURCE_MOUSE != InputDevice.SOURCE_MOUSE
|
!isPhysicalKeyboard
|
||||||
) {
|
) {
|
||||||
return super.dispatchKeyEvent(event)
|
return super.dispatchKeyEvent(event)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
|||||||
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
|
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
|
||||||
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
|
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
|
||||||
RENDERER_DEBUG("debug"),
|
RENDERER_DEBUG("debug"),
|
||||||
|
RENDERER_VERTEX_INPUT_DYNAMIC_STATE("vertex_input_dynamic_state"),
|
||||||
RENDERER_PROVOKING_VERTEX("provoking_vertex"),
|
RENDERER_PROVOKING_VERTEX("provoking_vertex"),
|
||||||
RENDERER_DESCRIPTOR_INDEXING("descriptor_indexing"),
|
RENDERER_DESCRIPTOR_INDEXING("descriptor_indexing"),
|
||||||
RENDERER_SAMPLE_SHADING("sample_shading"),
|
RENDERER_SAMPLE_SHADING("sample_shading"),
|
||||||
|
|||||||
@@ -146,6 +146,13 @@ abstract class SettingsItem(
|
|||||||
descriptionId = R.string.provoking_vertex_description
|
descriptionId = R.string.provoking_vertex_description
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
put(
|
||||||
|
SwitchSetting(
|
||||||
|
BooleanSetting.RENDERER_VERTEX_INPUT_DYNAMIC_STATE,
|
||||||
|
titleId = R.string.vertex_input_dynamic_state,
|
||||||
|
descriptionId = R.string.vertex_input_dynamic_state_description
|
||||||
|
)
|
||||||
|
)
|
||||||
put(
|
put(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
BooleanSetting.RENDERER_DESCRIPTOR_INDEXING,
|
BooleanSetting.RENDERER_DESCRIPTOR_INDEXING,
|
||||||
@@ -755,6 +762,7 @@ abstract class SettingsItem(
|
|||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
BooleanSetting.ENABLE_UPDATE_CHECKS,
|
BooleanSetting.ENABLE_UPDATE_CHECKS,
|
||||||
titleId = R.string.enable_update_checks,
|
titleId = R.string.enable_update_checks,
|
||||||
|
descriptionId = R.string.enable_update_checks_description,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
put(
|
put(
|
||||||
|
|||||||
@@ -453,6 +453,7 @@ class SettingsFragmentPresenter(
|
|||||||
sl.apply {
|
sl.apply {
|
||||||
add(HeaderSetting(R.string.veil_extensions))
|
add(HeaderSetting(R.string.veil_extensions))
|
||||||
add(ByteSetting.RENDERER_DYNA_STATE.key)
|
add(ByteSetting.RENDERER_DYNA_STATE.key)
|
||||||
|
add(BooleanSetting.RENDERER_VERTEX_INPUT_DYNAMIC_STATE.key)
|
||||||
add(BooleanSetting.RENDERER_PROVOKING_VERTEX.key)
|
add(BooleanSetting.RENDERER_PROVOKING_VERTEX.key)
|
||||||
add(BooleanSetting.RENDERER_DESCRIPTOR_INDEXING.key)
|
add(BooleanSetting.RENDERER_DESCRIPTOR_INDEXING.key)
|
||||||
add(BooleanSetting.RENDERER_SAMPLE_SHADING.key)
|
add(BooleanSetting.RENDERER_SAMPLE_SHADING.key)
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import org.yuzu.yuzu_emu.R
|
|||||||
import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding
|
import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding
|
||||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||||
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
||||||
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
|
|
||||||
class AboutFragment : Fragment() {
|
class AboutFragment : Fragment() {
|
||||||
private var _binding: FragmentAboutBinding? = null
|
private var _binding: FragmentAboutBinding? = null
|
||||||
@@ -78,11 +79,15 @@ class AboutFragment : Fragment() {
|
|||||||
binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment)
|
binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.textVersionName.text = BuildConfig.VERSION_NAME
|
val buildName = getString(R.string.app_name_suffixed)
|
||||||
|
val buildVersion = NativeLibrary.getBuildVersion()
|
||||||
|
val fullVersionText = "$buildName ($buildVersion)"
|
||||||
|
|
||||||
|
binding.textVersionName.text = fullVersionText
|
||||||
binding.buttonVersionName.setOnClickListener {
|
binding.buttonVersionName.setOnClickListener {
|
||||||
val clipBoard =
|
val clipBoard =
|
||||||
requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
val clip = ClipData.newPlainText(getString(R.string.build), BuildConfig.GIT_HASH)
|
val clip = ClipData.newPlainText(getString(R.string.build), fullVersionText)
|
||||||
clipBoard.setPrimaryClip(clip)
|
clipBoard.setPrimaryClip(clip)
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
|||||||
@@ -145,13 +145,12 @@ class GamePropertiesFragment : Fragment() {
|
|||||||
val seconds = playTimeSeconds % 60
|
val seconds = playTimeSeconds % 60
|
||||||
|
|
||||||
val readablePlayTime = when {
|
val readablePlayTime = when {
|
||||||
hours > 0 -> "${hours}h ${minutes}m ${seconds}s"
|
hours > 0 -> "$hours${getString(R.string.hours_abbr)} $minutes${getString(R.string.minutes_abbr)} $seconds${getString(R.string.seconds_abbr)}"
|
||||||
minutes > 0 -> "${minutes}m ${seconds}s"
|
minutes > 0 -> "$minutes${getString(R.string.minutes_abbr)} $seconds${getString(R.string.seconds_abbr)}"
|
||||||
else -> "${seconds}s"
|
else -> "$seconds${getString(R.string.seconds_abbr)}"
|
||||||
}
|
}
|
||||||
|
|
||||||
append(getString(R.string.playtime))
|
append(getString(R.string.playtime) + " " + readablePlayTime)
|
||||||
append(readablePlayTime)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.playtime.setOnClickListener {
|
binding.playtime.setOnClickListener {
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class GamesFragment : Fragment() {
|
|||||||
private var originalHeaderLeftMargin: Int? = null
|
private var originalHeaderLeftMargin: Int? = null
|
||||||
|
|
||||||
private var lastViewType: Int = GameAdapter.VIEW_TYPE_GRID
|
private var lastViewType: Int = GameAdapter.VIEW_TYPE_GRID
|
||||||
|
private var fallbackBottomInset: Int = 0
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val SEARCH_TEXT = "SearchText"
|
private const val SEARCH_TEXT = "SearchText"
|
||||||
@@ -208,12 +209,12 @@ class GamesFragment : Fragment() {
|
|||||||
else -> throw IllegalArgumentException("Invalid view type: $savedViewType")
|
else -> throw IllegalArgumentException("Invalid view type: $savedViewType")
|
||||||
}
|
}
|
||||||
if (savedViewType == GameAdapter.VIEW_TYPE_CAROUSEL) {
|
if (savedViewType == GameAdapter.VIEW_TYPE_CAROUSEL) {
|
||||||
doOnNextLayout {
|
(binding.gridGames as? View)?.let { it -> ViewCompat.requestApplyInsets(it)}
|
||||||
(this as? CarouselRecyclerView)?.setCarouselMode(true, gameAdapter)
|
doOnNextLayout { //Carousel: important to avoid overlap issues
|
||||||
adapter = gameAdapter
|
(this as? CarouselRecyclerView)?.notifyLaidOut(fallbackBottomInset)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
(this as? CarouselRecyclerView)?.setCarouselMode(false)
|
(this as? CarouselRecyclerView)?.setupCarousel(false)
|
||||||
}
|
}
|
||||||
adapter = gameAdapter
|
adapter = gameAdapter
|
||||||
lastViewType = savedViewType
|
lastViewType = savedViewType
|
||||||
@@ -237,9 +238,8 @@ class GamesFragment : Fragment() {
|
|||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
if (getCurrentViewType() == GameAdapter.VIEW_TYPE_CAROUSEL) {
|
if (getCurrentViewType() == GameAdapter.VIEW_TYPE_CAROUSEL) {
|
||||||
(binding.gridGames as? CarouselRecyclerView)?.restoreScrollState(
|
(binding.gridGames as? CarouselRecyclerView)?.setupCarousel(true)
|
||||||
gamesViewModel.lastScrollPosition
|
(binding.gridGames as? CarouselRecyclerView)?.restoreScrollState(gamesViewModel.lastScrollPosition)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,6 +494,11 @@ class GamesFragment : Fragment() {
|
|||||||
mlpFab.rightMargin = rightInset + fabPadding
|
mlpFab.rightMargin = rightInset + fabPadding
|
||||||
binding.addDirectory.layoutParams = mlpFab
|
binding.addDirectory.layoutParams = mlpFab
|
||||||
|
|
||||||
|
val navInsets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars())
|
||||||
|
val gestureInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures())
|
||||||
|
val bottomInset = maxOf(navInsets.bottom, gestureInsets.bottom, cutoutInsets.bottom)
|
||||||
|
fallbackBottomInset = bottomInset
|
||||||
|
(binding.gridGames as? CarouselRecyclerView)?.notifyInsetsReady(bottomInset)
|
||||||
windowInsets
|
windowInsets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,8 +53,16 @@ import androidx.core.content.edit
|
|||||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
||||||
import kotlin.text.compareTo
|
import kotlin.text.compareTo
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
|
import com.google.android.material.textview.MaterialTextView
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
|
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
|
import org.yuzu.yuzu_emu.updater.APKDownloader
|
||||||
|
import org.yuzu.yuzu_emu.updater.APKInstaller
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity(), ThemeProvider {
|
class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
private lateinit var binding: ActivityMainBinding
|
private lateinit var binding: ActivityMainBinding
|
||||||
@@ -186,9 +194,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
.setTitle(R.string.update_available)
|
.setTitle(R.string.update_available)
|
||||||
.setMessage(getString(R.string.update_available_description, version))
|
.setMessage(getString(R.string.update_available_description, version))
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
val url = NativeLibrary.getUpdateUrl(version)
|
downloadAndInstallUpdate(version)
|
||||||
val intent = Intent(Intent.ACTION_VIEW, url.toUri())
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
}
|
||||||
.setNeutralButton(R.string.cancel) { dialog, _ ->
|
.setNeutralButton(R.string.cancel) { dialog, _ ->
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
@@ -201,6 +207,87 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun downloadAndInstallUpdate(version: String) {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val packageId = applicationContext.packageName
|
||||||
|
val apkUrl = NativeLibrary.getUpdateApkUrl(version, packageId)
|
||||||
|
val apkFile = File(cacheDir, "update-$version.apk")
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
showDownloadProgressDialog()
|
||||||
|
}
|
||||||
|
|
||||||
|
val downloader = APKDownloader(apkUrl, apkFile)
|
||||||
|
downloader.download(
|
||||||
|
onProgress = { progress ->
|
||||||
|
runOnUiThread {
|
||||||
|
updateDownloadProgress(progress)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onComplete = { success ->
|
||||||
|
runOnUiThread {
|
||||||
|
dismissDownloadProgressDialog()
|
||||||
|
if (success) {
|
||||||
|
val installer = APKInstaller(this@MainActivity)
|
||||||
|
installer.install(
|
||||||
|
apkFile,
|
||||||
|
onComplete = {
|
||||||
|
Toast.makeText(
|
||||||
|
this@MainActivity,
|
||||||
|
R.string.update_installed_successfully,
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
},
|
||||||
|
onFailure = { exception ->
|
||||||
|
Toast.makeText(
|
||||||
|
this@MainActivity,
|
||||||
|
getString(R.string.update_install_failed, exception.message),
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Toast.makeText(
|
||||||
|
this@MainActivity,
|
||||||
|
getString(R.string.update_download_failed) + "\n\nURL: $apkUrl",
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var progressDialog: androidx.appcompat.app.AlertDialog? = null
|
||||||
|
private var progressBar: LinearProgressIndicator? = null
|
||||||
|
private var progressMessage: MaterialTextView? = null
|
||||||
|
|
||||||
|
private fun showDownloadProgressDialog() {
|
||||||
|
val progressView = layoutInflater.inflate(R.layout.dialog_download_progress, null)
|
||||||
|
progressBar = progressView.findViewById(R.id.download_progress_bar)
|
||||||
|
progressMessage = progressView.findViewById(R.id.download_progress_message)
|
||||||
|
|
||||||
|
progressDialog = MaterialAlertDialogBuilder(this)
|
||||||
|
.setTitle(R.string.downloading_update)
|
||||||
|
.setView(progressView)
|
||||||
|
.setCancelable(false)
|
||||||
|
.create()
|
||||||
|
progressDialog?.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateDownloadProgress(progress: Int) {
|
||||||
|
progressBar?.progress = progress
|
||||||
|
progressMessage?.text = "$progress%"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun dismissDownloadProgressDialog() {
|
||||||
|
progressDialog?.dismiss()
|
||||||
|
progressDialog = null
|
||||||
|
progressBar = null
|
||||||
|
progressMessage = null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun displayMultiplayerDialog() {
|
fun displayMultiplayerDialog() {
|
||||||
val dialog = NetPlayDialog(this)
|
val dialog = NetPlayDialog(this)
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.updater
|
||||||
|
|
||||||
|
import okhttp3.Call
|
||||||
|
import okhttp3.Callback
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
class APKDownloader(private val url: String, private val outputFile: File) {
|
||||||
|
|
||||||
|
fun download(onProgress: (Int) -> Unit, onComplete: (Boolean) -> Unit) {
|
||||||
|
val client = OkHttpClient()
|
||||||
|
val request = Request.Builder().url(url).build()
|
||||||
|
|
||||||
|
client.newCall(request).enqueue(object : Callback {
|
||||||
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
onComplete(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResponse(call: Call, response: Response) {
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
response.body?.let { body ->
|
||||||
|
val contentLength = body.contentLength()
|
||||||
|
try {
|
||||||
|
val inputStream = body.byteStream()
|
||||||
|
val outputStream = FileOutputStream(outputFile)
|
||||||
|
val buffer = ByteArray(4096)
|
||||||
|
var bytesRead: Int
|
||||||
|
var totalBytesRead: Long = 0
|
||||||
|
|
||||||
|
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
|
||||||
|
outputStream.write(buffer, 0, bytesRead)
|
||||||
|
totalBytesRead += bytesRead
|
||||||
|
val progress = (totalBytesRead * 100 / contentLength).toInt()
|
||||||
|
onProgress(progress)
|
||||||
|
}
|
||||||
|
outputStream.flush()
|
||||||
|
outputStream.close()
|
||||||
|
inputStream.close()
|
||||||
|
onComplete(true)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
onComplete(false)
|
||||||
|
}
|
||||||
|
} ?: run {
|
||||||
|
onComplete(false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onComplete(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.updater
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class APKInstaller(private val context: Context) {
|
||||||
|
|
||||||
|
fun install(apkFile: File, onComplete: () -> Unit, onFailure: (Exception) -> Unit) {
|
||||||
|
try {
|
||||||
|
val apkUri: Uri = FileProvider.getUriForFile(
|
||||||
|
context,
|
||||||
|
context.applicationContext.packageName + ".provider",
|
||||||
|
apkFile
|
||||||
|
)
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW)
|
||||||
|
intent.setDataAndType(apkUri, "application/vnd.android.package-archive")
|
||||||
|
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
context.startActivity(intent)
|
||||||
|
|
||||||
|
GlobalScope.launch {
|
||||||
|
val receiver = AppInstallReceiver(onComplete, onFailure)
|
||||||
|
context.registerReceiver(receiver, IntentFilter().apply {
|
||||||
|
addAction(Intent.ACTION_PACKAGE_ADDED)
|
||||||
|
addAction(Intent.ACTION_PACKAGE_REPLACED)
|
||||||
|
addDataScheme("package")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
onFailure(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.updater
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.util.Log
|
||||||
|
|
||||||
|
class AppInstallReceiver(
|
||||||
|
private val onComplete: () -> Unit,
|
||||||
|
private val onFailure: (Exception) -> Unit
|
||||||
|
) : BroadcastReceiver() {
|
||||||
|
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
val packageName = intent.data?.schemeSpecificPart
|
||||||
|
when (intent.action) {
|
||||||
|
Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_REPLACED -> {
|
||||||
|
Log.i("AppInstallReceiver", "Package installed or updated: $packageName")
|
||||||
|
onComplete()
|
||||||
|
context.unregisterReceiver(this)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
onFailure(Exception("Installation failed for package: $packageName"))
|
||||||
|
context.unregisterReceiver(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,9 +20,8 @@ import androidx.core.view.doOnNextLayout
|
|||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CarouselRecyclerView encapsulates all carousel logic for the games UI.
|
* CarouselRecyclerView encapsulates all carousel content for the games UI.
|
||||||
* It manages overlapping cards, center snapping, custom drawing order,
|
* It manages overlapping cards, center snapping, custom drawing order,
|
||||||
* joypad & fling navigation and mid-screen swipe-to-refresh.
|
* joypad & fling navigation and mid-screen swipe-to-refresh.
|
||||||
*/
|
*/
|
||||||
@@ -34,6 +33,7 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
|
|
||||||
private var overlapFactor: Float = 0f
|
private var overlapFactor: Float = 0f
|
||||||
private var overlapPx: Int = 0
|
private var overlapPx: Int = 0
|
||||||
|
private var bottomInset: Int = -1
|
||||||
private var overlapDecoration: OverlappingDecoration? = null
|
private var overlapDecoration: OverlappingDecoration? = null
|
||||||
private var pagerSnapHelper: PagerSnapHelper? = null
|
private var pagerSnapHelper: PagerSnapHelper? = null
|
||||||
private var scalingScrollListener: OnScrollListener? = null
|
private var scalingScrollListener: OnScrollListener? = null
|
||||||
@@ -202,46 +202,61 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setCarouselMode(enabled: Boolean, gameAdapter: GameAdapter? = null) {
|
fun refreshView() {
|
||||||
|
updateChildScalesAndAlpha()
|
||||||
|
focusCenteredCard()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notifyInsetsReady(newBottomInset: Int) {
|
||||||
|
if (bottomInset != newBottomInset) {
|
||||||
|
bottomInset = newBottomInset
|
||||||
|
}
|
||||||
|
setupCarousel(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notifyLaidOut(fallBackBottomInset: Int) {
|
||||||
|
if (bottomInset < 0) bottomInset = fallBackBottomInset
|
||||||
|
var gameAdapter = adapter as? GameAdapter ?: return
|
||||||
|
var newCardSize = cardSize(bottomInset)
|
||||||
|
if (gameAdapter.cardSize != newCardSize) {
|
||||||
|
gameAdapter.setCardSize(newCardSize)
|
||||||
|
}
|
||||||
|
setupCarousel(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cardSize(bottomInset: Int): Int {
|
||||||
|
val internalFactor = resources.getFraction(R.fraction.carousel_card_size_factor, 1, 1)
|
||||||
|
val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn(
|
||||||
|
0f,
|
||||||
|
1f
|
||||||
|
)
|
||||||
|
return (userFactor * (height - bottomInset)).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setupCarousel(enabled: Boolean) {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
|
val gameAdapter = adapter as? GameAdapter ?: return
|
||||||
|
if (gameAdapter.cardSize == 0) return
|
||||||
|
if (bottomInset < 0) return
|
||||||
|
|
||||||
useCustomDrawingOrder = true
|
useCustomDrawingOrder = true
|
||||||
|
val cardSize = gameAdapter.cardSize
|
||||||
|
|
||||||
val insets = rootWindowInsets?.let { WindowInsetsCompat.toWindowInsetsCompat(it, this) }
|
val internalOverlapFactor = resources.getFraction(R.fraction.carousel_overlap_factor,1,1)
|
||||||
val bottomInset = insets?.getInsets(WindowInsetsCompat.Type.systemBars())?.bottom ?: 0
|
overlapFactor = preferences.getFloat(CAROUSEL_OVERLAP_FACTOR, internalOverlapFactor).coerceIn(0f,1f)
|
||||||
val internalFactor = resources.getFraction(R.fraction.carousel_card_size_factor, 1, 1)
|
|
||||||
val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn(
|
|
||||||
0f,
|
|
||||||
1f
|
|
||||||
)
|
|
||||||
val cardSize = (userFactor * (height - bottomInset)).toInt()
|
|
||||||
gameAdapter?.setCardSize(cardSize)
|
|
||||||
|
|
||||||
val internalOverlapFactor = resources.getFraction(
|
|
||||||
R.fraction.carousel_overlap_factor,
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
)
|
|
||||||
overlapFactor = preferences.getFloat(CAROUSEL_OVERLAP_FACTOR, internalOverlapFactor).coerceIn(
|
|
||||||
0f,
|
|
||||||
1f
|
|
||||||
)
|
|
||||||
overlapPx = (cardSize * overlapFactor).toInt()
|
overlapPx = (cardSize * overlapFactor).toInt()
|
||||||
|
|
||||||
val internalFlingMultiplier = resources.getFraction(
|
val internalFlingMultiplier = resources.getFraction(R.fraction.carousel_fling_multiplier,1,1)
|
||||||
R.fraction.carousel_fling_multiplier,
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
)
|
|
||||||
flingMultiplier = preferences.getFloat(
|
flingMultiplier = preferences.getFloat(
|
||||||
CAROUSEL_FLING_MULTIPLIER,
|
CAROUSEL_FLING_MULTIPLIER,
|
||||||
internalFlingMultiplier
|
internalFlingMultiplier
|
||||||
).coerceIn(1f, 5f)
|
).coerceIn(1f, 5f)
|
||||||
|
|
||||||
gameAdapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
gameAdapter .registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||||
override fun onChanged() {
|
override fun onChanged() {
|
||||||
if (pendingScrollAfterReload) {
|
if (pendingScrollAfterReload) {
|
||||||
post {
|
doOnNextLayout {
|
||||||
jigglyScroll()
|
refreshView()
|
||||||
pendingScrollAfterReload = false
|
pendingScrollAfterReload = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,7 +272,7 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
addItemDecoration(overlapDecoration!!)
|
addItemDecoration(overlapDecoration!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gradual scalingAdd commentMore actions
|
// Gradual scaling on scroll
|
||||||
if (scalingScrollListener == null) {
|
if (scalingScrollListener == null) {
|
||||||
scalingScrollListener = object : OnScrollListener() {
|
scalingScrollListener = object : OnScrollListener() {
|
||||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
@@ -315,8 +330,7 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
super.scrollToPosition(position)
|
super.scrollToPosition(position)
|
||||||
(layoutManager as? LinearLayoutManager)?.scrollToPositionWithOffset(position, overlapPx)
|
(layoutManager as? LinearLayoutManager)?.scrollToPositionWithOffset(position, overlapPx)
|
||||||
doOnNextLayout {
|
doOnNextLayout {
|
||||||
updateChildScalesAndAlpha()
|
refreshView()
|
||||||
focusCenteredCard()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,12 +396,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
return sorted[i].first
|
return sorted[i].first
|
||||||
}
|
}
|
||||||
|
|
||||||
fun jigglyScroll() {
|
|
||||||
scrollBy(-1, 0)
|
|
||||||
scrollBy(1, 0)
|
|
||||||
focusCenteredCard()
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class OverlappingDecoration(private val overlap: Int) : ItemDecoration() {
|
inner class OverlappingDecoration(private val overlap: Int) : ItemDecoration() {
|
||||||
override fun getItemOffsets(
|
override fun getItemOffsets(
|
||||||
outRect: Rect,
|
outRect: Rect,
|
||||||
|
|||||||
@@ -1613,6 +1613,43 @@ JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateUrl(
|
|||||||
env->ReleaseStringUTFChars(version, version_str);
|
env->ReleaseStringUTFChars(version, version_str);
|
||||||
return env->NewStringUTF(url.c_str());
|
return env->NewStringUTF(url.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateApkUrl(
|
||||||
|
JNIEnv* env,
|
||||||
|
jobject obj,
|
||||||
|
jstring version,
|
||||||
|
jstring packageId) {
|
||||||
|
const char* version_str = env->GetStringUTFChars(version, nullptr);
|
||||||
|
const char* package_id_str = env->GetStringUTFChars(packageId, nullptr);
|
||||||
|
|
||||||
|
std::string variant;
|
||||||
|
std::string package_id(package_id_str);
|
||||||
|
|
||||||
|
if (package_id.find("dev.legacy.eden_emulator") != std::string::npos) {
|
||||||
|
variant = "legacy";
|
||||||
|
} else if (package_id.find("com.miHoYo.Yuanshen") != std::string::npos) {
|
||||||
|
variant = "optimized";
|
||||||
|
} else {
|
||||||
|
variant = "standard";
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string apk_filename = fmt::format("Eden-Android-{}-{}.apk", version_str, variant);
|
||||||
|
const std::string url = fmt::format("{}/{}/releases/download/{}/{}",
|
||||||
|
std::string{Common::g_build_auto_update_website},
|
||||||
|
std::string{Common::g_build_auto_update_repo},
|
||||||
|
version_str,
|
||||||
|
apk_filename);
|
||||||
|
|
||||||
|
env->ReleaseStringUTFChars(version, version_str);
|
||||||
|
env->ReleaseStringUTFChars(packageId, package_id_str);
|
||||||
|
return env->NewStringUTF(url.c_str());
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getBuildVersion(
|
||||||
|
JNIEnv* env,
|
||||||
|
[[maybe_unused]] jobject obj) {
|
||||||
|
return env->NewStringUTF(Common::g_build_version);
|
||||||
|
}
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/download_progress_message"
|
||||||
|
style="@style/TextAppearance.Material3.BodyMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:text="0%"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
|
android:id="@+id/download_progress_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:max="100"
|
||||||
|
android:progress="0"
|
||||||
|
app:indicatorColor="?attr/colorPrimary"
|
||||||
|
app:trackColor="?attr/colorSurfaceVariant"
|
||||||
|
app:trackCornerRadius="4dp"
|
||||||
|
app:trackThickness="8dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -94,6 +94,8 @@
|
|||||||
<string name="dyna_state">Extended Dynamic State</string>
|
<string name="dyna_state">Extended Dynamic State</string>
|
||||||
<string name="dyna_state_description">Controls the number of features that can be used in Extended Dynamic State. Higher numbers allow for more features and can increase performance, but may cause issues with some drivers and vendors. The default value may vary depending on your system and hardware capabilities. This value can be changed until stability and a better visual quality are achieved.</string>
|
<string name="dyna_state_description">Controls the number of features that can be used in Extended Dynamic State. Higher numbers allow for more features and can increase performance, but may cause issues with some drivers and vendors. The default value may vary depending on your system and hardware capabilities. This value can be changed until stability and a better visual quality are achieved.</string>
|
||||||
<string name="disabled">Disabled</string>
|
<string name="disabled">Disabled</string>
|
||||||
|
<string name="vertex_input_dynamic_state">Vertex Input Dynamic State</string>
|
||||||
|
<string name="vertex_input_dynamic_state_description">Enables vertex input dynamic state feature for better quality and performance.</string>
|
||||||
<string name="provoking_vertex">Provoking Vertex</string>
|
<string name="provoking_vertex">Provoking Vertex</string>
|
||||||
<string name="provoking_vertex_description">Improves lighting and vertex handling in certain games. Only supported on Vulkan 1.0+ GPUs.</string>
|
<string name="provoking_vertex_description">Improves lighting and vertex handling in certain games. Only supported on Vulkan 1.0+ GPUs.</string>
|
||||||
<string name="descriptor_indexing">Descriptor Indexing</string>
|
<string name="descriptor_indexing">Descriptor Indexing</string>
|
||||||
@@ -269,9 +271,14 @@
|
|||||||
<string name="folder">Folder</string>
|
<string name="folder">Folder</string>
|
||||||
<string name="dont_show_again">Don\'t Show Again</string>
|
<string name="dont_show_again">Don\'t Show Again</string>
|
||||||
<string name="add_directory_success">New game directory added successfully </string>
|
<string name="add_directory_success">New game directory added successfully </string>
|
||||||
<string name="enable_update_checks">Check for updates on app startup.</string>
|
<string name="enable_update_checks">Check for Updates</string>
|
||||||
|
<string name="enable_update_checks_description">Check for updates on launch, and optionally download and install the new update.</string>
|
||||||
<string name="update_available">Update Available</string>
|
<string name="update_available">Update Available</string>
|
||||||
<string name="update_available_description">A new version is available: %1$s\n\nWould you like to download it?</string>
|
<string name="update_available_description">A new version is available: %1$s\n\nWould you like to download it?</string>
|
||||||
|
<string name="downloading_update">Downloading Update</string>
|
||||||
|
<string name="update_download_failed">Failed to download update</string>
|
||||||
|
<string name="update_installed_successfully">Update installed successfully</string>
|
||||||
|
<string name="update_install_failed">Failed to install update: %1$s</string>
|
||||||
<string name="home_search">Search</string>
|
<string name="home_search">Search</string>
|
||||||
<string name="home_settings">Settings</string>
|
<string name="home_settings">Settings</string>
|
||||||
<string name="empty_gamelist">No files were found or no game directory has been selected yet.</string>
|
<string name="empty_gamelist">No files were found or no game directory has been selected yet.</string>
|
||||||
@@ -441,9 +448,9 @@
|
|||||||
<string name="user_data_import_success">User data imported successfully</string>
|
<string name="user_data_import_success">User data imported successfully</string>
|
||||||
<string name="user_data_export_cancelled">Export cancelled</string>
|
<string name="user_data_export_cancelled">Export cancelled</string>
|
||||||
<string name="user_data_import_failed_description">Make sure the user data folders are at the root of the zip folder and contain a config file at config/config.ini and try again.</string>
|
<string name="user_data_import_failed_description">Make sure the user data folders are at the root of the zip folder and contain a config file at config/config.ini and try again.</string>
|
||||||
<string name="discord_link" translatable="false">https://discord.gg/kXAmGCXBGD</string>
|
<string name="discord_link" translatable="false">https://discord.gg/HstXbPch7X</string>
|
||||||
<string name="revolt_link" translatable="false">https://rvlt.gg/qKgFEAbH</string>
|
<string name="revolt_link" translatable="false">https://rvlt.gg/qKgFEAbH</string>
|
||||||
<string name="x_link" translatable="false">https://x.com/edenemuofficial</string>
|
<string name="x_link" translatable="false">https://nitter.poast.org/edenemuofficial</string>
|
||||||
<string name="website_link" translatable="false">https://eden-emu.dev</string>
|
<string name="website_link" translatable="false">https://eden-emu.dev</string>
|
||||||
<string name="github_link" translatable="false">https://git.eden-emu.dev/eden-emu</string>
|
<string name="github_link" translatable="false">https://git.eden-emu.dev/eden-emu</string>
|
||||||
|
|
||||||
@@ -706,7 +713,7 @@
|
|||||||
<string name="copy_details">Copy details</string>
|
<string name="copy_details">Copy details</string>
|
||||||
<string name="add_ons">Add-ons</string>
|
<string name="add_ons">Add-ons</string>
|
||||||
<string name="add_ons_description">Toggle mods, updates and DLC</string>
|
<string name="add_ons_description">Toggle mods, updates and DLC</string>
|
||||||
<string name="playtime">Playtime: </string>
|
<string name="playtime">Playtime:</string>
|
||||||
<string name="reset_playtime">Clear Playtime</string>
|
<string name="reset_playtime">Clear Playtime</string>
|
||||||
<string name="reset_playtime_description">Reset the current game\'s playtime back to 0 seconds</string>
|
<string name="reset_playtime_description">Reset the current game\'s playtime back to 0 seconds</string>
|
||||||
<string name="reset_playtime_warning_description">This will clear the current game\'s playtime data. Are you sure?</string>
|
<string name="reset_playtime_warning_description">This will clear the current game\'s playtime data. Are you sure?</string>
|
||||||
@@ -714,6 +721,9 @@
|
|||||||
<string name="edit_playtime">Edit Playtime</string>
|
<string name="edit_playtime">Edit Playtime</string>
|
||||||
<string name="hours">Hours</string>
|
<string name="hours">Hours</string>
|
||||||
<string name="minutes">Minutes</string>
|
<string name="minutes">Minutes</string>
|
||||||
|
<string name="hours_abbr">h</string>
|
||||||
|
<string name="minutes_abbr">m</string>
|
||||||
|
<string name="seconds_abbr">s</string>
|
||||||
<string name="hours_must_be_between_0_and_9999">Hours must be between 0 and 9999</string>
|
<string name="hours_must_be_between_0_and_9999">Hours must be between 0 and 9999</string>
|
||||||
<string name="minutes_must_be_between_0_and_59">Minutes must be between 0 and 59</string>
|
<string name="minutes_must_be_between_0_and_59">Minutes must be between 0 and 59</string>
|
||||||
<string name="seconds_must_be_between_0_and_59">Seconds must be between 0 and 59</string>
|
<string name="seconds_must_be_between_0_and_59">Seconds must be between 0 and 59</string>
|
||||||
|
|||||||
8
src/android/app/src/main/res/xml/file_paths.xml
Normal file
8
src/android/app/src/main/res/xml/file_paths.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- this is required to share files in the app's internal storage -->
|
||||||
|
<cache-path name="apk_cache" path="." />
|
||||||
|
<external-cache-path name="external_apk_cache" path="." />
|
||||||
|
<files-path name="files" path="." />
|
||||||
|
<external-files-path name="external_files" path="." />
|
||||||
|
</paths>
|
||||||
@@ -546,6 +546,7 @@ struct Values {
|
|||||||
Category::RendererExtensions,
|
Category::RendererExtensions,
|
||||||
Specialization::Scalar};
|
Specialization::Scalar};
|
||||||
|
|
||||||
|
SwitchableSetting<bool> vertex_input_dynamic_state{linkage, true, "vertex_input_dynamic_state", Category::RendererExtensions};
|
||||||
SwitchableSetting<bool> provoking_vertex{linkage, false, "provoking_vertex", Category::RendererExtensions};
|
SwitchableSetting<bool> provoking_vertex{linkage, false, "provoking_vertex", Category::RendererExtensions};
|
||||||
SwitchableSetting<bool> descriptor_indexing{linkage, false, "descriptor_indexing", Category::RendererExtensions};
|
SwitchableSetting<bool> descriptor_indexing{linkage, false, "descriptor_indexing", Category::RendererExtensions};
|
||||||
SwitchableSetting<bool> sample_shading{linkage, false, "sample_shading", Category::RendererExtensions, Specialization::Paired};
|
SwitchableSetting<bool> sample_shading{linkage, false, "sample_shading", Category::RendererExtensions, Specialization::Paired};
|
||||||
|
|||||||
@@ -167,25 +167,9 @@ void PlayTimeManager::ResetProgramPlayTime(u64 program_id) {
|
|||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string PlayTimeManager::GetReadablePlayTime(u64 time_seconds) {
|
std::string PlayTimeManager::GetReadablePlayTime(u64 t) {
|
||||||
if (time_seconds == 0) {
|
return t > 0 ? fmt::format("{:02}:{:02}:{:02}", t / 3600, (t / 60) % 60, t % 60)
|
||||||
return {};
|
: std::string{};
|
||||||
}
|
|
||||||
|
|
||||||
const auto time_minutes = std::max(static_cast<double>(time_seconds) / 60.0, 1.0);
|
|
||||||
const auto time_hours = static_cast<double>(time_seconds) / 3600.0;
|
|
||||||
const bool is_minutes = time_minutes < 60.0;
|
|
||||||
|
|
||||||
if (is_minutes) {
|
|
||||||
return fmt::format("{:.0f} m", time_minutes);
|
|
||||||
} else {
|
|
||||||
const bool has_remainder = time_seconds % 60 != 0;
|
|
||||||
if (has_remainder) {
|
|
||||||
return fmt::format("{:.1f} h", time_hours);
|
|
||||||
} else {
|
|
||||||
return fmt::format("{:.0f} h", time_hours);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string PlayTimeManager::GetPlayTimeHours(u64 time_seconds) {
|
std::string PlayTimeManager::GetPlayTimeHours(u64 time_seconds) {
|
||||||
|
|||||||
@@ -329,6 +329,11 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent)
|
|||||||
tr("Extended Dynamic State"),
|
tr("Extended Dynamic State"),
|
||||||
tr("Controls the number of features that can be used in Extended Dynamic State.\nHigher numbers allow for more features and can increase performance, but may cause issues.\nThe default value is per-system."));
|
tr("Controls the number of features that can be used in Extended Dynamic State.\nHigher numbers allow for more features and can increase performance, but may cause issues.\nThe default value is per-system."));
|
||||||
|
|
||||||
|
INSERT(Settings,
|
||||||
|
vertex_input_dynamic_state,
|
||||||
|
tr("Vertex Input Dynamic State"),
|
||||||
|
tr("Enables vertex input dynamic state feature for better quality and performance."));
|
||||||
|
|
||||||
INSERT(Settings,
|
INSERT(Settings,
|
||||||
provoking_vertex,
|
provoking_vertex,
|
||||||
tr("Provoking Vertex"),
|
tr("Provoking Vertex"),
|
||||||
|
|||||||
@@ -1196,17 +1196,15 @@ void RasterizerOpenGL::SyncLogicOpState() {
|
|||||||
|
|
||||||
if (device.IsAmd()) {
|
if (device.IsAmd()) {
|
||||||
using namespace Tegra::Engines;
|
using namespace Tegra::Engines;
|
||||||
struct In {
|
|
||||||
const Maxwell3D::Regs::VertexAttribute::Type d;
|
|
||||||
In(Maxwell3D::Regs::VertexAttribute::Type n) : d(n) {}
|
|
||||||
bool operator()(Maxwell3D::Regs::VertexAttribute n) const {
|
|
||||||
return n.type == d;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
bool has_float =
|
bool has_float = std::any_of(
|
||||||
std::any_of(regs.vertex_attrib_format.begin(), regs.vertex_attrib_format.end(),
|
regs.vertex_attrib_format.begin(),
|
||||||
In(Maxwell3D::Regs::VertexAttribute::Type::Float));
|
regs.vertex_attrib_format.end(),
|
||||||
|
[](const auto& n) {
|
||||||
|
return n.type == Maxwell3D::Regs::VertexAttribute::Type::Float;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
regs.logic_op.enable = static_cast<u32>(!has_float);
|
regs.logic_op.enable = static_cast<u32>(!has_float);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -579,8 +579,7 @@ void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 fir
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size,
|
void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride) {
|
||||||
u32 stride) {
|
|
||||||
if (index >= device.GetMaxVertexInputBindings()) {
|
if (index >= device.GetMaxVertexInputBindings()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -845,10 +845,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
|||||||
VK_DYNAMIC_STATE_LINE_WIDTH,
|
VK_DYNAMIC_STATE_LINE_WIDTH,
|
||||||
};
|
};
|
||||||
if (key.state.extended_dynamic_state) {
|
if (key.state.extended_dynamic_state) {
|
||||||
static constexpr std::array extended{
|
std::vector<VkDynamicState> extended{
|
||||||
VK_DYNAMIC_STATE_CULL_MODE_EXT,
|
VK_DYNAMIC_STATE_CULL_MODE_EXT,
|
||||||
VK_DYNAMIC_STATE_FRONT_FACE_EXT,
|
VK_DYNAMIC_STATE_FRONT_FACE_EXT,
|
||||||
//VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT, //Disabled for VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME
|
|
||||||
VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT,
|
VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT,
|
||||||
VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT,
|
VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT,
|
||||||
VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT,
|
VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT,
|
||||||
@@ -856,6 +855,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
|||||||
VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT,
|
VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT,
|
||||||
VK_DYNAMIC_STATE_STENCIL_OP_EXT,
|
VK_DYNAMIC_STATE_STENCIL_OP_EXT,
|
||||||
};
|
};
|
||||||
|
if (!device.IsExtVertexInputDynamicStateSupported()) {
|
||||||
|
extended.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT);
|
||||||
|
}
|
||||||
if (key.state.dynamic_vertex_input) {
|
if (key.state.dynamic_vertex_input) {
|
||||||
dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
|
dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -404,17 +404,13 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
|||||||
device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays);
|
device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays);
|
||||||
}
|
}
|
||||||
|
|
||||||
const u8 dynamic_state = Settings::values.dyna_state.GetValue();
|
|
||||||
|
|
||||||
LOG_INFO(Render_Vulkan, "DynamicState value is set to {}", (u32) dynamic_state);
|
|
||||||
|
|
||||||
dynamic_features = DynamicFeatures{
|
dynamic_features = DynamicFeatures{
|
||||||
.has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported() && dynamic_state > 0,
|
.has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported(),
|
||||||
.has_extended_dynamic_state_2 = device.IsExtExtendedDynamicState2Supported() && dynamic_state > 1,
|
.has_extended_dynamic_state_2 = device.IsExtExtendedDynamicState2Supported(),
|
||||||
.has_extended_dynamic_state_2_extra = device.IsExtExtendedDynamicState2ExtrasSupported() && dynamic_state > 1,
|
.has_extended_dynamic_state_2_extra = device.IsExtExtendedDynamicState2ExtrasSupported(),
|
||||||
.has_extended_dynamic_state_3_blend = device.IsExtExtendedDynamicState3BlendingSupported() && dynamic_state > 2,
|
.has_extended_dynamic_state_3_blend = device.IsExtExtendedDynamicState3BlendingSupported(),
|
||||||
.has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported() && dynamic_state > 2,
|
.has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported(),
|
||||||
.has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported() && dynamic_state > 0,
|
.has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,41 +62,29 @@ struct DrawParams {
|
|||||||
VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t index, float scale) {
|
VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t index, float scale) {
|
||||||
const auto& src = regs.viewport_transform[index];
|
const auto& src = regs.viewport_transform[index];
|
||||||
const auto conv = [scale](float value) {
|
const auto conv = [scale](float value) {
|
||||||
float new_value = value * scale;
|
float const new_value = value * scale;
|
||||||
if (scale < 1.0f) {
|
return scale < 1.0f
|
||||||
const bool sign = std::signbit(value);
|
? std::round(std::abs(new_value)) * (std::signbit(new_value) ? -1.f : 1.f)
|
||||||
new_value = std::round(std::abs(new_value));
|
: new_value;
|
||||||
new_value = sign ? -new_value : new_value;
|
|
||||||
}
|
|
||||||
return new_value;
|
|
||||||
};
|
};
|
||||||
const float x = conv(src.translate_x - src.scale_x);
|
float const w = src.scale_x;
|
||||||
const float width = conv(src.scale_x * 2.0f);
|
float h = src.scale_y;
|
||||||
float y = conv(src.translate_y - src.scale_y);
|
if (regs.window_origin.mode == Maxwell::WindowOrigin::Mode::LowerLeft) // Flip by surface clip height
|
||||||
float height = conv(src.scale_y * 2.0f);
|
h = -h;
|
||||||
|
if (!device.IsNvViewportSwizzleSupported() && src.swizzle.y == Maxwell::ViewportSwizzle::NegativeY) // Flip by viewport height
|
||||||
const bool lower_left = regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft;
|
h = -h;
|
||||||
const bool y_negate = !device.IsNvViewportSwizzleSupported() &&
|
// In theory, a raster flip is equivalent to a texture flip for a whole square viewport
|
||||||
src.swizzle.y == Maxwell::ViewportSwizzle::NegativeY;
|
// TODO: one day implement this properly and raster flip the triangles, not the whole viewport... guh
|
||||||
|
if(regs.viewport_transform[1].scale_y == 0 && regs.window_origin.flip_y != 0)
|
||||||
if (lower_left) {
|
h = -h;
|
||||||
// Flip by surface clip height
|
float const x = src.translate_x - w;
|
||||||
y += conv(static_cast<f32>(regs.surface_clip.height));
|
float const y = src.translate_y - h;
|
||||||
height = -height;
|
float const reduce_z = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1.0f : 0.0f;
|
||||||
}
|
|
||||||
|
|
||||||
if (y_negate) {
|
|
||||||
// Flip by viewport height
|
|
||||||
y += height;
|
|
||||||
height = -height;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float reduce_z = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1.0f : 0.0f;
|
|
||||||
VkViewport viewport{
|
VkViewport viewport{
|
||||||
.x = x,
|
.x = conv(x),
|
||||||
.y = y,
|
.y = conv(y),
|
||||||
.width = width != 0.0f ? width : 1.0f,
|
.width = w != 0.0f ? conv(w * 2.f) : 1.0f,
|
||||||
.height = height != 0.0f ? height : 1.0f,
|
.height = h != 0.0f ? conv(h * 2.f) : 1.0f,
|
||||||
.minDepth = src.translate_z - src.scale_z * reduce_z,
|
.minDepth = src.translate_z - src.scale_z * reduce_z,
|
||||||
.maxDepth = src.translate_z + src.scale_z,
|
.maxDepth = src.translate_z + src.scale_z,
|
||||||
};
|
};
|
||||||
@@ -945,7 +933,6 @@ bool AccelerateDMA::BufferToImage(const Tegra::DMA::ImageCopy& copy_info,
|
|||||||
|
|
||||||
void RasterizerVulkan::UpdateDynamicStates() {
|
void RasterizerVulkan::UpdateDynamicStates() {
|
||||||
auto& regs = maxwell3d->regs;
|
auto& regs = maxwell3d->regs;
|
||||||
|
|
||||||
UpdateViewportsState(regs);
|
UpdateViewportsState(regs);
|
||||||
UpdateScissorsState(regs);
|
UpdateScissorsState(regs);
|
||||||
UpdateDepthBias(regs);
|
UpdateDepthBias(regs);
|
||||||
@@ -953,10 +940,7 @@ void RasterizerVulkan::UpdateDynamicStates() {
|
|||||||
UpdateDepthBounds(regs);
|
UpdateDepthBounds(regs);
|
||||||
UpdateStencilFaces(regs);
|
UpdateStencilFaces(regs);
|
||||||
UpdateLineWidth(regs);
|
UpdateLineWidth(regs);
|
||||||
|
if (device.IsExtExtendedDynamicStateSupported()) {
|
||||||
const u8 dynamic_state = Settings::values.dyna_state.GetValue();
|
|
||||||
|
|
||||||
if (device.IsExtExtendedDynamicStateSupported() && dynamic_state > 0) {
|
|
||||||
UpdateCullMode(regs);
|
UpdateCullMode(regs);
|
||||||
UpdateDepthCompareOp(regs);
|
UpdateDepthCompareOp(regs);
|
||||||
UpdateFrontFace(regs);
|
UpdateFrontFace(regs);
|
||||||
@@ -966,42 +950,45 @@ void RasterizerVulkan::UpdateDynamicStates() {
|
|||||||
UpdateDepthTestEnable(regs);
|
UpdateDepthTestEnable(regs);
|
||||||
UpdateDepthWriteEnable(regs);
|
UpdateDepthWriteEnable(regs);
|
||||||
UpdateStencilTestEnable(regs);
|
UpdateStencilTestEnable(regs);
|
||||||
if (device.IsExtExtendedDynamicState2Supported() && dynamic_state > 1) {
|
if (device.IsExtExtendedDynamicState2Supported()) {
|
||||||
UpdatePrimitiveRestartEnable(regs);
|
UpdatePrimitiveRestartEnable(regs);
|
||||||
UpdateRasterizerDiscardEnable(regs);
|
UpdateRasterizerDiscardEnable(regs);
|
||||||
UpdateDepthBiasEnable(regs);
|
UpdateDepthBiasEnable(regs);
|
||||||
}
|
}
|
||||||
if (device.IsExtExtendedDynamicState3EnablesSupported() && dynamic_state > 2) {
|
if (device.IsExtExtendedDynamicState3EnablesSupported()) {
|
||||||
using namespace Tegra::Engines;
|
using namespace Tegra::Engines;
|
||||||
if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE || device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) {
|
if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE || device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) {
|
||||||
struct In {
|
const auto has_float = std::any_of(
|
||||||
const Maxwell3D::Regs::VertexAttribute::Type d;
|
regs.vertex_attrib_format.begin(),
|
||||||
In(Maxwell3D::Regs::VertexAttribute::Type n) : d(n) {}
|
regs.vertex_attrib_format.end(),
|
||||||
bool operator()(Maxwell3D::Regs::VertexAttribute n) const {
|
[](const auto& attrib) {
|
||||||
return n.type == d;
|
return attrib.type == Maxwell3D::Regs::VertexAttribute::Type::Float;
|
||||||
}
|
}
|
||||||
};
|
);
|
||||||
auto has_float = std::any_of(regs.vertex_attrib_format.begin(), regs.vertex_attrib_format.end(), In(Maxwell3D::Regs::VertexAttribute::Type::Float));
|
|
||||||
if (regs.logic_op.enable) {
|
if (regs.logic_op.enable) {
|
||||||
regs.logic_op.enable = static_cast<u32>(!has_float);
|
regs.logic_op.enable = static_cast<u32>(!has_float);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UpdateLogicOpEnable(regs);
|
UpdateLogicOpEnable(regs);
|
||||||
UpdateDepthClampEnable(regs);
|
UpdateDepthClampEnable(regs);
|
||||||
UpdateLineStippleEnable(regs);
|
|
||||||
UpdateConservativeRasterizationMode(regs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (device.IsExtExtendedDynamicState2ExtrasSupported() && dynamic_state > 1) {
|
if (device.IsExtExtendedDynamicState2ExtrasSupported()) {
|
||||||
UpdateLogicOp(regs);
|
UpdateLogicOp(regs);
|
||||||
}
|
}
|
||||||
if (device.IsExtExtendedDynamicState3BlendingSupported() && dynamic_state > 2) {
|
if (device.IsExtExtendedDynamicState3BlendingSupported()) {
|
||||||
UpdateBlending(regs);
|
UpdateBlending(regs);
|
||||||
}
|
}
|
||||||
|
if (device.IsExtExtendedDynamicState3EnablesSupported()) {
|
||||||
|
UpdateLineStippleEnable(regs);
|
||||||
|
UpdateConservativeRasterizationMode(regs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (device.IsExtVertexInputDynamicStateSupported() && dynamic_state > 0)
|
if (device.IsExtVertexInputDynamicStateSupported()) {
|
||||||
if (auto* gp = pipeline_cache.CurrentGraphicsPipeline(); gp && gp->HasDynamicVertexInput())
|
if (auto* gp = pipeline_cache.CurrentGraphicsPipeline(); gp && gp->HasDynamicVertexInput()) {
|
||||||
UpdateVertexInput(regs);
|
UpdateVertexInput(regs);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerVulkan::HandleTransformFeedback() {
|
void RasterizerVulkan::HandleTransformFeedback() {
|
||||||
@@ -1027,10 +1014,10 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!regs.viewport_scale_offset_enabled) {
|
if (!regs.viewport_scale_offset_enabled) {
|
||||||
float x = static_cast<float>(regs.surface_clip.x);
|
float x = float(regs.surface_clip.x);
|
||||||
float y = static_cast<float>(regs.surface_clip.y);
|
float y = float(regs.surface_clip.y);
|
||||||
float width = (std::max)(1.0f, static_cast<float>(regs.surface_clip.width));
|
float width = (std::max)(1.0f, float(regs.surface_clip.width));
|
||||||
float height = (std::max)(1.0f, static_cast<float>(regs.surface_clip.height));
|
float height = (std::max)(1.0f, float(regs.surface_clip.height));
|
||||||
if (regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft) {
|
if (regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft) {
|
||||||
y += height;
|
y += height;
|
||||||
height = -height;
|
height = -height;
|
||||||
|
|||||||
@@ -287,10 +287,10 @@ void Scheduler::EndRenderPass()
|
|||||||
|
|
||||||
for (size_t i = 0; i < num_images; ++i) {
|
for (size_t i = 0; i < num_images; ++i) {
|
||||||
const VkImageSubresourceRange& range = ranges[i];
|
const VkImageSubresourceRange& range = ranges[i];
|
||||||
const bool is_color = range.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT;
|
const bool is_color = (range.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0;
|
||||||
const bool is_depth_stencil = range.aspectMask
|
const bool is_depth_stencil = (range.aspectMask
|
||||||
& (VK_IMAGE_ASPECT_DEPTH_BIT
|
& (VK_IMAGE_ASPECT_DEPTH_BIT
|
||||||
| VK_IMAGE_ASPECT_STENCIL_BIT);
|
| VK_IMAGE_ASPECT_STENCIL_BIT)) !=0;
|
||||||
|
|
||||||
VkAccessFlags src_access = 0;
|
VkAccessFlags src_access = 0;
|
||||||
VkPipelineStageFlags this_stage = 0;
|
VkPipelineStageFlags this_stage = 0;
|
||||||
@@ -326,14 +326,19 @@ void Scheduler::EndRenderPass()
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Graft: ensure explicit fragment tests + color output stages are always synchronized (AMD/Windows fix)
|
||||||
|
src_stages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
|
||||||
|
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
||||||
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||||
|
|
||||||
cmdbuf.EndRenderPass();
|
cmdbuf.EndRenderPass();
|
||||||
|
|
||||||
cmdbuf.PipelineBarrier(src_stages,
|
cmdbuf.PipelineBarrier(src_stages,
|
||||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||||
0,
|
0,
|
||||||
{},
|
nullptr,
|
||||||
{},
|
nullptr,
|
||||||
{barriers.data(), num_images} // Batched image barriers
|
vk::Span(barriers.data(), num_images) // Batched image barriers
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -494,18 +494,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
|||||||
CollectPhysicalMemoryInfo();
|
CollectPhysicalMemoryInfo();
|
||||||
CollectToolingInfo();
|
CollectToolingInfo();
|
||||||
|
|
||||||
if (is_qualcomm || is_turnip) {
|
|
||||||
LOG_WARNING(Render_Vulkan,
|
|
||||||
"Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color");
|
|
||||||
//RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color,
|
|
||||||
//VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_qualcomm) {
|
if (is_qualcomm) {
|
||||||
LOG_WARNING(Render_Vulkan,
|
|
||||||
"Qualcomm drivers have a slow VK_KHR_push_descriptor implementation");
|
|
||||||
//RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
|
||||||
|
|
||||||
LOG_WARNING(Render_Vulkan,
|
LOG_WARNING(Render_Vulkan,
|
||||||
"Disabling shader float controls and 64-bit integer features on Qualcomm proprietary drivers");
|
"Disabling shader float controls and 64-bit integer features on Qualcomm proprietary drivers");
|
||||||
RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME);
|
RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME);
|
||||||
@@ -544,93 +533,36 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
|||||||
if (arch >= NvidiaArchitecture::Arch_AmpereOrNewer) {
|
if (arch >= NvidiaArchitecture::Arch_AmpereOrNewer) {
|
||||||
LOG_WARNING(Render_Vulkan, "Ampere and newer have broken float16 math");
|
LOG_WARNING(Render_Vulkan, "Ampere and newer have broken float16 math");
|
||||||
features.shader_float16_int8.shaderFloat16 = false;
|
features.shader_float16_int8.shaderFloat16 = false;
|
||||||
} else if (arch <= NvidiaArchitecture::Arch_Volta) {
|
|
||||||
if (nv_major_version < 527) {
|
|
||||||
LOG_WARNING(Render_Vulkan, "Volta and older have broken VK_KHR_push_descriptor");
|
|
||||||
//RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nv_major_version >= 510) {
|
if (nv_major_version >= 510) {
|
||||||
LOG_WARNING(Render_Vulkan, "NVIDIA Drivers >= 510 do not support MSAA image blits");
|
LOG_WARNING(Render_Vulkan, "NVIDIA Drivers >= 510 do not support MSAA image blits");
|
||||||
cant_blit_msaa = true;
|
cant_blit_msaa = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (extensions.extended_dynamic_state && is_radv) {
|
|
||||||
// Mask driver version variant
|
|
||||||
const u32 version = (properties.properties.driverVersion << 3) >> 3;
|
|
||||||
if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) {
|
|
||||||
LOG_WARNING(Render_Vulkan,
|
|
||||||
"RADV versions older than 21.2 have broken VK_EXT_extended_dynamic_state");
|
|
||||||
//RemoveExtensionFeature(extensions.extended_dynamic_state,
|
|
||||||
//features.extended_dynamic_state,
|
|
||||||
//VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (extensions.extended_dynamic_state2 && is_radv) {
|
|
||||||
const u32 version = (properties.properties.driverVersion << 3) >> 3;
|
|
||||||
if (version < VK_MAKE_API_VERSION(0, 22, 3, 1)) {
|
|
||||||
LOG_WARNING(
|
|
||||||
Render_Vulkan,
|
|
||||||
"RADV versions older than 22.3.1 have broken VK_EXT_extended_dynamic_state2");
|
|
||||||
// RemoveExtensionFeature(extensions.extended_dynamic_state2,
|
|
||||||
// features.extended_dynamic_state2,
|
|
||||||
// VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (extensions.extended_dynamic_state2 && is_qualcomm) {
|
|
||||||
const u32 version = (properties.properties.driverVersion << 3) >> 3;
|
|
||||||
if (version >= VK_MAKE_API_VERSION(0, 0, 676, 0) &&
|
|
||||||
version < VK_MAKE_API_VERSION(0, 0, 680, 0)) {
|
|
||||||
// Qualcomm Adreno 7xx drivers do not properly support extended_dynamic_state2.
|
|
||||||
LOG_WARNING(Render_Vulkan,
|
|
||||||
"Qualcomm Adreno 7xx drivers have broken VK_EXT_extended_dynamic_state2");
|
|
||||||
//RemoveExtensionFeature(extensions.extended_dynamic_state2,
|
|
||||||
//features.extended_dynamic_state2,
|
|
||||||
//VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (extensions.extended_dynamic_state3 && is_radv) {
|
if (extensions.extended_dynamic_state3 && is_radv) {
|
||||||
LOG_WARNING(Render_Vulkan, "RADV has broken extendedDynamicState3ColorBlendEquation");
|
LOG_WARNING(Render_Vulkan, "RADV has broken extendedDynamicState3ColorBlendEquation");
|
||||||
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = true;
|
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false;
|
||||||
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = true;
|
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false;
|
||||||
dynamic_state3_blending = true;
|
dynamic_state3_blending = false;
|
||||||
|
|
||||||
const u32 version = (properties.properties.driverVersion << 3) >> 3;
|
const u32 version = (properties.properties.driverVersion << 3) >> 3;
|
||||||
if (version < VK_MAKE_API_VERSION(0, 23, 1, 0)) {
|
if (version < VK_MAKE_API_VERSION(0, 23, 1, 0)) {
|
||||||
LOG_WARNING(Render_Vulkan,
|
LOG_WARNING(Render_Vulkan,
|
||||||
"RADV versions older than 23.1.0 have broken depth clamp dynamic state");
|
"RADV versions older than 23.1.0 have broken depth clamp dynamic state");
|
||||||
features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable = true;
|
features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable = false;
|
||||||
dynamic_state3_enables = true;
|
dynamic_state3_enables = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extensions.extended_dynamic_state3 && (is_amd_driver || driver_id == VK_DRIVER_ID_SAMSUNG_PROPRIETARY)) {
|
if (extensions.extended_dynamic_state3 && (is_amd_driver || driver_id == VK_DRIVER_ID_SAMSUNG_PROPRIETARY)) {
|
||||||
// AMD and Samsung drivers have broken extendedDynamicState3ColorBlendEquation
|
// AMD and Samsung drivers have broken extendedDynamicState3ColorBlendEquation
|
||||||
LOG_WARNING(Render_Vulkan,
|
LOG_WARNING(Render_Vulkan,
|
||||||
"AMD and Samsung drivers have broken extendedDynamicState3ColorBlendEquation");
|
"AMD and Samsung drivers have broken extendedDynamicState3ColorBlendEquation");
|
||||||
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = true;
|
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false;
|
||||||
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = true;
|
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false;
|
||||||
dynamic_state3_blending = true;
|
dynamic_state3_blending = false;
|
||||||
}
|
|
||||||
if (extensions.vertex_input_dynamic_state && is_radv) {
|
|
||||||
// TODO(ameerj): Blacklist only offending driver versions
|
|
||||||
// TODO(ameerj): Confirm if RDNA1 is affected
|
|
||||||
const bool is_rdna2 =
|
|
||||||
supported_extensions.contains(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME);
|
|
||||||
if (is_rdna2) {
|
|
||||||
LOG_WARNING(Render_Vulkan,
|
|
||||||
"RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware");
|
|
||||||
// RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
|
|
||||||
// features.vertex_input_dynamic_state,
|
|
||||||
// VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (extensions.vertex_input_dynamic_state && is_qualcomm) {
|
|
||||||
// Qualcomm drivers do not properly support vertex_input_dynamic_state.
|
|
||||||
LOG_WARNING(Render_Vulkan,
|
|
||||||
"Qualcomm drivers have broken VK_EXT_vertex_input_dynamic_state");
|
|
||||||
//RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
|
|
||||||
// features.vertex_input_dynamic_state,
|
|
||||||
// VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sets_per_pool = 64;
|
sets_per_pool = 64;
|
||||||
@@ -644,12 +576,14 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
|||||||
has_broken_cube_compatibility = true;
|
has_broken_cube_compatibility = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_qualcomm) {
|
if (is_qualcomm) {
|
||||||
const u32 version = (properties.properties.driverVersion << 3) >> 3;
|
const u32 version = (properties.properties.driverVersion << 3) >> 3;
|
||||||
if (version < VK_MAKE_API_VERSION(0, 255, 615, 512)) {
|
if (version < VK_MAKE_API_VERSION(0, 255, 615, 512)) {
|
||||||
has_broken_parallel_compiling = true;
|
has_broken_parallel_compiling = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extensions.sampler_filter_minmax && is_amd) {
|
if (extensions.sampler_filter_minmax && is_amd) {
|
||||||
// Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
|
// Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
|
||||||
if (!features.shader_float16_int8.shaderFloat16) {
|
if (!features.shader_float16_int8.shaderFloat16) {
|
||||||
@@ -660,24 +594,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extensions.vertex_input_dynamic_state && is_intel_windows) {
|
|
||||||
const u32 version = (properties.properties.driverVersion << 3) >> 3;
|
|
||||||
if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) {
|
|
||||||
LOG_WARNING(Render_Vulkan, "Intel has broken VK_EXT_vertex_input_dynamic_state");
|
|
||||||
//RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
|
|
||||||
//features.vertex_input_dynamic_state,
|
|
||||||
//VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (features.shader_float16_int8.shaderFloat16 && is_intel_windows) {
|
if (features.shader_float16_int8.shaderFloat16 && is_intel_windows) {
|
||||||
// Intel's compiler crashes when using fp16 on Astral Chain, disable it for the time being.
|
// Intel's compiler crashes when using fp16 on Astral Chain, disable it for the time being.
|
||||||
LOG_WARNING(Render_Vulkan, "Intel has broken float16 math");
|
LOG_WARNING(Render_Vulkan, "Intel has broken float16 math");
|
||||||
features.shader_float16_int8.shaderFloat16 = false;
|
features.shader_float16_int8.shaderFloat16 = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_intel_windows) {
|
if (is_intel_windows) {
|
||||||
LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits");
|
LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits");
|
||||||
cant_blit_msaa = true;
|
cant_blit_msaa = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
has_broken_compute =
|
has_broken_compute =
|
||||||
CheckBrokenCompute(properties.driver.driverID, properties.properties.driverVersion) &&
|
CheckBrokenCompute(properties.driver.driverID, properties.properties.driverVersion) &&
|
||||||
!Settings::values.enable_compute_pipelines.GetValue();
|
!Settings::values.enable_compute_pipelines.GetValue();
|
||||||
@@ -685,24 +612,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
|||||||
LOG_WARNING(Render_Vulkan, "Driver does not support native BGR format");
|
LOG_WARNING(Render_Vulkan, "Driver does not support native BGR format");
|
||||||
must_emulate_bgr565 = true;
|
must_emulate_bgr565 = true;
|
||||||
}
|
}
|
||||||
if (extensions.push_descriptor && is_intel_anv) {
|
|
||||||
const u32 version = (properties.properties.driverVersion << 3) >> 3;
|
|
||||||
if (version >= VK_MAKE_API_VERSION(0, 22, 3, 0) &&
|
|
||||||
version < VK_MAKE_API_VERSION(0, 23, 2, 0)) {
|
|
||||||
// Disable VK_KHR_push_descriptor due to
|
|
||||||
// mesa/mesa/-/commit/ff91c5ca42bc80aa411cb3fd8f550aa6fdd16bdc
|
|
||||||
LOG_WARNING(Render_Vulkan,
|
|
||||||
"ANV drivers 22.3.0 to 23.1.0 have broken VK_KHR_push_descriptor");
|
|
||||||
//RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
|
||||||
}
|
|
||||||
} else if (extensions.push_descriptor && is_nvidia) {
|
|
||||||
const auto arch = GetNvidiaArch();
|
|
||||||
if (arch <= NvidiaArchitecture::Arch_Pascal) {
|
|
||||||
LOG_WARNING(Render_Vulkan,
|
|
||||||
"Pascal and older architectures have broken VK_KHR_push_descriptor");
|
|
||||||
//RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_mvk) {
|
if (is_mvk) {
|
||||||
LOG_WARNING(Render_Vulkan,
|
LOG_WARNING(Render_Vulkan,
|
||||||
@@ -730,8 +639,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
|||||||
"Removing extendedDynamicState3 due to missing extendedDynamicState2");
|
"Removing extendedDynamicState3 due to missing extendedDynamicState2");
|
||||||
RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3,
|
RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3,
|
||||||
VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
|
VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
|
||||||
dynamic_state3_blending = true;
|
dynamic_state3_blending = false;
|
||||||
dynamic_state3_enables = true;
|
dynamic_state3_enables = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mesa Intel drivers on UHD 620 have broken EDS causing extreme flickering - unknown if it affects other iGPUs
|
// Mesa Intel drivers on UHD 620 have broken EDS causing extreme flickering - unknown if it affects other iGPUs
|
||||||
@@ -743,22 +652,26 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
|||||||
Settings::values.dyna_state.SetValue(0);
|
Settings::values.dyna_state.SetValue(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::values.dyna_state.GetValue() == 0) {
|
switch (Settings::values.dyna_state.GetValue()) {
|
||||||
must_emulate_scaled_formats = true;
|
case 0:
|
||||||
LOG_INFO(Render_Vulkan, "Extended dynamic state is fully disabled, scaled format emulation is ON");
|
|
||||||
|
|
||||||
RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
|
|
||||||
RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
|
RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
|
||||||
|
[[fallthrough]];
|
||||||
|
case 1:
|
||||||
RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
|
RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
|
||||||
|
[[fallthrough]];
|
||||||
|
case 2:
|
||||||
RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
|
RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
|
||||||
RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
|
|
||||||
dynamic_state3_blending = false;
|
dynamic_state3_blending = false;
|
||||||
dynamic_state3_enables = false;
|
dynamic_state3_enables = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
LOG_INFO(Render_Vulkan, "All dynamic state extensions and features have been disabled");
|
if (!extensions.extended_dynamic_state) {
|
||||||
} else {
|
Settings::values.vertex_input_dynamic_state.SetValue(false);
|
||||||
must_emulate_scaled_formats = false;
|
}
|
||||||
LOG_INFO(Render_Vulkan, "Extended dynamic state is enabled, scaled format emulation is OFF");
|
|
||||||
|
if (!Settings::values.vertex_input_dynamic_state.GetValue()) {
|
||||||
|
RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions), first_next, dld);
|
logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions), first_next, dld);
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ li.checked::marker { content: "\2612"; }
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="labelLinks">
|
<widget class="QLabel" name="labelLinks">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string><html><head/><body><p><a href="https://eden-emulator.github.io/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://git.eden-emu.dev"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://git.eden-emu.dev/eden-emu/eden/activity/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://discord.gg/kXAmGCXBGD"><span style=" text-decoration: underline; color:#039be5;">Discord</span></a> | <a href="https://rvlt.gg/qKgFEAbH"><span style=" text-decoration: underline; color:#039be5;">Revolt</span></a> | <a href="https://nitter.poast.org/edenemuofficial"><span style=" text-decoration: underline; color:#039be5;">Twitter</span></a> | <a href="https://git.eden-emu.dev/eden-emu/eden/src/branch/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html></string>
|
<string><html><head/><body><p><a href="https://eden-emulator.github.io/"><span style=" text-decoration: underline; color:#039be5;">Website</span></a> | <a href="https://git.eden-emu.dev"><span style=" text-decoration: underline; color:#039be5;">Source Code</span></a> | <a href="https://git.eden-emu.dev/eden-emu/eden/activity/contributors"><span style=" text-decoration: underline; color:#039be5;">Contributors</span></a> | <a href="https://discord.gg/HstXbPch7X"><span style=" text-decoration: underline; color:#039be5;">Discord</span></a> | <a href="https://rvlt.gg/qKgFEAbH"><span style=" text-decoration: underline; color:#039be5;">Revolt</span></a> | <a href="https://nitter.poast.org/edenemuofficial"><span style=" text-decoration: underline; color:#039be5;">Twitter</span></a> | <a href="https://git.eden-emu.dev/eden-emu/eden/src/branch/master/LICENSE.txt"><span style=" text-decoration: underline; color:#039be5;">License</span></a></p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="openExternalLinks">
|
<property name="openExternalLinks">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
|||||||
@@ -83,14 +83,6 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent,
|
|||||||
: QDialog(parent), input_subsystem{input_subsystem_},
|
: QDialog(parent), input_subsystem{input_subsystem_},
|
||||||
ui(std::make_unique<Ui::ConfigureMotionTouch>()) {
|
ui(std::make_unique<Ui::ConfigureMotionTouch>()) {
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
ui->udp_learn_more->setOpenExternalLinks(true);
|
|
||||||
ui->udp_learn_more->setText(
|
|
||||||
tr("<a "
|
|
||||||
"href='https://eden-emulator.github.io/wiki/"
|
|
||||||
"using-a-controller-or-android-phone-for-motion-or-touch-input'><span "
|
|
||||||
"style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>"));
|
|
||||||
|
|
||||||
SetConfiguration();
|
SetConfiguration();
|
||||||
UpdateUiDisplay();
|
UpdateUiDisplay();
|
||||||
ConnectEvents();
|
ConnectEvents();
|
||||||
|
|||||||
@@ -182,13 +182,6 @@
|
|||||||
<property name="rightMargin">
|
<property name="rightMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="udp_learn_more">
|
|
||||||
<property name="text">
|
|
||||||
<string>Learn More</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="udp_test">
|
<widget class="QPushButton" name="udp_test">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<item row="0" column="0" colspan="4">
|
<item row="0" column="0" colspan="4">
|
||||||
<widget class="QLabel" name="label_1">
|
<widget class="QLabel" name="label_1">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string><html><head/><body><p>Reads controller input from scripts in the same format as TAS-nx scripts.<br/>For a more detailed explanation, please consult the <a href="https://eden-emulator.github.io/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">help page</span></a> on the Eden website.</p></body></html></string>
|
<string><html><head/><body><p>Reads controller input from scripts in the same format as TAS-nx scripts.<br/>For a more detailed explanation, please consult the user handbook.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="openExternalLinks">
|
<property name="openExternalLinks">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
|||||||
@@ -577,10 +577,7 @@ MainWindow::MainWindow(bool has_broken_vulkan)
|
|||||||
UISettings::values.has_broken_vulkan = true;
|
UISettings::values.has_broken_vulkan = true;
|
||||||
|
|
||||||
QMessageBox::warning(this, tr("Broken Vulkan Installation Detected"),
|
QMessageBox::warning(this, tr("Broken Vulkan Installation Detected"),
|
||||||
tr("Vulkan initialization failed during boot.<br><br>Click <a "
|
tr("Vulkan initialization failed during boot."));
|
||||||
"href='https://eden-emulator.github.io/wiki/faq/"
|
|
||||||
"#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>"
|
|
||||||
"here for instructions to fix the issue</a>."));
|
|
||||||
|
|
||||||
#ifdef HAS_OPENGL
|
#ifdef HAS_OPENGL
|
||||||
Settings::values.renderer_backend = Settings::RendererBackend::OpenGL;
|
Settings::values.renderer_backend = Settings::RendererBackend::OpenGL;
|
||||||
@@ -1913,10 +1910,8 @@ bool MainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPar
|
|||||||
tr("You are using the deconstructed ROM directory format for this game, which is an "
|
tr("You are using the deconstructed ROM directory format for this game, which is an "
|
||||||
"outdated format that has been superseded by others such as NCA, NAX, XCI, or "
|
"outdated format that has been superseded by others such as NCA, NAX, XCI, or "
|
||||||
"NSP. Deconstructed ROM directories lack icons, metadata, and update "
|
"NSP. Deconstructed ROM directories lack icons, metadata, and update "
|
||||||
"support.<br><br>For an explanation of the various Switch formats Eden supports, <a "
|
"support.<br>For an explanation of the various Switch formats Eden supports, "
|
||||||
"href='https://eden-emulator.github.io/wiki/overview-of-switch-game-formats'>check "
|
"out our user handbook. This message will not be shown again."));
|
||||||
"out our "
|
|
||||||
"wiki</a>. This message will not be shown again."));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result != Core::SystemResultStatus::Success) {
|
if (result != Core::SystemResultStatus::Success) {
|
||||||
@@ -2738,13 +2733,12 @@ void MainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
|
|||||||
directory = it->second.second;
|
directory = it->second.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDesktopServices::openUrl(
|
QDesktopServices::openUrl(QUrl(QStringLiteral("https://www.emuready.com/listings?emulatorIds=43bfc023-ec22-422d-8324-048a8ec9f28f") + directory));
|
||||||
QUrl(QStringLiteral("https://eden-emulator.github.io/game/") + directory));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
|
void MainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
|
||||||
const QtCommon::Game::ShortcutTarget target) {
|
const QtCommon::Game::ShortcutTarget target) {
|
||||||
// Create shortcut
|
// Create shortcu
|
||||||
std::string arguments = fmt::format("-g \"{:s}\"", game_path);
|
std::string arguments = fmt::format("-g \"{:s}\"", game_path);
|
||||||
|
|
||||||
QtCommon::Game::CreateShortcut(game_path, program_id, "", target, arguments, true);
|
QtCommon::Game::CreateShortcut(game_path, program_id, "", target, arguments, true);
|
||||||
|
|||||||
Reference in New Issue
Block a user