Compare commits

...

22 Commits

Author SHA1 Message Date
CamilleLaVey 95759ae726 [TEST] Quick change on depth's handling 2026-08-12 01:30:23 -04:00
CamilleLaVey dba3bfe74b [TEST] Depth stencil resolve + MSAA depth stencil 2026-08-12 01:19:52 -04:00
CamilleLaVey 6d9e4b97a6 [TEST] MSAA uploads 2026-08-12 00:44:14 -04:00
CamilleLaVey c14bfc3d5e [TEST] Fix vkFormat accessors 2026-08-11 23:25:46 -04:00
CamilleLaVey a238536d6d [TEST] Geometry shader changes + barrier fixes 2026-08-11 23:07:23 -04:00
CamilleLaVey 9fe8674760 [TEST] Fix TFB subdependency 2026-08-11 21:30:31 -04:00
CamilleLaVey 97547657b5 [TEST] Fix missing barrier + removal of required aligments checks inside swizzle textures 2026-08-11 21:00:58 -04:00
CamilleLaVey f73164b191 [TEST] Adjustments on pipeline hash collisions 2026-08-11 20:34:08 -04:00
CamilleLaVey 35c420582f [TEST] Remove subpass dependency 2026-08-11 20:10:53 -04:00
CamilleLaVey b2b7a34957 [TEST] Ported PSO optimizations from tiled-gpu-v2 2026-08-10 03:54:18 -04:00
CamilleLaVey d7a564b088 [TEST] Another bind vertex buffer optimization from previous handling 2026-08-10 03:30:58 -04:00
CamilleLaVey 1d57d4a73d [TEST] Playing with SGSR 2026-08-10 02:57:05 -04:00
CamilleLaVey 3af03332cc [TEST] fast path on swizzle decoding 2026-08-10 01:41:21 -04:00
CamilleLaVey a6e506c328 [TEST] Bring the unswizzle work from tiled-gpu-v2 2026-08-10 01:29:27 -04:00
crueter 8648c27cbb [cmake] Simplify DetectPlatform and normalize platform names (#4183)
Since 3.25 introduced `LINUX` and `BSD`, use them, and standardize other platforms to that style

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4183
Reviewed-by: Samuel <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-08-10 04:57:38 +02:00
codeman4033 def76946f0 [forgejo] Replace mentions of yuzu in issue templates with eden (#3842)
Also fix the broken link for log files in the bug report

Signed-off-by: codeman4033 <codeman4033@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3842
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Samuel <lizzie@eden-emu.dev>
2026-08-10 03:55:03 +02:00
BoiledElectricity 13d39f39aa [qt] fix relative ROM paths passed on the CLI on macOS (#4105)
On macOS, convert relative paths to absolute paths before changing the
working directory. Otherwise, valid Switch formats may incorrectly fail
with a "The ROM format is not supported" error.

Fixes: https://github.com/eden-emulator/Issue-Reports/issues/404
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4105
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Samuel <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-08-09 17:18:58 +02:00
lizzie 7731b5bc86 [core/hle/kernel/svc] demote ResetSignal log Debug->Trace (#4161)
I don't see why it's LOG_DEBUG if WaitSynchronization is LOG_TRACE, leaves incomplete data
may as well bundle it to trace with the others? :)

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4161
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-08-09 14:13:27 +02:00
lizzie c4ab2f6b9e [cmake] Add ENABLE_WERROR option (#4080)
Should warnings be errors? that's a philosophical debate which I can only answer with "no, but if you think otherwise here is an option for you"

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4080
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-08-09 02:56:28 +02:00
xbzk dd12266c26 [input] added option to disable wgi/xinput to prevent SDL GUIDE hack (#4237)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
So, many users hate the fact when they press START+SELECT, an additional HOME event is sent.

That is annoying coz for most users' default hotkey settings, it closes eden when not in game, and when in game it calls a NOT controller responsive prompt about leaving Eden.
There are several games with reasons to press start+select, also there are homebrews which relies on that combo to open internal menus.
Again. That is annoying.

Digging deep i've found that SDL implements something called guide hack: When Start+Select is pressed it sends a synthetic GUIDE(which code equals to HOME).
This was intended for controllers missing the HOME button, but SDL fails to track them all, and misses API to disabling it.

I've figured out a patch to SDL to disable it, but since it would be better to invest on a PR straight into SDL repo, on our side i thought it better to simply add an option to disable WGI (Windows.Gaming.Input) and Xinput, both paths which could lead into the hack enabling.

Setting is available in a remote place no one will bother.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4237
Reviewed-by: Samuel <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-08-09 02:53:37 +02:00
crueter a3d32a18b8 [meta] Discourage profanity usage (#4239)
I'm not into millennial humor, personally.

Proactively removed as much profanity as I could grep.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4239
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Samuel <lizzie@eden-emu.dev>
2026-08-09 02:53:16 +02:00
crueter c0ffc900cd [ci] Only update translations once a week (#4256)
They've slowed down enough at this point.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4256
2026-08-08 05:46:52 +02:00
87 changed files with 2203 additions and 619 deletions
+2 -2
View File
@@ -1,9 +1,9 @@
name: tx-pull
on:
# tuesday, saturday at 2pm
# monday at 4pm
schedule:
- cron: '0 14 * * 2,6'
- cron: '0 16 * * 1'
workflow_dispatch:
jobs:
+2 -2
View File
@@ -3,7 +3,7 @@ description: File a bug report
body:
- type: markdown
attributes:
value: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu.
value: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with Eden.
- type: checkboxes
attributes:
label: Is there an existing issue for this?
@@ -43,7 +43,7 @@ body:
id: log
attributes:
label: Log File
description: A log file will help our developers to better diagnose and fix the issue. Instructions can be found [here](https://yuzu-emu.org/help/reference/log-files).
description: A log file will help our developers to better diagnose and fix the issue. Instructions can be found [here](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md).
validations:
required: true
- type: textarea
+2 -2
View File
@@ -4,7 +4,7 @@ labels: "request"
body:
- type: markdown
attributes:
value: Tech support does not belong here. You should only file an issue here if you are requesting a feature you believe would make yuzu better.
value: Tech support does not belong here. You should only file an issue here if you are requesting a feature you believe would make Eden better.
- type: checkboxes
attributes:
label: Is there an existing issue for this?
@@ -23,6 +23,6 @@ body:
id: why-feature
attributes:
label: Why would this feature be useful?
description: A brief description of why this feature would make yuzu better.
description: A brief description of why this feature would make Eden better.
validations:
required: true
+29 -15
View File
@@ -28,23 +28,23 @@ if (NOT DEFINED ARCHITECTURE)
endif()
# Needed for FFmpeg w/ VAAPI and DRM
if (PLATFORM_OPENBSD)
if (OPENBSD)
# OpenBSD 7.8 broke libcxx when upgrading, so we must define the PSTL backend manually
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R6/include -D_LIBCPP_PSTL_BACKEND_SERIAL=1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R6/include -D_LIBCPP_PSTL_BACKEND_SERIAL=1")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/X11R6/lib")
elseif (PLATFORM_NETBSD)
elseif (NETBSD)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R7/include -I${CMAKE_SYSROOT}/usr/pkg/include/c++/v1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R7/include -I${CMAKE_SYSROOT}/usr/pkg/include/c++/v1")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/X11R7/lib")
endif()
# NetBSD: Fun for the whole family!
if (PLATFORM_NETBSD)
if (NETBSD)
set(ENV{PKG_CONFIG_PATH} "${PKG_CONFIG_PATH}:${CMAKE_SYSROOT}/usr/pkg/lib/ffmpeg7/pkgconfig")
endif()
cmake_dependent_option(YUZU_STATIC_ROOM "Build a static room executable only (CI only)" OFF "PLATFORM_LINUX" OFF)
cmake_dependent_option(YUZU_STATIC_ROOM "Build a static room executable only (CI only)" OFF "LINUX" OFF)
if (YUZU_STATIC_ROOM)
set(YUZU_ROOM ON)
set(YUZU_ROOM_STANDALONE ON)
@@ -79,6 +79,7 @@ set(YUZU_QT_MIRROR "" CACHE STRING "What mirror to use for downloading the bundl
cmake_dependent_option(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF)
option(ENABLE_DEBUG_TOOLS "Enable debugging tools (maxwell disassembler, SPIRV translator, etc)" OFF)
option(ENABLE_WERROR "Enable -Werror diagnostics" ON)
# non-linux bundled qt are static
if (YUZU_USE_BUNDLED_QT AND (APPLE OR NOT UNIX))
@@ -157,6 +158,19 @@ if (CXX_CLANG_CL)
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-reserved-identifier>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-deprecated-declarations>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-cast-function-type-mismatch>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c99-extensions>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++17-compat>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++11-compat-reserved-user-defined-literal>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++11-compat-deprecated-writable-strings>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++11-compat-pedantic>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++11-compat>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++0x-compat>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++98-c++11-compat-binary-literal>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++98-compat-pedantic>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++98-compat>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c99-compat>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c98-compat>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c99-extensions>
$<$<COMPILE_LANGUAGE:C,CXX>:/EHsc>)
# REQUIRED CPU features IN Windows-amd64
if (ARCHITECTURE_x86_64)
@@ -210,7 +224,7 @@ endif()
# ffmpeg
option(YUZU_USE_BUNDLED_FFMPEG "Download bundled FFmpeg" ${EXT_DEFAULT})
cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from external source" "${PLATFORM_SUN}" "NOT WIN32 AND NOT ANDROID" OFF)
cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from external source" "${SOLARIS}" "NOT WIN32 AND NOT ANDROID" OFF)
# sirit
set(BUNDLED_SIRIT_DEFAULT OFF)
@@ -221,7 +235,7 @@ endif()
option(YUZU_USE_BUNDLED_SIRIT "Download bundled sirit" ${BUNDLED_SIRIT_DEFAULT})
# FreeBSD 15+ has libusb, versions below should disable it
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "WIN32 OR PLATFORM_LINUX OR PLATFORM_FREEBSD OR APPLE" OFF)
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "WIN32 OR LINUX OR FREEBSD OR APPLE" OFF)
cmake_dependent_option(ENABLE_OPENGL "Enable OpenGL" ON "NOT (WIN32 AND ARCHITECTURE_arm64) AND NOT APPLE" OFF)
mark_as_advanced(FORCE ENABLE_OPENGL)
@@ -235,7 +249,7 @@ option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}")
# Install udev rules on Linux (mainly for gyros)
# Only acts on joysticks and nothing else.
cmake_dependent_option(YUZU_INSTALL_UDEV_RULES "Install udev rules for gyro access" OFF "PLATFORM_LINUX" OFF)
cmake_dependent_option(YUZU_INSTALL_UDEV_RULES "Install udev rules for gyro access" OFF "LINUX" OFF)
option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android" ON)
@@ -248,7 +262,7 @@ cmake_dependent_option(YUZU_ROOM_STANDALONE "Enable standalone room executable"
cmake_dependent_option(YUZU_CMD "Compile the eden-cli executable" ON "NOT ANDROID" OFF)
cmake_dependent_option(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR PLATFORM_LINUX" OFF)
cmake_dependent_option(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR LINUX" OFF)
option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" ON)
set(YUZU_TZDB_PATH "" CACHE STRING "Path to a pre-downloaded timezone database")
@@ -258,7 +272,7 @@ cmake_dependent_option(YUZU_USE_BUNDLED_MOLTENVK "Download bundled MoltenVK lib"
option(YUZU_DISABLE_LLVM "Disable LLVM (useful for CI)" OFF)
set(DEFAULT_YUZU_USE_BUNDLED_OPENSSL OFF)
if (EXT_DEFAULT OR PLATFORM_SUN OR PLATFORM_OPENBSD)
if (EXT_DEFAULT OR SOLARIS OR OPENBSD)
set(DEFAULT_YUZU_USE_BUNDLED_OPENSSL ON)
endif()
@@ -281,7 +295,7 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/hooks/pre-commit AND NOT EXISTS ${PROJECT_SOURCE
endif()
endif()
if (ARCHITECTURE_arm64 AND (ANDROID OR PLATFORM_LINUX))
if (ARCHITECTURE_arm64 AND (ANDROID OR LINUX))
set(HAS_NCE 1)
add_compile_definitions(HAS_NCE=1)
endif()
@@ -290,7 +304,7 @@ if (YUZU_ROOM)
add_compile_definitions(YUZU_ROOM)
endif()
if (UNIX AND NOT (PLATFORM_LINUX OR WIN32))
if (UNIX AND NOT (LINUX OR WIN32))
if(CXX_APPLE OR CXX_CLANG)
# libc++ has stop_token and jthread as experimental
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexperimental-library")
@@ -404,7 +418,7 @@ if (Boost_ADDED)
if (NOT MSVC OR CXX_CLANG)
# boost sucks
if (PLATFORM_SUN)
if (SOLARIS)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-pthreads>)
endif()
@@ -508,9 +522,9 @@ elseif (WIN32)
# PSAPI is the Process Status API
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version crypt32 rpcrt4 gdi32 wldap32 mswsock)
endif()
elseif (PLATFORM_MANAGARM)
elseif (MANAGARM)
set(PLATFORM_LIBRARIES iconv intl)
elseif (PLATFORM_HAIKU)
elseif (HAIKUOS)
# Haiku is so special :)
set(PLATFORM_LIBRARIES bsd /boot/system/lib/libnetwork.so)
elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
@@ -604,7 +618,7 @@ if (ENABLE_QT)
# Best practice is to ask for all components at once, so they are from the same version
set(YUZU_QT_COMPONENTS Core Widgets Charts Concurrent Gui)
if (PLATFORM_LINUX OR PLATFORM_FREEBSD)
if (LINUX OR FREEBSD)
list(APPEND YUZU_QT_COMPONENTS DBus)
# yes Qt, we get it
set(QT_NO_PRIVATE_MODULE_WARNING ON)
+1 -1
View File
@@ -13,7 +13,7 @@ find_package_handle_standard_args(Opus
VERSION_VAR OPUS_VERSION
)
if (PLATFORM_MSYS)
if (MSYS2)
FixMsysPath(PkgConfig::OPUS)
endif()
+1 -1
View File
@@ -14,7 +14,7 @@ find_package_handle_standard_args(enet
VERSION_VAR ENET_VERSION
)
if (PLATFORM_MSYS)
if (MSYS2)
FixMsysPath(PkgConfig::ENET)
endif()
+1 -1
View File
@@ -14,7 +14,7 @@ find_package_handle_standard_args(libusb
VERSION_VAR LIBUSB_VERSION
)
if (PLATFORM_MSYS)
if (MSYS2)
FixMsysPath(PkgConfig::LIBUSB)
endif()
+1 -1
View File
@@ -13,7 +13,7 @@ else()
find_package(PkgConfig QUIET)
pkg_search_module(LZ4 QUIET IMPORTED_TARGET liblz4)
if (PLATFORM_MSYS)
if (MSYS2)
FixMsysPath(PkgConfig::LZ4)
endif()
+5 -1
View File
@@ -22,9 +22,13 @@ Eden is free, open-source, copyleft software, licensed under the terms of the [G
- No LLM or AI usage, *period*, for patches, pull requests, issues, comments, debugging, brainstorming, etc.
- For details on why, see the [detailed AI policy](docs/policies/AI.md).
- Usage of any form of profanity or otherwise unsavory language is generally discouraged.
- This is primarily because it rarely helps to actually understand what's going on.
- Remember that your comments should be focused and actually address what's happening. With very few exceptions, expletives are actively detrimental at best.
- New code must follow the same general style as the surrounding codebase. Exceptions may be granted in certain cases.
- Maintainers reserve the right to change your patches and pull requests at will. We will try to avoid this.
- You should respect all decisions made by the [code owners](docs/CODEOWNERS) in your particular subsystem. If you feel they are overstepping or are incorrect, don't be afraid to stand your ground!
- You should generally respect all decisions made by the [code owners](docs/CODEOWNERS) in your particular subsystem.
- However, if you feel they are overstepping or are incorrect, don't be afraid to stand your ground! Maintainers are not always correct.
- While we do *not* adhere to the terms of a formal code of conduct, you will generally be expected to respect other developers, contributors, and community members.
- You **must** have basic knowledge of [Git](https://git-scm.com/learn). Knowing how to manage your branches and follow proper fork policies is a necessity.
+2
View File
@@ -46,6 +46,8 @@ These options control dependencies.
- `YUZU_INSTALL_UDEV_RULES` (OFF) Install udev rules to enable hidraw access
- Needed for gyroscopes
- Only available on Linux
- `ENABLE_DEBUG_TOOLS` (OFF) Enables debugging and development tools, see [tools](../tools/README.md).
- `ENABLE_WERROR` (ON) Enables warnings as errors (-Werror).
### Flavors
+1 -1
View File
@@ -34,7 +34,7 @@ Everyone has their own way of viewing good/bad C++ practices, my general outline
- The reason is because the project has `-fno-rtti` disabled by default, due to the costs of dynamic polymorphism.
- Always copy-on-value for objects with `sizeof(void *) >= sizeof(T) * 2`, i.e objects sized as 2 pointers or less, for bigger objects you can use ref/pointer as usual.
- Try using move semantics instead of references, whenever possible.
- Remember function parameters are extremelly cheap as fuck, don't be afraid to place upto 8 parameters on a given function.
- Function parameters are cheap. Don't be afraid to use as many as needed (API usability permitting).
- Don't save a reference in structures of a parent object, i.e:
```c++
+1 -1
View File
@@ -164,7 +164,7 @@ if (NOT ANDROID)
AddJsonPackage(sdl3)
else()
message(STATUS "Using bundled SDL3")
if (PLATFORM_FREEBSD)
if (FREEBSD)
set(BUILD_SHARED_LIBS ON)
endif()
AddJsonPackage(sdl3-ci)
+23 -80
View File
@@ -1,45 +1,46 @@
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-FileCopyrightText: Copyright 2026 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
## DetectPlatform ##
# This is a small helper that sets PLATFORM_<platform> variables for various
# This is a small helper that sets platform variables for various
# operating systems and distributions. Note that Apple, Windows, Android, etc.
# are not covered, as CMake already does that for us.
# It also sets CXX_<compiler> for the C++ compiler.
# Furthermore, some platforms have really silly requirements/quirks, so this
# also does a few of those.
# This module contains contributions from the Eden Emulator Project,
# notably from crueter and Lizzie.
if (${CMAKE_SYSTEM_NAME} STREQUAL "SunOS")
set(PLATFORM_SUN ON)
set(SOLARIS ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "OpenOrbis")
set(PLATFORM_PS4 ON)
set(OPENORBIS ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "managarm")
set(PLATFORM_MANAGARM ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
set(PLATFORM_FREEBSD ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
set(PLATFORM_OPENBSD ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "NetBSD")
set(PLATFORM_NETBSD ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "DragonFly")
set(PLATFORM_DRAGONFLYBSD ON)
set(MANAGARM ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Haiku")
set(PLATFORM_HAIKU ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(PLATFORM_LINUX ON)
set(HAIKUOS ON)
endif()
# BSD
if (DEFINED BSD)
if ("${BSD}" STREQUAL "DragonFlyBSD")
set(DRAGONFLYBSD ON)
elseif ("${BSD}" STREQUAL "FreeBSD")
set(FREEBSD ON)
elseif ("${BSD}" STREQUAL "OpenBSD")
set(OPENBSD ON)
elseif ("${BSD}" STREQUAL "NetBSD")
set(NETBSD ON)
endif()
endif()
# dumb heuristic to detect msys2
if (CMAKE_COMMAND MATCHES "msys64")
set(PLATFORM_MSYS ON)
set(MSYS2 ON)
endif()
# compiler checks
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(CXX_CLANG ON)
if (MSVC)
@@ -47,8 +48,6 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
endif()
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CXX_GCC ON)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(CXX_CL ON)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
set(CXX_ICC ON)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
@@ -61,37 +60,13 @@ if(MINGW AND CXX_CLANG)
set(CMAKE_SYSTEM_VERSION 10.0.0)
endif()
# NB: this does not account for SPARC
if (PLATFORM_SUN)
# Terrific OpenIndiana pkg shenanigans
list(APPEND CMAKE_PREFIX_PATH
"${CMAKE_SYSROOT}/usr/lib/qt/6.6/lib/amd64/cmake")
list(APPEND CMAKE_MODULE_PATH
"${CMAKE_SYSROOT}/usr/lib/qt/6.6/lib/amd64/cmake")
# Amazing - absolutely incredible
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SYSROOT}/usr/lib/amd64/cmake")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SYSROOT}/usr/lib/amd64/cmake")
# For some mighty reason, doing a normal release build sometimes
# may not trigger the proper -O3 switch to materialize
if (CMAKE_BUILD_TYPE MATCHES "Release")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
endif()
if (CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2")
endif()
endif()
# MSYS2 utilities
# Sometimes, PkgConfig modules will incorrectly reference / when CMake
# wants you to reference it as C:/msys64/. This function corrects that.
# Example in a Find module:
#[[
if (PLATFORM_MSYS)
if (MSYS2)
FixMsysPath(PkgConfig::OPUS)
endif()
]]
@@ -116,41 +91,9 @@ function(FixMsysPath target)
INTERFACE_INCLUDE_DIRECTORIES ${include_dir})
endfunction()
# MSYSTEM handling + program_path
if (PLATFORM_MSYS)
# really, really dumb heuristic to detect what environment we are in
macro(system var)
if (CMAKE_COMMAND MATCHES ${var})
set(MSYSTEM ${var})
endif()
endmacro()
system(mingw64)
system(clang64)
system(clangarm64)
system(ucrt64)
if (NOT DEFINED MSYSTEM)
set(MSYSTEM msys2)
endif()
# We generally want to prioritize environment-specific binaries if possible
# some, like autoconf, are not present on environments besides msys2 though
set(CMAKE_PROGRAM_PATH C:/msys64/${MSYSTEM}/bin C:/msys64/usr/bin)
set(ENV{PKG_CONFIG_PATH} C:/msys64/${MSYSTEM}/lib/pkgconfig)
endif()
# This saves a truly ridiculous amount of time during linking
# In my tests, without this, Eden takes 2 mins, with this, it takes 3-5 seconds
# or on GitHub Actions, 10 minutes -> 3 seconds
# Saves linking time
if (MINGW)
set(MINGW_FLAGS "-Wl,--strip-all -Wl,--gc-sections")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE
"${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${MINGW_FLAGS}")
endif()
# awesome
if (PLATFORM_FREEBSD OR PLATFORM_DRAGONFLYBSD)
set(CMAKE_EXE_LINKER_FLAGS
"${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/local/lib")
endif()
+5 -5
View File
@@ -56,7 +56,7 @@ if (NOT YUZU_USE_BUNDLED_FFMPEG)
endif()
endif()
if (PLATFORM_PS4 OR PLATFORM_MANAGARM)
if (OPENORBIS OR MANAGARM)
# Doesn't support VA-API, don't go thru the embarrassment of trying to enable it
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vaapi)
elseif (ANDROID)
@@ -75,7 +75,7 @@ elseif (UNIX AND NOT DEFINED FFmpeg_IS_CROSS_COMPILING AND NOT ANDROID)
if(X11_FOUND)
if (NOT APPLE)
# In Solaris needs explicit linking for ffmpeg which links to /lib/amd64/libX11.so
if(PLATFORM_SUN)
if(SOLARIS)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
X11
"${CMAKE_SYSROOT}/usr/lib/xorg/amd64/libdrm.so")
@@ -158,7 +158,7 @@ elseif (UNIX AND NOT DEFINED FFmpeg_IS_CROSS_COMPILING AND NOT ANDROID)
endif()
endif()
if (PLATFORM_PS4)
if (OPENORBIS)
list(APPEND FFmpeg_CROSS_COMPILE_LIBS
-lkernel
-lSceUserService
@@ -172,7 +172,7 @@ if (PLATFORM_PS4)
--extra-cxxflags=${CMAKE_SYSROOT}/usr/include
--extra-libs="${FFmpeg_CROSS_COMPILE_LIBS}"
)
elseif (PLATFORM_MANAGARM)
elseif (MANAGARM)
# Required for proper stuff
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS
--disable-pthreads
@@ -299,7 +299,7 @@ else()
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
# BSD make or Solaris make don't support ffmpeg make-j8
if (PLATFORM_LINUX OR ANDROID OR APPLE OR WIN32 OR PLATFORM_FREEBSD)
if (LINUX OR ANDROID OR APPLE OR WIN32 OR FREEBSD)
set(FFmpeg_MAKE_ARGS -j${SYSTEM_THREADS})
else()
set(FFmpeg_MAKE_ARGS "")
+1 -1
View File
@@ -11,7 +11,7 @@ if (NOT libusb_ADDED)
endif()
# TODO: *BSD fails to compile--may need different configs/symbols
if (MINGW OR PLATFORM_LINUX OR APPLE)
if (MINGW OR LINUX OR APPLE)
set(LIBUSB_FOUND ON CACHE BOOL "libusb is present" FORCE)
set(LIBUSB_VERSION "1.0.24" CACHE STRING "libusb version string" FORCE)
+2 -2
View File
@@ -1177,7 +1177,7 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re
#endif
#ifndef STBI_NO_TGA
// test tga last because it's a crappy test!
// test tga last because it's a bad test!
if (stbi__tga_test(s))
return stbi__tga_load(s,x,y,comp,req_comp, ri);
#endif
@@ -7662,7 +7662,7 @@ static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp)
if (stbi__hdr_info(s, x, y, comp)) return 1;
#endif
// test tga last because it's a crappy test!
// test tga last because it's a bad test!
#ifndef STBI_NO_TGA
if (stbi__tga_info(s, x, y, comp))
return 1;
+21 -9
View File
@@ -155,12 +155,24 @@ else()
$<$<COMPILE_LANGUAGE:CXX>:-fno-rtti>)
endif()
if (ENABLE_WERROR)
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=all>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=extra>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=missing-declarations>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=shadow>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=unused>)
else()
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:-Wall>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wextra>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wmissing-declarations>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wshadow>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wunused>
# Some compilers could particularly misbehave :)
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-error-all>)
endif()
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=all>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=extra>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=missing-declarations>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=shadow>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=unused>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-attributes>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-invalid-offsetof>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-unused-parameter>
@@ -169,9 +181,9 @@ else()
if (CXX_CLANG OR CXX_ICC OR CXX_APPLE) # Clang, AppleClang, or Intel C++
if (NOT MSVC)
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=shadow-uncaptured-local>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=implicit-fallthrough>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=type-limits>)
$<$<COMPILE_LANGUAGE:C,CXX>:-Wshadow-uncaptured-local>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wimplicit-fallthrough>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wtype-limits>)
endif()
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-braced-scalar-init>
@@ -181,7 +193,7 @@ else()
if (ARCHITECTURE_x86_64)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mcx16>)
if (PLATFORM_LINUX OR PLATFORM_FREEBSD)
if (LINUX OR FREEBSD)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mtls-dialect=gnu2>)
endif()
endif()
@@ -586,6 +586,7 @@ abstract class SettingsItem(
IntSetting.FSR_SHARPENING_SLIDER,
titleId = R.string.fsr_sharpness,
descriptionId = R.string.fsr_sharpness_description,
max = 200,
units = "%"
)
)
@@ -1182,7 +1182,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
container,
IntSetting.FSR_SHARPENING_SLIDER,
minValue = 0,
maxValue = 100,
maxValue = 200,
units = "%"
)
}
+1 -1
View File
@@ -222,7 +222,7 @@ if (MSVC)
)
else()
target_compile_options(audio_core PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>)
endif()
+1 -1
View File
@@ -220,7 +220,7 @@ endif()
if(CXX_CLANG)
target_compile_options(common PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-fsized-deallocation>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=unreachable-code-aggressive>)
$<$<COMPILE_LANGUAGE:C,CXX>:-Wunreachable-code-aggressive>)
target_compile_definitions(
common
PRIVATE
+9 -1
View File
@@ -702,7 +702,15 @@ struct Values {
// Controls
InputSetting<std::array<PlayerInput, 10>> players;
Setting<bool> disable_wgi_xinput{
linkage, false, "disable_wgi_xinput", Category::Controls, Specialization::Default,
// Only read/write disable_wgi_xinput on Windows platforms
#ifdef _WIN32
true
#else
false
#endif
};
Setting<bool> enable_raw_input{
linkage, false, "enable_raw_input", Category::Controls, Specialization::Default,
// Only read/write enable_raw_input on Windows platforms
+2 -2
View File
@@ -1173,7 +1173,7 @@ add_library(core STATIC
if (ENABLE_WIFI_SCAN)
target_sources(core PRIVATE internal_network/wifi_scanner.cpp)
if (PLATFORM_LINUX)
if (LINUX)
target_link_libraries(core PRIVATE iw)
endif()
else()
@@ -1199,7 +1199,7 @@ if (MSVC)
)
else()
target_compile_options(core PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-cast-function-type>
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>)
@@ -28,7 +28,7 @@ Result CloseHandle(Core::System& system, Handle handle) {
/// Clears the signaled state of an event or process.
Result ResetSignal(Core::System& system, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called handle {:#08x}", handle);
LOG_TRACE(Kernel_SVC, "called handle {:#08x}", handle);
// Get the current handle table.
const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator
@@ -84,7 +84,7 @@ private:
RestrictionSettings restriction_settings{};
std::array<char, 8> pin_code{};
Capability capability{};
// TODO: this is RAW as fuck
// TODO: this is raw
PlayTimerSettings raw_play_timer_settings{};
KernelHelpers::ServiceContext service_context;
@@ -1020,7 +1020,7 @@ Result ISystemSettingsServer::GetBatteryLot(Out<BatteryLot> out_battery_lot) {
c.lot_number[1] = 'H';
c.lot_number[2] = 'A';
c.lot_number[3] = 'C';
// TODO: I have no fucking idea what the letters mean
// TODO: what do the letters mean?
c.lot_number[4] = 'H';
c.lot_number[5] = 'Z';
c.lot_number[6] = 'Z';
@@ -88,25 +88,10 @@ Result IApplicationDisplayService::GetIndirectDisplayTransactionService(
}
Result IApplicationDisplayService::OpenDisplay(Out<u64> out_display_id, DisplayName display_name) {
LOG_DEBUG(Service_VI, "called with display_name={}", display_name.data());
// Ensure the display name is null-terminated
display_name[display_name.size() - 1] = '\0';
// According to switchbrew, only "Default", "External", "Edid", "Internal" and "Null" are valid
const std::array<std::string_view, 5> valid_names = {
"Default", "External", "Edid", "Internal", "Null"
};
bool valid_name = false;
for (const auto& name : valid_names) {
if (name == display_name.data()) {
valid_name = true;
break;
}
}
R_UNLESS(valid_name, ResultOperationFailed);
LOG_DEBUG(Service_VI, "called with display_name={}", display_name.data());
R_RETURN(m_container->OpenDisplay(out_display_id, display_name));
}
+2 -1
View File
@@ -531,7 +531,8 @@ int TranslateTypeToNative(Type type) {
NETWORK_PROTOCOL_TRANSLATE_ELEM(MPLS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PFSYNC)
#elif defined(__linux__)
// Other platforms get fucked
// Other platforms may not support some niche protocols.
// This is usually not an issue
#define NETWORK_PROTOCOL_TRANSLATE_LIST \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IP) \
/*NETWORK_PROTOCOL_TRANSLATE_ELEM(HOPOPTS)*/ \
+1 -1
View File
@@ -25,7 +25,7 @@ endif()
# Dynarmic project options
option(DYNARMIC_ENABLE_CPU_FEATURE_DETECTION "Turning this off causes dynarmic to assume the host CPU doesn't support anything later than SSE3" ON)
if (PLATFORM_OPENBSD OR PLATFORM_DRAGONFLY OR PLATFORM_NETBSD)
if (OPENBSD OR DRAGONFLY OR NETBSD)
set(REQUIRE_WX ON)
else()
set(REQUIRE_WX OFF)
+1 -1
View File
@@ -363,7 +363,7 @@ elseif (APPLE)
backend/exception_handler_macos_mig.c
)
endif()
elseif (UNIX AND NOT PLATFORM_HAIKU)
elseif (UNIX AND NOT HAIKUOS)
# Haiku lacks <ucontext.h>
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_link_libraries(dynarmic PRIVATE rt)
@@ -234,7 +234,7 @@ private:
BlockOfCode block_of_code;
A32EmitX64 emitter;
Optimization::PolyfillOptions polyfill_options;
// Keep it here, you don't wanna mess with the fuckery that's initializer lists
// Keep it here in order to not mess with initializer lists
const A32::UserConfig conf;
Jit* jit_interface;
@@ -2160,8 +2160,6 @@ void EmitFPVectorToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
ROUNDING_MODE_CASE(CASE, 0x3e) \
ROUNDING_MODE_CASE(CASE, 0x3f)
// FUCK YOU MSVC, FUCKING DEPTH CANT EVEN HANDLE 8+16+32+64 DEPTH OF A ELSE STATMENT YOU FUCKING STUPID
// BURN MSVC BURN IT STUPID COMPILER CAN'T EVEN COMPILE THE MOST BASIC C++
ROUNDING_MODE_SWITCH(ToNearest_TieEven)
ROUNDING_MODE_SWITCH(TowardsPlusInfinity)
ROUNDING_MODE_SWITCH(TowardsMinusInfinity)
+1 -1
View File
@@ -787,7 +787,7 @@ static void FoldCountLeadingZeros(IR::Inst& inst, bool is_32_bit) {
/// Folds division operations based on the following:
///
/// 1. x / 0 -> 0 (NOTE: This is an ARM-specific behavior defined in the architecture reference manual)
/// 2a. 0x8000_0000 / 0xFFFF_FFFF -> 0x8000_0000 (NOTE: More ARM bullshit)
/// 2a. 0x8000_0000 / 0xFFFF_FFFF -> 0x8000_0000 (NOTE: More ARM errata)
/// 2b. 0x8000_0000_0000_0000 / 0xFFFF_FFFF_FFFF_FFFF -> 0x8000_0000_0000_0000
/// 3. imm_x / imm_y -> result
/// 4. x / 1 -> x
+1 -1
View File
@@ -151,7 +151,7 @@ if (MSVC)
)
else()
target_compile_options(hid_core PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-cast-function-type>
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>)
+1 -1
View File
@@ -44,7 +44,7 @@ if (MSVC)
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
)
else()
target_compile_options(input_common PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>)
target_compile_options(input_common PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>)
endif()
if (ANDROID)
+5 -2
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -157,8 +160,8 @@ Common::ParamPackage Android::BuildButtonParamPackageForButton(PadIdentifier ide
bool Android::MatchVID(Common::UUID device, const std::vector<std::string>& vids) const {
for (size_t i = 0; i < vids.size(); ++i) {
auto fucker = device.RawString();
if (fucker.find(vids[i]) != std::string::npos) {
auto dev_str = device.RawString();
if (dev_str.find(vids[i]) != std::string::npos) {
return true;
}
}
+7
View File
@@ -648,6 +648,13 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
// Disable raw input. When enabled this setting causes SDL to die when a web applet opens
SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, Settings::values.enable_raw_input ? "1" : "0");
#ifdef _WIN32
if (Settings::values.disable_wgi_xinput) {
SDL_SetHintWithPriority(SDL_HINT_JOYSTICK_RAWINPUT_CORRELATE_XINPUT, "0", SDL_HINT_OVERRIDE);
SDL_SetHintWithPriority(SDL_HINT_JOYSTICK_WGI, "0", SDL_HINT_OVERRIDE);
}
#endif
// SDL3 defaults Steam Controller Bluetooth HIDAPI support to off, which can disable gyro.
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAM, "1");
SDL_SetHint(SDL_HINT_GAMECONTROLLER_SENSOR_FUSION, "1");
+2 -2
View File
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
@@ -28,6 +28,6 @@ if (ENABLE_WEB_SERVICE)
endif()
# Solaris uses /lib/amd64/libsocket.so and /lib/amd64/libnsl.so
if (PLATFORM_SUN)
if (SOLARIS)
target_link_libraries(network PRIVATE socket nsl)
endif()
+1 -1
View File
@@ -253,7 +253,7 @@ if (MSVC)
)
else()
target_compile_options(shader_recompiler PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
# Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
# And this in turns limits the size of a std::array.
$<$<CXX_COMPILER_ID:Clang>:-fbracket-depth=1024>
@@ -50,12 +50,6 @@ constexpr std::array RGBA_LUT{
R | G | B | A, //
};
void CheckAlignment(IR::Reg reg, size_t alignment) {
if (!IR::IsAligned(reg, alignment)) {
throw NotImplementedException("Unaligned source register {}", reg);
}
}
template <typename... Args>
IR::Value Composite(TranslatorVisitor& v, Args... regs) {
return v.ir.CompositeConstruct(v.F(regs)...);
@@ -86,67 +80,53 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
info.type.Assign(TextureType::Color2D);
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_b), zero, {}, info);
case 3: // 2D.LL
CheckAlignment(reg_a, 2);
info.type.Assign(TextureType::Color2D);
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b), {},
info);
case 4: // 2D.DC
CheckAlignment(reg_a, 2);
info.type.Assign(TextureType::Color2D);
info.is_depth.Assign(1);
return v.ir.ImageSampleDrefImplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b),
{}, {}, {}, info);
case 5: // 2D.LL.DC
CheckAlignment(reg_a, 2);
CheckAlignment(reg_b, 2);
info.type.Assign(TextureType::Color2D);
info.is_depth.Assign(1);
return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1),
v.F(reg_b + 1), v.F(reg_b), {}, info);
case 6: // 2D.LZ.DC
CheckAlignment(reg_a, 2);
info.type.Assign(TextureType::Color2D);
info.is_depth.Assign(1);
return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b),
zero, {}, info);
case 7: // ARRAY_2D
CheckAlignment(reg_a, 2);
info.type.Assign(TextureType::ColorArray2D);
return v.ir.ImageSampleImplicitLod(
handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))),
{}, {}, {}, info);
case 8: // ARRAY_2D.LZ
CheckAlignment(reg_a, 2);
info.type.Assign(TextureType::ColorArray2D);
return v.ir.ImageSampleExplicitLod(
handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))),
zero, {}, info);
case 9: // ARRAY_2D.LZ.DC
CheckAlignment(reg_a, 2);
CheckAlignment(reg_b, 2);
info.type.Assign(TextureType::ColorArray2D);
info.is_depth.Assign(1);
return v.ir.ImageSampleDrefExplicitLod(
handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))),
v.F(reg_b + 1), zero, {}, info);
case 10: // 3D
CheckAlignment(reg_a, 2);
info.type.Assign(TextureType::Color3D);
return v.ir.ImageSampleImplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), {}, {},
{}, info);
case 11: // 3D.LZ
CheckAlignment(reg_a, 2);
info.type.Assign(TextureType::Color3D);
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), zero, {},
info);
case 12: // CUBE
CheckAlignment(reg_a, 2);
info.type.Assign(TextureType::ColorCube);
return v.ir.ImageSampleImplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), {}, {},
{}, info);
case 13: // CUBE.LL
CheckAlignment(reg_a, 2);
CheckAlignment(reg_b, 2);
info.type.Assign(TextureType::ColorCube);
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b),
v.F(reg_b + 1), {}, info);
@@ -187,12 +167,10 @@ IR::Reg RegStoreComponent32(u64 insn, unsigned index) {
case 0:
return texs.dest_reg_a;
case 1:
CheckAlignment(texs.dest_reg_a, 2);
return texs.dest_reg_a + 1;
case 2:
return texs.dest_reg_b;
case 3:
CheckAlignment(texs.dest_reg_b, 2);
return texs.dest_reg_b + 1;
}
throw LogicError("Invalid store index {}", index);
@@ -34,12 +34,6 @@ union Encoding {
BitField<36, 13, u64> cbuf_offset;
};
void CheckAlignment(IR::Reg reg, size_t alignment) {
if (!IR::IsAligned(reg, alignment)) {
throw NotImplementedException("Unaligned source register {}", reg);
}
}
IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg reg) {
const IR::U32 value{v.X(reg)};
return v.ir.CompositeConstruct(v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(6), true),
@@ -60,18 +54,15 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
info.is_depth.Assign(tld4s.dc != 0 ? 1 : 0);
IR::Value coords;
if (tld4s.aoffi != 0) {
CheckAlignment(reg_a, 2);
coords = v.ir.CompositeConstruct(v.F(reg_a), v.F(reg_a + 1));
IR::Value offset = MakeOffset(v, reg_b);
if (tld4s.dc != 0) {
CheckAlignment(reg_b, 2);
IR::F32 dref = v.F(reg_b + 1);
return v.ir.ImageGatherDref(handle, coords, offset, {}, dref, info);
}
return v.ir.ImageGather(handle, coords, offset, {}, info);
}
if (tld4s.dc != 0) {
CheckAlignment(reg_a, 2);
coords = v.ir.CompositeConstruct(v.F(reg_a), v.F(reg_a + 1));
IR::F32 dref = v.F(reg_b);
return v.ir.ImageGatherDref(handle, coords, {}, {}, dref, info);
@@ -86,12 +77,10 @@ IR::Reg RegStoreComponent32(u64 insn, size_t index) {
case 0:
return tlds4.dest_reg_a;
case 1:
CheckAlignment(tlds4.dest_reg_a, 2);
return tlds4.dest_reg_a + 1;
case 2:
return tlds4.dest_reg_b;
case 3:
CheckAlignment(tlds4.dest_reg_b, 2);
return tlds4.dest_reg_b + 1;
}
throw LogicError("Invalid store index {}", index);
@@ -55,12 +55,6 @@ union Encoding {
BitField<53, 4, u64> encoding;
};
void CheckAlignment(IR::Reg reg, size_t alignment) {
if (!IR::IsAligned(reg, alignment)) {
throw NotImplementedException("Unaligned source register {}", reg);
}
}
IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg reg) {
const IR::U32 value{v.X(reg)};
return v.ir.CompositeConstruct(v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true),
@@ -92,38 +86,31 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_b));
break;
case 4:
CheckAlignment(reg_a, 2);
texture_type = Shader::TextureType::Color2D;
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
offsets = MakeOffset(v, reg_b);
break;
case 5:
CheckAlignment(reg_a, 2);
texture_type = Shader::TextureType::Color2D;
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
lod = v.X(reg_b);
break;
case 6:
CheckAlignment(reg_a, 2);
texture_type = Shader::TextureType::Color2D;
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
multisample = v.X(reg_b);
break;
case 7:
CheckAlignment(reg_a, 2);
texture_type = Shader::TextureType::Color3D;
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1), v.X(reg_b));
break;
case 8: {
CheckAlignment(reg_b, 2);
const IR::U32 array{v.ir.BitFieldExtract(v.X(reg_a), v.ir.Imm32(0), v.ir.Imm32(16))};
texture_type = Shader::TextureType::ColorArray2D;
coords = v.ir.CompositeConstruct(v.X(reg_b), v.X(reg_b + 1), array);
break;
}
case 12:
CheckAlignment(reg_a, 2);
CheckAlignment(reg_b, 2);
texture_type = Shader::TextureType::Color2D;
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
lod = v.X(reg_b);
@@ -166,12 +153,10 @@ IR::Reg RegStoreComponent32(u64 insn, unsigned index) {
case 0:
return tlds.dest_reg_a;
case 1:
CheckAlignment(tlds.dest_reg_a, 2);
return tlds.dest_reg_a + 1;
case 2:
return tlds.dest_reg_b;
case 3:
CheckAlignment(tlds.dest_reg_b, 2);
return tlds.dest_reg_b + 1;
}
throw LogicError("Invalid store index {}", index);
@@ -169,11 +169,36 @@ std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings(
return mapping;
}
struct PassthroughVertices {
u32 count;
u32 first;
u32 stride;
};
PassthroughVertices GetPassthroughVertices(InputTopology input_topology) {
switch (input_topology) {
case InputTopology::Points:
return {1, 0, 1};
case InputTopology::Lines:
return {2, 0, 1};
case InputTopology::LinesAdjacency:
return {2, 1, 1};
case InputTopology::Triangles:
return {3, 0, 1};
case InputTopology::TrianglesAdjacency:
return {3, 0, 2};
}
return {3, 0, 1};
}
void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program,
const Shader::VaryingState& passthrough_mask,
bool passthrough_position,
std::optional<IR::Attribute> passthrough_layer_attr) {
for (u32 i = 0; i < program.output_vertices; i++) {
std::optional<IR::Attribute> passthrough_layer_attr,
InputTopology input_topology) {
const PassthroughVertices vertices{GetPassthroughVertices(input_topology)};
for (u32 vertex = 0; vertex < vertices.count; vertex++) {
const u32 i = vertices.first + vertex * vertices.stride;
// Assign generics from input
for (u32 j = 0; j < 32; j++) {
if (!passthrough_mask.Generic(j)) {
@@ -208,25 +233,16 @@ void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program,
ir.EndPrimitive(ir.Imm32(0));
}
u32 GetOutputTopologyVertices(OutputTopology output_topology) {
switch (output_topology) {
case OutputTopology::PointList:
return 1;
case OutputTopology::LineStrip:
return 2;
default:
return 3;
}
}
void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info) {
void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info,
InputTopology input_topology) {
for (IR::Block* const block : program.blocks) {
for (IR::Inst& inst : block->Instructions()) {
if (inst.GetOpcode() == IR::Opcode::Epilogue) {
IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)};
EmitGeometryPassthrough(
ir, program, program.info.passthrough,
program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {});
program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {},
input_topology);
}
}
}
@@ -235,7 +251,8 @@ void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInf
} // Anonymous namespace
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) {
Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info,
InputTopology input_topology) {
HostTranslateInfo normalized_host_info{host_info};
normalized_host_info.ApplyDescriptorLimitPolicy();
@@ -264,8 +281,9 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
}
if (!normalized_host_info.support_geometry_shader_passthrough) {
program.output_vertices = GetOutputTopologyVertices(program.output_topology);
LowerGeometryPassthrough(program, normalized_host_info);
program.output_vertices = GetPassthroughVertices(input_topology).count;
LowerGeometryPassthrough(program, normalized_host_info, input_topology);
program.is_geometry_passthrough = false;
}
}
break;
@@ -414,11 +432,12 @@ IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
ObjectPool<IR::Block>& block_pool,
const HostTranslateInfo& host_info,
IR::Program& source_program,
Shader::OutputTopology output_topology) {
Shader::OutputTopology output_topology,
InputTopology input_topology) {
IR::Program program;
program.stage = Stage::Geometry;
program.output_topology = output_topology;
program.output_vertices = GetOutputTopologyVertices(output_topology);
program.output_vertices = GetPassthroughVertices(input_topology).count;
program.is_geometry_passthrough = false;
program.info.loads.mask = source_program.info.stores.mask;
@@ -433,7 +452,7 @@ IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
IR::IREmitter ir{*current_block};
EmitGeometryPassthrough(ir, program, program.info.stores, true,
source_program.info.emulated_layer);
source_program.info.emulated_layer, input_topology);
IR::Block* return_block{block_pool.Create(inst_pool)};
IR::IREmitter{*return_block}.Epilogue();
@@ -18,7 +18,8 @@ namespace Shader::Maxwell {
[[nodiscard]] IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool,
ObjectPool<IR::Block>& block_pool, Environment& env,
Flow::CFG& cfg, const HostTranslateInfo& host_info);
Flow::CFG& cfg, const HostTranslateInfo& host_info,
InputTopology input_topology);
[[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b,
Environment& env_vertex_b);
@@ -32,6 +33,7 @@ void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_inf
ObjectPool<IR::Block>& block_pool,
const HostTranslateInfo& host_info,
IR::Program& source_program,
Shader::OutputTopology output_topology);
Shader::OutputTopology output_topology,
InputTopology input_topology);
} // namespace Shader::Maxwell
+1 -1
View File
@@ -373,7 +373,7 @@ else()
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-unused-local-typedef>)
else()
target_compile_options(video_core PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>)
target_compile_options(video_core PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>)
endif()
target_compile_options(video_core PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>)
+28 -27
View File
@@ -7,6 +7,7 @@
#pragma once
#include <algorithm>
#include <bit>
#include <memory>
#include <numeric>
@@ -809,46 +810,46 @@ void BufferCache<P>::BindHostVertexBuffers() {
if (use_optimized_vertex_buffers) {
auto& flags = maxwell3d->dirty.flags;
u32 enabled_mask = enabled_vertex_buffers_mask;
HostBindings<Buffer> bindings{};
u32 last_index = (std::numeric_limits<u32>::max)();
const auto flush_bindings = [&]() {
if (bindings.buffers.empty()) {
return;
}
bindings.max_index = bindings.min_index + static_cast<u32>(bindings.buffers.size());
runtime.BindVertexBuffers(bindings);
bindings = HostBindings<Buffer>{};
last_index = (std::numeric_limits<u32>::max)();
};
while (enabled_mask != 0) {
const u32 index = std::countr_zero(enabled_mask);
enabled_mask &= (enabled_mask - 1);
const u32 enabled_mask = enabled_vertex_buffers_mask;
bool any_dirty = false;
u32 pending_mask = enabled_mask;
while (pending_mask != 0) {
const u32 index = std::countr_zero(pending_mask);
pending_mask &= (pending_mask - 1);
const Binding& binding = VertexBufferSlot(index);
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer, binding.buffer_id);
SynchronizeBuffer(buffer, binding.device_addr, binding.size);
if (!flags[Dirty::VertexBuffer0 + index]) {
flush_bindings();
continue;
}
any_dirty |= flags[Dirty::VertexBuffer0 + index];
}
if (enabled_mask == 0 || !any_dirty) {
return;
}
const u32 min_index = static_cast<u32>(std::countr_zero(enabled_mask));
const u32 max_index = 32u - static_cast<u32>(std::countl_zero(enabled_mask));
HostBindings<Buffer> bindings{};
bindings.min_index = min_index;
bindings.max_index = max_index;
for (u32 index = min_index; index < max_index; ++index) {
flags[Dirty::VertexBuffer0 + index] = false;
const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
if ((enabled_mask & (1u << index)) == 0) {
bindings.buffers.push_back(&slot_buffers[NULL_BUFFER_ID]);
bindings.offsets.push_back(0);
bindings.sizes.push_back(0);
bindings.strides.push_back(stride);
continue;
}
const Binding& binding = VertexBufferSlot(index);
Buffer& buffer = slot_buffers[binding.buffer_id];
const u32 offset = buffer.Offset(binding.device_addr);
buffer.MarkUsage(offset, binding.size);
if (!bindings.buffers.empty() && index != last_index + 1) {
flush_bindings();
}
if (bindings.buffers.empty()) {
bindings.min_index = index;
}
bindings.buffers.push_back(&buffer);
bindings.offsets.push_back(offset);
bindings.sizes.push_back(binding.size);
bindings.strides.push_back(stride);
last_index = index;
}
flush_bindings();
runtime.BindVertexBuffers(bindings);
} else {
HostBindings<typename P::Buffer> host_bindings;
bool any_valid{false};
+1 -1
View File
@@ -2257,7 +2257,7 @@ public:
/// Returns whether the vertex array specified by index is supposed to be
/// accessed per instance or not.
bool IsInstancingEnabled(std::size_t index) const {
return bool(is_instanced[index]); //FUCK YOU MSVC
return bool(is_instanced[index]);
}
};
-1
View File
@@ -40,7 +40,6 @@ public:
}
struct Regs {
// No fucking idea
INSERT_PADDING_BYTES_NOINIT(0x48);
} regs{};
private:
+1 -1
View File
@@ -963,7 +963,7 @@ void Vic::WriteABGR(const OutputSurfaceConfig& output_surface_config, VideoPixel
auto pixel1213 = _mm_load_si128((__m128i*)&inp[src + x + 12]);
auto pixel1415 = _mm_load_si128((__m128i*)&inp[src + x + 14]);
// Right-shift the channels by 16 to un-do the left shit on read and bring the range
// Right-shift the channels by 16 to un-do the left shift on read and bring the range
// back to 8-bit.
pixel01 = _mm_srli_epi16(pixel01, 2);
pixel23 = _mm_srli_epi16(pixel23, 2);
+5 -1
View File
@@ -17,11 +17,13 @@ set(SHADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/astc_decoder.comp
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d_buffer.comp
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_bcn.comp
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_buffer.comp
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d24s8.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d32f.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_d32f_to_abgr8.frag
@@ -32,6 +34,8 @@ set(SHADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.comp
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth_stencil.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_s8d24_to_abgr8.frag
${CMAKE_CURRENT_SOURCE_DIR}/full_screen_triangle.vert
${CMAKE_CURRENT_SOURCE_DIR}/fxaa.frag
@@ -88,7 +92,7 @@ set(SHADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/sgsr1_shader_mobile_edge_direction.frag
)
if (PLATFORM_HAIKU)
if (HAIKUOS)
# glslangValidator WILL crash, glslang will not
set(GLSLANGVALIDATOR "glslang")
else()
@@ -0,0 +1,104 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 430
#extension GL_EXT_shader_16bit_storage : require
#extension GL_EXT_shader_8bit_storage : require
#define BINDING_INPUT_BUFFER 0
#define BINDING_OUTPUT_BUFFER 1
layout(push_constant) uniform PushConstants {
uvec3 dim;
uint bytes_per_block_log2;
uvec3 origin;
uint layer_stride;
uint block_size;
uint x_shift;
uint block_height;
uint block_height_mask;
} pc;
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; };
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer {
uint out_u32[];
};
layout(local_size_x = 16, local_size_y = 8, local_size_z = 1) in;
const uint GOB_SIZE_X = 64;
const uint GOB_SIZE_Y = 8;
const uint GOB_SIZE_X_SHIFT = 6;
const uint GOB_SIZE_Y_SHIFT = 3;
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT;
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
uint SwizzleTable(uint pos) {
const uint t[8] = uint[](
0x12100200, 0x13110301, 0x16140604, 0x17150705,
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
);
const uint i = pos >> 4;
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
return (h << 4) | (pos & 0xf);
}
uint SwizzleOffset(uvec2 pos) {
pos = pos & SWIZZLE_MASK;
return SwizzleTable(pos.y * 64u + pos.x);
}
uvec4 ReadTexel(uint offset) {
switch (pc.bytes_per_block_log2) {
case 2u:
return uvec4(u32data[offset / 4u], 0u, 0u, 0u);
case 3u:
return uvec4(u64data[offset / 8u], 0u, 0u);
case 4u:
return u128data[offset / 16u];
}
return uvec4(0u);
}
void main() {
uvec3 coord = gl_GlobalInvocationID;
if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) {
return;
}
uvec3 pos = coord + pc.origin;
pos.x <<= pc.bytes_per_block_log2;
uint swizzle = SwizzleOffset(pos.xy);
uint block_y = pos.y >> GOB_SIZE_Y_SHIFT;
uint offset = 0u;
offset += pos.z * pc.layer_stride;
offset += (block_y >> pc.block_height) * pc.block_size;
offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT;
offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift;
offset += swizzle;
uvec4 texel = ReadTexel(offset);
uint words = 1u << (pc.bytes_per_block_log2 - 2u);
uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y;
uint out_idx = linear_index * words;
out_u32[out_idx] = texel.x;
if (words > 1u) {
out_u32[out_idx + 1u] = texel.y;
}
if (words > 2u) {
out_u32[out_idx + 2u] = texel.z;
out_u32[out_idx + 3u] = texel.w;
}
}
@@ -0,0 +1,105 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 430
#define BINDING_INPUT_BUFFER 0
#define BINDING_OUTPUT_BUFFER 1
layout(push_constant) uniform PushConstants {
uvec3 dim;
uint bytes_per_block_log2;
uvec3 origin;
uint slice_size;
uint block_size;
uint x_shift;
uint block_height;
uint block_height_mask;
uint block_depth;
uint block_depth_mask;
} pc;
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; };
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer {
uint out_u32[];
};
layout(local_size_x = 8, local_size_y = 8, local_size_z = 4) in;
const uint GOB_SIZE_X = 64;
const uint GOB_SIZE_Y = 8;
const uint GOB_SIZE_X_SHIFT = 6;
const uint GOB_SIZE_Y_SHIFT = 3;
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT;
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
uint SwizzleTable(uint pos) {
const uint t[8] = uint[](
0x12100200, 0x13110301, 0x16140604, 0x17150705,
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
);
const uint i = pos >> 4;
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
return (h << 4) | (pos & 0xf);
}
uint SwizzleOffset(uvec2 pos) {
pos = pos & SWIZZLE_MASK;
return SwizzleTable(pos.y * 64u + pos.x);
}
uvec4 ReadTexel(uint offset) {
switch (pc.bytes_per_block_log2) {
case 2u:
return uvec4(u32data[offset / 4u], 0u, 0u, 0u);
case 3u:
return uvec4(u64data[offset / 8u], 0u, 0u);
case 4u:
return u128data[offset / 16u];
}
return uvec4(0u);
}
void main() {
uvec3 coord = gl_GlobalInvocationID;
if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) {
return;
}
uvec3 pos = coord + pc.origin;
pos.x <<= pc.bytes_per_block_log2;
uint swizzle = SwizzleOffset(pos.xy);
uint block_y = pos.y >> GOB_SIZE_Y_SHIFT;
uint offset = 0u;
offset += (pos.z >> pc.block_depth) * pc.slice_size;
offset += (pos.z & pc.block_depth_mask) << (GOB_SIZE_SHIFT + pc.block_height);
offset += (block_y >> pc.block_height) * pc.block_size;
offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT;
offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift;
offset += swizzle;
uvec4 texel = ReadTexel(offset);
uint words = 1u << (pc.bytes_per_block_log2 - 2u);
uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y;
uint out_idx = linear_index * words;
out_u32[out_idx] = texel.x;
if (words > 1u) {
out_u32[out_idx + 1u] = texel.y;
}
if (words > 2u) {
out_u32[out_idx + 2u] = texel.z;
out_u32[out_idx + 3u] = texel.w;
}
}
@@ -0,0 +1,19 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 450 core
layout(binding = 0) uniform sampler2D img_in;
layout(push_constant) uniform PushConstants {
ivec2 dst_offset;
ivec2 src_offset;
ivec2 scale;
};
void main() {
const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset;
const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x);
const ivec2 coord = msaa_coord * scale + sample_offset + src_offset;
gl_FragDepth = texelFetch(img_in, coord, 0).r;
}
@@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 450 core
#extension GL_ARB_shader_stencil_export : require
layout(binding = 0) uniform sampler2D depth_tex;
layout(binding = 1) uniform usampler2D stencil_tex;
layout(push_constant) uniform PushConstants {
ivec2 dst_offset;
ivec2 src_offset;
ivec2 scale;
};
void main() {
const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset;
const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x);
const ivec2 coord = msaa_coord * scale + sample_offset + src_offset;
gl_FragDepth = texelFetch(depth_tex, coord, 0).r;
gl_FragStencilRefARB = int(texelFetch(stencil_tex, coord, 0).r);
}
@@ -7,6 +7,7 @@ layout(push_constant) uniform constants {
vec2 scale;
vec2 size;
vec2 resize_factor;
vec2 crop_offset;
float edge_sharpness;
};
layout(location = 0) out highp vec2 texcoord;
@@ -15,5 +16,5 @@ void main() {
float x = float((gl_VertexIndex & 1) << 2);
float y = float((gl_VertexIndex & 2) << 1);
gl_Position = vec4(x - 1.0f, y - 1.0f, 0.0, 1.0f) * vec4(sign(resize_factor), 1.f, 1.f);
texcoord = vec2(x, y) * abs(resize_factor) * 0.5;
texcoord = crop_offset + vec2(x, y) * abs(resize_factor) * 0.5;
}
@@ -14,6 +14,7 @@ layout(push_constant) uniform constants {
vec2 scale;
vec2 size;
vec2 resize_factor;
vec2 crop_offset;
float edge_sharpness;
};
layout(set = 0, binding = 0) uniform sampler2D sampler0;
@@ -13,6 +13,7 @@
layout( push_constant ) uniform constants {
vec4 ViewportInfo[1];
vec2 ResizeFactor;
vec2 CropOffset;
float EdgeSharpness;
};
layout(set = 0, binding = 0) uniform sampler2D ps0;
+1 -1
View File
@@ -441,7 +441,7 @@ void MacroInterpreterImpl::Reset() {
pc = 0;
delayed_pc = {};
method_address.raw = 0;
// Vector must hold its last indices otherwise wonky shit will happen
// Vector must hold its last indices otherwise chaos will ensue
// The next parameter index starts at 1, because $r1 already has the value of the first
// parameter.
next_parameter_index = 1;
+1 -1
View File
@@ -59,7 +59,7 @@ std::vector<std::string> GetExtensions() {
std::vector<std::string> extensions;
for (GLint index = 0; index < num_extensions; ++index) {
auto const* p = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, GLuint(index)));
if (p != nullptr) // Fuck you? - sincerely, buggy mesa drivers
if (p != nullptr)
extensions.push_back(std::string{p});
}
return extensions;
@@ -72,6 +72,25 @@ Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topolo
}
}
Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology) {
switch (topology) {
case Maxwell::PrimitiveTopology::Points:
return Shader::InputTopology::Points;
case Maxwell::PrimitiveTopology::Lines:
case Maxwell::PrimitiveTopology::LineLoop:
case Maxwell::PrimitiveTopology::LineStrip:
return Shader::InputTopology::Lines;
case Maxwell::PrimitiveTopology::LinesAdjacency:
case Maxwell::PrimitiveTopology::LineStripAdjacency:
return Shader::InputTopology::LinesAdjacency;
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
return Shader::InputTopology::TrianglesAdjacency;
default:
return Shader::InputTopology::Triangles;
}
}
Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
const Shader::IR::Program& program,
const Shader::IR::Program* previous_program,
@@ -127,33 +146,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
default:
break;
}
switch (key.gs_input_topology) {
case Maxwell::PrimitiveTopology::Points:
info.input_topology = Shader::InputTopology::Points;
break;
case Maxwell::PrimitiveTopology::Lines:
case Maxwell::PrimitiveTopology::LineLoop:
case Maxwell::PrimitiveTopology::LineStrip:
info.input_topology = Shader::InputTopology::Lines;
break;
case Maxwell::PrimitiveTopology::Triangles:
case Maxwell::PrimitiveTopology::TriangleStrip:
case Maxwell::PrimitiveTopology::TriangleFan:
case Maxwell::PrimitiveTopology::Quads:
case Maxwell::PrimitiveTopology::QuadStrip:
case Maxwell::PrimitiveTopology::Polygon:
case Maxwell::PrimitiveTopology::Patches:
info.input_topology = Shader::InputTopology::Triangles;
break;
case Maxwell::PrimitiveTopology::LinesAdjacency:
case Maxwell::PrimitiveTopology::LineStripAdjacency:
info.input_topology = Shader::InputTopology::LinesAdjacency;
break;
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
info.input_topology = Shader::InputTopology::TrianglesAdjacency;
break;
}
info.input_topology = MaxwellToInputTopology(key.gs_input_topology);
info.glasm_use_storage_buffers = glasm_use_storage_buffers;
return info;
}
@@ -483,8 +476,10 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
&& index == u32(Maxwell::ShaderType::Geometry);
if (key.unique_hashes[index] == 0 && is_emulated_stage) {
auto topology = MaxwellToOutputTopology(key.gs_input_topology);
programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
*layer_source_program, topology);
programs[index] =
GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
*layer_source_program, topology,
MaxwellToInputTopology(key.gs_input_topology));
continue;
}
if (key.unique_hashes[index] == 0) {
@@ -502,13 +497,15 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
if (!uses_vertex_a || index != 1) {
// Normal path
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
MaxwellToInputTopology(key.gs_input_topology));
total_storage_buffers += Shader::NumDescriptors(programs[index].info.storage_buffers_descriptors);
} else {
// VertexB path when VertexA is present.
auto& program_va{programs[0]};
auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
MaxwellToInputTopology(key.gs_input_topology))};
total_storage_buffers += Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors);
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
}
@@ -597,7 +594,8 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
env.Dump(hash, key.unique_hash);
}
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
Shader::InputTopology::Points)};
const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)};
Shader::RuntimeInfo info;
info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks();
+293 -13
View File
@@ -22,6 +22,8 @@
#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h"
#include "video_core/host_shaders/convert_msaa_to_non_msaa_frag_spv.h"
#include "video_core/host_shaders/convert_non_msaa_to_msaa_frag_spv.h"
#include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_frag_spv.h"
#include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_stencil_frag_spv.h"
#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h"
#include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
@@ -519,7 +521,8 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
}
[[nodiscard]] vk::ImageView MakeMSAACopyView(const vk::Device& device, VkImage image,
VkFormat format, u32 base_level) {
VkFormat format, u32 base_level,
VkImageAspectFlags aspect_mask) {
return device.CreateImageView(VkImageViewCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = nullptr,
@@ -534,7 +537,7 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
},
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.aspectMask = aspect_mask,
.baseMipLevel = base_level,
.levelCount = 1,
.baseArrayLayer = 0,
@@ -586,6 +589,10 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
msaa_copy_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
one_texture_set_layout.address(),
PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(MSAACopyPushConstants)>))),
msaa_copy_depth_stencil_pipeline_layout(
device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
two_textures_set_layout.address(),
PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(MSAACopyPushConstants)>))),
full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)),
blit_color_to_color_frag(BuildShader(device, BLIT_COLOR_FLOAT_FRAG_SPV)),
blit_color_msaa_frag(BuildShader(device, BLIT_COLOR_MSAA_FRAG_SPV)),
@@ -610,6 +617,12 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)),
convert_msaa_to_non_msaa_frag(BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_FRAG_SPV)),
convert_non_msaa_to_msaa_frag(BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_FRAG_SPV)),
convert_non_msaa_to_msaa_depth_frag(
BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_FRAG_SPV)),
convert_non_msaa_to_msaa_depth_stencil_frag(
device.IsExtShaderStencilExportSupported()
? BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_STENCIL_FRAG_SPV)
: vk::ShaderModule{}),
linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)),
nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {}
@@ -920,10 +933,12 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i
ASSERT(copy.dst_subresource.num_layers == 1);
vk::ImageView src_view =
MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format,
static_cast<u32>(copy.src_subresource.base_level));
static_cast<u32>(copy.src_subresource.base_level),
VK_IMAGE_ASPECT_COLOR_BIT);
vk::ImageView dst_view =
MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format,
static_cast<u32>(copy.dst_subresource.base_level));
static_cast<u32>(copy.dst_subresource.base_level),
VK_IMAGE_ASPECT_COLOR_BIT);
const VkOffset2D dst_offset{copy.dst_offset.x, copy.dst_offset.y};
const VkExtent2D dst_extent{copy.extent.width, copy.extent.height};
const VkRect2D render_area{
@@ -1191,7 +1206,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}));
}, device.StaticPipelineCache()));
return *blit_color_pipelines.back();
}
@@ -1223,7 +1238,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}));
}, device.StaticPipelineCache()));
return *blit_depth_stencil_pipelines.back();
}
@@ -1276,7 +1291,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}));
}, device.StaticPipelineCache()));
return *clear_color_pipelines.back();
}
@@ -1332,7 +1347,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}));
}, device.StaticPipelineCache()));
return *clear_stencil_pipelines.back();
}
@@ -1375,7 +1390,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}));
}, device.StaticPipelineCache()));
return *blit_msaa_color_pipelines.back();
}
@@ -1413,10 +1428,202 @@ VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPas
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}));
}, device.StaticPipelineCache()));
return *pipelines.back();
}
void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage dst_image,
VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
std::span<const VideoCommon::ImageCopy> copies,
bool copy_stencil) {
while (!msaa_copy_resources.empty() && scheduler.IsFree(msaa_copy_resources.front().tick)) {
msaa_copy_resources.pop_front();
}
const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(static_cast<int>(num_samples));
const s32 scale_x = 1 << samples_x;
const s32 scale_y = 1 << samples_y;
const VkSampleCountFlagBits samples = SampleCountFlag(num_samples);
RenderPassKey renderpass_key{};
renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid);
renderpass_key.depth_format = dst_format;
renderpass_key.samples = samples;
const VkRenderPass renderpass = render_pass_cache.Get(renderpass_key);
const MSAACopyPipelineKey key{
.renderpass = renderpass,
.samples = samples,
.msaa_to_non_msaa = false,
};
const VkPipeline pipeline = FindOrEmplaceMSAACopyDepthPipeline(key, copy_stencil);
const VkPipelineLayout layout = copy_stencil ? *msaa_copy_depth_stencil_pipeline_layout
: *msaa_copy_pipeline_layout;
const VkSampler sampler = *nearest_sampler;
const VkFormat src_vk_format =
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format;
const VkFormat dst_vk_format =
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, dst_format).format;
const VkImageAspectFlags attachment_aspect =
VideoCore::Surface::GetFormatType(dst_format) ==
VideoCore::Surface::SurfaceType::DepthStencil
? VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT
: VK_IMAGE_ASPECT_DEPTH_BIT;
for (const VideoCommon::ImageCopy& copy : copies) {
ASSERT(copy.src_subresource.base_layer == 0);
ASSERT(copy.src_subresource.num_layers == 1);
ASSERT(copy.dst_subresource.base_layer == 0);
ASSERT(copy.dst_subresource.num_layers == 1);
vk::ImageView src_view =
MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format,
static_cast<u32>(copy.src_subresource.base_level),
VK_IMAGE_ASPECT_DEPTH_BIT);
vk::ImageView src_stencil_view =
copy_stencil ? MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format,
static_cast<u32>(copy.src_subresource.base_level),
VK_IMAGE_ASPECT_STENCIL_BIT)
: vk::ImageView{};
vk::ImageView dst_view =
MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format,
static_cast<u32>(copy.dst_subresource.base_level),
attachment_aspect);
const VkOffset2D dst_offset{copy.dst_offset.x, copy.dst_offset.y};
const VkExtent2D dst_extent{copy.extent.width, copy.extent.height};
const VkRect2D render_area{
.offset = dst_offset,
.extent = dst_extent,
};
vk::Framebuffer framebuffer = device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.renderPass = renderpass,
.attachmentCount = 1,
.pAttachments = dst_view.address(),
.width = static_cast<u32>(dst_offset.x) + dst_extent.width,
.height = static_cast<u32>(dst_offset.y) + dst_extent.height,
.layers = 1,
});
const MSAACopyPushConstants push_constants{
.dst_offset = {dst_offset.x, dst_offset.y},
.src_offset = {copy.src_offset.x, copy.src_offset.y},
.scale = {scale_x, scale_y},
};
scheduler.RequestOutsideRenderPassOperationContext();
const VkImageView src_stencil_handle = copy_stencil ? *src_stencil_view : VK_NULL_HANDLE;
scheduler.Record([this, pipeline, layout, sampler, renderpass,
framebuffer_handle = *framebuffer, src_view_handle = *src_view,
src_stencil_handle, src = src_image, dst = dst_image, render_area,
attachment_aspect, push_constants](vk::CommandBuffer cmdbuf) {
const VkImageSubresourceRange src_range{
.aspectMask = attachment_aspect,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
};
const std::array pre_barriers{
VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = src,
.subresourceRange = src_range,
},
VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst,
.subresourceRange = src_range,
},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
0, nullptr, nullptr, pre_barriers);
const VkRenderPassBeginInfo renderpass_bi{
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.pNext = nullptr,
.renderPass = renderpass,
.framebuffer = framebuffer_handle,
.renderArea = render_area,
.clearValueCount = 0,
.pClearValues = nullptr,
};
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
const VkDescriptorSet descriptor_set =
src_stencil_handle != VK_NULL_HANDLE
? two_textures_descriptor_allocator.Commit()
: one_texture_descriptor_allocator.Commit();
if (src_stencil_handle != VK_NULL_HANDLE) {
UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_view_handle,
src_stencil_handle);
} else {
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view_handle);
}
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
nullptr);
const VkViewport viewport{
.x = static_cast<float>(render_area.offset.x),
.y = static_cast<float>(render_area.offset.y),
.width = static_cast<float>(render_area.extent.width),
.height = static_cast<float>(render_area.extent.height),
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
cmdbuf.SetViewport(0, viewport);
cmdbuf.SetScissor(0, render_area);
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, push_constants);
cmdbuf.Draw(3, 1, 0, 0);
cmdbuf.EndRenderPass();
const VkImageMemoryBarrier post_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst,
.subresourceRange = src_range,
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, 0, post_barrier);
});
msaa_copy_resources.push_back(MSAACopyResources{
.tick = scheduler.CurrentTick(),
.src_view = std::move(src_view),
.dst_view = std::move(dst_view),
.framebuffer = std::move(framebuffer),
});
if (copy_stencil) {
msaa_copy_resources.push_back(MSAACopyResources{
.tick = scheduler.CurrentTick(),
.src_view = std::move(src_stencil_view),
.dst_view = vk::ImageView{},
.framebuffer = vk::Framebuffer{},
});
}
}
}
VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key) {
const auto it = std::ranges::find(msaa_copy_keys, key);
if (it != msaa_copy_keys.end()) {
@@ -1458,10 +1665,83 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipeline
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}));
}, device.StaticPipelineCache()));
return *msaa_copy_pipelines.back();
}
VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPipelineKey& key,
bool copy_stencil) {
auto& keys = copy_stencil ? msaa_copy_depth_stencil_keys : msaa_copy_depth_keys;
auto& pipelines = copy_stencil ? msaa_copy_depth_stencil_pipelines : msaa_copy_depth_pipelines;
const auto it = std::ranges::find(keys, key);
if (it != keys.end()) {
return *pipelines[std::distance(keys.begin(), it)];
}
keys.push_back(key);
const std::array stages =
MakeStages(*clear_color_vert, copy_stencil ? *convert_non_msaa_to_msaa_depth_stencil_frag
: *convert_non_msaa_to_msaa_depth_frag);
const VkPipelineMultisampleStateCreateInfo multisample_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.rasterizationSamples = key.samples,
.sampleShadingEnable = VK_TRUE,
.minSampleShading = 1.0f,
.pSampleMask = nullptr,
.alphaToCoverageEnable = VK_FALSE,
.alphaToOneEnable = VK_FALSE,
};
static constexpr VkStencilOpState REPLACE_STENCIL_OP{
.failOp = VK_STENCIL_OP_REPLACE,
.passOp = VK_STENCIL_OP_REPLACE,
.depthFailOp = VK_STENCIL_OP_REPLACE,
.compareOp = VK_COMPARE_OP_ALWAYS,
.compareMask = 0xFF,
.writeMask = 0xFF,
.reference = 0,
};
const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.depthTestEnable = VK_TRUE,
.depthWriteEnable = VK_TRUE,
.depthCompareOp = VK_COMPARE_OP_ALWAYS,
.depthBoundsTestEnable = VK_FALSE,
.stencilTestEnable = copy_stencil ? VK_TRUE : VK_FALSE,
.front = copy_stencil ? REPLACE_STENCIL_OP : VkStencilOpState{},
.back = copy_stencil ? REPLACE_STENCIL_OP : VkStencilOpState{},
.minDepthBounds = 0.0f,
.maxDepthBounds = 0.0f,
};
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci =
GetPipelineInputAssemblyStateCreateInfo(device);
pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &multisample_ci,
.pDepthStencilState = &depth_stencil_ci,
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.layout = copy_stencil ? *msaa_copy_depth_stencil_pipeline_layout
: *msaa_copy_pipeline_layout,
.renderPass = key.renderpass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}, device.StaticPipelineCache()));
return *pipelines.back();
}
void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
ConvertPipeline(pipeline, renderpass, false);
}
@@ -1499,7 +1779,7 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
});
}, device.StaticPipelineCache());
}
void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
@@ -1542,7 +1822,7 @@ void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass rende
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
});
}, device.StaticPipelineCache());
}
} // namespace Vulkan
@@ -116,6 +116,11 @@ public:
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
std::span<const VideoCommon::ImageCopy> copies, bool msaa_to_non_msaa);
void CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage dst_image,
VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
std::span<const VideoCommon::ImageCopy> copies, bool copy_stencil);
private:
void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
const ImageView& src_image_view);
@@ -131,6 +136,9 @@ private:
[[nodiscard]] VkPipeline FindOrEmplaceClearStencilPipeline(
const BlitDepthStencilPipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPipelineKey& key,
bool copy_stencil);
[[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass,
bool resolve_stencil);
@@ -162,6 +170,7 @@ private:
vk::PipelineLayout two_textures_pipeline_layout;
vk::PipelineLayout clear_color_pipeline_layout;
vk::PipelineLayout msaa_copy_pipeline_layout;
vk::PipelineLayout msaa_copy_depth_stencil_pipeline_layout;
vk::ShaderModule full_screen_vert;
vk::ShaderModule blit_color_to_color_frag;
vk::ShaderModule blit_color_msaa_frag;
@@ -180,6 +189,8 @@ private:
vk::ShaderModule convert_s8d24_to_abgr8_frag;
vk::ShaderModule convert_msaa_to_non_msaa_frag;
vk::ShaderModule convert_non_msaa_to_msaa_frag;
vk::ShaderModule convert_non_msaa_to_msaa_depth_frag;
vk::ShaderModule convert_non_msaa_to_msaa_depth_stencil_frag;
vk::Sampler linear_sampler;
vk::Sampler nearest_sampler;
@@ -193,6 +204,10 @@ private:
std::vector<vk::Pipeline> clear_stencil_pipelines;
std::vector<MSAACopyPipelineKey> msaa_copy_keys;
std::vector<vk::Pipeline> msaa_copy_pipelines;
std::vector<MSAACopyPipelineKey> msaa_copy_depth_keys;
std::vector<vk::Pipeline> msaa_copy_depth_pipelines;
std::vector<MSAACopyPipelineKey> msaa_copy_depth_stencil_keys;
std::vector<vk::Pipeline> msaa_copy_depth_stencil_pipelines;
std::vector<BlitMSAAPipelineKey> blit_msaa_color_keys;
std::vector<vk::Pipeline> blit_msaa_color_pipelines;
std::vector<VkRenderPass> resolve_depth_keys;
+18 -14
View File
@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
#include "common/common_types.h"
#include "common/div_ceil.h"
#include "common/settings.h"
@@ -17,7 +19,7 @@
namespace Vulkan {
using PushConstants = std::array<u32, 4 + 2 + 1>;
using PushConstants = std::array<u32, 4 + 2 + 2 + 1>;
SGSR::SGSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, VkExtent2D extent, bool edge_dir)
: m_memory_allocator{memory_allocator}
@@ -100,26 +102,28 @@ VkImageView SGSR::Draw(const Device& device, Scheduler& scheduler, size_t image_
const f32 input_image_width = f32(input_image_extent.width);
const f32 input_image_height = f32(input_image_extent.height);
const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width;
const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height;
// expected [0, 2]
const f32 sharpening = f32(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
const f32 crop_width = (crop_rect.right - crop_rect.left) * input_image_width;
const f32 crop_height = (crop_rect.bottom - crop_rect.top) * input_image_height;
static constexpr f32 EDGE_SHARPNESS_MAX = 2.0f;
const f32 edge_sharpness =
EDGE_SHARPNESS_MAX - f32(Settings::values.fsr_sharpening_slider.GetValue()) / 200.0f;
// p = (tex * viewport) / input = [0,n] (normalized texcoords)
// p * input = [0,1024], [0,768]
// layout( push_constant ) uniform constants {
// highp vec4 ViewportInfo[1];
// highp vec2 ResizeFactor;
// highp vec2 CropOffset;
// highp float EdgeSharpness;
// };
PushConstants viewport_con{};
viewport_con[0] = std::bit_cast<u32>(std::abs(1.f / viewport_width));
viewport_con[1] = std::bit_cast<u32>(std::abs(1.f / viewport_height));
viewport_con[2] = std::bit_cast<u32>(std::abs(viewport_width));
viewport_con[3] = std::bit_cast<u32>(std::abs(viewport_height));
viewport_con[4] = std::bit_cast<u32>(viewport_width / input_image_width);
viewport_con[5] = std::bit_cast<u32>(viewport_height / input_image_height);
viewport_con[6] = std::bit_cast<u32>(sharpening);
viewport_con[0] = std::bit_cast<u32>(1.f / input_image_width);
viewport_con[1] = std::bit_cast<u32>(1.f / input_image_height);
viewport_con[2] = std::bit_cast<u32>(input_image_width);
viewport_con[3] = std::bit_cast<u32>(input_image_height);
viewport_con[4] = std::bit_cast<u32>(crop_width / input_image_width);
viewport_con[5] = std::bit_cast<u32>(crop_height / input_image_height);
viewport_con[6] = std::bit_cast<u32>((std::min)(crop_rect.left, crop_rect.right));
viewport_con[7] = std::bit_cast<u32>((std::min)(crop_rect.top, crop_rect.bottom));
viewport_con[8] = std::bit_cast<u32>(edge_sharpness);
UploadImages(device, scheduler);
UpdateDescriptorSets(device, source_image_view, image_index);
@@ -491,7 +491,7 @@ static vk::Pipeline CreateWrappedPipelineImpl(
.subpass = 0,
.basePipelineHandle = 0,
.basePipelineIndex = 0,
});
}, device.StaticPipelineCache());
}
vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
@@ -22,7 +22,9 @@
#include "video_core/host_shaders/resolve_conditional_render_comp_spv.h"
#include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h"
#include "video_core/host_shaders/vulkan_uint8_comp_spv.h"
#include "video_core/host_shaders/block_linear_unswizzle_2d_buffer_comp_spv.h"
#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h"
#include "video_core/host_shaders/block_linear_unswizzle_3d_buffer_comp_spv.h"
#include "video_core/renderer_vulkan/vk_compute_pass.h"
#include "video_core/surface.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
@@ -268,7 +270,7 @@ ComputePass::ComputePass(const Device& device_, Scheduler& scheduler, Descriptor
.layout = *layout,
.basePipelineHandle = {},
.basePipelineIndex = 0,
});
}, device.StaticPipelineCache());
}
ComputePass::~ComputePass() = default;
@@ -872,4 +874,479 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
});
}
namespace {
constexpr u32 BL2D_BINDING_INPUT_BUFFER = 0;
constexpr u32 BL2D_BINDING_OUTPUT_BUFFER = 1;
struct alignas(16) BlockLinearUnswizzle2DPushConstants {
std::array<u32, 3> dim;
u32 bytes_per_block_log2;
std::array<u32, 3> origin;
u32 layer_stride;
u32 block_size;
u32 x_shift;
u32 block_height;
u32 block_height_mask;
};
static_assert(sizeof(BlockLinearUnswizzle2DPushConstants) <= 128);
constexpr std::array<VkDescriptorSetLayoutBinding, 2> BL2D_BINDINGS{{
{
.binding = BL2D_BINDING_INPUT_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
{
.binding = BL2D_BINDING_OUTPUT_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
}};
constexpr std::array<VkDescriptorUpdateTemplateEntry, 2> BL2D_TEMPLATE{{
{
.dstBinding = BL2D_BINDING_INPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL2D_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
},
{
.dstBinding = BL2D_BINDING_OUTPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL2D_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
},
}};
constexpr DescriptorBankInfo BL2D_BANK_INFO{
.uniform_buffers = 0,
.storage_buffers = 2,
.texture_buffers = 0,
.image_buffers = 0,
.textures = 0,
.images = 0,
.score = 2,
};
} // Anonymous namespace
BlockLinearUnswizzle2DPass::BlockLinearUnswizzle2DPass(
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
: ComputePass(device_, scheduler_, descriptor_pool_, BL2D_BINDINGS, BL2D_TEMPLATE,
BL2D_BANK_INFO,
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzle2DPushConstants)>,
BLOCK_LINEAR_UNSWIZZLE_2D_BUFFER_COMP_SPV),
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
BlockLinearUnswizzle2DPass::~BlockLinearUnswizzle2DPass() = default;
bool BlockLinearUnswizzle2DPass::IsSupported(const VideoCommon::ImageInfo& info) {
if (info.type != VideoCommon::ImageType::e2D) {
return false;
}
if (info.resources.levels != 1 || info.resources.layers != 1) {
return false;
}
if (info.num_samples > 1) {
return false;
}
if (VideoCore::Surface::IsPixelFormatASTC(info.format) ||
VideoCore::Surface::IsPixelFormatBCn(info.format)) {
return false;
}
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format);
if (bytes_per_block != 4 && bytes_per_block != 8 && bytes_per_block != 16) {
return false;
}
return VideoCore::Surface::DefaultBlockWidth(info.format) == 1 &&
VideoCore::Surface::DefaultBlockHeight(info.format) == 1;
}
void BlockLinearUnswizzle2DPass::Unswizzle(
Image& image, const StagingBufferRef& swizzled,
std::span<const VideoCommon::SwizzleParameters> swizzles) {
if (swizzles.empty()) {
return;
}
const VideoCommon::SwizzleParameters& sw = swizzles.front();
const auto params = VideoCommon::Accelerated::MakeBlockLinearSwizzle2DParams(sw, image.info);
const u32 width = sw.num_tiles.width;
const u32 height = sw.num_tiles.height;
const u32 depth = image.info.resources.layers;
const u32 bytes_per_block = 1u << params.bytes_per_block_log2;
const VkDeviceSize output_size =
static_cast<VkDeviceSize>(width) * height * depth * bytes_per_block;
const StagingBufferRef output =
staging_buffer_pool.Request(static_cast<size_t>(output_size), MemoryUsage::DeviceLocal);
BlockLinearUnswizzle2DPushConstants pc{};
pc.dim = {width, height, depth};
pc.bytes_per_block_log2 = params.bytes_per_block_log2;
pc.origin = params.origin;
pc.layer_stride = params.layer_stride;
pc.block_size = params.block_size;
pc.x_shift = params.x_shift;
pc.block_height = params.block_height;
pc.block_height_mask = params.block_height_mask;
scheduler.RequestOutsideRenderPassOperationContext();
compute_pass_descriptor_queue.Acquire(scheduler, 2);
compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, sw.buffer_offset + swizzled.offset,
image.guest_size_bytes - sw.buffer_offset);
compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size);
const void* descriptor_data = compute_pass_descriptor_queue.UpdateData();
const VkDescriptorSet set = descriptor_allocator.Commit();
const u32 gx = Common::DivCeil(width, 16u);
const u32 gy = Common::DivCeil(height, 8u);
const bool is_initialized = image.ExchangeInitialization();
const VkBuffer out_buffer = output.buffer;
const VkDeviceSize out_offset = output.offset;
const VkImage dst_image = image.Handle();
const VkImageAspectFlags aspect = image.AspectMask();
scheduler.Record([this, set, descriptor_data, pc, gx, gy, depth, output_size, out_buffer,
out_offset, dst_image, aspect, width, height,
is_initialized](vk::CommandBuffer cmdbuf) {
if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) {
return;
}
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc);
cmdbuf.Dispatch(gx, gy, depth);
const VkBufferMemoryBarrier buffer_barrier{
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = out_buffer,
.offset = out_offset,
.size = output_size,
};
const VkImageMemoryBarrier pre_copy{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = static_cast<VkAccessFlags>(is_initialized ? VK_ACCESS_SHADER_READ_BIT
: VK_ACCESS_NONE),
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image,
.subresourceRange{
.aspectMask = aspect,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
(is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE
: VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT),
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, pre_copy);
const VkBufferImageCopy copy{
.bufferOffset = out_offset,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource{
.aspectMask = aspect,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = depth,
},
.imageOffset = {0, 0, 0},
.imageExtent = {width, height, 1},
};
cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
const VkImageMemoryBarrier post_copy{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image,
.subresourceRange{
.aspectMask = aspect,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
0, {}, {}, post_copy);
});
}
namespace {
constexpr u32 BL3DB_BINDING_INPUT_BUFFER = 0;
constexpr u32 BL3DB_BINDING_OUTPUT_BUFFER = 1;
struct alignas(16) BlockLinearUnswizzle3DBufferPushConstants {
std::array<u32, 3> dim;
u32 bytes_per_block_log2;
std::array<u32, 3> origin;
u32 slice_size;
u32 block_size;
u32 x_shift;
u32 block_height;
u32 block_height_mask;
u32 block_depth;
u32 block_depth_mask;
};
static_assert(sizeof(BlockLinearUnswizzle3DBufferPushConstants) <= 128);
constexpr std::array<VkDescriptorSetLayoutBinding, 2> BL3DB_BINDINGS{{
{
.binding = BL3DB_BINDING_INPUT_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
{
.binding = BL3DB_BINDING_OUTPUT_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
}};
constexpr std::array<VkDescriptorUpdateTemplateEntry, 2> BL3DB_TEMPLATE{{
{
.dstBinding = BL3DB_BINDING_INPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL3DB_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
},
{
.dstBinding = BL3DB_BINDING_OUTPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL3DB_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
},
}};
constexpr DescriptorBankInfo BL3DB_BANK_INFO{
.uniform_buffers = 0,
.storage_buffers = 2,
.texture_buffers = 0,
.image_buffers = 0,
.textures = 0,
.images = 0,
.score = 2,
};
} // Anonymous namespace
BlockLinearUnswizzle3DBufferPass::BlockLinearUnswizzle3DBufferPass(
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
: ComputePass(device_, scheduler_, descriptor_pool_, BL3DB_BINDINGS, BL3DB_TEMPLATE,
BL3DB_BANK_INFO,
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzle3DBufferPushConstants)>,
BLOCK_LINEAR_UNSWIZZLE_3D_BUFFER_COMP_SPV),
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
BlockLinearUnswizzle3DBufferPass::~BlockLinearUnswizzle3DBufferPass() = default;
bool BlockLinearUnswizzle3DBufferPass::IsSupported(const Device& device,
const VideoCommon::ImageInfo& info) {
if (info.type != VideoCommon::ImageType::e3D) {
return false;
}
if (info.resources.levels != 1 || info.resources.layers != 1) {
return false;
}
if (info.num_samples > 1) {
return false;
}
if (info.size.depth <= 1) {
return false;
}
if (VideoCore::Surface::IsPixelFormatASTC(info.format)) {
return false;
}
if (VideoCore::Surface::IsPixelFormatBCn(info.format) && !device.IsOptimalBcnSupported()) {
return false;
}
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format);
return bytes_per_block == 4 || bytes_per_block == 8 || bytes_per_block == 16;
}
void BlockLinearUnswizzle3DBufferPass::Unswizzle(
Image& image, const StagingBufferRef& swizzled,
std::span<const VideoCommon::SwizzleParameters> swizzles) {
if (swizzles.empty()) {
return;
}
const VideoCommon::SwizzleParameters& sw = swizzles.front();
const auto params = VideoCommon::Accelerated::MakeBlockLinearSwizzle3DParams(sw, image.info);
const u32 blocks_x = sw.num_tiles.width;
const u32 blocks_y = sw.num_tiles.height;
const u32 blocks_z = sw.num_tiles.depth;
const u32 bytes_per_block = 1u << params.bytes_per_block_log2;
const VkDeviceSize output_size =
static_cast<VkDeviceSize>(blocks_x) * blocks_y * blocks_z * bytes_per_block;
const StagingBufferRef output =
staging_buffer_pool.Request(static_cast<size_t>(output_size), MemoryUsage::DeviceLocal);
BlockLinearUnswizzle3DBufferPushConstants pc{};
pc.dim = {blocks_x, blocks_y, blocks_z};
pc.bytes_per_block_log2 = params.bytes_per_block_log2;
pc.origin = params.origin;
pc.slice_size = params.slice_size;
pc.block_size = params.block_size;
pc.x_shift = params.x_shift;
pc.block_height = params.block_height;
pc.block_height_mask = params.block_height_mask;
pc.block_depth = params.block_depth;
pc.block_depth_mask = params.block_depth_mask;
scheduler.RequestOutsideRenderPassOperationContext();
compute_pass_descriptor_queue.Acquire(scheduler, 2);
compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, sw.buffer_offset + swizzled.offset,
image.guest_size_bytes - sw.buffer_offset);
compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size);
const void* descriptor_data = compute_pass_descriptor_queue.UpdateData();
const VkDescriptorSet set = descriptor_allocator.Commit();
const u32 gx = Common::DivCeil(blocks_x, 8u);
const u32 gy = Common::DivCeil(blocks_y, 8u);
const u32 gz = Common::DivCeil(blocks_z, 4u);
const bool is_initialized = image.ExchangeInitialization();
const VkBuffer out_buffer = output.buffer;
const VkDeviceSize out_offset = output.offset;
const VkImage dst_image = image.Handle();
const VkImageAspectFlags aspect = image.AspectMask();
const VkExtent3D extent{
.width = image.info.size.width,
.height = image.info.size.height,
.depth = image.info.size.depth,
};
scheduler.Record([this, set, descriptor_data, pc, gx, gy, gz, output_size, out_buffer,
out_offset, dst_image, aspect, extent,
is_initialized](vk::CommandBuffer cmdbuf) {
if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) {
return;
}
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc);
cmdbuf.Dispatch(gx, gy, gz);
const VkBufferMemoryBarrier buffer_barrier{
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = out_buffer,
.offset = out_offset,
.size = output_size,
};
const VkImageMemoryBarrier pre_copy{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = static_cast<VkAccessFlags>(is_initialized ? VK_ACCESS_SHADER_READ_BIT
: VK_ACCESS_NONE),
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image,
.subresourceRange{
.aspectMask = aspect,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
(is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE
: VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT),
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, pre_copy);
const VkBufferImageCopy copy{
.bufferOffset = out_offset,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource{
.aspectMask = aspect,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset = {0, 0, 0},
.imageExtent = extent,
};
cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
const VkImageMemoryBarrier post_copy{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image,
.subresourceRange{
.aspectMask = aspect,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
0, {}, {}, post_copy);
});
}
} // namespace Vulkan
@@ -164,4 +164,42 @@ private:
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
};
class BlockLinearUnswizzle2DPass final : public ComputePass {
public:
explicit BlockLinearUnswizzle2DPass(const Device& device_, Scheduler& scheduler_,
DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
~BlockLinearUnswizzle2DPass();
[[nodiscard]] static bool IsSupported(const VideoCommon::ImageInfo& info);
void Unswizzle(Image& image, const StagingBufferRef& swizzled,
std::span<const VideoCommon::SwizzleParameters> swizzles);
private:
Scheduler& scheduler;
StagingBufferPool& staging_buffer_pool;
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
};
class BlockLinearUnswizzle3DBufferPass final : public ComputePass {
public:
explicit BlockLinearUnswizzle3DBufferPass(
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
~BlockLinearUnswizzle3DBufferPass();
[[nodiscard]] static bool IsSupported(const Device& device, const VideoCommon::ImageInfo& info);
void Unswizzle(Image& image, const StagingBufferRef& swizzled,
std::span<const VideoCommon::SwizzleParameters> swizzles);
private:
Scheduler& scheduler;
StagingBufferPool& staging_buffer_pool;
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
};
} // namespace Vulkan
@@ -140,6 +140,8 @@ RenderPassKey MakeRenderPassKey(const FixedPipelineState& state, const Device& d
});
key.resolve_color =
key.samples != VK_SAMPLE_COUNT_1_BIT && has_color && device.IsTiler();
key.resolve_depth_stencil = key.samples != VK_SAMPLE_COUNT_1_BIT && device.IsTiler() &&
SupportsDepthStencilResolve(device, key.depth_format);
return key;
}
@@ -63,6 +63,8 @@ using VideoCommon::GenericEnvironment;
using VideoCommon::GraphicsEnvironment;
constexpr u32 CACHE_VERSION = 18;
constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128;
constexpr size_t VULKAN_CACHE_FLUSH_MIN_SECONDS = 30;
constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
template <typename Container>
@@ -150,6 +152,25 @@ Shader::AttributeType AttributeType(const FixedPipelineState& state, size_t inde
return Shader::AttributeType::Disabled;
}
Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology) {
switch (topology) {
case Maxwell::PrimitiveTopology::Points:
return Shader::InputTopology::Points;
case Maxwell::PrimitiveTopology::Lines:
case Maxwell::PrimitiveTopology::LineLoop:
case Maxwell::PrimitiveTopology::LineStrip:
return Shader::InputTopology::Lines;
case Maxwell::PrimitiveTopology::LinesAdjacency:
case Maxwell::PrimitiveTopology::LineStripAdjacency:
return Shader::InputTopology::LinesAdjacency;
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
return Shader::InputTopology::TrianglesAdjacency;
default:
return Shader::InputTopology::Triangles;
}
}
Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> programs,
const GraphicsPipelineCacheKey& key,
const Shader::IR::Program& program,
@@ -268,33 +289,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
default:
break;
}
switch (key.state.topology) {
case Maxwell::PrimitiveTopology::Points:
info.input_topology = Shader::InputTopology::Points;
break;
case Maxwell::PrimitiveTopology::Lines:
case Maxwell::PrimitiveTopology::LineLoop:
case Maxwell::PrimitiveTopology::LineStrip:
info.input_topology = Shader::InputTopology::Lines;
break;
case Maxwell::PrimitiveTopology::Triangles:
case Maxwell::PrimitiveTopology::TriangleStrip:
case Maxwell::PrimitiveTopology::TriangleFan:
case Maxwell::PrimitiveTopology::Quads:
case Maxwell::PrimitiveTopology::QuadStrip:
case Maxwell::PrimitiveTopology::Polygon:
case Maxwell::PrimitiveTopology::Patches:
info.input_topology = Shader::InputTopology::Triangles;
break;
case Maxwell::PrimitiveTopology::LinesAdjacency:
case Maxwell::PrimitiveTopology::LineStripAdjacency:
info.input_topology = Shader::InputTopology::LinesAdjacency;
break;
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
info.input_topology = Shader::InputTopology::TrianglesAdjacency;
break;
}
info.input_topology = MaxwellToInputTopology(key.state.topology);
info.force_early_z = key.state.early_z != 0;
info.y_negate = key.state.y_negate != 0;
return info;
@@ -699,6 +694,10 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
if (use_vulkan_pipeline_cache) {
SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
CACHE_VERSION);
size_t size = 0;
vulkan_pipeline_cache.Read(&size, nullptr);
last_cache_size.store(size, std::memory_order_relaxed);
last_flush = std::chrono::steady_clock::now();
}
if (state.statistics) {
@@ -706,6 +705,35 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
}
}
void PipelineCache::QueueVulkanPipelineCacheFlush() {
if (!use_vulkan_pipeline_cache || vulkan_pipeline_cache_filename.empty()) {
return;
}
if (++pipelines_since_flush < VULKAN_CACHE_FLUSH_PIPELINES) {
return;
}
const auto now = std::chrono::steady_clock::now();
const auto megabytes = last_cache_size.load(std::memory_order_relaxed) / (1024 * 1024);
const std::chrono::seconds interval{
std::max<size_t>(VULKAN_CACHE_FLUSH_MIN_SECONDS, megabytes)};
if (last_flush.time_since_epoch().count() != 0 && now - last_flush < interval) {
return;
}
if (flush_in_flight.exchange(true, std::memory_order_acq_rel)) {
return;
}
pipelines_since_flush = 0;
last_flush = now;
serialization_thread.QueueWork([this] {
SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
CACHE_VERSION);
size_t size = 0;
vulkan_pipeline_cache.Read(&size, nullptr);
last_cache_size.store(size, std::memory_order_relaxed);
flush_in_flight.store(false, std::memory_order_release);
});
}
GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() {
const auto [pair, is_new]{graphics_cache.try_emplace(graphics_key)};
auto& pipeline{pair->second};
@@ -744,7 +772,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
bool build_in_parallel) try {
auto hash = key.Hash();
LOG_INFO(Render_Vulkan, "{:#016x}", hash);
LOG_DEBUG(Render_Vulkan, "{:#016x}", hash);
size_t env_index{0};
std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
const bool uses_vertex_a{key.unique_hashes[0] != 0};
@@ -758,8 +786,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
index == static_cast<u32>(Maxwell::ShaderType::Geometry);
if (key.unique_hashes[index] == 0 && is_emulated_stage) {
auto topology = MaxwellToOutputTopology(key.state.topology);
programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
*layer_source_program, topology);
programs[index] =
GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
*layer_source_program, topology,
MaxwellToInputTopology(key.state.topology));
continue;
}
if (key.unique_hashes[index] == 0) {
@@ -772,11 +802,13 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
if (!uses_vertex_a || index != 1) {
// Normal path
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
MaxwellToInputTopology(key.state.topology));
} else {
// VertexB path when VertexA is present.
auto& program_va{programs[0]};
auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
MaxwellToInputTopology(key.state.topology))};
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
}
@@ -880,6 +912,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
}
SerializePipeline(key, env_ptrs, pipeline_cache_filename, CACHE_VERSION);
});
QueueVulkanPipelineCacheFlush();
return pipeline;
}
@@ -899,6 +932,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env_},
pipeline_cache_filename, CACHE_VERSION);
});
QueueVulkanPipelineCacheFlush();
return pipeline;
}
@@ -911,7 +945,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
return nullptr;
}
LOG_INFO(Render_Vulkan, "{:#016x}", hash);
LOG_DEBUG(Render_Vulkan, "{:#016x}", hash);
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
@@ -920,7 +954,8 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
env.Dump(hash, key.unique_hash);
}
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
Shader::InputTopology::Points)};
const VkDriverIdKHR driver_id = device.GetDriverID();
const bool needs_shared_mem_clamp =
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
@@ -7,6 +7,8 @@
#pragma once
#include <array>
#include <atomic>
#include <chrono>
#include <cstddef>
#include <filesystem>
#include <memory>
@@ -144,6 +146,8 @@ private:
vk::PipelineCache LoadVulkanPipelineCache(const std::filesystem::path& filename,
u32 expected_cache_version);
void QueueVulkanPipelineCacheFlush();
const Device& device;
Scheduler& scheduler;
DescriptorPool& descriptor_pool;
@@ -171,6 +175,10 @@ private:
std::filesystem::path vulkan_pipeline_cache_filename;
vk::PipelineCache vulkan_pipeline_cache;
size_t pipelines_since_flush{};
std::chrono::steady_clock::time_point last_flush{};
std::atomic<size_t> last_cache_size{};
std::atomic_bool flush_in_flight{};
Common::ThreadWorker workers;
Common::ThreadWorker serialization_thread;
@@ -725,6 +725,9 @@ public:
if (!device.IsExtTransformFeedbackSupported()) {
return;
}
if (!scheduler.IsRenderPassActive()) {
return;
}
FlushBeginTFB();
has_started = true;
}
@@ -741,6 +744,9 @@ public:
if (has_flushed_end_pending) {
if (scheduler.IsRenderPassActive()) {
FlushEndTFB();
} else {
has_flushed_end_pending = false;
has_started = false;
}
}
runtime.View3DRegs([this](Maxwell3D& maxwell3d) {
@@ -1286,33 +1286,42 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
regs.zeta.format == Tegra::DepthFormat::X8Z24_UNORM ||
regs.zeta.format == Tegra::DepthFormat::S8Z24_UNORM ||
regs.zeta.format == Tegra::DepthFormat::V8Z24_UNORM;
const bool forces_unorm_representation = device.IsExtDepthBiasControlSupported();
if (is_d24 && !device.SupportsD24DepthBuffer()) {
static constexpr const size_t length = sizeof(NEEDS_D24) / sizeof(NEEDS_D24[0]);
static constexpr double GUEST_TO_HOST_UNORM_BITS =
static_cast<double>(1ULL << (32 - 24));
static constexpr const u64* start = NEEDS_D24;
static constexpr const u64* end = NEEDS_D24 + length;
if (forces_unorm_representation) {
units = static_cast<float>(static_cast<double>(units) * GUEST_TO_HOST_UNORM_BITS);
} else {
static constexpr const size_t length = sizeof(NEEDS_D24) / sizeof(NEEDS_D24[0]);
const u64* it = std::find(start, end, program_id);
static constexpr const u64* start = NEEDS_D24;
static constexpr const u64* end = NEEDS_D24 + length;
if (it != end) {
// the base formulas can be obtained from here:
// https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias
const double rescale_factor =
static_cast<double>(1ULL << (32 - 24)) / (static_cast<double>(0x1.ep+127));
units = static_cast<float>(static_cast<double>(units) * rescale_factor);
const u64* it = std::find(start, end, program_id);
if (it != end) {
// the base formulas can be obtained from here:
// https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias
const double rescale_factor =
GUEST_TO_HOST_UNORM_BITS / (static_cast<double>(0x1.ep+127));
units = static_cast<float>(static_cast<double>(units) * rescale_factor);
}
}
}
scheduler.Record([constant = units, clamp = regs.depth_bias_clamp,
factor = regs.slope_scale_depth_bias, this](vk::CommandBuffer cmdbuf) {
if (device.IsExtDepthBiasControlSupported()) {
static VkDepthBiasRepresentationInfoEXT bias_info{
factor = regs.slope_scale_depth_bias,
forces_unorm_representation, this](vk::CommandBuffer cmdbuf) {
if (forces_unorm_representation) {
const VkDepthBiasRepresentationInfoEXT bias_info{
.sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_REPRESENTATION_INFO_EXT,
.pNext = nullptr,
.depthBiasRepresentation =
VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT,
.depthBiasExact = VK_FALSE,
.depthBiasExact = static_cast<VkBool32>(device.HasExactDepthBiasControl()),
};
cmdbuf.SetDepthBias(constant, clamp, factor, &bias_info);
@@ -1336,8 +1345,14 @@ void RasterizerVulkan::UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs)
if (!state_tracker.TouchDepthBounds()) {
return;
}
scheduler.Record([min = regs.depth_bounds[0], max = regs.depth_bounds[1]](
vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBounds(min, max); });
float min = regs.depth_bounds[0];
float max = regs.depth_bounds[1];
if (!device.IsExtDepthRangeUnrestrictedSupported()) {
min = std::clamp(min, 0.0f, 1.0f);
max = std::clamp(max, 0.0f, 1.0f);
}
scheduler.Record(
[min, max](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBounds(min, max); });
}
void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) {
@@ -65,8 +65,61 @@ using VideoCore::Surface::SurfaceType;
.finalLayout = VK_IMAGE_LAYOUT_GENERAL,
};
}
struct ResolveAspects {
bool depth;
bool stencil;
};
struct ResolveModes {
VkResolveModeFlagBits depth;
VkResolveModeFlagBits stencil;
};
constexpr ResolveAspects GetResolveAspects(PixelFormat format) {
const SurfaceType surface_type = GetSurfaceType(format);
return ResolveAspects{
.depth = surface_type == SurfaceType::Depth ||
surface_type == SurfaceType::DepthStencil,
.stencil = surface_type == SurfaceType::Stencil ||
surface_type == SurfaceType::DepthStencil,
};
}
ResolveModes PickResolveModes(const Device& device, PixelFormat format) {
constexpr VkResolveModeFlagBits mode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
const ResolveAspects aspects = GetResolveAspects(format);
ResolveModes modes{
.depth = VK_RESOLVE_MODE_NONE,
.stencil = VK_RESOLVE_MODE_NONE,
};
if (aspects.depth && (device.GetDepthResolveModes() & mode) != 0) {
modes.depth = mode;
}
if (aspects.stencil && (device.GetStencilResolveModes() & mode) != 0) {
modes.stencil = mode;
}
return modes;
}
} // Anonymous namespace
bool SupportsDepthStencilResolve(const Device& device, PixelFormat depth_format) {
if (depth_format == PixelFormat::Invalid || !device.IsKhrDepthStencilResolveSupported()) {
return false;
}
const ResolveAspects aspects = GetResolveAspects(depth_format);
if (!aspects.depth && !aspects.stencil) {
return false;
}
const ResolveModes modes = PickResolveModes(device, depth_format);
if ((aspects.depth && modes.depth == VK_RESOLVE_MODE_NONE) ||
(aspects.stencil && modes.stencil == VK_RESOLVE_MODE_NONE)) {
return false;
}
return modes.depth == modes.stencil || device.SupportsIndependentResolveNone();
}
RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {}
VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
@@ -75,7 +128,9 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
if (!is_new) {
return *pair->second;
}
boost::container::static_vector<VkAttachmentDescription, 9> descriptions;
static constexpr size_t MAX_ATTACHMENTS =
2 * std::tuple_size_v<decltype(RenderPassKey::color_formats)> + 2;
boost::container::static_vector<VkAttachmentDescription, MAX_ATTACHMENTS> descriptions;
std::array<VkAttachmentReference, 8> references{};
u32 num_attachments{};
u32 num_colors{};
@@ -133,6 +188,21 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
}
}
}
const bool do_resolve_depth_stencil = key.resolve_depth_stencil && has_depth &&
key.samples != VK_SAMPLE_COUNT_1_BIT &&
SupportsDepthStencilResolve(*device, key.depth_format);
VkAttachmentReference depth_resolve_reference{};
if (do_resolve_depth_stencil) {
depth_resolve_reference = VkAttachmentReference{
.attachment = static_cast<u32>(descriptions.size()),
.layout = VK_IMAGE_LAYOUT_GENERAL,
};
VkAttachmentDescription resolve_desc =
AttachmentDescription(*device, key.depth_format, VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE);
resolve_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
descriptions.push_back(resolve_desc);
}
const VkSubpassDescription subpass{
.flags = 0,
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
@@ -145,18 +215,117 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
.preserveAttachmentCount = 0,
.pPreserveAttachments = nullptr,
};
const VkSubpassDependency dependency{
.srcSubpass = 0, // Current subpass
.dstSubpass = 0, // Same subpass (self-dependency)
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT
const VkSubpassDependency counter_resume_dependency{
.srcSubpass = 0,
.dstSubpass = 0,
.srcStageMask = VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
.dstStageMask = VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
.srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT,
.dstAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT,
.dependencyFlags = 0,
};
const bool can_resume_transform_feedback = device->IsExtTransformFeedbackSupported();
if (device->IsKhrCreateRenderPass2Supported()) {
boost::container::static_vector<VkAttachmentDescription2, MAX_ATTACHMENTS> descriptions2;
for (const VkAttachmentDescription& description : descriptions) {
descriptions2.push_back(VkAttachmentDescription2{
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2,
.pNext = nullptr,
.flags = description.flags,
.format = description.format,
.samples = description.samples,
.loadOp = description.loadOp,
.storeOp = description.storeOp,
.stencilLoadOp = description.stencilLoadOp,
.stencilStoreOp = description.stencilStoreOp,
.initialLayout = description.initialLayout,
.finalLayout = description.finalLayout,
});
}
const auto promote = [](const VkAttachmentReference& reference) {
return VkAttachmentReference2{
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2,
.pNext = nullptr,
.attachment = reference.attachment,
.layout = reference.layout,
.aspectMask = 0,
};
};
std::array<VkAttachmentReference2, 8> references2{};
std::array<VkAttachmentReference2, 8> resolve_references2{};
for (size_t index = 0; index < references.size(); ++index) {
references2[index] = promote(references[index]);
resolve_references2[index] = promote(resolve_references[index]);
}
const VkAttachmentReference2 depth_reference2 = promote(depth_reference);
const VkAttachmentReference2 depth_resolve_reference2 = promote(depth_resolve_reference);
const ResolveModes resolve_modes = PickResolveModes(*device, key.depth_format);
const VkSubpassDescriptionDepthStencilResolve depth_stencil_resolve{
.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE,
.pNext = nullptr,
.depthResolveMode = resolve_modes.depth,
.stencilResolveMode = resolve_modes.stencil,
.pDepthStencilResolveAttachment = &depth_resolve_reference2,
};
const VkSubpassDescription2 subpass2{
.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2,
.pNext = do_resolve_depth_stencil ? &depth_stencil_resolve : nullptr,
.flags = 0,
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.viewMask = 0,
.inputAttachmentCount = 0,
.pInputAttachments = nullptr,
.colorAttachmentCount = num_attachments,
.pColorAttachments = references2.data(),
.pResolveAttachments = do_resolve_color ? resolve_references2.data() : nullptr,
.pDepthStencilAttachment = has_depth ? &depth_reference2 : nullptr,
.preserveAttachmentCount = 0,
.pPreserveAttachments = nullptr,
};
const VkMemoryBarrier2 counter_resume_barrier{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT,
.srcAccessMask = VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT,
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT,
.dstAccessMask = VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT,
};
VkSubpassDependency2 counter_resume_dependency2{
.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2,
.pNext = nullptr,
.srcSubpass = counter_resume_dependency.srcSubpass,
.dstSubpass = counter_resume_dependency.dstSubpass,
.srcStageMask = counter_resume_dependency.srcStageMask,
.dstStageMask = counter_resume_dependency.dstStageMask,
.srcAccessMask = counter_resume_dependency.srcAccessMask,
.dstAccessMask = counter_resume_dependency.dstAccessMask,
.dependencyFlags = counter_resume_dependency.dependencyFlags,
.viewOffset = 0,
};
if (device->HasSynchronization2()) {
counter_resume_dependency2.pNext = &counter_resume_barrier;
counter_resume_dependency2.srcStageMask = 0;
counter_resume_dependency2.dstStageMask = 0;
counter_resume_dependency2.srcAccessMask = 0;
counter_resume_dependency2.dstAccessMask = 0;
}
pair->second = device->GetLogical().CreateRenderPass2({
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2,
.pNext = nullptr,
.flags = 0,
.attachmentCount = static_cast<u32>(descriptions2.size()),
.pAttachments = descriptions2.empty() ? nullptr : descriptions2.data(),
.subpassCount = 1,
.pSubpasses = &subpass2,
.dependencyCount = can_resume_transform_feedback ? 1u : 0u,
.pDependencies = can_resume_transform_feedback ? &counter_resume_dependency2 : nullptr,
.correlatedViewMaskCount = 0,
.pCorrelatedViewMasks = nullptr,
});
return *pair->second;
}
pair->second = device->GetLogical().CreateRenderPass({
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.pNext = nullptr,
@@ -165,8 +334,8 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
.pAttachments = descriptions.empty() ? nullptr : descriptions.data(),
.subpassCount = 1,
.pSubpasses = &subpass,
.dependencyCount = 1,
.pDependencies = &dependency,
.dependencyCount = can_resume_transform_feedback ? 1u : 0u,
.pDependencies = can_resume_transform_feedback ? &counter_resume_dependency : nullptr,
});
return *pair->second;
}
@@ -9,6 +9,7 @@
#include <mutex>
#include <ankerl/unordered_dense.h>
#include "common/container_hash.h"
#include "video_core/surface.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
@@ -21,6 +22,7 @@ struct RenderPassKey {
VideoCore::Surface::PixelFormat depth_format;
VkSampleCountFlagBits samples;
bool resolve_color;
bool resolve_depth_stencil;
u32 color_clear_mask;
bool depth_stencil_clear;
u32 color_discard_mask;
@@ -31,17 +33,27 @@ struct RenderPassKey {
namespace std {
template <>
struct hash<Vulkan::RenderPassKey> {
static_assert(std::tuple_size_v<decltype(Vulkan::RenderPassKey::color_formats)> <= 8);
static_assert(static_cast<u32>(VideoCore::Surface::PixelFormat::Invalid) <= 0xFF);
static_assert(static_cast<u32>(VideoCore::Surface::PixelFormat::Max) <= 0xFF);
static_assert(VK_SAMPLE_COUNT_64_BIT <= 0xFF);
[[nodiscard]] size_t operator()(const Vulkan::RenderPassKey& key) const noexcept {
size_t value = static_cast<size_t>(key.depth_format) << 48;
value ^= static_cast<size_t>(key.samples) << 52;
value ^= static_cast<size_t>(key.resolve_color) << 63;
value ^= static_cast<size_t>(key.color_clear_mask) << 54;
value ^= static_cast<size_t>(key.depth_stencil_clear) << 62;
value ^= static_cast<size_t>(key.color_discard_mask) << 24;
for (size_t i = 0; i < key.color_formats.size(); ++i) {
value ^= static_cast<size_t>(key.color_formats[i]) << (i * 6);
u64 formats = 0;
for (size_t index = 0; index < key.color_formats.size(); ++index) {
formats |= static_cast<u64>(key.color_formats[index]) << (index * 8);
}
return value;
const u64 state = static_cast<u64>(key.depth_format) |
(static_cast<u64>(key.samples) << 8) |
(static_cast<u64>(key.color_clear_mask) << 16) |
(static_cast<u64>(key.color_discard_mask) << 24) |
(static_cast<u64>(key.resolve_color) << 32) |
(static_cast<u64>(key.depth_stencil_clear) << 33) |
(static_cast<u64>(key.resolve_depth_stencil) << 34);
size_t seed = 0;
Common::HashCombine(seed, formats);
Common::HashCombine(seed, state);
return seed;
}
};
} // namespace std
@@ -50,6 +62,9 @@ namespace Vulkan {
class Device;
[[nodiscard]] bool SupportsDepthStencilResolve(const Device& device,
VideoCore::Surface::PixelFormat depth_format);
class RenderPassCache {
public:
explicit RenderPassCache(const Device& device_);
@@ -410,8 +410,17 @@ void Scheduler::EndRenderPass()
Record([num_images = num_renderpass_images,
images = renderpass_images,
ranges = renderpass_image_ranges,
consumer_stages = device.AttachmentConsumerStages(),
has_transform_feedback = device.IsExtTransformFeedbackSupported()](
vk::CommandBuffer cmdbuf) {
static constexpr VkAccessFlags SHADER_ACCESS =
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
static constexpr VkAccessFlags COLOR_ACCESS =
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
static constexpr VkAccessFlags DEPTH_STENCIL_ACCESS =
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
std::array<VkImageMemoryBarrier, 9> barriers;
for (size_t i = 0; i < num_images; ++i) {
const VkImageSubresourceRange& range = ranges[i];
@@ -421,24 +430,25 @@ void Scheduler::EndRenderPass()
| VK_IMAGE_ASPECT_STENCIL_BIT)) !=0;
VkAccessFlags src_access = 0;
VkAccessFlags dst_access = SHADER_ACCESS;
if (is_color)
if (is_color) {
src_access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
else if (is_depth_stencil)
dst_access |= COLOR_ACCESS;
} else if (is_depth_stencil) {
src_access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
else
dst_access |= DEPTH_STENCIL_ACCESS;
} else {
src_access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dst_access |= COLOR_ACCESS | DEPTH_STENCIL_ACCESS;
}
barriers[i] = VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = src_access,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.dstAccessMask = dst_access,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
@@ -449,7 +459,7 @@ void Scheduler::EndRenderPass()
}
cmdbuf.EndRenderPass();
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, consumer_stages,
0, nullptr, nullptr, vk::Span(barriers.data(), num_images));
if (has_transform_feedback) {
static constexpr VkMemoryBarrier XFB_OUTPUT_BARRIER{
@@ -628,18 +628,12 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
.subresourceRange = subresource_range,
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
read_barrier);
cmdbuf.CopyBufferToImage(src_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copies);
// TODO: Move this to another API
cmdbuf.PipelineBarrier(
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
0, nullptr, nullptr, write_barrier);
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0,
nullptr, nullptr, write_barrier);
}
[[nodiscard]] VkImageBlit MakeImageBlit(const Region2D& dst_region, const Region2D& src_region,
@@ -933,6 +927,10 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
staging_buffer_pool, compute_pass_descriptor_queue);
}
bl2d_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
compute_pass_descriptor_queue);
bl3db_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
compute_pass_descriptor_queue);
}
void TextureCacheRuntime::Finish() {
@@ -995,13 +993,25 @@ VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) {
}
VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, VkFormat format,
VkExtent2D extent, u32 layers) {
VkExtent2D extent, u32 layers,
VkImageAspectFlags aspect_mask) {
ResolveShadow& shadow = resolve_shadows[msaa_image];
if (shadow.image && shadow.format == format && shadow.extent.width == extent.width &&
shadow.extent.height == extent.height && shadow.layers == layers) {
shadow.extent.height == extent.height && shadow.layers == layers &&
shadow.aspect_mask == aspect_mask) {
shadow.up_to_date = true;
return *shadow.view;
}
VkImageUsageFlags shadow_usage =
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
shadow_usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
} else {
shadow_usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
}
if (shadow.image) {
pending_resolve_shadows.emplace_back(scheduler.CurrentTick(), std::move(shadow));
}
shadow.image = memory_allocator.CreateImage(VkImageCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = nullptr,
@@ -1013,8 +1023,7 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk
.arrayLayers = layers,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
.usage = shadow_usage,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
@@ -1029,7 +1038,7 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk
.format = format,
.components{},
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.aspectMask = aspect_mask,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
@@ -1039,6 +1048,7 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk
shadow.format = format;
shadow.extent = extent;
shadow.layers = layers;
shadow.aspect_mask = aspect_mask;
shadow.up_to_date = true;
return *shadow.view;
}
@@ -1060,7 +1070,14 @@ void TextureCacheRuntime::InvalidateResolveShadow(VkImage msaa_image) {
}
void TextureCacheRuntime::EraseResolveShadow(VkImage msaa_image) {
resolve_shadows.erase(msaa_image);
const auto it = resolve_shadows.find(msaa_image);
if (it == resolve_shadows.end()) {
return;
}
if (it->second.image) {
pending_resolve_shadows.emplace_back(scheduler.CurrentTick(), std::move(it->second));
}
resolve_shadows.erase(it);
}
void TextureCacheRuntime::BarrierFeedbackLoop() {
@@ -1195,7 +1212,8 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src,
cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, READ_BARRIER, {}, middle_out_barrier);
cmdbuf.CopyBufferToImage(copy_buffer, dst_image, VK_IMAGE_LAYOUT_GENERAL, vk_out_copies);
cmdbuf.CopyBufferToImage(copy_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
vk_out_copies);
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
0, {}, {}, post_barriers);
});
@@ -1357,133 +1375,83 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
}
switch (dst_view.format) {
case PixelFormat::D24_UNORM_S8_UINT:
if (src_view.format == PixelFormat::A8B8G8R8_UNORM
|| src_view.format == PixelFormat::B8G8R8A8_UNORM
|| src_view.format == PixelFormat::A8B8G8R8_SRGB
|| src_view.format == PixelFormat::B8G8R8A8_SRGB) {
return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view);
case PixelFormat::R16_UNORM:
if (src_view.format == PixelFormat::D16_UNORM) {
return blit_image_helper.ConvertD16ToR16(dst, src_view);
}
break;
case PixelFormat::A8B8G8R8_SRGB:
case PixelFormat::B8G8R8A8_SRGB:
case PixelFormat::B8G8R8A8_UNORM:
if (src_view.format == PixelFormat::D32_FLOAT) {
return blit_image_helper.ConvertD32FToABGR8(dst, src_view);
}
break;
case PixelFormat::A8B8G8R8_UNORM:
case PixelFormat::A8B8G8R8_SNORM:
case PixelFormat::A8B8G8R8_SINT:
case PixelFormat::A8B8G8R8_UINT:
case PixelFormat::R5G6B5_UNORM:
case PixelFormat::B5G6R5_UNORM:
case PixelFormat::A1R5G5B5_UNORM:
case PixelFormat::A2B10G10R10_UNORM:
case PixelFormat::A2B10G10R10_UINT:
case PixelFormat::A2R10G10B10_UNORM:
case PixelFormat::A1B5G5R5_UNORM:
case PixelFormat::A5B5G5R1_UNORM:
case PixelFormat::R8_UNORM:
case PixelFormat::R8_SNORM:
case PixelFormat::R8_SINT:
case PixelFormat::R8_UINT:
case PixelFormat::R16G16B16A16_FLOAT:
case PixelFormat::R16G16B16A16_UNORM:
case PixelFormat::R16G16B16A16_SNORM:
case PixelFormat::R16G16B16A16_SINT:
case PixelFormat::R16G16B16A16_UINT:
case PixelFormat::B10G11R11_FLOAT:
case PixelFormat::R32G32B32A32_UINT:
case PixelFormat::BC1_RGBA_UNORM:
case PixelFormat::BC2_UNORM:
case PixelFormat::BC3_UNORM:
case PixelFormat::BC4_UNORM:
case PixelFormat::BC4_SNORM:
case PixelFormat::BC5_UNORM:
case PixelFormat::BC5_SNORM:
case PixelFormat::BC7_UNORM:
case PixelFormat::BC6H_UFLOAT:
case PixelFormat::BC6H_SFLOAT:
case PixelFormat::ASTC_2D_4X4_UNORM:
case PixelFormat::B8G8R8A8_UNORM:
case PixelFormat::R32G32B32A32_FLOAT:
case PixelFormat::R32G32B32A32_SINT:
case PixelFormat::R32G32_FLOAT:
case PixelFormat::R32G32_SINT:
case PixelFormat::R32_FLOAT:
if (src_view.format == PixelFormat::D32_FLOAT &&
(dst_view.format == PixelFormat::B5G6R5_UNORM ||
Settings::values.fix_bloom_effects.GetValue())) {
const Region2D region{
.start = {0, 0},
.end = {static_cast<s32>(dst->RenderArea().width),
static_cast<s32>(dst->RenderArea().height)},
};
return blit_image_helper.BlitColor(dst, src_view, region, region,
Tegra::Engines::Fermi2D::Filter::Point,
Tegra::Engines::Fermi2D::Operation::SrcCopy);
if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) {
return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view);
}
if (src_view.format == PixelFormat::D24_UNORM_S8_UINT) {
return blit_image_helper.ConvertS8D24ToABGR8(dst, src_view);
}
if (src_view.format == PixelFormat::D32_FLOAT) {
return blit_image_helper.ConvertD32FToABGR8(dst, src_view);
}
break;
case PixelFormat::R32_FLOAT:
if (src_view.format == PixelFormat::D32_FLOAT) {
return blit_image_helper.ConvertD32ToR32(dst, src_view);
}
break;
case PixelFormat::R16_FLOAT:
case PixelFormat::R16_UNORM:
case PixelFormat::R16_SNORM:
case PixelFormat::R16_UINT:
case PixelFormat::R16_SINT:
case PixelFormat::R16G16_UNORM:
case PixelFormat::R16G16_FLOAT:
case PixelFormat::R16G16_UINT:
case PixelFormat::R16G16_SINT:
case PixelFormat::R16G16_SNORM:
case PixelFormat::R32G32B32_FLOAT:
case PixelFormat::A8B8G8R8_SRGB:
case PixelFormat::R8G8_UNORM:
case PixelFormat::R8G8_SNORM:
case PixelFormat::R8G8_SINT:
case PixelFormat::R8G8_UINT:
case PixelFormat::R32G32_UINT:
case PixelFormat::R16G16B16X16_FLOAT:
case PixelFormat::R32_UINT:
case PixelFormat::R32_SINT:
case PixelFormat::ASTC_2D_8X8_UNORM:
case PixelFormat::ASTC_2D_8X5_UNORM:
case PixelFormat::ASTC_2D_5X4_UNORM:
case PixelFormat::B8G8R8A8_SRGB:
case PixelFormat::BC1_RGBA_SRGB:
case PixelFormat::BC2_SRGB:
case PixelFormat::BC3_SRGB:
case PixelFormat::BC7_SRGB:
case PixelFormat::A4B4G4R4_UNORM:
case PixelFormat::G4R4_UNORM:
case PixelFormat::ASTC_2D_4X4_SRGB:
case PixelFormat::ASTC_2D_8X8_SRGB:
case PixelFormat::ASTC_2D_8X5_SRGB:
case PixelFormat::ASTC_2D_5X4_SRGB:
case PixelFormat::ASTC_2D_5X5_UNORM:
case PixelFormat::ASTC_2D_5X5_SRGB:
case PixelFormat::ASTC_2D_10X8_UNORM:
case PixelFormat::ASTC_2D_10X8_SRGB:
case PixelFormat::ASTC_2D_6X6_UNORM:
case PixelFormat::ASTC_2D_6X6_SRGB:
case PixelFormat::ASTC_2D_10X6_UNORM:
case PixelFormat::ASTC_2D_10X6_SRGB:
case PixelFormat::ASTC_2D_10X5_UNORM:
case PixelFormat::ASTC_2D_10X5_SRGB:
case PixelFormat::ASTC_2D_10X10_UNORM:
case PixelFormat::ASTC_2D_10X10_SRGB:
case PixelFormat::ASTC_2D_12X10_UNORM:
case PixelFormat::ASTC_2D_12X10_SRGB:
case PixelFormat::ASTC_2D_12X12_UNORM:
case PixelFormat::ASTC_2D_12X12_SRGB:
case PixelFormat::ASTC_2D_8X6_UNORM:
case PixelFormat::ASTC_2D_8X6_SRGB:
case PixelFormat::ASTC_2D_6X5_UNORM:
case PixelFormat::ASTC_2D_6X5_SRGB:
case PixelFormat::E5B9G9R9_FLOAT:
case PixelFormat::D32_FLOAT:
case PixelFormat::D16_UNORM:
case PixelFormat::X8_D24_UNORM:
case PixelFormat::S8_UINT:
if (src_view.format == PixelFormat::R16_UNORM) {
return blit_image_helper.ConvertR16ToD16(dst, src_view);
}
break;
case PixelFormat::S8_UINT_D24_UNORM:
case PixelFormat::D32_FLOAT_S8_UINT:
case PixelFormat::Invalid:
if (src_view.format == PixelFormat::A8B8G8R8_UNORM ||
src_view.format == PixelFormat::B8G8R8A8_UNORM) {
return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view);
}
break;
case PixelFormat::D32_FLOAT:
if (src_view.format == PixelFormat::A8B8G8R8_UNORM ||
src_view.format == PixelFormat::B8G8R8A8_UNORM ||
src_view.format == PixelFormat::A8B8G8R8_SRGB ||
src_view.format == PixelFormat::B8G8R8A8_SRGB) {
return blit_image_helper.ConvertABGR8ToD32F(dst, src_view);
}
if (src_view.format == PixelFormat::R32_FLOAT) {
return blit_image_helper.ConvertR32ToD32(dst, src_view);
}
break;
case PixelFormat::D24_UNORM_S8_UINT:
if (src_view.format == PixelFormat::A8B8G8R8_UNORM ||
src_view.format == PixelFormat::B8G8R8A8_UNORM ||
src_view.format == PixelFormat::A8B8G8R8_SRGB ||
src_view.format == PixelFormat::B8G8R8A8_SRGB) {
return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view);
}
break;
default:
LOG_DEBUG(Render_Vulkan, "Unimplemented texture conversion from {} to {} format type", src_view.format, dst_view.format);
break;
}
if (src_view.format == PixelFormat::D32_FLOAT &&
VideoCore::Surface::GetFormatType(dst_view.format) == SurfaceType::ColorTexture &&
(dst_view.format == PixelFormat::B5G6R5_UNORM ||
Settings::values.fix_bloom_effects.GetValue())) {
const Region2D region{
.start = {0, 0},
.end = {static_cast<s32>(dst->RenderArea().width),
static_cast<s32>(dst->RenderArea().height)},
};
return blit_image_helper.BlitColor(dst, src_view, region, region,
Tegra::Engines::Fermi2D::Filter::Point,
Tegra::Engines::Fermi2D::Operation::SrcCopy);
}
LOG_DEBUG(Render_Vulkan, "Unimplemented texture conversion from {} to {} format type", src_view.format, dst_view.format);
}
VkFormat TextureCacheRuntime::GetSupportedFormat(VkFormat requested_format,
@@ -1640,31 +1608,41 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
std::span<const VideoCommon::ImageCopy> copies) {
const bool msaa_to_non_msaa = src.info.num_samples > 1 && dst.info.num_samples == 1;
const u32 num_samples = msaa_to_non_msaa ? src.info.num_samples : dst.info.num_samples;
if (dst.AspectMask() != VK_IMAGE_ASPECT_COLOR_BIT ||
VideoCore::Surface::IsPixelFormatInteger(dst.info.format)) {
UNIMPLEMENTED_MSG("Copying images with different samples is not supported.");
return;
}
if (ENABLE_MSAA_RESOLVE_CONSUME && msaa_to_non_msaa && copies.size() == 1 &&
src.info.format == dst.info.format) {
const VideoCommon::ImageCopy& copy = copies.front();
const ResolveShadow* const shadow = GetValidResolveShadow(src.Handle());
if (shadow != nullptr && copy.src_offset.x == 0 && copy.src_offset.y == 0 &&
if (shadow != nullptr && shadow->aspect_mask == dst.AspectMask() &&
copy.src_offset.x == 0 && copy.src_offset.y == 0 &&
copy.src_subresource.base_level == 0 &&
static_cast<u32>(copy.extent.width) <= shadow->extent.width &&
static_cast<u32>(copy.extent.height) <= shadow->extent.height) {
static_cast<u32>(copy.extent.height) <= shadow->extent.height &&
static_cast<u32>(copy.src_subresource.base_layer + copy.src_subresource.num_layers) <=
shadow->layers) {
const VkImageAspectFlags aspect_mask = shadow->aspect_mask;
VkPipelineStageFlags attachment_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkAccessFlags attachment_write = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
VkAccessFlags attachment_read_write =
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
attachment_stage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
attachment_write = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
attachment_read_write = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
}
const VkImage shadow_image = *shadow->image;
const VkImage dst_image = dst.Handle();
const VkImageCopy region{
.srcSubresource{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.aspectMask = aspect_mask,
.mipLevel = 0,
.baseArrayLayer = static_cast<u32>(copy.src_subresource.base_layer),
.layerCount = static_cast<u32>(copy.src_subresource.num_layers),
},
.srcOffset = {0, 0, 0},
.dstSubresource{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.aspectMask = aspect_mask,
.mipLevel = static_cast<u32>(copy.dst_subresource.base_level),
.baseArrayLayer = static_cast<u32>(copy.dst_subresource.base_layer),
.layerCount = static_cast<u32>(copy.dst_subresource.num_layers),
@@ -1673,26 +1651,27 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
.extent = {copy.extent.width, copy.extent.height, 1},
};
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([shadow_image, dst_image, region](vk::CommandBuffer cmdbuf) {
scheduler.Record([shadow_image, dst_image, region, aspect_mask, attachment_stage,
attachment_write,
attachment_read_write](vk::CommandBuffer cmdbuf) {
const std::array pre_barriers{
VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.srcAccessMask = attachment_write,
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = shadow_image,
.subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0,
.subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
VK_REMAINING_ARRAY_LAYERS},
},
VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT |
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | attachment_write |
VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
@@ -1700,7 +1679,7 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image,
.subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0,
.subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
VK_REMAINING_ARRAY_LAYERS},
},
};
@@ -1715,28 +1694,25 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = shadow_image,
.subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0,
.subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
VK_REMAINING_ARRAY_LAYERS},
},
VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT |
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | attachment_read_write |
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image,
.subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0,
.subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
VK_REMAINING_ARRAY_LAYERS},
},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_TRANSFER_BIT,
cmdbuf.PipelineBarrier(attachment_stage | VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, nullptr, nullptr,
pre_barriers);
cmdbuf.CopyImage(shadow_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_image,
@@ -1748,6 +1724,11 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
return;
}
}
if (dst.AspectMask() != VK_IMAGE_ASPECT_COLOR_BIT ||
VideoCore::Surface::IsPixelFormatInteger(dst.info.format)) {
UNIMPLEMENTED_MSG("Copying images with different samples is not supported.");
return;
}
blit_image_helper.CopyMSAA(render_pass_cache, dst.Handle(), dst.info.format, src.Handle(),
src.info.format, num_samples, copies, msaa_to_non_msaa);
}
@@ -1772,6 +1753,9 @@ void TextureCacheRuntime::TickFrame() {
std::erase_if(pending_msaa_images, [this](const auto& pending) {
return scheduler.IsFree(pending.first);
});
std::erase_if(pending_resolve_shadows, [this](const auto& pending) {
return scheduler.IsFree(pending.first);
});
}
Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_,
@@ -1796,6 +1780,13 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
}
flags |= VideoCommon::ImageFlagBits::Converted;
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
} else if (runtime->bl2d_unswizzle_pass && BlockLinearUnswizzle2DPass::IsSupported(info)) {
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
} else if (runtime->bl3db_unswizzle_pass &&
BlockLinearUnswizzle3DBufferPass::IsSupported(runtime->device, info)) {
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
}
if (IsPixelFormatBCn(info.format) && !runtime->device.IsOptimalBcnSupported()) {
flags |= VideoCommon::ImageFlagBits::Converted;
@@ -1882,11 +1873,21 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
ScaleDown(true);
}
const bool msaa_upload_is_depth = (aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0;
const bool wants_msaa_upload = info.num_samples > 1
&& (aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0
&& ((aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0 || msaa_upload_is_depth)
&& !VideoCore::Surface::IsPixelFormatInteger(info.format);
if (wants_msaa_upload) {
const bool msaa_upload_copies_stencil =
msaa_upload_is_depth && (aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0 &&
runtime->device.IsExtShaderStencilExportSupported();
const VkImageAspectFlags upload_aspect_mask =
msaa_upload_is_depth
? (msaa_upload_copies_stencil
? VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT
: VK_IMAGE_ASPECT_DEPTH_BIT)
: aspect_mask;
ImageInfo temp_info = info;
temp_info.num_samples = 1;
@@ -1898,10 +1899,10 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
vk::Image temp_image = runtime->memory_allocator.CreateImage(image_ci);
scheduler->RequestOutsideRenderPassOperationContext();
auto vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask);
auto vk_copies = TransformBufferImageCopies(copies, offset, upload_aspect_mask);
const VkBuffer src_buffer = buffer;
const VkImage temp_vk_image = *temp_image;
const VkImageAspectFlags vk_aspect_mask = aspect_mask;
const VkImageAspectFlags vk_aspect_mask = upload_aspect_mask;
scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask,
vk_copies](vk::CommandBuffer cmdbuf) {
@@ -1923,9 +1924,16 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
image_copies.push_back(image_copy);
}
runtime->blit_image_helper.CopyMSAA(runtime->render_pass_cache, Handle(), info.format,
temp_vk_image, info.format, info.num_samples,
image_copies, false);
if (msaa_upload_is_depth) {
runtime->blit_image_helper.CopyMSAADepth(runtime->render_pass_cache, Handle(),
info.format, temp_vk_image, info.format,
info.num_samples, image_copies,
msaa_upload_copies_stencil);
} else {
runtime->blit_image_helper.CopyMSAA(runtime->render_pass_cache, Handle(), info.format,
temp_vk_image, info.format, info.num_samples,
image_copies, false);
}
initialized = true;
runtime->pending_msaa_images.emplace_back(scheduler->CurrentTick(), std::move(temp_image));
@@ -2690,7 +2698,7 @@ Framebuffer::~Framebuffer() = default;
void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
std::span<ImageView*, NUM_RT> color_buffers,
ImageView* depth_buffer, bool is_rescaled_) {
boost::container::small_vector<VkImageView, NUM_RT + 1> attachments;
boost::container::small_vector<VkImageView, NUM_RT * 2 + 2> attachments;
RenderPassKey renderpass_key{};
s32 num_layers = 1;
@@ -2719,6 +2727,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
++num_images;
}
const size_t num_colors = attachments.size();
VkImage depth_image = VK_NULL_HANDLE;
VkImageAspectFlags depth_aspect_mask = 0;
if (depth_buffer) {
width = (std::min)(width, is_rescaled ? resolution.ScaleUp(depth_buffer->size.width)
: depth_buffer->size.width);
@@ -2734,6 +2744,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
++num_images;
has_depth = (subresource_range.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0;
has_stencil = (subresource_range.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0;
depth_image = depth_buffer->ImageHandle();
depth_aspect_mask = subresource_range.aspectMask;
} else {
renderpass_key.depth_format = PixelFormat::Invalid;
}
@@ -2742,6 +2754,12 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
samples != VK_SAMPLE_COUNT_1_BIT && num_colors > 0 && runtime.device.IsTiler();
renderpass_key.resolve_color = do_resolve_color;
const bool do_resolve_depth_stencil =
samples != VK_SAMPLE_COUNT_1_BIT && depth_image != VK_NULL_HANDLE &&
runtime.device.IsTiler() &&
SupportsDepthStencilResolve(runtime.device, renderpass_key.depth_format);
renderpass_key.resolve_depth_stencil = do_resolve_depth_stencil;
discard_msaa_color =
ENABLE_MSAA_RESOLVE_CONSUME && ENABLE_MSAA_COLOR_DISCARD && do_resolve_color;
@@ -2762,8 +2780,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true, format).format;
if (ENABLE_MSAA_RESOLVE_CONSUME) {
const VkImage msaa_image = images[rt_map[index]];
attachments.push_back(runtime.GetOrCreateResolveShadow(msaa_image, vk_format,
render_area, layers));
attachments.push_back(runtime.GetOrCreateResolveShadow(
msaa_image, vk_format, render_area, layers, VK_IMAGE_ASPECT_COLOR_BIT));
continue;
}
VkImageCreateInfo resolve_ci{
@@ -2808,6 +2826,16 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
}
}
if (do_resolve_depth_stencil) {
const u32 layers = static_cast<u32>((std::max)(num_layers, 1));
const VkFormat vk_format =
MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true,
renderpass_key.depth_format)
.format;
attachments.push_back(runtime.GetOrCreateResolveShadow(depth_image, vk_format, render_area,
layers, depth_aspect_mask));
}
num_color_buffers = static_cast<u32>(num_colors);
framebuffer = runtime.device.GetLogical().CreateFramebuffer({
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
@@ -2827,11 +2855,25 @@ VkRenderPass Framebuffer::RenderPassVariant(u32 color_clear_mask, bool depth_ste
if (color_clear_mask == 0 && !depth_stencil_clear && color_discard_mask == 0) {
return renderpass;
}
static_assert(NUM_RT <= 8);
const u32 variant_key = color_clear_mask | (color_discard_mask << 8) |
(static_cast<u32>(depth_stencil_clear) << 16);
for (u32 index = 0; index < num_memoized_variants; ++index) {
if (variant_keys[index] == variant_key) {
return variant_render_passes[index];
}
}
RenderPassKey key = render_pass_key;
key.color_clear_mask = color_clear_mask;
key.depth_stencil_clear = depth_stencil_clear;
key.color_discard_mask = color_discard_mask;
return render_pass_cache->Get(key);
const VkRenderPass variant = render_pass_cache->Get(key);
if (num_memoized_variants < variant_keys.size()) {
variant_keys[num_memoized_variants] = variant_key;
variant_render_passes[num_memoized_variants] = variant;
++num_memoized_variants;
}
return variant;
}
void TextureCacheRuntime::AccelerateImageUpload(
@@ -2843,6 +2885,15 @@ void TextureCacheRuntime::AccelerateImageUpload(
return astc_decoder_pass->Assemble(image, map, swizzles);
}
if (bl2d_unswizzle_pass && BlockLinearUnswizzle2DPass::IsSupported(image.info)) {
return bl2d_unswizzle_pass->Unswizzle(image, map, swizzles);
}
if (bl3db_unswizzle_pass &&
BlockLinearUnswizzle3DBufferPass::IsSupported(device, image.info)) {
return bl3db_unswizzle_pass->Unswizzle(image, map, swizzles);
}
if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) {
if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) {
ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture");
@@ -117,11 +117,13 @@ public:
VkFormat format = VK_FORMAT_UNDEFINED;
VkExtent2D extent{};
u32 layers = 0;
VkImageAspectFlags aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT;
bool up_to_date = false;
};
[[nodiscard]] VkImageView GetOrCreateResolveShadow(VkImage msaa_image, VkFormat format,
VkExtent2D extent, u32 layers);
VkExtent2D extent, u32 layers,
VkImageAspectFlags aspect_mask);
[[nodiscard]] const ResolveShadow* GetValidResolveShadow(VkImage msaa_image) const;
@@ -149,6 +151,8 @@ public:
std::optional<ASTCDecoderPass> astc_decoder_pass;
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
std::optional<BlockLinearUnswizzle2DPass> bl2d_unswizzle_pass;
std::optional<BlockLinearUnswizzle3DBufferPass> bl3db_unswizzle_pass;
const Settings::ResolutionScalingInfo& resolution;
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
@@ -156,6 +160,7 @@ public:
std::array<vk::Buffer, indexing_slots> buffers{};
std::vector<std::pair<u64, vk::Image>> pending_msaa_images;
ankerl::unordered_dense::map<VkImage, ResolveShadow> resolve_shadows;
std::vector<std::pair<u64, ResolveShadow>> pending_resolve_shadows;
};
class Framebuffer {
@@ -246,6 +251,8 @@ public:
}
private:
static constexpr size_t NUM_MEMOIZED_RENDER_PASS_VARIANTS = 8;
vk::Framebuffer framebuffer;
VkRenderPass renderpass{};
VkExtent2D render_area{};
@@ -263,6 +270,9 @@ private:
RenderPassKey render_pass_key{};
RenderPassCache* render_pass_cache{nullptr};
bool discard_msaa_color{};
mutable std::array<u32, NUM_MEMOIZED_RENDER_PASS_VARIANTS> variant_keys{};
mutable std::array<VkRenderPass, NUM_MEMOIZED_RENDER_PASS_VARIANTS> variant_render_passes{};
mutable u32 num_memoized_variants{};
};
class Image : public VideoCommon::ImageBase {
+58 -35
View File
@@ -1,9 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
#include <cmath>
#include <cstring>
@@ -30,12 +31,62 @@ constexpr u32 pdep(u32 value) {
return result;
}
constexpr u32 SWIZZLE_RUN_BYTES = 16;
constexpr u32 SWIZZLE_RUN_SHIFT = 4;
constexpr u32 SWIZZLE_RUN_MASK = SWIZZLE_RUN_BYTES - 1;
constexpr u32 SWIZZLE_RUN_INDEX_MASK = GOB_SIZE_X / SWIZZLE_RUN_BYTES - 1;
static_assert((SWIZZLE_X_BITS & SWIZZLE_RUN_MASK) == SWIZZLE_RUN_MASK);
constexpr std::array<u32, GOB_SIZE_X / SWIZZLE_RUN_BYTES> SWIZZLE_X_RUN_TABLE = [] {
std::array<u32, GOB_SIZE_X / SWIZZLE_RUN_BYTES> table{};
for (u32 index = 0; index < static_cast<u32>(table.size()); ++index) {
table[index] = pdep<SWIZZLE_X_BITS>(index << SWIZZLE_RUN_SHIFT);
}
return table;
}();
template <u32 mask, u32 incr_amount>
void incrpdep(u32& value) {
static constexpr u32 swizzled_incr = pdep<mask>(incr_amount);
value = ((value | ~mask) + swizzled_incr) & mask;
}
template <bool TO_LINEAR>
void SwizzleRow(std::span<u8> output, std::span<const u8> input, u32 offset_zy, u32 swizzled_y,
u32 x_shift, u32 x, u32 num_bytes, u32 linear) {
const auto copy = [&](u32 swizzled_x, u32 count) {
const u32 swizzled =
offset_zy + ((x >> GOB_SIZE_X_SHIFT) << x_shift) + (swizzled_x | swizzled_y);
u8* const dst = &output[TO_LINEAR ? swizzled : linear];
const u8* const src = &input[TO_LINEAR ? linear : swizzled];
std::memcpy(dst, src, count);
x += count;
linear += count;
};
u32 swizzled_run = SWIZZLE_X_RUN_TABLE[(x >> SWIZZLE_RUN_SHIFT) & SWIZZLE_RUN_INDEX_MASK];
u32 remaining = num_bytes;
const u32 head =
(std::min)(SWIZZLE_RUN_BYTES - (x & SWIZZLE_RUN_MASK), remaining) & SWIZZLE_RUN_MASK;
if (head != 0) {
copy(swizzled_run | (x & SWIZZLE_RUN_MASK), head);
remaining -= head;
incrpdep<SWIZZLE_X_BITS, SWIZZLE_RUN_BYTES>(swizzled_run);
}
while (remaining >= SWIZZLE_RUN_BYTES) {
copy(swizzled_run, SWIZZLE_RUN_BYTES);
remaining -= SWIZZLE_RUN_BYTES;
incrpdep<SWIZZLE_X_BITS, SWIZZLE_RUN_BYTES>(swizzled_run);
}
if (remaining != 0) {
copy(swizzled_run, remaining);
}
}
template <bool TO_LINEAR, u32 BYTES_PER_PIXEL>
void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32 height, u32 depth,
u32 block_height, u32 block_depth, u32 stride) {
@@ -70,23 +121,9 @@ void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32
const u32 offset_y = (block_y >> block_height) * block_size +
((block_y & block_height_mask) << GOB_SIZE_SHIFT);
u32 swizzled_x = pdep<SWIZZLE_X_BITS>(origin_x * BYTES_PER_PIXEL);
for (u32 column = 0; column < width;
++column, incrpdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) {
const u32 x = (column + origin_x) * BYTES_PER_PIXEL;
const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift;
const u32 base_swizzled_offset = offset_z + offset_y + offset_x;
const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y);
const u32 unswizzled_offset =
slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL;
u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset];
const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset];
std::memcpy(dst, src, BYTES_PER_PIXEL);
}
SwizzleRow<TO_LINEAR>(output, input, offset_z + offset_y, swizzled_y, x_shift,
origin_x * BYTES_PER_PIXEL, width * BYTES_PER_PIXEL,
slice * pitch * height + line * pitch);
}
}
}
@@ -129,23 +166,9 @@ void SwizzleSubrectImpl(std::span<u8> output, std::span<const u8> input, u32 wid
const u32 offset_y = (block_y >> block_height) * block_size +
((block_y & block_height_mask) << GOB_SIZE_SHIFT);
u32 swizzled_x = pdep<SWIZZLE_X_BITS>(origin_x * BYTES_PER_PIXEL);
for (u32 column = 0; column < extent_x;
++column, incrpdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) {
const u32 x = (column + origin_x) * BYTES_PER_PIXEL;
const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift;
const u32 base_swizzled_offset = offset_z + offset_y + offset_x;
const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y);
const u32 unswizzled_offset =
slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL;
u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset];
const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset];
std::memcpy(dst, src, BYTES_PER_PIXEL);
}
SwizzleRow<TO_LINEAR>(output, input, offset_z + offset_y, swizzled_y, x_shift,
origin_x * BYTES_PER_PIXEL, extent_x * BYTES_PER_PIXEL,
slice * pitch * height + line * pitch);
}
unprocessed_lines -= lines_in_y;
if (unprocessed_lines == 0) {
@@ -7,6 +7,8 @@
#include <algorithm>
#include <bitset>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <optional>
#include <thread>
#include <ankerl/unordered_dense.h>
@@ -16,6 +18,8 @@
#include <fmt/format.h>
#include "common/assert.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/literals.h"
#include <ranges>
#include "common/settings.h"
@@ -393,6 +397,17 @@ std::vector<const char*> ExtensionListForVulkan(
return output;
}
constexpr std::array<char, 8> STATIC_CACHE_MAGIC_NUMBER{'e', 'd', 'e', 'n', 's', 't', 'p', 'c'};
constexpr u32 STATIC_CACHE_VERSION = 1;
std::filesystem::path StaticPipelineCacheFilename() {
const auto shader_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
if (!Common::FS::CreateDir(shader_dir)) {
return {};
}
return shader_dir / "vulkan_static_pipelines.bin";
}
} // Anonymous namespace
void Device::RemoveExtension(bool& extension, const std::string& extension_name) {
@@ -780,15 +795,100 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
vk::Check(vmaCreateAllocator(&allocator_info, &allocator));
owns_static_pipeline_cache = surface != VkSurfaceKHR{};
LoadStaticPipelineCache();
// Initialize GPU logging if enabled
InitializeGPULogging();
}
Device::~Device() {
SaveStaticPipelineCache();
ShutdownGPULogging();
vmaDestroyAllocator(allocator);
}
void Device::LoadStaticPipelineCache() {
const auto create = [this](size_t size, const void* data) {
static_pipeline_cache = logical.CreatePipelineCache({
.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.initialDataSize = size,
.pInitialData = data,
});
};
if (!owns_static_pipeline_cache) {
create(0, nullptr);
return;
}
const auto filename = StaticPipelineCacheFilename();
if (filename.empty()) {
create(0, nullptr);
return;
}
std::vector<char> data;
try {
std::ifstream file(filename, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
create(0, nullptr);
return;
}
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
const size_t total = static_cast<size_t>(file.tellg());
file.seekg(0, std::ios::beg);
std::array<char, 8> magic{};
u32 version{};
if (total < magic.size() + sizeof(version)) {
create(0, nullptr);
return;
}
file.read(magic.data(), magic.size())
.read(reinterpret_cast<char*>(&version), sizeof(version));
if (magic != STATIC_CACHE_MAGIC_NUMBER || version != STATIC_CACHE_VERSION) {
create(0, nullptr);
return;
}
data.resize(total - magic.size() - sizeof(version));
file.read(data.data(), static_cast<std::streamsize>(data.size()));
} catch (const std::ios_base::failure& e) {
create(0, nullptr);
return;
}
create(data.size(), data.empty() ? nullptr : data.data());
}
void Device::SaveStaticPipelineCache() const {
if (!owns_static_pipeline_cache || !static_pipeline_cache) {
return;
}
const auto filename = StaticPipelineCacheFilename();
if (filename.empty()) {
return;
}
size_t size = 0;
std::vector<char> data;
static_pipeline_cache.Read(&size, nullptr);
if (size == 0) {
return;
}
data.resize(size);
static_pipeline_cache.Read(&size, data.data());
try {
std::ofstream file(filename, std::ios::binary | std::ios::trunc);
file.exceptions(std::ofstream::failbit);
if (!file.is_open()) {
return;
}
file.write(STATIC_CACHE_MAGIC_NUMBER.data(), STATIC_CACHE_MAGIC_NUMBER.size())
.write(reinterpret_cast<const char*>(&STATIC_CACHE_VERSION),
sizeof(STATIC_CACHE_VERSION))
.write(data.data(), static_cast<std::streamsize>(size));
} catch (const std::ios_base::failure& e) {
Common::FS::RemoveFile(filename);
}
}
VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage,
FormatType format_type) const {
if (IsFormatSupported(wanted_format, wanted_usage, format_type)) {
@@ -975,6 +1075,12 @@ bool Device::GetSuitability(bool requires_swapchain) {
FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION);
FOR_EACH_VK_EXTENSION(EXTENSION);
extensions.depth_stencil_resolve =
extensions.depth_stencil_resolve &&
(instance_version >= VK_API_VERSION_1_2 || extensions.create_renderpass2);
RemoveExtensionIfUnsuitable(extensions.depth_stencil_resolve,
VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME);
if (supported_extensions.contains(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME)) {
loaded_extensions.erase(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
loaded_extensions.insert(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME);
@@ -1122,6 +1228,11 @@ bool Device::GetSuitability(bool requires_swapchain) {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR;
SetNext(next, properties.push_descriptor);
}
if (extensions.depth_stencil_resolve || instance_version >= VK_API_VERSION_1_2) {
properties.depth_stencil_resolve.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES;
SetNext(next, properties.depth_stencil_resolve);
}
if (extensions.descriptor_buffer) {
properties.descriptor_buffer.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT;
@@ -90,6 +90,8 @@ VK_DEFINE_HANDLE(VmaAllocator)
EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \
EXTENSION(EXT, TOOLING_INFO, tooling_info) \
EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \
EXTENSION(KHR, CREATE_RENDERPASS_2, create_renderpass2) \
EXTENSION(KHR, DEPTH_STENCIL_RESOLVE, depth_stencil_resolve) \
EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \
EXTENSION(KHR, DRIVER_PROPERTIES, driver_properties) \
EXTENSION(KHR, PUSH_DESCRIPTOR, push_descriptor) \
@@ -268,6 +270,10 @@ public:
return physical;
}
VkPipelineCache StaticPipelineCache() const noexcept {
return *static_pipeline_cache;
}
/// Returns the main graphics queue.
vk::Queue GetGraphicsQueue() const {
return graphics_queue;
@@ -607,6 +613,37 @@ FN_MAX_LIMIT_LIST
return extensions.shader_stencil_export;
}
/// Returns true if the device supports VK_KHR_create_renderpass2.
bool IsKhrCreateRenderPass2Supported() const {
return extensions.create_renderpass2 || instance_version >= VK_API_VERSION_1_2;
}
/// Returns true if the device supports VK_KHR_depth_stencil_resolve.
bool IsKhrDepthStencilResolveSupported() const {
return (extensions.depth_stencil_resolve || instance_version >= VK_API_VERSION_1_2) &&
IsKhrCreateRenderPass2Supported();
}
/// Returns the supported resolve modes for the depth aspect.
VkResolveModeFlags GetDepthResolveModes() const {
return properties.depth_stencil_resolve.supportedDepthResolveModes;
}
/// Returns the supported resolve modes for the stencil aspect.
VkResolveModeFlags GetStencilResolveModes() const {
return properties.depth_stencil_resolve.supportedStencilResolveModes;
}
/// Returns true if the depth and stencil aspects may resolve with different modes.
bool SupportsIndependentResolve() const {
return properties.depth_stencil_resolve.independentResolve == VK_TRUE;
}
/// Returns true if only one of the depth and stencil aspects may be resolved.
bool SupportsIndependentResolveNone() const {
return properties.depth_stencil_resolve.independentResolveNone == VK_TRUE;
}
/// Returns true if depth/stencil operations can be performed efficiently.
/// Either through shader export or hardware blits.
bool CanPerformDepthStencilOperations() const {
@@ -957,6 +994,21 @@ FN_MAX_LIMIT_LIST
return features2.features.multiViewport;
}
VkPipelineStageFlags AttachmentConsumerStages() const {
VkPipelineStageFlags stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT |
VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT |
VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT |
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
if (features2.features.geometryShader) {
stages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
}
return stages;
}
/// Returns true if the device supports VK_KHR_maintenance1.
bool IsKhrMaintenance1Supported() const {
return extensions.maintenance1;
@@ -1088,6 +1140,9 @@ private:
/// Returns true if the device natively supports blitting depth stencil images.
bool TestDepthStencilBlits(VkFormat format) const;
void LoadStaticPipelineCache();
void SaveStaticPipelineCache() const;
private:
VkInstance instance; ///< Vulkan instance.
VmaAllocator allocator; ///< VMA allocator.
@@ -1096,6 +1151,8 @@ private:
vk::Device logical; ///< Logical device.
vk::Queue graphics_queue; ///< Main graphics queue.
vk::Queue present_queue; ///< Main present queue.
vk::PipelineCache static_pipeline_cache;
bool owns_static_pipeline_cache{};
u32 instance_version{}; ///< Vulkan instance version.
u32 graphics_family{}; ///< Main graphics queue family index.
u32 present_family{}; ///< Main present queue family index.
@@ -1142,6 +1199,7 @@ private:
VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{};
VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{};
VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{};
VkPhysicalDeviceProperties properties{};
};
@@ -184,6 +184,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkCreatePipelineLayout);
X(vkCreateQueryPool);
X(vkCreateRenderPass);
X(vkCreateRenderPass2);
X(vkCreateSampler);
X(vkCreateSemaphore);
X(vkCreateShaderModule);
@@ -270,6 +271,11 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
if (!dld.vkQueueSubmit2) {
Proc(dld.vkQueueSubmit2, dld, "vkQueueSubmit2KHR", device);
}
// Render pass creation v2 is core in Vulkan 1.2, otherwise requires VK_KHR_create_renderpass2
if (!dld.vkCreateRenderPass2) {
Proc(dld.vkCreateRenderPass2, dld, "vkCreateRenderPass2KHR", device);
}
#undef X
}
@@ -725,6 +731,12 @@ RenderPass Device::CreateRenderPass(const VkRenderPassCreateInfo& ci) const {
return RenderPass(object, handle, *dld);
}
RenderPass Device::CreateRenderPass2(const VkRenderPassCreateInfo2& ci) const {
VkRenderPass object;
Check(dld->vkCreateRenderPass2(handle, &ci, nullptr, &object));
return RenderPass(object, handle, *dld);
}
DescriptorSetLayout Device::CreateDescriptorSetLayout(
const VkDescriptorSetLayoutCreateInfo& ci) const {
VkDescriptorSetLayout object;
@@ -300,6 +300,7 @@ struct DeviceDispatch : InstanceDispatch {
PFN_vkCreatePipelineLayout vkCreatePipelineLayout{};
PFN_vkCreateQueryPool vkCreateQueryPool{};
PFN_vkCreateRenderPass vkCreateRenderPass{};
PFN_vkCreateRenderPass2 vkCreateRenderPass2{};
PFN_vkCreateSampler vkCreateSampler{};
PFN_vkCreateSemaphore vkCreateSemaphore{};
PFN_vkCreateShaderModule vkCreateShaderModule{};
@@ -1045,6 +1046,8 @@ public:
[[nodiscard]] RenderPass CreateRenderPass(const VkRenderPassCreateInfo& ci) const;
[[nodiscard]] RenderPass CreateRenderPass2(const VkRenderPassCreateInfo2& ci) const;
[[nodiscard]] DescriptorSetLayout CreateDescriptorSetLayout(
const VkDescriptorSetLayoutCreateInfo& ci) const;
@@ -1526,7 +1529,7 @@ public:
}
void SetDepthBias(float constant_factor, float clamp, float slope_factor,
VkDepthBiasRepresentationInfoEXT* extra) const noexcept {
const VkDepthBiasRepresentationInfoEXT* extra) const noexcept {
VkDepthBiasInfoEXT info{
.sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_INFO_EXT,
.pNext = extra,
+1 -1
View File
@@ -8,7 +8,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
# Set the RPATH for Qt Libraries
# This must be done before the `yuzu` target is created
if (YUZU_USE_BUNDLED_QT AND PLATFORM_LINUX)
if (YUZU_USE_BUNDLED_QT AND LINUX)
set(CMAKE_BUILD_RPATH "${CMAKE_BINARY_DIR}/bin/lib/")
endif()
@@ -99,6 +99,7 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(Core::HID::HIDCore& hid_core_, QW
#ifndef _WIN32
ui->enable_raw_input->setVisible(false);
ui->disable_wgi_xinput->setVisible(false);
#endif
LoadConfiguration();
@@ -139,6 +140,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
Settings::values.emulate_analog_keyboard = ui->emulate_analog_keyboard->isChecked();
Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
Settings::values.enable_raw_input = ui->enable_raw_input->isChecked();
Settings::values.disable_wgi_xinput = ui->disable_wgi_xinput->isChecked();
Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked();
Settings::values.controller_navigation = ui->controller_navigation->isChecked();
Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked();
@@ -174,6 +176,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
ui->emulate_analog_keyboard->setChecked(Settings::values.emulate_analog_keyboard.GetValue());
ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue());
ui->disable_wgi_xinput->setChecked(Settings::values.disable_wgi_xinput.GetValue());
ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue());
ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue());
ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue());
@@ -2757,6 +2757,22 @@
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QCheckBox" name="disable_wgi_xinput">
<property name="toolTip">
<string>Aimed to disable SDL GUIDE button hack: synthetic GUIDE(HOME) event when SELECT(MINUS) + START(PLUS) pressed. May impact Win related trigger/rumble/etc stuff</string>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>23</height>
</size>
</property>
<property name="text">
<string>Disable SDL WGI/XInput (Requires restart)</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
+16
View File
@@ -16,6 +16,12 @@
#include <sys/resource.h>
#endif
#if defined(__APPLE__)
#include <climits>
#include <cstdlib>
#include <cstring>
#endif
#include "main_window.h"
#ifdef _WIN32
@@ -126,6 +132,16 @@ int main(int argc, char* argv[]) {
#endif // _WIN32
#if defined(__APPLE__)
// Convert the relative path to an absolute path before the chdir
char resolved[PATH_MAX];
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "-input-profile") == 0) {
++i;
} else if (argv[i][0] != '-' && argv[i][0] != '/' && realpath(argv[i], resolved)) {
argv[i] = strdup(resolved);
}
}
// If you start a bundle (binary) on OSX without the Terminal, the working directory is "/".
// But since we require the working directory to be the executable path for the location of
// the user folder in the Qt Frontend, we need to cd into that working directory
+2 -1
View File
@@ -1,7 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// Qt on macOS doesn't define VMA shit
// Static Qt on macOS doesn't use Vulkan
// Other platforms do, and thus have conflicting VulkanMemoryAllocator symbols
#if defined(QT_STATICPLUGIN) && !defined(__APPLE__)
#undef VMA_IMPLEMENTATION
#endif
+1 -1
View File
@@ -38,7 +38,7 @@ void MigrationWorker::process() {
try {
fs::remove_all(eden_dir);
} catch (fs::filesystem_error& _) {
// ignore because linux does stupid crap sometimes
// directory may not exist at this point, that's fine
}
switch (strategy) {