Compare commits

...

83 Commits

Author SHA1 Message Date
dcc51d5526 renderer_vulkan: Fix flipped screenshot 2023-09-13 01:27:51 +03:00
164d85709c Revert "gl_rasterizer: Separable shaders for everyone"
Causes crashes on mali GPUs, will need separate PR

This reverts commit d22d556d30.
2023-09-13 00:47:45 +03:00
eb6ef052d5 renderer_vulkan: Fix hybrid screen 2023-09-13 00:38:54 +03:00
868dc0c11f vk_swapchain: Add missing check 2023-09-11 01:34:53 +03:00
f17f127f46 vk_pipeline_cache: Improve shadow detection 2023-09-11 01:26:44 +03:00
5c3f6e3b72 renderer_vulkan: Port some recent shader fixes 2023-09-11 01:12:37 +03:00
da891af113 renderer/vulkan: Support VK_EXT_fragment_shader_interlock for shadow rendering. (#51) 2023-09-11 00:38:01 +03:00
ba89673d77 vk_stream_buffer: Respect non coherent access alignment
* Required by nvidia GPUs on MacOS
2023-09-11 00:38:01 +03:00
cc9768963a renderer/vulkan: Fix binding/unbinding of shadow rendering buffer. 2023-09-11 00:38:01 +03:00
37f5d66c10 sdl: Fix surface initialization on macOS. (#49)
* sdl: Fix surface initialization on macOS.

* sdl: Fix render window events not being handled under Vulkan.
2023-09-11 00:38:01 +03:00
6bfe1daac7 vk_pipeline_cache: Skip cache save when no pipeline cache exists
* This is the cache when loading a save state
2023-09-11 00:38:01 +03:00
bcbce50120 renderer_vulkan: Fix dynamic state being lost 2023-09-11 00:38:01 +03:00
a608e33593 android: More robust surface recreation 2023-09-11 00:38:01 +03:00
ff10decc2c vk_rasterizer: Bump async threshold to 6
* Many games have fullscreen quads with 6 vertices. Fixes pokemon textures missing with async shaders
2023-09-11 00:38:01 +03:00
78050813c4 vk_instance: Blacklist VK_EXT_pipeline_creation_cache_control with nvidia gpus
* Resolves crashes when async shader compilation is enabled
2023-09-11 00:38:01 +03:00
b758dd214a vk_present_window: Match guest swapchain size to vulkan image count
* Less latency and fixes crashes that were caused by images being deleted before free
2023-09-11 00:38:01 +03:00
8267da3874 vk_swapchain: Ensure vsync doesn't lock framerate 2023-09-11 00:38:01 +03:00
9e7ae03b6c vk_graphics_pipeline: Only fast compile if no shaders are pending
* With this shaders weren't being compiled in parallel
2023-09-11 00:38:01 +03:00
631c777983 renderer_vulkan: Implement reinterpretation with copy
* Allows reinterpreteration with simply copy on AMD
2023-09-11 00:38:01 +03:00
622c20761c vk_blit_helper: Corect depth to color convertion 2023-09-11 00:38:01 +03:00
d22d556d30 gl_rasterizer: Separable shaders for everyone 2023-09-11 00:38:01 +03:00
c281c1a5cc externals: Update vulkan-headers 2023-09-11 00:38:01 +03:00
fd38e33fb2 vk_renderpass_cache: Bring back renderpass flushing 2023-09-11 00:38:01 +03:00
66c5f59627 vk_instance: Enable robust buffer access
* Improves stability on mali devices
2023-09-11 00:38:01 +03:00
faf6b36f3b android: Move PollEvents to OpenGL window
* Vulkan does not need this and it causes problems
2023-09-11 00:38:01 +03:00
dd71859818 vk_rasterizer: More robust attribute loading 2023-09-11 00:38:01 +03:00
fe724600ab vk_graphics_pipeline: Fix async shader compilation
* We were actually waiting for the pipelines regardless of the setting, oops
2023-09-11 00:38:01 +03:00
a12b01105d android: Allow async presentation toggle 2023-09-11 00:38:01 +03:00
8c05a52a65 vk_rasterizer: Correct special unbind 2023-09-11 00:38:00 +03:00
e5ba2abd1c citra_qt: Fix invalid characters 2023-09-11 00:38:00 +03:00
8421be7ebf rasterizer_cache: Proper surface unregister 2023-09-11 00:38:00 +03:00
ba9f1f8ae9 vk_instance: Set moltenvk configuration 2023-09-11 00:38:00 +03:00
6c391971c6 android: Custom driver code 2023-09-11 00:38:00 +03:00
85dd604a7e vk_renderpass_cache: Bump pixel format count 2023-09-11 00:38:00 +03:00
edd8de29ae renderer/vulkan: Make gl_Position invariant. (#48)
This fixes an issue with black artifacts in Pokemon games on Apple GPUs.
If the vertex calculations differ slightly between render passes, it can
cause parts of model faces to fail depth test.
2023-09-11 00:38:00 +03:00
89226eea2a qt: Fix fullscreen and resize issues on macOS. (#47)
* qt: Fix bugged macOS full screen transition.

* renderer/vulkan: Fix swapchain recreation destroying in-use semaphore.
2023-09-11 00:38:00 +03:00
59549a2eb6 renderer_vulkan: Add vulkan backend 2023-09-11 00:38:00 +03:00
a8d590ae80 vk_instance: Collect tooling info 2023-09-11 00:38:00 +03:00
6b51afaf1f citra_qt: Add vulkan options to the GUI 2023-09-11 00:38:00 +03:00
a67bfe544d code: Prepare frontend for vulkan support 2023-09-11 00:37:53 +03:00
bbb47cd753 gles: fix crashing on mali gpus (#6956)
* gles: reorder framebuffer fetch extension directives

* Address review comments
2023-09-11 00:30:30 +03:00
0b0d3a4ac3 gpu: Correct display transfer output with vertical flip+crop lines (#6952) 2023-09-11 00:30:06 +03:00
6aa31d6ec2 video_core: Use epsilons for clip planes (#6945)
* video_core: Use epsilons for clip planes

* video_core: Add comments
2023-09-11 00:29:55 +03:00
3e254d01ee Add Kdevelop files to .gitignore (#6951) 2023-09-07 11:52:40 -07:00
928f352c94 renderer_gl: Use explicit bindings (#6940)
* renderer_gl: Use explicit bindings

* gl_state: Match shadow order with vulkan
2023-09-07 21:42:22 +03:00
e2d8eef5fa qt: Fix arguments to maintenancetool for update check. (#6946) 2023-09-04 17:25:41 -07:00
9e898bca06 build: Fix Qt installer build script. (#6938) 2023-09-01 15:18:47 -07:00
cab0ad50f0 gl_shader_decompiler: Fix min/max NaN edge case (#6935) 2023-08-31 13:37:53 -07:00
5ad58e0605 shader: Fix shadow_texture_orthographic not being set correctly. (#6937) 2023-08-31 13:37:39 -07:00
1159e4d928 video_core: Take factors into account with min/max blending functions (#6925)
* sw_framebuffer: Take factors into account for min/max blending

* renderer_gl: Take factors into account for min/max blending

* Address review comments

* gl_shader_gen: Fix frambuffer fetch on qcom and mali

* renderer_opengl: Add fallback path for mesa

* gl_shader_gen: Avoid emitting blend emulation if minmax_factor is present
2023-08-30 21:26:28 +03:00
93c7c6a995 service: Downgrade some spammy logs to trace level. (#6936) 2023-08-30 19:19:38 +02:00
81a5e2355a shader: Fix address register offset behavior in GLSL. (#6920) 2023-08-28 13:23:59 -07:00
d2260bafef fix: Added padding to reset defaults button (#6926)
The button did not have enough padding, now it does!

Co-authored-by: liamwhite <liamwhite@users.noreply.github.com>
Co-authored-by: Benjamin <73490201+benjaminhalko@users.noreply.github.com>
2023-08-28 12:17:59 -07:00
d1f600601d renderer_software: Multi-thread processing (#6698)
* renderer_software: Multi-thread processing

* Doubles the performance in most cases

* renderer_software: Move memory access out of the raster loop

* Profiling shows this has a significant impact
2023-08-28 01:09:23 -07:00
8b218e1b7d tev: Use primary color for previous source in first stage (#6921) 2023-08-27 23:59:32 -07:00
04aeecabcf arm: Do not try to access null page table. (#6930) 2023-08-27 17:26:31 +02:00
df57012c50 video_core: Correct shader interpeter address register handling. (#6910) 2023-08-27 01:49:31 +03:00
61cf550d0c video_core: Avoid setting alpha tev with Dot3_RGBA8 color op (#6907)
* Further reduces unnecessary shader regenerations
2023-08-27 01:48:45 +03:00
f2e0748a22 build: Enable link time optimization in release builds. (#6887) 2023-08-26 11:15:13 -07:00
edf157200d qt: Support updating an installed app image. (#6915) 2023-08-24 03:57:11 -07:00
a94297922b shader_interpreter: Fix control flow edge cases and implement break/breakc (#6844) 2023-08-21 18:12:46 -07:00
894c1c85a5 Include Citra Icon in release (#6905) 2023-08-21 22:34:54 +02:00
e129f07047 [ShaderGen] Avoid setting lighting states if components aren't enabled (#6895)
* [ShaderGen] Avoid setting lighting states if components aren't enabled

* [Squash] clang-format
2023-08-20 21:12:38 -07:00
cf54210f42 ci: Remove compatibility artifact naming for releases. (#6647) 2023-08-20 16:41:13 -07:00
b2092de871 build: Fix web service functionality. (#6903) 2023-08-19 17:34:35 -07:00
6ddf4b241f renderer/vulkan: Emulate custom border colors in shaders when unavailable. (#6878) 2023-08-17 22:22:25 +02:00
f3d92dd3b8 Fix post processing shaders crashing on Intel GPUs (#6890) 2023-08-17 22:17:24 +02:00
1a6860f35c opengl: disable ClearTexture on Intel drivers (#6879) 2023-08-17 22:16:51 +02:00
bc0bf4d3d2 (gl/vk)_shader_gen: Use floor instead of int cast (#6885) 2023-08-17 22:16:28 +02:00
1d3bf64f13 qt: Make input configuration scrollable. (#6881) 2023-08-16 19:27:01 -07:00
c3609785ff build: Add flag for disabling bundling target. (#6845) 2023-08-16 19:26:52 -07:00
66404a669f build: Fixes for a few minor issues (#6886) 2023-08-14 09:47:17 -07:00
6a1fd38063 build: Use new glslang tool name. (#6889) 2023-08-13 15:45:22 -07:00
a27971e723 service/cecd: Fix buffer writes in ReadData. (#6880) 2023-08-11 10:31:24 -07:00
eb8d2941c9 savestates: save the build name to be displayed when there's a version mismatch (#6493)
* savestates: add a build_name field to the header

* savestates: display build name on save/load menu

* savestates: add zero member to header just in case of UB from an older save state

* savestates: add legacy hash lookup

* savestate_data: update hash database
2023-08-10 20:55:22 -07:00
af78268dd5 qt: Update Qt to 6.5.1 (#6863) 2023-08-10 14:32:51 -07:00
baca2bfc6b qt: Only register graphics debugger as observer if visible. (#6875) 2023-08-09 17:31:09 -07:00
33a2113b71 service: nfp: Fix size and increase timeout (#6868) 2023-08-08 14:00:53 -07:00
1f07ab8643 http/ac: Fix failing assert and stub GetConnectingProxyEnable (#6864) 2023-08-08 14:00:42 -07:00
b739bd2632 qt: Fix double dialog and crash when OpenGL is selected on unsupported platform. (#6867) 2023-08-07 18:21:42 -07:00
f76165d848 frame_dumper: Use jthread (#6855) 2023-08-07 18:21:28 -07:00
ac78b74c45 gl_texture_runtime: Implement ARB_clear_texture (#6850) 2023-08-08 01:10:28 +03:00
a478bedb12 rasterizer_cache: Bring back custom format parameter (#6873) 2023-08-08 01:10:11 +03:00
174 changed files with 14984 additions and 2009 deletions

View File

@ -7,13 +7,6 @@ REV_NAME="citra-${OS}-${TARGET}-${GITDATE}-${GITREV}"
# Find out what release we are building # Find out what release we are building
if [[ "$GITHUB_REF_NAME" =~ ^canary- ]] || [[ "$GITHUB_REF_NAME" =~ ^nightly- ]]; then if [[ "$GITHUB_REF_NAME" =~ ^canary- ]] || [[ "$GITHUB_REF_NAME" =~ ^nightly- ]]; then
RELEASE_NAME=$(echo $GITHUB_REF_NAME | cut -d- -f1) RELEASE_NAME=$(echo $GITHUB_REF_NAME | cut -d- -f1)
# For compatibility with existing installs, use mingw/osx in the archive and target names.
if [ "$TARGET" = "msys2" ]; then
REV_NAME="citra-${OS}-mingw-${GITDATE}-${GITREV}"
RELEASE_NAME="${RELEASE_NAME}-mingw"
elif [ "$OS" = "macos" ]; then
REV_NAME="citra-osx-${TARGET}-${GITDATE}-${GITREV}"
fi
else else
RELEASE_NAME=head RELEASE_NAME=head
fi fi

View File

@ -158,12 +158,24 @@ jobs:
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }} key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}
restore-keys: | restore-keys: |
${{ runner.os }}-${{ matrix.target }}- ${{ runner.os }}-${{ matrix.target }}-
- name: Increase Pagefile size
uses: al-cheb/configure-pagefile-action@v1.2
with:
minimum-size: 2GB
maximum-size: 6GB
- name: Set up MSVC - name: Set up MSVC
uses: ilammy/msvc-dev-cmd@v1 uses: ilammy/msvc-dev-cmd@v1
if: ${{ matrix.target == 'msvc' }} if: ${{ matrix.target == 'msvc' }}
- name: Install MSVC extra tools - name: Install extra tools (MSVC)
run: choco install ccache ninja wget run: choco install ccache ninja wget
if: ${{ matrix.target == 'msvc' }} if: ${{ matrix.target == 'msvc' }}
- name: Set up Vulkan SDK (MSVC)
uses: humbletim/setup-vulkan-sdk@v1.2.0
if: ${{ matrix.target == 'msvc' }}
with:
vulkan-query-version: latest
vulkan-components: Glslang
vulkan-use-cache: true
- name: Set up MSYS2 - name: Set up MSYS2
uses: msys2/setup-msys2@v2 uses: msys2/setup-msys2@v2
if: ${{ matrix.target == 'msys2' }} if: ${{ matrix.target == 'msys2' }}
@ -172,16 +184,10 @@ jobs:
update: true update: true
install: git make p7zip install: git make p7zip
pacboy: >- pacboy: >-
toolchain:p ccache:p cmake:p ninja:p toolchain:p ccache:p cmake:p ninja:p glslang:p
qt6-base:p qt6-multimedia:p qt6-multimedia-wmf:p qt6-tools:p qt6-translations:p qt6-base:p qt6-multimedia:p qt6-multimedia-wmf:p qt6-tools:p qt6-translations:p
- name: Setup Vulkan SDK - name: Test glslang
uses: humbletim/setup-vulkan-sdk@v1.2.0 run: glslang --version || glslangValidator --version
with:
vulkan-query-version: latest
vulkan-components: Glslang
vulkan-use-cache: true
- name: Test glslangValidator
run: glslangValidator --version
- name: Disable line ending translation - name: Disable line ending translation
run: git config --global core.autocrlf input run: git config --global core.autocrlf input
- name: Build - name: Build

2
.gitignore vendored
View File

@ -9,10 +9,12 @@ src/common/scm_rev.cpp
# Project/editor files # Project/editor files
*.swp *.swp
*.kdev4
.idea/ .idea/
.vs/ .vs/
.vscode/ .vscode/
.cache/ .cache/
.kdev4/
cmake-build-debug/ cmake-build-debug/
cmake-build-release/ cmake-build-release/
CMakeLists.txt.user* CMakeLists.txt.user*

3
.gitmodules vendored
View File

@ -82,3 +82,6 @@
[submodule "library-headers"] [submodule "library-headers"]
path = externals/library-headers/library-headers path = externals/library-headers/library-headers
url = https://github.com/citra-emu/ext-library-headers.git url = https://github.com/citra-emu/ext-library-headers.git
[submodule "libadrenotools"]
path = externals/libadrenotools
url = https://github.com/bylaws/libadrenotools

View File

@ -40,8 +40,10 @@ endif()
if (CMAKE_BUILD_TYPE STREQUAL Debug) if (CMAKE_BUILD_TYPE STREQUAL Debug)
set(IS_DEBUG_BUILD ON) set(IS_DEBUG_BUILD ON)
set(IS_RELEASE_BUILD OFF)
else() else()
set(IS_DEBUG_BUILD OFF) set(IS_DEBUG_BUILD OFF)
set(IS_RELEASE_BUILD ON)
endif() endif()
option(ENABLE_SDL2 "Enable using SDL2" ON) option(ENABLE_SDL2 "Enable using SDL2" ON)
@ -69,9 +71,11 @@ option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
CMAKE_DEPENDENT_OPTION(ENABLE_MF "Use Media Foundation decoder (preferred over FFmpeg)" ON "WIN32" OFF) CMAKE_DEPENDENT_OPTION(ENABLE_MF "Use Media Foundation decoder (preferred over FFmpeg)" ON "WIN32" OFF)
CMAKE_DEPENDENT_OPTION(ENABLE_AUDIOTOOLBOX "Use AudioToolbox decoder (preferred over FFmpeg)" ON "APPLE" OFF) CMAKE_DEPENDENT_OPTION(ENABLE_AUDIOTOOLBOX "Use AudioToolbox decoder (preferred over FFmpeg)" ON "APPLE" OFF)
CMAKE_DEPENDENT_OPTION(CITRA_ENABLE_BUNDLE_TARGET "Enable the distribution bundling target." ON "NOT ANDROID AND NOT IOS" OFF)
# Compile options # Compile options
CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ${IS_DEBUG_BUILD} "MINGW" OFF) CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ${IS_DEBUG_BUILD} "MINGW" OFF)
option(ENABLE_LTO "Enable link time optimization" OFF) option(ENABLE_LTO "Enable link time optimization" ${IS_RELEASE_BUILD})
option(CITRA_USE_PRECOMPILED_HEADERS "Use precompiled headers" ON) option(CITRA_USE_PRECOMPILED_HEADERS "Use precompiled headers" ON)
option(CITRA_WARNINGS_AS_ERRORS "Enable warnings as errors" ON) option(CITRA_WARNINGS_AS_ERRORS "Enable warnings as errors" ON)
@ -220,7 +224,7 @@ find_package(Threads REQUIRED)
if (ENABLE_QT) if (ENABLE_QT)
if (NOT USE_SYSTEM_QT) if (NOT USE_SYSTEM_QT)
download_qt(6.5.0) download_qt(6.5.1)
endif() endif()
find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia Concurrent) find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia Concurrent)
@ -397,7 +401,7 @@ else()
endif() endif()
# Create target for outputting distributable bundles. # Create target for outputting distributable bundles.
if (NOT ANDROID AND NOT IOS) if (CITRA_ENABLE_BUNDLE_TARGET)
include(BundleTarget) include(BundleTarget)
if (ENABLE_SDL2_FRONTEND) if (ENABLE_SDL2_FRONTEND)
bundle_target(citra) bundle_target(citra)

View File

@ -1,4 +1,5 @@
# To use this as a script, make sure you pass in the variables BASE_DIR, SRC_DIR, BUILD_DIR, and TARGET_FILE # To use this as a script, make sure you pass in the variables BASE_DIR, SRC_DIR, BUILD_DIR, and TARGET_FILE
cmake_minimum_required(VERSION 3.15)
if(WIN32) if(WIN32)
set(PLATFORM "windows") set(PLATFORM "windows")
@ -12,7 +13,8 @@ endif()
list(APPEND CMAKE_MODULE_PATH "${BASE_DIR}/CMakeModules") list(APPEND CMAKE_MODULE_PATH "${BASE_DIR}/CMakeModules")
include(DownloadExternals) include(DownloadExternals)
download_qt(tools_ifw QT_PREFIX) download_qt(tools_ifw)
get_external_prefix(qt QT_PREFIX)
file(GLOB_RECURSE INSTALLER_BASE "${QT_PREFIX}/**/installerbase*") file(GLOB_RECURSE INSTALLER_BASE "${QT_PREFIX}/**/installerbase*")
file(GLOB_RECURSE BINARY_CREATOR "${QT_PREFIX}/**/binarycreator*") file(GLOB_RECURSE BINARY_CREATOR "${QT_PREFIX}/**/binarycreator*")

View File

@ -189,6 +189,12 @@ else()
add_custom_command( add_custom_command(
TARGET bundle TARGET bundle
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/bundle/") COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/bundle/")
add_custom_command(
TARGET bundle
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/bundle/dist/")
add_custom_command(
TARGET bundle
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/dist/icon.png" "${CMAKE_BINARY_DIR}/bundle/dist/citra.png")
add_custom_command( add_custom_command(
TARGET bundle TARGET bundle
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/license.txt" "${CMAKE_BINARY_DIR}/bundle/") COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/license.txt" "${CMAKE_BINARY_DIR}/bundle/")

View File

@ -1,13 +1,19 @@
# This function downloads Qt using aqt. # This function downloads Qt using aqt. The path of the downloaded content will be added to the CMAKE_PREFIX_PATH.
# Params: # Params:
# target: Qt dependency to install. Specify a version number to download Qt, or "tools_(name)" for a specific build tool. # target: Qt dependency to install. Specify a version number to download Qt, or "tools_(name)" for a specific build tool.
# prefix_var: Name of a variable which will be set with the path to the extracted contents.
function(download_qt target) function(download_qt target)
if (target MATCHES "tools_.*")
set(DOWNLOAD_QT_TOOL ON)
else()
set(DOWNLOAD_QT_TOOL OFF)
endif()
# Determine installation parameters for OS, architecture, and compiler # Determine installation parameters for OS, architecture, and compiler
if (WIN32) if (WIN32)
set(host "windows") set(host "windows")
set(type "desktop") set(type "desktop")
if (NOT DOWNLOAD_QT_TOOL)
if (MINGW) if (MINGW)
set(arch "win64_mingw") set(arch "win64_mingw")
set(arch_path "mingw_64") set(arch_path "mingw_64")
@ -23,9 +29,10 @@ function(download_qt target)
else() else()
message(FATAL_ERROR "Unsupported bundled Qt toolchain. Enable USE_SYSTEM_QT and provide your own.") message(FATAL_ERROR "Unsupported bundled Qt toolchain. Enable USE_SYSTEM_QT and provide your own.")
endif() endif()
endif()
elseif (APPLE) elseif (APPLE)
set(host "mac") set(host "mac")
if (IOS) if (IOS AND NOT DOWNLOAD_QT_TOOL)
set(type "ios") set(type "ios")
set(arch "ios") set(arch "ios")
set(arch_path "ios") set(arch_path "ios")
@ -45,8 +52,8 @@ function(download_qt target)
get_external_prefix(qt base_path) get_external_prefix(qt base_path)
file(MAKE_DIRECTORY "${base_path}") file(MAKE_DIRECTORY "${base_path}")
if (target MATCHES "tools_.*") if (DOWNLOAD_QT_TOOL)
set(prefix "${base_path}") set(prefix "${base_path}/Tools")
set(install_args install-tool --outputdir ${base_path} ${host} desktop ${target}) set(install_args install-tool --outputdir ${base_path} ${host} desktop ${target})
else() else()
set(prefix "${base_path}/${target}/${arch_path}") set(prefix "${base_path}/${target}/${arch_path}")
@ -54,7 +61,8 @@ function(download_qt target)
set(host_flag "--autodesktop") set(host_flag "--autodesktop")
set(host_prefix "${base_path}/${target}/${host_arch_path}") set(host_prefix "${base_path}/${target}/${host_arch_path}")
endif() endif()
set(install_args install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} ${host_flag} -m qtmultimedia) set(install_args install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} ${host_flag}
-m qtmultimedia --archives qttranslations qttools qtsvg qtbase)
endif() endif()
if (NOT EXISTS "${prefix}") if (NOT EXISTS "${prefix}")
@ -62,7 +70,7 @@ function(download_qt target)
if (WIN32) if (WIN32)
set(aqt_path "${base_path}/aqt.exe") set(aqt_path "${base_path}/aqt.exe")
file(DOWNLOAD file(DOWNLOAD
https://github.com/miurahr/aqtinstall/releases/download/v3.1.4/aqt.exe https://github.com/miurahr/aqtinstall/releases/download/v3.1.7/aqt.exe
${aqt_path} SHOW_PROGRESS) ${aqt_path} SHOW_PROGRESS)
execute_process(COMMAND ${aqt_path} ${install_args} execute_process(COMMAND ${aqt_path} ${install_args}
WORKING_DIRECTORY ${base_path}) WORKING_DIRECTORY ${base_path})

View File

@ -12,8 +12,16 @@ set(HASH_FILES
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h" "${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h"
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.cpp" "${VIDEO_CORE}/renderer_opengl/gl_shader_util.cpp"
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.h" "${VIDEO_CORE}/renderer_opengl/gl_shader_util.h"
"${VIDEO_CORE}/renderer_vulkan/vk_shader_gen.cpp"
"${VIDEO_CORE}/renderer_vulkan/vk_shader_gen.h"
"${VIDEO_CORE}/renderer_vulkan/vk_shader_gen_spv.cpp"
"${VIDEO_CORE}/renderer_vulkan/vk_shader_gen_spv.h"
"${VIDEO_CORE}/renderer_vulkan/vk_shader_util.cpp"
"${VIDEO_CORE}/renderer_vulkan/vk_shader_util.h"
"${VIDEO_CORE}/shader/shader.cpp" "${VIDEO_CORE}/shader/shader.cpp"
"${VIDEO_CORE}/shader/shader.h" "${VIDEO_CORE}/shader/shader.h"
"${VIDEO_CORE}/shader/shader_uniforms.cpp"
"${VIDEO_CORE}/shader/shader_uniforms.h"
"${VIDEO_CORE}/pica.cpp" "${VIDEO_CORE}/pica.cpp"
"${VIDEO_CORE}/pica.h" "${VIDEO_CORE}/pica.h"
"${VIDEO_CORE}/regs_framebuffer.h" "${VIDEO_CORE}/regs_framebuffer.h"

View File

@ -13,6 +13,8 @@ set(BUILD_DIR "${CMAKE_BINARY_DIR}/installer")
set(DIST_DIR "${BUILD_DIR}/dist") set(DIST_DIR "${BUILD_DIR}/dist")
set(TARGET_FILE "${DIST_DIR}/citra-setup-${PLATFORM}") set(TARGET_FILE "${DIST_DIR}/citra-setup-${PLATFORM}")
file(MAKE_DIRECTORY "${BUILD_DIR}" "${DIST_DIR}")
# Adds a custom target that will run the BuildInstaller.cmake file # Adds a custom target that will run the BuildInstaller.cmake file
# CMake can't just run a cmake function as a custom command, so this is a way around it. # CMake can't just run a cmake function as a custom command, so this is a way around it.
# Calls the cmake command and runs a cmake file in "scripting" mode passing in variables with -D # Calls the cmake command and runs a cmake file in "scripting" mode passing in variables with -D

View File

@ -26,3 +26,8 @@ QPushButton#3DOptionStatusBarButton {
QPushButton#3DOptionStatusBarButton:hover { QPushButton#3DOptionStatusBarButton:hover {
border: 1px solid #76797C; border: 1px solid #76797C;
} }
QPushButton#button_reset_defaults {
min-width: 57px;
padding: 4px 8px;
}

View File

@ -1072,6 +1072,10 @@ QPushButton:focus {
border: 1px solid #1464A0; border: 1px solid #1464A0;
} }
QPushButton#button_reset_defaults {
padding: 3px 6px;
}
/* QToolButton ------------------------------------------------------------ /* QToolButton ------------------------------------------------------------
https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtoolbutton https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtoolbutton

View File

@ -97,6 +97,7 @@ set(ENABLE_GLSLANG_BINARIES OFF CACHE BOOL "")
set(ENABLE_SPVREMAPPER OFF CACHE BOOL "") set(ENABLE_SPVREMAPPER OFF CACHE BOOL "")
set(ENABLE_CTEST OFF CACHE BOOL "") set(ENABLE_CTEST OFF CACHE BOOL "")
set(ENABLE_HLSL OFF CACHE BOOL "") set(ENABLE_HLSL OFF CACHE BOOL "")
set(BUILD_EXTERNAL OFF CACHE BOOL "")
add_subdirectory(glslang) add_subdirectory(glslang)
# inih # inih
@ -241,3 +242,11 @@ target_include_directories(vma SYSTEM INTERFACE ./vma/include)
# vulkan-headers # vulkan-headers
add_library(vulkan-headers INTERFACE) add_library(vulkan-headers INTERFACE)
target_include_directories(vulkan-headers SYSTEM INTERFACE ./vulkan-headers/include) target_include_directories(vulkan-headers SYSTEM INTERFACE ./vulkan-headers/include)
if (APPLE)
target_include_directories(vulkan-headers SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK)
endif()
# adrenotools
if (ANDROID)
add_subdirectory(libadrenotools)
endif()

View File

@ -1,28 +1,32 @@
/* /*
OpenGL, OpenGL ES loader generated by glad 0.1.36 on Sat Apr 1 20:34:42 2023. OpenGL, OpenGL ES loader generated by glad 0.1.34 on Sat Aug 26 18:38:43 2023.
Language/Generator: C/C++ Language/Generator: C/C++
Specification: gl Specification: gl
APIs: gl=4.3, gles2=3.2 APIs: gl=4.3, gles2=3.2
Profile: core Profile: core
Extensions: Extensions:
GL_AMD_blend_minmax_factor,
GL_ARB_buffer_storage, GL_ARB_buffer_storage,
GL_ARB_clear_texture, GL_ARB_clear_texture,
GL_ARB_get_texture_sub_image, GL_ARB_get_texture_sub_image,
GL_ARB_texture_compression_bptc, GL_ARB_texture_compression_bptc,
GL_ARM_shader_framebuffer_fetch,
GL_EXT_buffer_storage, GL_EXT_buffer_storage,
GL_EXT_clip_cull_distance, GL_EXT_clip_cull_distance,
GL_EXT_texture_compression_s3tc GL_EXT_shader_framebuffer_fetch,
GL_EXT_texture_compression_s3tc,
GL_NV_blend_minmax_factor
Loader: True Loader: True
Local files: False Local files: False
Omit khrplatform: False Omit khrplatform: False
Reproducible: False Reproducible: False
Commandline: Commandline:
--profile="core" --api="gl=4.3,gles2=3.2" --generator="c" --spec="gl" --extensions="GL_ARB_buffer_storage,GL_ARB_clear_texture,GL_ARB_get_texture_sub_image,GL_ARB_texture_compression_bptc,GL_EXT_buffer_storage,GL_EXT_clip_cull_distance,GL_EXT_texture_compression_s3tc" --profile="core" --api="gl=4.3,gles2=3.2" --generator="c" --spec="gl" --extensions="GL_AMD_blend_minmax_factor,GL_ARB_buffer_storage,GL_ARB_clear_texture,GL_ARB_get_texture_sub_image,GL_ARB_texture_compression_bptc,GL_ARM_shader_framebuffer_fetch,GL_EXT_buffer_storage,GL_EXT_clip_cull_distance,GL_EXT_shader_framebuffer_fetch,GL_EXT_texture_compression_s3tc,GL_NV_blend_minmax_factor"
Online: Online:
https://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gl%3D4.3&api=gles2%3D3.2&extensions=GL_ARB_buffer_storage&extensions=GL_ARB_clear_texture&extensions=GL_ARB_get_texture_sub_image&extensions=GL_ARB_texture_compression_bptc&extensions=GL_EXT_buffer_storage&extensions=GL_EXT_clip_cull_distance&extensions=GL_EXT_texture_compression_s3tc https://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gl%3D4.3&api=gles2%3D3.2&extensions=GL_AMD_blend_minmax_factor&extensions=GL_ARB_buffer_storage&extensions=GL_ARB_clear_texture&extensions=GL_ARB_get_texture_sub_image&extensions=GL_ARB_texture_compression_bptc&extensions=GL_ARM_shader_framebuffer_fetch&extensions=GL_EXT_buffer_storage&extensions=GL_EXT_clip_cull_distance&extensions=GL_EXT_shader_framebuffer_fetch&extensions=GL_EXT_texture_compression_s3tc&extensions=GL_NV_blend_minmax_factor
*/ */
@ -3320,6 +3324,8 @@ typedef void (APIENTRYP PFNGLGETNUNIFORMUIVPROC)(GLuint program, GLint location,
GLAPI PFNGLGETNUNIFORMUIVPROC glad_glGetnUniformuiv; GLAPI PFNGLGETNUNIFORMUIVPROC glad_glGetnUniformuiv;
#define glGetnUniformuiv glad_glGetnUniformuiv #define glGetnUniformuiv glad_glGetnUniformuiv
#endif #endif
#define GL_FACTOR_MIN_AMD 0x901C
#define GL_FACTOR_MAX_AMD 0x901D
#define GL_MAP_PERSISTENT_BIT 0x0040 #define GL_MAP_PERSISTENT_BIT 0x0040
#define GL_MAP_COHERENT_BIT 0x0080 #define GL_MAP_COHERENT_BIT 0x0080
#define GL_DYNAMIC_STORAGE_BIT 0x0100 #define GL_DYNAMIC_STORAGE_BIT 0x0100
@ -3332,10 +3338,13 @@ GLAPI PFNGLGETNUNIFORMUIVPROC glad_glGetnUniformuiv;
#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D #define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D
#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E #define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E
#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F #define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F
#define GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT 0x8A52
#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
#define GL_FETCH_PER_SAMPLE_ARM 0x8F65
#define GL_FRAGMENT_SHADER_FRAMEBUFFER_FETCH_MRT_ARM 0x8F66
#define GL_MAP_PERSISTENT_BIT_EXT 0x0040 #define GL_MAP_PERSISTENT_BIT_EXT 0x0040
#define GL_MAP_COHERENT_BIT_EXT 0x0080 #define GL_MAP_COHERENT_BIT_EXT 0x0080
#define GL_DYNAMIC_STORAGE_BIT_EXT 0x0100 #define GL_DYNAMIC_STORAGE_BIT_EXT 0x0100
@ -3354,6 +3363,10 @@ GLAPI PFNGLGETNUNIFORMUIVPROC glad_glGetnUniformuiv;
#define GL_CLIP_DISTANCE5_EXT 0x3005 #define GL_CLIP_DISTANCE5_EXT 0x3005
#define GL_CLIP_DISTANCE6_EXT 0x3006 #define GL_CLIP_DISTANCE6_EXT 0x3006
#define GL_CLIP_DISTANCE7_EXT 0x3007 #define GL_CLIP_DISTANCE7_EXT 0x3007
#ifndef GL_AMD_blend_minmax_factor
#define GL_AMD_blend_minmax_factor 1
GLAPI int GLAD_GL_AMD_blend_minmax_factor;
#endif
#ifndef GL_ARB_buffer_storage #ifndef GL_ARB_buffer_storage
#define GL_ARB_buffer_storage 1 #define GL_ARB_buffer_storage 1
GLAPI int GLAD_GL_ARB_buffer_storage; GLAPI int GLAD_GL_ARB_buffer_storage;
@ -3385,10 +3398,22 @@ GLAPI PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC glad_glGetCompressedTextureSubImage;
#define GL_ARB_texture_compression_bptc 1 #define GL_ARB_texture_compression_bptc 1
GLAPI int GLAD_GL_ARB_texture_compression_bptc; GLAPI int GLAD_GL_ARB_texture_compression_bptc;
#endif #endif
#ifndef GL_EXT_shader_framebuffer_fetch
#define GL_EXT_shader_framebuffer_fetch 1
GLAPI int GLAD_GL_EXT_shader_framebuffer_fetch;
#endif
#ifndef GL_EXT_texture_compression_s3tc #ifndef GL_EXT_texture_compression_s3tc
#define GL_EXT_texture_compression_s3tc 1 #define GL_EXT_texture_compression_s3tc 1
GLAPI int GLAD_GL_EXT_texture_compression_s3tc; GLAPI int GLAD_GL_EXT_texture_compression_s3tc;
#endif #endif
#ifndef GL_NV_blend_minmax_factor
#define GL_NV_blend_minmax_factor 1
GLAPI int GLAD_GL_NV_blend_minmax_factor;
#endif
#ifndef GL_ARM_shader_framebuffer_fetch
#define GL_ARM_shader_framebuffer_fetch 1
GLAPI int GLAD_GL_ARM_shader_framebuffer_fetch;
#endif
#ifndef GL_EXT_buffer_storage #ifndef GL_EXT_buffer_storage
#define GL_EXT_buffer_storage 1 #define GL_EXT_buffer_storage 1
GLAPI int GLAD_GL_EXT_buffer_storage; GLAPI int GLAD_GL_EXT_buffer_storage;
@ -3400,10 +3425,18 @@ GLAPI PFNGLBUFFERSTORAGEEXTPROC glad_glBufferStorageEXT;
#define GL_EXT_clip_cull_distance 1 #define GL_EXT_clip_cull_distance 1
GLAPI int GLAD_GL_EXT_clip_cull_distance; GLAPI int GLAD_GL_EXT_clip_cull_distance;
#endif #endif
#ifndef GL_EXT_shader_framebuffer_fetch
#define GL_EXT_shader_framebuffer_fetch 1
GLAPI int GLAD_GL_EXT_shader_framebuffer_fetch;
#endif
#ifndef GL_EXT_texture_compression_s3tc #ifndef GL_EXT_texture_compression_s3tc
#define GL_EXT_texture_compression_s3tc 1 #define GL_EXT_texture_compression_s3tc 1
GLAPI int GLAD_GL_EXT_texture_compression_s3tc; GLAPI int GLAD_GL_EXT_texture_compression_s3tc;
#endif #endif
#ifndef GL_NV_blend_minmax_factor
#define GL_NV_blend_minmax_factor 1
GLAPI int GLAD_GL_NV_blend_minmax_factor;
#endif
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -1,28 +1,32 @@
/* /*
OpenGL, OpenGL ES loader generated by glad 0.1.36 on Sat Apr 1 20:34:42 2023. OpenGL, OpenGL ES loader generated by glad 0.1.34 on Sat Aug 26 18:38:43 2023.
Language/Generator: C/C++ Language/Generator: C/C++
Specification: gl Specification: gl
APIs: gl=4.3, gles2=3.2 APIs: gl=4.3, gles2=3.2
Profile: core Profile: core
Extensions: Extensions:
GL_AMD_blend_minmax_factor,
GL_ARB_buffer_storage, GL_ARB_buffer_storage,
GL_ARB_clear_texture, GL_ARB_clear_texture,
GL_ARB_get_texture_sub_image, GL_ARB_get_texture_sub_image,
GL_ARB_texture_compression_bptc, GL_ARB_texture_compression_bptc,
GL_ARM_shader_framebuffer_fetch,
GL_EXT_buffer_storage, GL_EXT_buffer_storage,
GL_EXT_clip_cull_distance, GL_EXT_clip_cull_distance,
GL_EXT_texture_compression_s3tc GL_EXT_shader_framebuffer_fetch,
GL_EXT_texture_compression_s3tc,
GL_NV_blend_minmax_factor
Loader: True Loader: True
Local files: False Local files: False
Omit khrplatform: False Omit khrplatform: False
Reproducible: False Reproducible: False
Commandline: Commandline:
--profile="core" --api="gl=4.3,gles2=3.2" --generator="c" --spec="gl" --extensions="GL_ARB_buffer_storage,GL_ARB_clear_texture,GL_ARB_get_texture_sub_image,GL_ARB_texture_compression_bptc,GL_EXT_buffer_storage,GL_EXT_clip_cull_distance,GL_EXT_texture_compression_s3tc" --profile="core" --api="gl=4.3,gles2=3.2" --generator="c" --spec="gl" --extensions="GL_AMD_blend_minmax_factor,GL_ARB_buffer_storage,GL_ARB_clear_texture,GL_ARB_get_texture_sub_image,GL_ARB_texture_compression_bptc,GL_ARM_shader_framebuffer_fetch,GL_EXT_buffer_storage,GL_EXT_clip_cull_distance,GL_EXT_shader_framebuffer_fetch,GL_EXT_texture_compression_s3tc,GL_NV_blend_minmax_factor"
Online: Online:
https://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gl%3D4.3&api=gles2%3D3.2&extensions=GL_ARB_buffer_storage&extensions=GL_ARB_clear_texture&extensions=GL_ARB_get_texture_sub_image&extensions=GL_ARB_texture_compression_bptc&extensions=GL_EXT_buffer_storage&extensions=GL_EXT_clip_cull_distance&extensions=GL_EXT_texture_compression_s3tc https://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gl%3D4.3&api=gles2%3D3.2&extensions=GL_AMD_blend_minmax_factor&extensions=GL_ARB_buffer_storage&extensions=GL_ARB_clear_texture&extensions=GL_ARB_get_texture_sub_image&extensions=GL_ARB_texture_compression_bptc&extensions=GL_ARM_shader_framebuffer_fetch&extensions=GL_EXT_buffer_storage&extensions=GL_EXT_clip_cull_distance&extensions=GL_EXT_shader_framebuffer_fetch&extensions=GL_EXT_texture_compression_s3tc&extensions=GL_NV_blend_minmax_factor
*/ */
#include <stdio.h> #include <stdio.h>
@ -853,13 +857,17 @@ PFNGLVIEWPORTARRAYVPROC glad_glViewportArrayv = NULL;
PFNGLVIEWPORTINDEXEDFPROC glad_glViewportIndexedf = NULL; PFNGLVIEWPORTINDEXEDFPROC glad_glViewportIndexedf = NULL;
PFNGLVIEWPORTINDEXEDFVPROC glad_glViewportIndexedfv = NULL; PFNGLVIEWPORTINDEXEDFVPROC glad_glViewportIndexedfv = NULL;
PFNGLWAITSYNCPROC glad_glWaitSync = NULL; PFNGLWAITSYNCPROC glad_glWaitSync = NULL;
int GLAD_GL_AMD_blend_minmax_factor = 0;
int GLAD_GL_ARB_buffer_storage = 0; int GLAD_GL_ARB_buffer_storage = 0;
int GLAD_GL_ARB_clear_texture = 0; int GLAD_GL_ARB_clear_texture = 0;
int GLAD_GL_ARB_get_texture_sub_image = 0; int GLAD_GL_ARB_get_texture_sub_image = 0;
int GLAD_GL_ARB_texture_compression_bptc = 0; int GLAD_GL_ARB_texture_compression_bptc = 0;
int GLAD_GL_ARM_shader_framebuffer_fetch = 0;
int GLAD_GL_EXT_buffer_storage = 0; int GLAD_GL_EXT_buffer_storage = 0;
int GLAD_GL_EXT_clip_cull_distance = 0; int GLAD_GL_EXT_clip_cull_distance = 0;
int GLAD_GL_EXT_shader_framebuffer_fetch = 0;
int GLAD_GL_EXT_texture_compression_s3tc = 0; int GLAD_GL_EXT_texture_compression_s3tc = 0;
int GLAD_GL_NV_blend_minmax_factor = 0;
PFNGLBUFFERSTORAGEPROC glad_glBufferStorage = NULL; PFNGLBUFFERSTORAGEPROC glad_glBufferStorage = NULL;
PFNGLCLEARTEXIMAGEPROC glad_glClearTexImage = NULL; PFNGLCLEARTEXIMAGEPROC glad_glClearTexImage = NULL;
PFNGLCLEARTEXSUBIMAGEPROC glad_glClearTexSubImage = NULL; PFNGLCLEARTEXSUBIMAGEPROC glad_glClearTexSubImage = NULL;
@ -1498,11 +1506,14 @@ static void load_GL_ARB_get_texture_sub_image(GLADloadproc load) {
} }
static int find_extensionsGL(void) { static int find_extensionsGL(void) {
if (!get_exts()) return 0; if (!get_exts()) return 0;
GLAD_GL_AMD_blend_minmax_factor = has_ext("GL_AMD_blend_minmax_factor");
GLAD_GL_ARB_buffer_storage = has_ext("GL_ARB_buffer_storage"); GLAD_GL_ARB_buffer_storage = has_ext("GL_ARB_buffer_storage");
GLAD_GL_ARB_clear_texture = has_ext("GL_ARB_clear_texture"); GLAD_GL_ARB_clear_texture = has_ext("GL_ARB_clear_texture");
GLAD_GL_ARB_get_texture_sub_image = has_ext("GL_ARB_get_texture_sub_image"); GLAD_GL_ARB_get_texture_sub_image = has_ext("GL_ARB_get_texture_sub_image");
GLAD_GL_ARB_texture_compression_bptc = has_ext("GL_ARB_texture_compression_bptc"); GLAD_GL_ARB_texture_compression_bptc = has_ext("GL_ARB_texture_compression_bptc");
GLAD_GL_EXT_shader_framebuffer_fetch = has_ext("GL_EXT_shader_framebuffer_fetch");
GLAD_GL_EXT_texture_compression_s3tc = has_ext("GL_EXT_texture_compression_s3tc"); GLAD_GL_EXT_texture_compression_s3tc = has_ext("GL_EXT_texture_compression_s3tc");
GLAD_GL_NV_blend_minmax_factor = has_ext("GL_NV_blend_minmax_factor");
free_exts(); free_exts();
return 1; return 1;
} }
@ -1971,9 +1982,12 @@ static void load_GL_EXT_buffer_storage(GLADloadproc load) {
} }
static int find_extensionsGLES2(void) { static int find_extensionsGLES2(void) {
if (!get_exts()) return 0; if (!get_exts()) return 0;
GLAD_GL_ARM_shader_framebuffer_fetch = has_ext("GL_ARM_shader_framebuffer_fetch");
GLAD_GL_EXT_buffer_storage = has_ext("GL_EXT_buffer_storage"); GLAD_GL_EXT_buffer_storage = has_ext("GL_EXT_buffer_storage");
GLAD_GL_EXT_clip_cull_distance = has_ext("GL_EXT_clip_cull_distance"); GLAD_GL_EXT_clip_cull_distance = has_ext("GL_EXT_clip_cull_distance");
GLAD_GL_EXT_shader_framebuffer_fetch = has_ext("GL_EXT_shader_framebuffer_fetch");
GLAD_GL_EXT_texture_compression_s3tc = has_ext("GL_EXT_texture_compression_s3tc"); GLAD_GL_EXT_texture_compression_s3tc = has_ext("GL_EXT_texture_compression_s3tc");
GLAD_GL_NV_blend_minmax_factor = has_ext("GL_NV_blend_minmax_factor");
free_exts(); free_exts();
return 1; return 1;
} }

1
externals/libadrenotools vendored Submodule

1071
externals/moltenvk/mvk_config.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,10 @@ include_directories(.)
set_property(DIRECTORY APPEND PROPERTY set_property(DIRECTORY APPEND PROPERTY
COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>) COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
if (ENABLE_LTO)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
# Set compilation flags # Set compilation flags
if (MSVC) if (MSVC)
set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE) set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)

View File

@ -359,6 +359,8 @@ public final class SettingsFragmentPresenter {
SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER); SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER);
Setting graphicsApi = rendererSection.getSetting(SettingsFile.KEY_GRAPHICS_API); Setting graphicsApi = rendererSection.getSetting(SettingsFile.KEY_GRAPHICS_API);
Setting spirvShaderGen = rendererSection.getSetting(SettingsFile.KEY_SPIRV_SHADER_GEN);
Setting asyncShaders = rendererSection.getSetting(SettingsFile.KEY_ASYNC_SHADERS);
Setting resolutionFactor = rendererSection.getSetting(SettingsFile.KEY_RESOLUTION_FACTOR); Setting resolutionFactor = rendererSection.getSetting(SettingsFile.KEY_RESOLUTION_FACTOR);
Setting filterMode = rendererSection.getSetting(SettingsFile.KEY_FILTER_MODE); Setting filterMode = rendererSection.getSetting(SettingsFile.KEY_FILTER_MODE);
Setting shadersAccurateMul = rendererSection.getSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL); Setting shadersAccurateMul = rendererSection.getSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL);
@ -377,6 +379,8 @@ public final class SettingsFragmentPresenter {
sl.add(new HeaderSetting(null, null, R.string.renderer, 0)); sl.add(new HeaderSetting(null, null, R.string.renderer, 0));
sl.add(new SingleChoiceSetting(SettingsFile.KEY_GRAPHICS_API, Settings.SECTION_RENDERER, R.string.graphics_api, 0, R.array.graphicsApiNames, R.array.graphicsApiValues, 0, graphicsApi)); sl.add(new SingleChoiceSetting(SettingsFile.KEY_GRAPHICS_API, Settings.SECTION_RENDERER, R.string.graphics_api, 0, R.array.graphicsApiNames, R.array.graphicsApiValues, 0, graphicsApi));
sl.add(new CheckBoxSetting(SettingsFile.KEY_SPIRV_SHADER_GEN, Settings.SECTION_RENDERER, R.string.spirv_shader_gen, R.string.spirv_shader_gen_description, true, spirvShaderGen));
sl.add(new CheckBoxSetting(SettingsFile.KEY_ASYNC_SHADERS, Settings.SECTION_RENDERER, R.string.async_shaders, R.string.async_shaders_description, false, asyncShaders));
sl.add(new SliderSetting(SettingsFile.KEY_RESOLUTION_FACTOR, Settings.SECTION_RENDERER, R.string.internal_resolution, R.string.internal_resolution_description, 1, 4, "x", 1, resolutionFactor)); sl.add(new SliderSetting(SettingsFile.KEY_RESOLUTION_FACTOR, Settings.SECTION_RENDERER, R.string.internal_resolution, R.string.internal_resolution_description, 1, 4, "x", 1, resolutionFactor));
sl.add(new CheckBoxSetting(SettingsFile.KEY_FILTER_MODE, Settings.SECTION_RENDERER, R.string.linear_filtering, R.string.linear_filtering_description, true, filterMode)); sl.add(new CheckBoxSetting(SettingsFile.KEY_FILTER_MODE, Settings.SECTION_RENDERER, R.string.linear_filtering, R.string.linear_filtering_description, true, filterMode));
sl.add(new CheckBoxSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL, Settings.SECTION_RENDERER, R.string.shaders_accurate_mul, R.string.shaders_accurate_mul_description, false, shadersAccurateMul)); sl.add(new CheckBoxSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL, Settings.SECTION_RENDERER, R.string.shaders_accurate_mul, R.string.shaders_accurate_mul_description, false, shadersAccurateMul));
@ -424,6 +428,6 @@ public final class SettingsFragmentPresenter {
sl.add(new CheckBoxSetting(SettingsFile.KEY_CPU_JIT, Settings.SECTION_CORE, R.string.cpu_jit, R.string.cpu_jit_description, true, useCpuJit, true, mView)); sl.add(new CheckBoxSetting(SettingsFile.KEY_CPU_JIT, Settings.SECTION_CORE, R.string.cpu_jit, R.string.cpu_jit_description, true, useCpuJit, true, mView));
sl.add(new CheckBoxSetting(SettingsFile.KEY_HW_SHADER, Settings.SECTION_RENDERER, R.string.hw_shaders, R.string.hw_shaders_description, true, hardwareShader, true, mView)); sl.add(new CheckBoxSetting(SettingsFile.KEY_HW_SHADER, Settings.SECTION_RENDERER, R.string.hw_shaders, R.string.hw_shaders_description, true, hardwareShader, true, mView));
sl.add(new CheckBoxSetting(SettingsFile.KEY_USE_VSYNC, Settings.SECTION_RENDERER, R.string.vsync, R.string.vsync_description, true, vsyncEnable)); sl.add(new CheckBoxSetting(SettingsFile.KEY_USE_VSYNC, Settings.SECTION_RENDERER, R.string.vsync, R.string.vsync_description, true, vsyncEnable));
sl.add(new CheckBoxSetting(SettingsFile.KEY_RENDERER_DEBUG, Settings.SECTION_RENDERER, R.string.renderer_debug, R.string.renderer_debug_description, false, rendererDebug)); sl.add(new CheckBoxSetting(SettingsFile.KEY_RENDERER_DEBUG, Settings.SECTION_DEBUG, R.string.renderer_debug, R.string.renderer_debug_description, false, rendererDebug));
} }
} }

View File

@ -45,6 +45,8 @@ public final class SettingsFile {
public static final String KEY_PREMIUM = "premium"; public static final String KEY_PREMIUM = "premium";
public static final String KEY_GRAPHICS_API = "graphics_api"; public static final String KEY_GRAPHICS_API = "graphics_api";
public static final String KEY_SPIRV_SHADER_GEN = "spirv_shader_gen";
public static final String KEY_ASYNC_SHADERS = "async_shader_compilation";
public static final String KEY_RENDERER_DEBUG = "renderer_debug"; public static final String KEY_RENDERER_DEBUG = "renderer_debug";
public static final String KEY_HW_SHADER = "use_hw_shader"; public static final String KEY_HW_SHADER = "use_hw_shader";
public static final String KEY_SHADERS_ACCURATE_MUL = "shaders_accurate_mul"; public static final String KEY_SHADERS_ACCURATE_MUL = "shaders_accurate_mul";

View File

@ -19,6 +19,10 @@ add_library(citra-android SHARED
default_ini.h default_ini.h
emu_window/emu_window.cpp emu_window/emu_window.cpp
emu_window/emu_window.h emu_window/emu_window.h
emu_window/emu_window_gl.cpp
emu_window/emu_window_gl.h
emu_window/emu_window_vk.cpp
emu_window/emu_window_vk.h
game_info.cpp game_info.cpp
game_settings.cpp game_settings.cpp
game_settings.h game_settings.h
@ -30,7 +34,7 @@ add_library(citra-android SHARED
ndk_motion.h ndk_motion.h
) )
target_link_libraries(citra-android PRIVATE audio_core citra_common citra_core input_common network) target_link_libraries(citra-android PRIVATE audio_core citra_common citra_core input_common network adrenotools)
target_link_libraries(citra-android PRIVATE android camera2ndk EGL glad inih jnigraphics log mediandk yuv) target_link_libraries(citra-android PRIVATE android camera2ndk EGL glad inih jnigraphics log mediandk yuv)
set(CPACK_PACKAGE_EXECUTABLES ${CPACK_PACKAGE_EXECUTABLES} citra-android) set(CPACK_PACKAGE_EXECUTABLES ${CPACK_PACKAGE_EXECUTABLES} citra-android)

View File

@ -147,6 +147,9 @@ void Config::ReadValues() {
Settings::values.shaders_accurate_mul = Settings::values.shaders_accurate_mul =
sdl2_config->GetBoolean("Renderer", "shaders_accurate_mul", false); sdl2_config->GetBoolean("Renderer", "shaders_accurate_mul", false);
ReadSetting("Renderer", Settings::values.graphics_api); ReadSetting("Renderer", Settings::values.graphics_api);
ReadSetting("Renderer", Settings::values.async_presentation);
ReadSetting("Renderer", Settings::values.async_shader_compilation);
ReadSetting("Renderer", Settings::values.spirv_shader_gen);
ReadSetting("Renderer", Settings::values.use_hw_shader); ReadSetting("Renderer", Settings::values.use_hw_shader);
ReadSetting("Renderer", Settings::values.use_shader_jit); ReadSetting("Renderer", Settings::values.use_shader_jit);
ReadSetting("Renderer", Settings::values.resolution_factor); ReadSetting("Renderer", Settings::values.resolution_factor);

View File

@ -99,9 +99,17 @@ cpu_clock_percentage =
[Renderer] [Renderer]
# Whether to render using OpenGL # Whether to render using OpenGL
# 1: OpenGLES (default) # 1: OpenGL ES (default), 2: Vulkan
graphics_api = graphics_api =
# Whether to compile shaders on multiple worker threads (Vulkan only)
# 0: Off, 1: On (default)
async_shader_compilation =
# Whether to emit PICA fragment shader using SPIRV or GLSL (Vulkan only)
# 0: GLSL, 1: SPIR-V (default)
spirv_shader_gen =
# Whether to use hardware shaders to emulate 3DS shaders # Whether to use hardware shaders to emulate 3DS shaders
# 0: Software, 1 (default): Hardware # 0: Software, 1 (default): Hardware
use_hw_shader = use_hw_shader =

View File

@ -6,10 +6,7 @@
#include <array> #include <array>
#include <cstdlib> #include <cstdlib>
#include <string> #include <string>
#include <android/native_window_jni.h> #include <android/native_window_jni.h>
#include <glad/glad.h>
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/settings.h" #include "common/settings.h"
#include "input_common/main.h" #include "input_common/main.h"
@ -20,52 +17,6 @@
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
static constexpr std::array<EGLint, 15> egl_attribs{EGL_SURFACE_TYPE,
EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE,
EGL_OPENGL_ES3_BIT_KHR,
EGL_BLUE_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_RED_SIZE,
8,
EGL_DEPTH_SIZE,
0,
EGL_STENCIL_SIZE,
0,
EGL_NONE};
static constexpr std::array<EGLint, 5> egl_empty_attribs{EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
static constexpr std::array<EGLint, 4> egl_context_attribs{EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
SharedContext_Android::SharedContext_Android(EGLDisplay egl_display, EGLConfig egl_config,
EGLContext egl_share_context)
: egl_display{egl_display}, egl_surface{eglCreatePbufferSurface(egl_display, egl_config,
egl_empty_attribs.data())},
egl_context{eglCreateContext(egl_display, egl_config, egl_share_context,
egl_context_attribs.data())} {
ASSERT_MSG(egl_surface, "eglCreatePbufferSurface() failed!");
ASSERT_MSG(egl_context, "eglCreateContext() failed!");
}
SharedContext_Android::~SharedContext_Android() {
if (!eglDestroySurface(egl_display, egl_surface)) {
LOG_CRITICAL(Frontend, "eglDestroySurface() failed");
}
if (!eglDestroyContext(egl_display, egl_context)) {
LOG_CRITICAL(Frontend, "eglDestroySurface() failed");
}
}
void SharedContext_Android::MakeCurrent() {
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
}
void SharedContext_Android::DoneCurrent() {
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
static bool IsPortraitMode() { static bool IsPortraitMode() {
return JNI_FALSE != IDCache::GetEnvForThread()->CallStaticBooleanMethod( return JNI_FALSE != IDCache::GetEnvForThread()->CallStaticBooleanMethod(
IDCache::GetNativeLibraryClass(), IDCache::GetIsPortraitMode()); IDCache::GetNativeLibraryClass(), IDCache::GetIsPortraitMode());
@ -79,7 +30,12 @@ static void UpdateLandscapeScreenLayout() {
void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) { void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
render_window = surface; render_window = surface;
window_info.type = Frontend::WindowSystemType::Android;
window_info.render_surface = surface;
StopPresenting(); StopPresenting();
OnFramebufferSizeChanged();
} }
bool EmuWindow_Android::OnTouchEvent(int x, int y, bool pressed) { bool EmuWindow_Android::OnTouchEvent(int x, int y, bool pressed) {
@ -98,6 +54,7 @@ void EmuWindow_Android::OnTouchMoved(int x, int y) {
void EmuWindow_Android::OnFramebufferSizeChanged() { void EmuWindow_Android::OnFramebufferSizeChanged() {
UpdateLandscapeScreenLayout(); UpdateLandscapeScreenLayout();
const bool is_portrait_mode{IsPortraitMode()}; const bool is_portrait_mode{IsPortraitMode()};
const int bigger{window_width > window_height ? window_width : window_height}; const int bigger{window_width > window_height ? window_width : window_height};
const int smaller{window_width < window_height ? window_width : window_height}; const int smaller{window_width < window_height ? window_width : window_height};
if (is_portrait_mode) { if (is_portrait_mode) {
@ -107,7 +64,7 @@ void EmuWindow_Android::OnFramebufferSizeChanged() {
} }
} }
EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface) { EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface) : host_window{surface} {
LOG_DEBUG(Frontend, "Initializing EmuWindow_Android"); LOG_DEBUG(Frontend, "Initializing EmuWindow_Android");
if (!surface) { if (!surface) {
@ -115,108 +72,10 @@ EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface) {
return; return;
} }
window_width = ANativeWindow_getWidth(surface);
window_height = ANativeWindow_getHeight(surface);
Network::Init(); Network::Init();
host_window = surface;
if (egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); egl_display == EGL_NO_DISPLAY) {
LOG_CRITICAL(Frontend, "eglGetDisplay() failed");
return;
}
if (eglInitialize(egl_display, 0, 0) != EGL_TRUE) {
LOG_CRITICAL(Frontend, "eglInitialize() failed");
return;
}
if (EGLint egl_num_configs{}; eglChooseConfig(egl_display, egl_attribs.data(), &egl_config, 1,
&egl_num_configs) != EGL_TRUE) {
LOG_CRITICAL(Frontend, "eglChooseConfig() failed");
return;
}
CreateWindowSurface();
if (eglQuerySurface(egl_display, egl_surface, EGL_WIDTH, &window_width) != EGL_TRUE) {
return;
}
if (eglQuerySurface(egl_display, egl_surface, EGL_HEIGHT, &window_height) != EGL_TRUE) {
return;
}
if (egl_context = eglCreateContext(egl_display, egl_config, 0, egl_context_attribs.data());
egl_context == EGL_NO_CONTEXT) {
LOG_CRITICAL(Frontend, "eglCreateContext() failed");
return;
}
if (eglSurfaceAttrib(egl_display, egl_surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED) !=
EGL_TRUE) {
LOG_CRITICAL(Frontend, "eglSurfaceAttrib() failed");
return;
}
if (core_context = CreateSharedContext(); !core_context) {
LOG_CRITICAL(Frontend, "CreateSharedContext() failed");
return;
}
if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context) != EGL_TRUE) {
LOG_CRITICAL(Frontend, "eglMakeCurrent() failed");
return;
}
if (!gladLoadGLES2Loader((GLADloadproc)eglGetProcAddress)) {
LOG_CRITICAL(Frontend, "gladLoadGLES2Loader() failed");
return;
}
if (!eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0)) {
LOG_CRITICAL(Frontend, "eglSwapInterval() failed");
return;
}
OnFramebufferSizeChanged();
}
bool EmuWindow_Android::CreateWindowSurface() {
if (!host_window) {
return true;
}
EGLint format{};
eglGetConfigAttrib(egl_display, egl_config, EGL_NATIVE_VISUAL_ID, &format);
ANativeWindow_setBuffersGeometry(host_window, 0, 0, format);
if (egl_surface = eglCreateWindowSurface(egl_display, egl_config, host_window, 0);
egl_surface == EGL_NO_SURFACE) {
return {};
}
return !!egl_surface;
}
void EmuWindow_Android::DestroyWindowSurface() {
if (!egl_surface) {
return;
}
if (eglGetCurrentSurface(EGL_DRAW) == egl_surface) {
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
if (!eglDestroySurface(egl_display, egl_surface)) {
LOG_CRITICAL(Frontend, "eglDestroySurface() failed");
}
egl_surface = EGL_NO_SURFACE;
}
void EmuWindow_Android::DestroyContext() {
if (!egl_context) {
return;
}
if (eglGetCurrentContext() == egl_context) {
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
if (!eglDestroyContext(egl_display, egl_context)) {
LOG_CRITICAL(Frontend, "eglDestroySurface() failed");
}
if (!eglTerminate(egl_display)) {
LOG_CRITICAL(Frontend, "eglTerminate() failed");
}
egl_context = EGL_NO_CONTEXT;
egl_display = EGL_NO_DISPLAY;
} }
EmuWindow_Android::~EmuWindow_Android() { EmuWindow_Android::~EmuWindow_Android() {
@ -224,48 +83,6 @@ EmuWindow_Android::~EmuWindow_Android() {
DestroyContext(); DestroyContext();
} }
std::unique_ptr<Frontend::GraphicsContext> EmuWindow_Android::CreateSharedContext() const {
return std::make_unique<SharedContext_Android>(egl_display, egl_config, egl_context);
}
void EmuWindow_Android::StopPresenting() {
if (presenting_state == PresentingState::Running) {
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
presenting_state = PresentingState::Stopped;
}
void EmuWindow_Android::TryPresenting() {
if (presenting_state != PresentingState::Running) {
if (presenting_state == PresentingState::Initial) {
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
presenting_state = PresentingState::Running;
} else {
return;
}
}
eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0);
if (VideoCore::g_renderer) {
VideoCore::g_renderer->TryPresent(0);
eglSwapBuffers(egl_display, egl_surface);
}
}
void EmuWindow_Android::PollEvents() {
if (!render_window) {
return;
}
host_window = render_window;
render_window = nullptr;
DestroyWindowSurface();
CreateWindowSurface();
OnFramebufferSizeChanged();
presenting_state = PresentingState::Initial;
}
void EmuWindow_Android::MakeCurrent() { void EmuWindow_Android::MakeCurrent() {
core_context->MakeCurrent(); core_context->MakeCurrent();
} }

View File

@ -5,38 +5,13 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
struct ANativeWindow;
class SharedContext_Android : public Frontend::GraphicsContext {
public:
SharedContext_Android(EGLDisplay egl_display, EGLConfig egl_config,
EGLContext egl_share_context);
~SharedContext_Android() override;
void MakeCurrent() override;
void DoneCurrent() override;
private:
EGLDisplay egl_display{};
EGLSurface egl_surface{};
EGLContext egl_context{};
};
class EmuWindow_Android : public Frontend::EmuWindow { class EmuWindow_Android : public Frontend::EmuWindow {
public: public:
EmuWindow_Android(ANativeWindow* surface); EmuWindow_Android(ANativeWindow* surface);
~EmuWindow_Android(); ~EmuWindow_Android();
void Present();
/// Called by the onSurfaceChanges() method to change the surface /// Called by the onSurfaceChanges() method to change the surface
void OnSurfaceChanged(ANativeWindow* surface); void OnSurfaceChanged(ANativeWindow* surface);
@ -46,38 +21,34 @@ public:
/// Handles movement of touch pointer /// Handles movement of touch pointer
void OnTouchMoved(int x, int y); void OnTouchMoved(int x, int y);
void PollEvents() override;
void MakeCurrent() override; void MakeCurrent() override;
void DoneCurrent() override; void DoneCurrent() override;
void TryPresenting(); virtual void TryPresenting() {}
void StopPresenting();
std::unique_ptr<GraphicsContext> CreateSharedContext() const override; virtual void StopPresenting() {}
private: protected:
void OnFramebufferSizeChanged(); void OnFramebufferSizeChanged();
bool CreateWindowSurface();
void DestroyWindowSurface();
void DestroyContext();
/// Creates the API specific window surface
virtual bool CreateWindowSurface() {
return false;
}
/// Destroys the API specific window surface
virtual void DestroyWindowSurface() {}
/// Destroys the graphics context
virtual void DestroyContext() {}
protected:
ANativeWindow* render_window{}; ANativeWindow* render_window{};
ANativeWindow* host_window{}; ANativeWindow* host_window{};
int window_width{}; int window_width{};
int window_height{}; int window_height{};
EGLConfig egl_config;
EGLSurface egl_surface{};
EGLContext egl_context{};
EGLDisplay egl_display{};
std::unique_ptr<Frontend::GraphicsContext> core_context; std::unique_ptr<Frontend::GraphicsContext> core_context;
enum class PresentingState {
Initial,
Running,
Stopped,
};
PresentingState presenting_state{};
}; };

View File

@ -0,0 +1,215 @@
// Copyright 2019 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include <cstdlib>
#include <string>
#include <android/native_window_jni.h>
#include <glad/glad.h>
#include "common/logging/log.h"
#include "common/settings.h"
#include "input_common/main.h"
#include "jni/emu_window/emu_window_gl.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
static constexpr std::array<EGLint, 15> egl_attribs{EGL_SURFACE_TYPE,
EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE,
EGL_OPENGL_ES3_BIT_KHR,
EGL_BLUE_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_RED_SIZE,
8,
EGL_DEPTH_SIZE,
0,
EGL_STENCIL_SIZE,
0,
EGL_NONE};
static constexpr std::array<EGLint, 5> egl_empty_attribs{EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
static constexpr std::array<EGLint, 4> egl_context_attribs{EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
class SharedContext_Android : public Frontend::GraphicsContext {
public:
SharedContext_Android(EGLDisplay egl_display, EGLConfig egl_config,
EGLContext egl_share_context)
: egl_display{egl_display}, egl_surface{eglCreatePbufferSurface(egl_display, egl_config,
egl_empty_attribs.data())},
egl_context{eglCreateContext(egl_display, egl_config, egl_share_context,
egl_context_attribs.data())} {
ASSERT_MSG(egl_surface, "eglCreatePbufferSurface() failed!");
ASSERT_MSG(egl_context, "eglCreateContext() failed!");
}
~SharedContext_Android() override {
if (!eglDestroySurface(egl_display, egl_surface)) {
LOG_CRITICAL(Frontend, "eglDestroySurface() failed");
}
if (!eglDestroyContext(egl_display, egl_context)) {
LOG_CRITICAL(Frontend, "eglDestroySurface() failed");
}
}
void MakeCurrent() override {
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
}
void DoneCurrent() override {
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
private:
EGLDisplay egl_display{};
EGLSurface egl_surface{};
EGLContext egl_context{};
};
EmuWindow_Android_OpenGL::EmuWindow_Android_OpenGL(ANativeWindow* surface)
: EmuWindow_Android{surface} {
if (egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); egl_display == EGL_NO_DISPLAY) {
LOG_CRITICAL(Frontend, "eglGetDisplay() failed");
return;
}
if (eglInitialize(egl_display, 0, 0) != EGL_TRUE) {
LOG_CRITICAL(Frontend, "eglInitialize() failed");
return;
}
if (EGLint egl_num_configs{}; eglChooseConfig(egl_display, egl_attribs.data(), &egl_config, 1,
&egl_num_configs) != EGL_TRUE) {
LOG_CRITICAL(Frontend, "eglChooseConfig() failed");
return;
}
CreateWindowSurface();
if (eglQuerySurface(egl_display, egl_surface, EGL_WIDTH, &window_width) != EGL_TRUE) {
return;
}
if (eglQuerySurface(egl_display, egl_surface, EGL_HEIGHT, &window_height) != EGL_TRUE) {
return;
}
if (egl_context = eglCreateContext(egl_display, egl_config, 0, egl_context_attribs.data());
egl_context == EGL_NO_CONTEXT) {
LOG_CRITICAL(Frontend, "eglCreateContext() failed");
return;
}
if (eglSurfaceAttrib(egl_display, egl_surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED) !=
EGL_TRUE) {
LOG_CRITICAL(Frontend, "eglSurfaceAttrib() failed");
return;
}
if (core_context = CreateSharedContext(); !core_context) {
LOG_CRITICAL(Frontend, "CreateSharedContext() failed");
return;
}
if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context) != EGL_TRUE) {
LOG_CRITICAL(Frontend, "eglMakeCurrent() failed");
return;
}
if (!gladLoadGLES2Loader((GLADloadproc)eglGetProcAddress)) {
LOG_CRITICAL(Frontend, "gladLoadGLES2Loader() failed");
return;
}
if (!eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0)) {
LOG_CRITICAL(Frontend, "eglSwapInterval() failed");
return;
}
OnFramebufferSizeChanged();
}
bool EmuWindow_Android_OpenGL::CreateWindowSurface() {
if (!host_window) {
return true;
}
EGLint format{};
eglGetConfigAttrib(egl_display, egl_config, EGL_NATIVE_VISUAL_ID, &format);
ANativeWindow_setBuffersGeometry(host_window, 0, 0, format);
if (egl_surface = eglCreateWindowSurface(egl_display, egl_config, host_window, 0);
egl_surface == EGL_NO_SURFACE) {
return {};
}
return egl_surface;
}
void EmuWindow_Android_OpenGL::DestroyWindowSurface() {
if (!egl_surface) {
return;
}
if (eglGetCurrentSurface(EGL_DRAW) == egl_surface) {
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
if (!eglDestroySurface(egl_display, egl_surface)) {
LOG_CRITICAL(Frontend, "eglDestroySurface() failed");
}
egl_surface = EGL_NO_SURFACE;
}
void EmuWindow_Android_OpenGL::DestroyContext() {
if (!egl_context) {
return;
}
if (eglGetCurrentContext() == egl_context) {
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
if (!eglDestroyContext(egl_display, egl_context)) {
LOG_CRITICAL(Frontend, "eglDestroySurface() failed");
}
if (!eglTerminate(egl_display)) {
LOG_CRITICAL(Frontend, "eglTerminate() failed");
}
egl_context = EGL_NO_CONTEXT;
egl_display = EGL_NO_DISPLAY;
}
std::unique_ptr<Frontend::GraphicsContext> EmuWindow_Android_OpenGL::CreateSharedContext() const {
return std::make_unique<SharedContext_Android>(egl_display, egl_config, egl_context);
}
void EmuWindow_Android_OpenGL::PollEvents() {
if (!render_window) {
return;
}
host_window = render_window;
render_window = nullptr;
DestroyWindowSurface();
CreateWindowSurface();
OnFramebufferSizeChanged();
presenting_state = PresentingState::Initial;
}
void EmuWindow_Android_OpenGL::StopPresenting() {
if (presenting_state == PresentingState::Running) {
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
presenting_state = PresentingState::Stopped;
}
void EmuWindow_Android_OpenGL::TryPresenting() {
if (presenting_state == PresentingState::Initial) [[unlikely]] {
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
presenting_state = PresentingState::Running;
}
if (presenting_state != PresentingState::Running) [[unlikely]] {
return;
}
eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0);
if (VideoCore::g_renderer) {
VideoCore::g_renderer->TryPresent(0);
eglSwapBuffers(egl_display, egl_surface);
}
}

View File

@ -0,0 +1,44 @@
// Copyright 2019 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include "jni/emu_window/emu_window.h"
struct ANativeWindow;
class EmuWindow_Android_OpenGL : public EmuWindow_Android {
public:
EmuWindow_Android_OpenGL(ANativeWindow* surface);
~EmuWindow_Android_OpenGL() override = default;
void TryPresenting() override;
void StopPresenting() override;
void PollEvents() override;
std::unique_ptr<GraphicsContext> CreateSharedContext() const override;
private:
bool CreateWindowSurface() override;
void DestroyWindowSurface() override;
void DestroyContext() override;
private:
EGLConfig egl_config;
EGLSurface egl_surface{};
EGLContext egl_context{};
EGLDisplay egl_display{};
enum class PresentingState {
Initial,
Running,
Stopped,
};
PresentingState presenting_state{};
};

View File

@ -0,0 +1,53 @@
// Copyright 2019 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstdlib>
#include <android/native_window_jni.h>
#include "common/logging/log.h"
#include "common/settings.h"
#include "jni/emu_window/emu_window_vk.h"
#include "video_core/video_core.h"
class GraphicsContext_Android final : public Frontend::GraphicsContext {
public:
explicit GraphicsContext_Android(std::shared_ptr<Common::DynamicLibrary> driver_library_)
: driver_library{driver_library_} {}
~GraphicsContext_Android() = default;
std::shared_ptr<Common::DynamicLibrary> GetDriverLibrary() override {
return driver_library;
}
private:
std::shared_ptr<Common::DynamicLibrary> driver_library;
};
EmuWindow_Android_Vulkan::EmuWindow_Android_Vulkan(
ANativeWindow* surface, std::shared_ptr<Common::DynamicLibrary> driver_library_)
: EmuWindow_Android{surface}, driver_library{driver_library_} {
CreateWindowSurface();
if (core_context = CreateSharedContext(); !core_context) {
LOG_CRITICAL(Frontend, "CreateSharedContext() failed");
return;
}
OnFramebufferSizeChanged();
}
bool EmuWindow_Android_Vulkan::CreateWindowSurface() {
if (!host_window) {
return true;
}
window_info.type = Frontend::WindowSystemType::Android;
window_info.render_surface = host_window;
return true;
}
std::unique_ptr<Frontend::GraphicsContext> EmuWindow_Android_Vulkan::CreateSharedContext() const {
return std::make_unique<GraphicsContext_Android>(driver_library);
}

View File

@ -0,0 +1,26 @@
// Copyright 2022 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "jni/emu_window/emu_window.h"
struct ANativeWindow;
class EmuWindow_Android_Vulkan : public EmuWindow_Android {
public:
EmuWindow_Android_Vulkan(ANativeWindow* surface,
std::shared_ptr<Common::DynamicLibrary> driver_library);
~EmuWindow_Android_Vulkan() override = default;
void PollEvents() override {}
std::unique_ptr<GraphicsContext> CreateSharedContext() const override;
private:
bool CreateWindowSurface() override;
private:
std::shared_ptr<Common::DynamicLibrary> driver_library;
};

View File

@ -4,13 +4,16 @@
#include <algorithm> #include <algorithm>
#include <thread> #include <thread>
#include <dlfcn.h>
#include <android/api-level.h> #include <android/api-level.h>
#include <android/native_window_jni.h> #include <android/native_window_jni.h>
#include "audio_core/dsp_interface.h" #include "audio_core/dsp_interface.h"
#include "common/aarch64/cpu_detect.h" #include "common/aarch64/cpu_detect.h"
#include "common/arch.h"
#include "common/common_paths.h" #include "common/common_paths.h"
#include "common/dynamic_library/dynamic_library.h"
#include "common/file_util.h" #include "common/file_util.h"
#include "common/logging/backend.h" #include "common/logging/backend.h"
#include "common/logging/log.h" #include "common/logging/log.h"
@ -33,7 +36,8 @@
#include "jni/camera/ndk_camera.h" #include "jni/camera/ndk_camera.h"
#include "jni/camera/still_image_camera.h" #include "jni/camera/still_image_camera.h"
#include "jni/config.h" #include "jni/config.h"
#include "jni/emu_window/emu_window.h" #include "jni/emu_window/emu_window_gl.h"
#include "jni/emu_window/emu_window_vk.h"
#include "jni/game_settings.h" #include "jni/game_settings.h"
#include "jni/id_cache.h" #include "jni/id_cache.h"
#include "jni/input_manager.h" #include "jni/input_manager.h"
@ -42,10 +46,15 @@
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
#if CITRA_ARCH(arm64)
#include <adrenotools/driver.h>
#endif
namespace { namespace {
ANativeWindow* s_surf; ANativeWindow* s_surf;
std::shared_ptr<Common::DynamicLibrary> vulkan_library{};
std::unique_ptr<EmuWindow_Android> window; std::unique_ptr<EmuWindow_Android> window;
std::atomic<bool> stop_run{true}; std::atomic<bool> stop_run{true};
@ -123,11 +132,14 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
const auto graphics_api = Settings::values.graphics_api.GetValue(); const auto graphics_api = Settings::values.graphics_api.GetValue();
switch (graphics_api) { switch (graphics_api) {
case Settings::GraphicsAPI::OpenGL: case Settings::GraphicsAPI::OpenGL:
window = std::make_unique<EmuWindow_Android>(s_surf); window = std::make_unique<EmuWindow_Android_OpenGL>(s_surf);
break;
case Settings::GraphicsAPI::Vulkan:
window = std::make_unique<EmuWindow_Android_Vulkan>(s_surf, vulkan_library);
break; break;
default: default:
LOG_CRITICAL(Frontend, "Unknown graphics API {}, using OpenGL", graphics_api); LOG_CRITICAL(Frontend, "Unknown graphics API {}, using Vulkan", graphics_api);
window = std::make_unique<EmuWindow_Android>(s_surf); window = std::make_unique<EmuWindow_Android_Vulkan>(s_surf, vulkan_library);
} }
Core::System& system{Core::System::GetInstance()}; Core::System& system{Core::System::GetInstance()};
@ -228,6 +240,37 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
return Core::System::ResultStatus::Success; return Core::System::ResultStatus::Success;
} }
void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir,
const std::string& custom_driver_name,
const std::string& file_redirect_dir) {
#if CITRA_ARCH(arm64)
void* handle{};
const char* file_redirect_dir_{};
int featureFlags{};
// Enable driver file redirection when renderer debugging is enabled.
if (Settings::values.renderer_debug && file_redirect_dir.size()) {
featureFlags |= ADRENOTOOLS_DRIVER_FILE_REDIRECT;
file_redirect_dir_ = file_redirect_dir.c_str();
}
// Try to load a custom driver.
if (custom_driver_name.size()) {
handle = adrenotools_open_libvulkan(
RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(),
custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr);
}
// Try to load the system driver.
if (!handle) {
handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(),
nullptr, nullptr, file_redirect_dir_, nullptr);
}
vulkan_library = std::make_shared<Common::DynamicLibrary>(handle);
#endif
}
extern "C" { extern "C" {
void Java_org_citra_citra_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env,
@ -238,6 +281,9 @@ void Java_org_citra_citra_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env,
if (window) { if (window) {
window->OnSurfaceChanged(s_surf); window->OnSurfaceChanged(s_surf);
} }
if (VideoCore::g_renderer) {
VideoCore::g_renderer->NotifySurfaceChanged();
}
LOG_INFO(Frontend, "Surface changed"); LOG_INFO(Frontend, "Surface changed");
} }
@ -258,6 +304,15 @@ void Java_org_citra_citra_1emu_NativeLibrary_DoFrame(JNIEnv* env, [[maybe_unused
window->TryPresenting(); window->TryPresenting();
} }
void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* env, jclass clazz,
jstring hook_lib_dir,
jstring custom_driver_dir,
jstring custom_driver_name,
jstring file_redirect_dir) {
InitializeGpuDriver(GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir),
GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir));
}
void Java_org_citra_citra_1emu_NativeLibrary_NotifyOrientationChange(JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_NotifyOrientationChange(JNIEnv* env,
[[maybe_unused]] jclass clazz, [[maybe_unused]] jclass clazz,
jint layout_option, jint layout_option,

View File

@ -177,11 +177,13 @@
</integer-array> </integer-array>
<string-array name="graphicsApiNames"> <string-array name="graphicsApiNames">
<item>OpenGLES</item> <item>OpenGL ES</item>
<item>Vulkan</item>
</string-array> </string-array>
<integer-array name="graphicsApiValues"> <integer-array name="graphicsApiValues">
<item>1</item> <item>1</item>
<item>2</item>
</integer-array> </integer-array>
<string-array name="textureFilterNames"> <string-array name="textureFilterNames">

View File

@ -74,6 +74,10 @@
<!-- Graphics settings strings --> <!-- Graphics settings strings -->
<string name="renderer">Renderer</string> <string name="renderer">Renderer</string>
<string name="graphics_api">Graphics API</string> <string name="graphics_api">Graphics API</string>
<string name="spirv_shader_gen">Enable SPIR-V shader generation</string>
<string name="spirv_shader_gen_description">Emits the fragment shader used to emulate PICA using SPIR-V instead of GLSL</string>
<string name="async_shaders">Enable asynchronous shader compilation</string>
<string name="async_shaders_description">Compiles shaders in the background to reduce stuttering during gameplay. When enabled expect temporary graphical glitches</string>
<string name="renderer_debug">Debug Renderer</string> <string name="renderer_debug">Debug Renderer</string>
<string name="renderer_debug_description">Log additional graphics related debug information. When enabled, game performance will be significantly reduced.</string> <string name="renderer_debug_description">Log additional graphics related debug information. When enabled, game performance will be significantly reduced.</string>
<string name="vsync">Enable V-Sync</string> <string name="vsync">Enable V-Sync</string>

View File

@ -47,9 +47,8 @@ add_library(audio_core STATIC
create_target_directory_groups(audio_core) create_target_directory_groups(audio_core)
target_link_libraries(audio_core PUBLIC citra_common) target_link_libraries(audio_core PUBLIC citra_common citra_core)
target_link_libraries(audio_core PRIVATE SoundTouch teakra) target_link_libraries(audio_core PRIVATE SoundTouch teakra)
set_target_properties(audio_core PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
add_definitions(-DSOUNDTOUCH_INTEGER_SAMPLES) add_definitions(-DSOUNDTOUCH_INTEGER_SAMPLES)
if(ENABLE_MF) if(ENABLE_MF)

View File

@ -12,6 +12,8 @@ add_executable(citra
emu_window/emu_window_sdl2_gl.h emu_window/emu_window_sdl2_gl.h
emu_window/emu_window_sdl2_sw.cpp emu_window/emu_window_sdl2_sw.cpp
emu_window/emu_window_sdl2_sw.h emu_window/emu_window_sdl2_sw.h
emu_window/emu_window_sdl2_vk.cpp
emu_window/emu_window_sdl2_vk.h
precompiled_headers.h precompiled_headers.h
resource.h resource.h
) )

View File

@ -15,6 +15,7 @@
#include "citra/emu_window/emu_window_sdl2.h" #include "citra/emu_window/emu_window_sdl2.h"
#include "citra/emu_window/emu_window_sdl2_gl.h" #include "citra/emu_window/emu_window_sdl2_gl.h"
#include "citra/emu_window/emu_window_sdl2_sw.h" #include "citra/emu_window/emu_window_sdl2_sw.h"
#include "citra/emu_window/emu_window_sdl2_vk.h"
#include "common/common_paths.h" #include "common/common_paths.h"
#include "common/detached_tasks.h" #include "common/detached_tasks.h"
#include "common/file_util.h" #include "common/file_util.h"
@ -351,6 +352,8 @@ int main(int argc, char** argv) {
switch (Settings::values.graphics_api.GetValue()) { switch (Settings::values.graphics_api.GetValue()) {
case Settings::GraphicsAPI::OpenGL: case Settings::GraphicsAPI::OpenGL:
return std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen, is_secondary); return std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen, is_secondary);
case Settings::GraphicsAPI::Vulkan:
return std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen, is_secondary);
case Settings::GraphicsAPI::Software: case Settings::GraphicsAPI::Software:
return std::make_unique<EmuWindow_SDL2_SW>(system, fullscreen, is_secondary); return std::make_unique<EmuWindow_SDL2_SW>(system, fullscreen, is_secondary);
} }

View File

@ -133,6 +133,10 @@ void Config::ReadValues() {
// Renderer // Renderer
ReadSetting("Renderer", Settings::values.graphics_api); ReadSetting("Renderer", Settings::values.graphics_api);
ReadSetting("Renderer", Settings::values.physical_device);
ReadSetting("Renderer", Settings::values.spirv_shader_gen);
ReadSetting("Renderer", Settings::values.async_shader_compilation);
ReadSetting("Renderer", Settings::values.async_presentation);
ReadSetting("Renderer", Settings::values.use_gles); ReadSetting("Renderer", Settings::values.use_gles);
ReadSetting("Renderer", Settings::values.use_hw_shader); ReadSetting("Renderer", Settings::values.use_hw_shader);
ReadSetting("Renderer", Settings::values.shaders_accurate_mul); ReadSetting("Renderer", Settings::values.shaders_accurate_mul);

View File

@ -99,7 +99,7 @@ cpu_clock_percentage =
[Renderer] [Renderer]
# Whether to render using OpenGL or Software # Whether to render using OpenGL or Software
# 0: Software, 1: OpenGL (default) # 0: Software, 1: OpenGL (default), 2: Vulkan
graphics_api = graphics_api =
# Whether to render using GLES or OpenGL # Whether to render using GLES or OpenGL

View File

@ -0,0 +1,90 @@
// Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstdlib>
#include <memory>
#include <string>
#include <SDL.h>
#include <SDL_syswm.h>
#include <fmt/format.h>
#include "citra/emu_window/emu_window_sdl2_vk.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "core/frontend/emu_window.h"
class DummyContext : public Frontend::GraphicsContext {};
EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen, bool is_secondary)
: EmuWindow_SDL2{system, is_secondary} {
const std::string window_title = fmt::format("Citra {} | {}-{}", Common::g_build_fullname,
Common::g_scm_branch, Common::g_scm_desc);
render_window =
SDL_CreateWindow(window_title.c_str(),
SDL_WINDOWPOS_UNDEFINED, // x position
SDL_WINDOWPOS_UNDEFINED, // y position
Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
SDL_SysWMinfo wm;
SDL_VERSION(&wm.version);
if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) {
LOG_CRITICAL(Frontend, "Failed to get information from the window manager");
std::exit(EXIT_FAILURE);
}
if (fullscreen) {
Fullscreen();
SDL_ShowCursor(false);
}
switch (wm.subsystem) {
#ifdef SDL_VIDEO_DRIVER_WINDOWS
case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS:
window_info.type = Frontend::WindowSystemType::Windows;
window_info.render_surface = reinterpret_cast<void*>(wm.info.win.window);
break;
#endif
#ifdef SDL_VIDEO_DRIVER_X11
case SDL_SYSWM_TYPE::SDL_SYSWM_X11:
window_info.type = Frontend::WindowSystemType::X11;
window_info.display_connection = wm.info.x11.display;
window_info.render_surface = reinterpret_cast<void*>(wm.info.x11.window);
break;
#endif
#ifdef SDL_VIDEO_DRIVER_WAYLAND
case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND:
window_info.type = Frontend::WindowSystemType::Wayland;
window_info.display_connection = wm.info.wl.display;
window_info.render_surface = wm.info.wl.surface;
break;
#endif
#ifdef SDL_VIDEO_DRIVER_COCOA
case SDL_SYSWM_TYPE::SDL_SYSWM_COCOA:
window_info.type = Frontend::WindowSystemType::MacOS;
window_info.render_surface = SDL_Metal_GetLayer(SDL_Metal_CreateView(render_window));
break;
#endif
#ifdef SDL_VIDEO_DRIVER_ANDROID
case SDL_SYSWM_TYPE::SDL_SYSWM_ANDROID:
window_info.type = Frontend::WindowSystemType::Android;
window_info.render_surface = reinterpret_cast<void*>(wm.info.android.window);
break;
#endif
default:
LOG_CRITICAL(Frontend, "Window manager subsystem {} not implemented", wm.subsystem);
std::exit(EXIT_FAILURE);
break;
}
render_window_id = SDL_GetWindowID(render_window);
OnResize();
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
SDL_PumpEvents();
}
EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default;
std::unique_ptr<Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const {
return std::make_unique<DummyContext>();
}

View File

@ -0,0 +1,24 @@
// Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "citra/emu_window/emu_window_sdl2.h"
namespace Frontend {
class GraphicsContext;
}
namespace Core {
class System;
}
class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
public:
explicit EmuWindow_SDL2_VK(Core::System& system_, bool fullscreen, bool is_secondary);
~EmuWindow_SDL2_VK() override;
std::unique_ptr<Frontend::GraphicsContext> CreateSharedContext() const override;
};

View File

@ -185,6 +185,8 @@ add_executable(citra-qt
util/spinbox.h util/spinbox.h
util/util.cpp util/util.cpp
util/util.h util/util.h
util/vk_device_info.cpp
util/vk_device_info.h
) )
file(GLOB COMPAT_LIST file(GLOB COMPAT_LIST
@ -341,6 +343,10 @@ if (USE_DISCORD_PRESENCE)
target_compile_definitions(citra-qt PRIVATE -DUSE_DISCORD_PRESENCE) target_compile_definitions(citra-qt PRIVATE -DUSE_DISCORD_PRESENCE)
endif() endif()
if (ENABLE_WEB_SERVICE)
target_link_libraries(citra-qt PRIVATE web_service)
endif()
if(UNIX AND NOT APPLE) if(UNIX AND NOT APPLE)
install(TARGETS citra-qt RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") install(TARGETS citra-qt RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
endif() endif()

View File

@ -229,20 +229,10 @@ class RenderWidget : public QWidget {
public: public:
RenderWidget(GRenderWindow* parent) : QWidget(parent) { RenderWidget(GRenderWindow* parent) : QWidget(parent) {
setMouseTracking(true); setMouseTracking(true);
}
virtual ~RenderWidget() = default;
virtual void Present() {}
void paintEvent(QPaintEvent* event) override {
Present();
update(); update();
} }
std::pair<unsigned, unsigned> GetSize() const { virtual ~RenderWidget() = default;
return std::make_pair(width(), height());
}
}; };
#ifdef HAS_OPENGL #ifdef HAS_OPENGL
@ -262,7 +252,7 @@ public:
context = std::move(context_); context = std::move(context_);
} }
void Present() override { void Present() {
if (!isVisible()) { if (!isVisible()) {
return; return;
} }
@ -278,6 +268,11 @@ public:
glFinish(); glFinish();
} }
void paintEvent(QPaintEvent* event) override {
Present();
update();
}
QPaintEngine* paintEngine() const override { QPaintEngine* paintEngine() const override {
return nullptr; return nullptr;
} }
@ -289,11 +284,27 @@ private:
}; };
#endif #endif
class VulkanRenderWidget : public RenderWidget {
public:
explicit VulkanRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {
setAttribute(Qt::WA_NativeWindow);
setAttribute(Qt::WA_PaintOnScreen);
if (GetWindowSystemType() == Frontend::WindowSystemType::Wayland) {
setAttribute(Qt::WA_DontCreateNativeAncestors);
}
windowHandle()->setSurfaceType(QWindow::VulkanSurface);
}
QPaintEngine* paintEngine() const override {
return nullptr;
}
};
struct SoftwareRenderWidget : public RenderWidget { struct SoftwareRenderWidget : public RenderWidget {
explicit SoftwareRenderWidget(GRenderWindow* parent, Core::System& system_) explicit SoftwareRenderWidget(GRenderWindow* parent, Core::System& system_)
: RenderWidget(parent), system(system_) {} : RenderWidget(parent), system(system_) {}
void Present() override { void Present() {
if (!isVisible()) { if (!isVisible()) {
return; return;
} }
@ -323,6 +334,11 @@ struct SoftwareRenderWidget : public RenderWidget {
painter.end(); painter.end();
} }
void paintEvent(QPaintEvent* event) override {
Present();
update();
}
QImage LoadFramebuffer(VideoCore::ScreenId screen_id) { QImage LoadFramebuffer(VideoCore::ScreenId screen_id) {
const auto& renderer = static_cast<SwRenderer::RendererSoftware&>(system.Renderer()); const auto& renderer = static_cast<SwRenderer::RendererSoftware&>(system.Renderer());
const auto& info = renderer.Screen(screen_id); const auto& info = renderer.Screen(screen_id);
@ -601,6 +617,9 @@ bool GRenderWindow::InitRenderTarget() {
return false; return false;
} }
break; break;
case Settings::GraphicsAPI::Vulkan:
InitializeVulkan();
break;
} }
// Update the Window System information with the new render target // Update the Window System information with the new render target
@ -686,6 +705,13 @@ bool GRenderWindow::InitializeOpenGL() {
#endif #endif
} }
void GRenderWindow::InitializeVulkan() {
auto child = new VulkanRenderWidget(this);
child_widget = child;
child_widget->windowHandle()->create();
main_context = std::make_unique<DummyContext>();
}
void GRenderWindow::InitializeSoftware() { void GRenderWindow::InitializeSoftware() {
child_widget = new SoftwareRenderWidget(this, system); child_widget = new SoftwareRenderWidget(this, system);
main_context = std::make_unique<DummyContext>(); main_context = std::make_unique<DummyContext>();

View File

@ -187,6 +187,7 @@ private:
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override; void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
bool InitializeOpenGL(); bool InitializeOpenGL();
void InitializeVulkan();
void InitializeSoftware(); void InitializeSoftware();
bool LoadOpenGL(); bool LoadOpenGL();

View File

@ -483,6 +483,7 @@ void Config::ReadDebuggingValues() {
ReadBasicSetting(Settings::values.use_gdbstub); ReadBasicSetting(Settings::values.use_gdbstub);
ReadBasicSetting(Settings::values.gdbstub_port); ReadBasicSetting(Settings::values.gdbstub_port);
ReadBasicSetting(Settings::values.renderer_debug); ReadBasicSetting(Settings::values.renderer_debug);
ReadBasicSetting(Settings::values.dump_command_buffers);
qt_config->beginGroup(QStringLiteral("LLE")); qt_config->beginGroup(QStringLiteral("LLE"));
for (const auto& service_module : Service::service_module_map) { for (const auto& service_module : Service::service_module_map) {
@ -627,6 +628,10 @@ void Config::ReadRendererValues() {
qt_config->beginGroup(QStringLiteral("Renderer")); qt_config->beginGroup(QStringLiteral("Renderer"));
ReadGlobalSetting(Settings::values.graphics_api); ReadGlobalSetting(Settings::values.graphics_api);
ReadGlobalSetting(Settings::values.physical_device);
ReadGlobalSetting(Settings::values.spirv_shader_gen);
ReadGlobalSetting(Settings::values.async_shader_compilation);
ReadGlobalSetting(Settings::values.async_presentation);
ReadGlobalSetting(Settings::values.use_hw_shader); ReadGlobalSetting(Settings::values.use_hw_shader);
ReadGlobalSetting(Settings::values.shaders_accurate_mul); ReadGlobalSetting(Settings::values.shaders_accurate_mul);
ReadGlobalSetting(Settings::values.use_disk_shader_cache); ReadGlobalSetting(Settings::values.use_disk_shader_cache);
@ -1107,6 +1112,10 @@ void Config::SaveRendererValues() {
qt_config->beginGroup(QStringLiteral("Renderer")); qt_config->beginGroup(QStringLiteral("Renderer"));
WriteGlobalSetting(Settings::values.graphics_api); WriteGlobalSetting(Settings::values.graphics_api);
WriteGlobalSetting(Settings::values.physical_device);
WriteGlobalSetting(Settings::values.spirv_shader_gen);
WriteGlobalSetting(Settings::values.async_shader_compilation);
WriteGlobalSetting(Settings::values.async_presentation);
WriteGlobalSetting(Settings::values.use_hw_shader); WriteGlobalSetting(Settings::values.use_hw_shader);
WriteGlobalSetting(Settings::values.shaders_accurate_mul); WriteGlobalSetting(Settings::values.shaders_accurate_mul);
WriteGlobalSetting(Settings::values.use_disk_shader_cache); WriteGlobalSetting(Settings::values.use_disk_shader_cache);

View File

@ -3,6 +3,7 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QDesktopServices> #include <QDesktopServices>
#include <QMessageBox>
#include <QUrl> #include <QUrl>
#include "citra_qt/configuration/configuration_shared.h" #include "citra_qt/configuration/configuration_shared.h"
#include "citra_qt/configuration/configure_debug.h" #include "citra_qt/configuration/configure_debug.h"
@ -12,6 +13,7 @@
#include "common/logging/backend.h" #include "common/logging/backend.h"
#include "common/settings.h" #include "common/settings.h"
#include "ui_configure_debug.h" #include "ui_configure_debug.h"
#include "video_core/renderer_vulkan/vk_instance.h"
// The QSlider doesn't have an easy way to set a custom step amount, // The QSlider doesn't have an easy way to set a custom step amount,
// so we can just convert from the sliders range (0 - 79) to the expected // so we can just convert from the sliders range (0 - 79) to the expected
@ -34,8 +36,39 @@ ConfigureDebug::ConfigureDebug(bool is_powered_on_, QWidget* parent)
QDesktopServices::openUrl(QUrl::fromLocalFile(path)); QDesktopServices::openUrl(QUrl::fromLocalFile(path));
}); });
connect(ui->toggle_renderer_debug, &QCheckBox::clicked, this, [this](bool checked) {
if (checked && Settings::values.graphics_api.GetValue() == Settings::GraphicsAPI::Vulkan) {
try {
Vulkan::Instance debug_inst{true};
} catch (vk::LayerNotPresentError&) {
ui->toggle_renderer_debug->toggle();
QMessageBox::warning(this, tr("Validation layer not available"),
tr("Unable to enable debug renderer because the layer "
"<strong>VK_LAYER_KHRONOS_validation</strong> is missing. "
"Please install the Vulkan SDK or the appropriate package "
"of your distribution"));
}
}
});
connect(ui->toggle_dump_command_buffers, &QCheckBox::clicked, this, [this](bool checked) {
if (checked && Settings::values.graphics_api.GetValue() == Settings::GraphicsAPI::Vulkan) {
try {
Vulkan::Instance debug_inst{false, true};
} catch (vk::LayerNotPresentError&) {
ui->toggle_dump_command_buffers->toggle();
QMessageBox::warning(this, tr("Command buffer dumping not available"),
tr("Unable to enable command buffer dumping because the layer "
"<strong>VK_LAYER_LUNARG_api_dump</strong> is missing. "
"Please install the Vulkan SDK or the appropriate package "
"of your distribution"));
}
}
});
ui->toggle_cpu_jit->setEnabled(!is_powered_on); ui->toggle_cpu_jit->setEnabled(!is_powered_on);
ui->toggle_renderer_debug->setEnabled(!is_powered_on); ui->toggle_renderer_debug->setEnabled(!is_powered_on);
ui->toggle_dump_command_buffers->setEnabled(!is_powered_on);
// Set a minimum width for the label to prevent the slider from changing size. // Set a minimum width for the label to prevent the slider from changing size.
// This scales across DPIs. (This value should be enough for "xxx%") // This scales across DPIs. (This value should be enough for "xxx%")
@ -62,6 +95,7 @@ void ConfigureDebug::SetConfiguration() {
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue())); ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue()));
ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue()); ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue());
ui->toggle_renderer_debug->setChecked(Settings::values.renderer_debug.GetValue()); ui->toggle_renderer_debug->setChecked(Settings::values.renderer_debug.GetValue());
ui->toggle_dump_command_buffers->setChecked(Settings::values.dump_command_buffers.GetValue());
if (!Settings::IsConfiguringGlobal()) { if (!Settings::IsConfiguringGlobal()) {
if (Settings::values.cpu_clock_percentage.UsingGlobal()) { if (Settings::values.cpu_clock_percentage.UsingGlobal()) {
@ -92,6 +126,7 @@ void ConfigureDebug::ApplyConfiguration() {
Common::Log::SetGlobalFilter(filter); Common::Log::SetGlobalFilter(filter);
Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked(); Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked();
Settings::values.renderer_debug = ui->toggle_renderer_debug->isChecked(); Settings::values.renderer_debug = ui->toggle_renderer_debug->isChecked();
Settings::values.dump_command_buffers = ui->toggle_dump_command_buffers->isChecked();
ConfigurationShared::ApplyPerGameSetting( ConfigurationShared::ApplyPerGameSetting(
&Settings::values.cpu_clock_percentage, ui->clock_speed_combo, &Settings::values.cpu_clock_percentage, ui->clock_speed_combo,

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>523</width> <width>523</width>
<height>447</height> <height>458</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -112,16 +112,6 @@
<string>CPU</string> <string>CPU</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="0">
<widget class="QCheckBox" name="toggle_cpu_jit">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enables the use of the ARM JIT compiler for emulating the 3DS CPUs. Don't disable unless for debugging purposes&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable CPU JIT</string>
</property>
</widget>
</item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QWidget" name="clock_speed_widget" native="true"> <widget class="QWidget" name="clock_speed_widget" native="true">
<layout class="QHBoxLayout" name="clock_speed_layout"> <layout class="QHBoxLayout" name="clock_speed_layout">
@ -202,6 +192,16 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="2" column="0">
<widget class="QCheckBox" name="toggle_cpu_jit">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enables the use of the ARM JIT compiler for emulating the 3DS CPUs. Don't disable unless for debugging purposes&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable CPU JIT</string>
</property>
</widget>
</item>
<item row="3" column="0"> <item row="3" column="0">
<widget class="QCheckBox" name="toggle_renderer_debug"> <widget class="QCheckBox" name="toggle_renderer_debug">
<property name="text"> <property name="text">
@ -209,6 +209,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0">
<widget class="QCheckBox" name="toggle_dump_command_buffers">
<property name="text">
<string>Dump command buffers</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@ -23,14 +23,14 @@
#include "ui_configure.h" #include "ui_configure.h"
ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Core::System& system_, ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Core::System& system_,
bool enable_web_config) std::span<const QString> physical_devices, bool enable_web_config)
: QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, registry{registry_}, : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, registry{registry_},
system{system_}, is_powered_on{system.IsPoweredOn()}, system{system_}, is_powered_on{system.IsPoweredOn()},
general_tab{std::make_unique<ConfigureGeneral>(this)}, general_tab{std::make_unique<ConfigureGeneral>(this)},
system_tab{std::make_unique<ConfigureSystem>(system, this)}, system_tab{std::make_unique<ConfigureSystem>(system, this)},
input_tab{std::make_unique<ConfigureInput>(this)}, input_tab{std::make_unique<ConfigureInput>(this)},
hotkeys_tab{std::make_unique<ConfigureHotkeys>(this)}, hotkeys_tab{std::make_unique<ConfigureHotkeys>(this)},
graphics_tab{std::make_unique<ConfigureGraphics>(is_powered_on, this)}, graphics_tab{std::make_unique<ConfigureGraphics>(physical_devices, is_powered_on, this)},
enhancements_tab{std::make_unique<ConfigureEnhancements>(this)}, enhancements_tab{std::make_unique<ConfigureEnhancements>(this)},
audio_tab{std::make_unique<ConfigureAudio>(is_powered_on, this)}, audio_tab{std::make_unique<ConfigureAudio>(is_powered_on, this)},
camera_tab{std::make_unique<ConfigureCamera>(this)}, camera_tab{std::make_unique<ConfigureCamera>(this)},

View File

@ -5,7 +5,9 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <span>
#include <QDialog> #include <QDialog>
#include <QString>
class HotkeyRegistry; class HotkeyRegistry;
@ -35,6 +37,7 @@ class ConfigureDialog : public QDialog {
public: public:
explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, Core::System& system, explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, Core::System& system,
std::span<const QString> physical_devices,
bool enable_web_config = true); bool enable_web_config = true);
~ConfigureDialog() override; ~ConfigureDialog() override;

View File

@ -7,16 +7,34 @@
#include "citra_qt/configuration/configure_graphics.h" #include "citra_qt/configuration/configure_graphics.h"
#include "common/settings.h" #include "common/settings.h"
#include "ui_configure_graphics.h" #include "ui_configure_graphics.h"
#include "video_core/renderer_vulkan/vk_instance.h"
ConfigureGraphics::ConfigureGraphics(bool is_powered_on, QWidget* parent) ConfigureGraphics::ConfigureGraphics(std::span<const QString> physical_devices, bool is_powered_on,
QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureGraphics>()) { : QWidget(parent), ui(std::make_unique<Ui::ConfigureGraphics>()) {
ui->setupUi(this); ui->setupUi(this);
SetupPerGameUI();
for (const QString& name : physical_devices) {
ui->physical_device_combo->addItem(name);
}
ui->toggle_vsync_new->setEnabled(!is_powered_on); ui->toggle_vsync_new->setEnabled(!is_powered_on);
ui->graphics_api_combo->setEnabled(!is_powered_on); ui->graphics_api_combo->setEnabled(!is_powered_on);
ui->physical_device_combo->setEnabled(!is_powered_on);
ui->toggle_async_shaders->setEnabled(!is_powered_on);
ui->toggle_async_present->setEnabled(!is_powered_on);
// Set the index to -1 to ensure the below lambda is called with setCurrentIndex // Set the index to -1 to ensure the below lambda is called with setCurrentIndex
ui->graphics_api_combo->setCurrentIndex(-1); ui->graphics_api_combo->setCurrentIndex(-1);
if (physical_devices.empty()) {
const u32 index = static_cast<u32>(Settings::GraphicsAPI::Vulkan);
ui->graphics_api_combo->removeItem(index);
ui->physical_device_combo->setVisible(false);
ui->spirv_shader_gen->setVisible(false);
}
connect(ui->graphics_api_combo, qOverload<int>(&QComboBox::currentIndexChanged), this, connect(ui->graphics_api_combo, qOverload<int>(&QComboBox::currentIndexChanged), this,
[this](int index) { [this](int index) {
const auto graphics_api = const auto graphics_api =
@ -35,7 +53,9 @@ ConfigureGraphics::ConfigureGraphics(bool is_powered_on, QWidget* parent)
ui->toggle_disk_shader_cache->setEnabled(checked && enabled); ui->toggle_disk_shader_cache->setEnabled(checked && enabled);
}); });
SetupPerGameUI(); connect(ui->graphics_api_combo, qOverload<int>(&QComboBox::currentIndexChanged), this,
&ConfigureGraphics::SetPhysicalDeviceComboVisibility);
SetConfiguration(); SetConfiguration();
} }
@ -47,15 +67,24 @@ void ConfigureGraphics::SetConfiguration() {
!Settings::values.graphics_api.UsingGlobal()); !Settings::values.graphics_api.UsingGlobal());
ConfigurationShared::SetPerGameSetting(ui->graphics_api_combo, ConfigurationShared::SetPerGameSetting(ui->graphics_api_combo,
&Settings::values.graphics_api); &Settings::values.graphics_api);
ConfigurationShared::SetHighlight(ui->physical_device_group,
!Settings::values.physical_device.UsingGlobal());
ConfigurationShared::SetPerGameSetting(ui->physical_device_combo,
&Settings::values.physical_device);
} else { } else {
ui->graphics_api_combo->setCurrentIndex( ui->graphics_api_combo->setCurrentIndex(
static_cast<int>(Settings::values.graphics_api.GetValue())); static_cast<int>(Settings::values.graphics_api.GetValue()));
ui->physical_device_combo->setCurrentIndex(
static_cast<int>(Settings::values.physical_device.GetValue()));
} }
ui->toggle_hw_shader->setChecked(Settings::values.use_hw_shader.GetValue()); ui->toggle_hw_shader->setChecked(Settings::values.use_hw_shader.GetValue());
ui->toggle_accurate_mul->setChecked(Settings::values.shaders_accurate_mul.GetValue()); ui->toggle_accurate_mul->setChecked(Settings::values.shaders_accurate_mul.GetValue());
ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue()); ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new.GetValue()); ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new.GetValue());
ui->spirv_shader_gen->setChecked(Settings::values.spirv_shader_gen.GetValue());
ui->toggle_async_shaders->setChecked(Settings::values.async_shader_compilation.GetValue());
ui->toggle_async_present->setChecked(Settings::values.async_presentation.GetValue());
if (Settings::IsConfiguringGlobal()) { if (Settings::IsConfiguringGlobal()) {
ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit.GetValue()); ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit.GetValue());
@ -65,6 +94,14 @@ void ConfigureGraphics::SetConfiguration() {
void ConfigureGraphics::ApplyConfiguration() { void ConfigureGraphics::ApplyConfiguration() {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.graphics_api, ConfigurationShared::ApplyPerGameSetting(&Settings::values.graphics_api,
ui->graphics_api_combo); ui->graphics_api_combo);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.physical_device,
ui->physical_device_combo);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_shader_compilation,
ui->toggle_async_shaders, async_shader_compilation);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_presentation,
ui->toggle_async_present, async_presentation);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.spirv_shader_gen,
ui->spirv_shader_gen, spirv_shader_gen);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_hw_shader, ui->toggle_hw_shader, ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_hw_shader, ui->toggle_hw_shader,
use_hw_shader); use_hw_shader);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.shaders_accurate_mul, ConfigurationShared::ApplyPerGameSetting(&Settings::values.shaders_accurate_mul,
@ -93,6 +130,11 @@ void ConfigureGraphics::SetupPerGameUI() {
Settings::values.use_disk_shader_cache.UsingGlobal()); Settings::values.use_disk_shader_cache.UsingGlobal());
ui->toggle_vsync_new->setEnabled(ui->toggle_vsync_new->isEnabled() && ui->toggle_vsync_new->setEnabled(ui->toggle_vsync_new->isEnabled() &&
Settings::values.use_vsync_new.UsingGlobal()); Settings::values.use_vsync_new.UsingGlobal());
ui->toggle_async_shaders->setEnabled(
Settings::values.async_shader_compilation.UsingGlobal());
ui->toggle_async_present->setEnabled(Settings::values.async_presentation.UsingGlobal());
ui->graphics_api_combo->setEnabled(Settings::values.graphics_api.UsingGlobal());
ui->physical_device_combo->setEnabled(Settings::values.physical_device.UsingGlobal());
return; return;
} }
@ -102,6 +144,10 @@ void ConfigureGraphics::SetupPerGameUI() {
ui->graphics_api_combo, ui->graphics_api_group, ui->graphics_api_combo, ui->graphics_api_group,
static_cast<u32>(Settings::values.graphics_api.GetValue(true))); static_cast<u32>(Settings::values.graphics_api.GetValue(true)));
ConfigurationShared::SetColoredComboBox(
ui->physical_device_combo, ui->physical_device_group,
static_cast<u32>(Settings::values.physical_device.GetValue(true)));
ConfigurationShared::SetColoredTristate(ui->toggle_hw_shader, Settings::values.use_hw_shader, ConfigurationShared::SetColoredTristate(ui->toggle_hw_shader, Settings::values.use_hw_shader,
use_hw_shader); use_hw_shader);
ConfigurationShared::SetColoredTristate( ConfigurationShared::SetColoredTristate(
@ -111,4 +157,34 @@ void ConfigureGraphics::SetupPerGameUI() {
use_disk_shader_cache); use_disk_shader_cache);
ConfigurationShared::SetColoredTristate(ui->toggle_vsync_new, Settings::values.use_vsync_new, ConfigurationShared::SetColoredTristate(ui->toggle_vsync_new, Settings::values.use_vsync_new,
use_vsync_new); use_vsync_new);
ConfigurationShared::SetColoredTristate(ui->toggle_async_shaders,
Settings::values.async_shader_compilation,
async_shader_compilation);
ConfigurationShared::SetColoredTristate(
ui->toggle_async_present, Settings::values.async_presentation, async_presentation);
ConfigurationShared::SetColoredTristate(ui->spirv_shader_gen, Settings::values.spirv_shader_gen,
spirv_shader_gen);
}
void ConfigureGraphics::SetPhysicalDeviceComboVisibility(int index) {
bool is_visible{};
// When configuring per-game the physical device combo should be
// shown either when the global api is used and that is Vulkan or
// Vulkan is set as the per-game api.
if (!Settings::IsConfiguringGlobal()) {
const auto global_graphics_api = Settings::values.graphics_api.GetValue(true);
const bool using_global = index == 0;
if (!using_global) {
index -= ConfigurationShared::USE_GLOBAL_OFFSET;
}
const auto graphics_api = static_cast<Settings::GraphicsAPI>(index);
is_visible = (using_global && global_graphics_api == Settings::GraphicsAPI::Vulkan) ||
graphics_api == Settings::GraphicsAPI::Vulkan;
} else {
const auto graphics_api = static_cast<Settings::GraphicsAPI>(index);
is_visible = graphics_api == Settings::GraphicsAPI::Vulkan;
}
ui->physical_device_group->setVisible(is_visible);
ui->spirv_shader_gen->setVisible(is_visible);
} }

View File

@ -5,6 +5,8 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <span>
#include <QString>
#include <QWidget> #include <QWidget>
namespace Ui { namespace Ui {
@ -19,7 +21,8 @@ class ConfigureGraphics : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureGraphics(bool is_powered_on, QWidget* parent = nullptr); explicit ConfigureGraphics(std::span<const QString> physical_devices, bool is_powered_on,
QWidget* parent = nullptr);
~ConfigureGraphics() override; ~ConfigureGraphics() override;
void ApplyConfiguration(); void ApplyConfiguration();
@ -30,11 +33,15 @@ public:
private: private:
void SetupPerGameUI(); void SetupPerGameUI();
void SetPhysicalDeviceComboVisibility(int index);
ConfigurationShared::CheckState use_hw_shader; ConfigurationShared::CheckState use_hw_shader;
ConfigurationShared::CheckState shaders_accurate_mul; ConfigurationShared::CheckState shaders_accurate_mul;
ConfigurationShared::CheckState use_disk_shader_cache; ConfigurationShared::CheckState use_disk_shader_cache;
ConfigurationShared::CheckState use_vsync_new; ConfigurationShared::CheckState use_vsync_new;
ConfigurationShared::CheckState async_shader_compilation;
ConfigurationShared::CheckState async_presentation;
ConfigurationShared::CheckState spirv_shader_gen;
std::unique_ptr<Ui::ConfigureGraphics> ui; std::unique_ptr<Ui::ConfigureGraphics> ui;
QColor bg_color; QColor bg_color;
}; };

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>400</width>
<height>443</height> <height>509</height>
</rect> </rect>
</property> </property>
<property name="minimumSize"> <property name="minimumSize">
@ -63,11 +63,51 @@
<string>OpenGL</string> <string>OpenGL</string>
</property> </property>
</item> </item>
<item>
<property name="text">
<string>Vulkan</string>
</property>
</item>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QWidget" name="physical_device_group" native="true">
<layout class="QHBoxLayout" name="physical_device_group_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="physical_device_label">
<property name="text">
<string>Physical Device</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="physical_device_combo"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="spirv_shader_gen">
<property name="text">
<string>SPIR-V Shader Generation</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@ -95,7 +135,7 @@
<item> <item>
<widget class="QCheckBox" name="toggle_hw_shader"> <widget class="QCheckBox" name="toggle_hw_shader">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use OpenGL to accelerate shader emulation.&lt;/p&gt;&lt;p&gt;Requires a relatively powerful GPU for better performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use the selected graphics API to accelerate shader emulation.&lt;/p&gt;&lt;p&gt;Requires a relatively powerful GPU for better performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Enable Hardware Shader</string> <string>Enable Hardware Shader</string>
@ -143,6 +183,26 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="toggle_async_shaders">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Compile shaders using background threads to avoid shader compilation stutter. Expect temporary graphical glitches&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable Async Shader Compilation</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="toggle_async_present">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Perform presentation on separate threads. Improves performance when using Vulkan in most games.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable Async Presentation</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@ -13,7 +13,7 @@
<property name="windowTitle"> <property name="windowTitle">
<string>ConfigureInput</string> <string>ConfigureInput</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_5"> <layout class="QVBoxLayout" name="rootLayout">
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_3"> <layout class="QHBoxLayout" name="horizontalLayout_3">
<item> <item>
@ -62,6 +62,13 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaContents">
<layout class="QVBoxLayout" name="verticalLayout_5">
<item> <item>
<layout class="QGridLayout" name="gridLayout_7"> <layout class="QGridLayout" name="gridLayout_7">
<item row="0" column="0"> <item row="0" column="0">
@ -712,6 +719,10 @@
</item> </item>
</layout> </layout>
</item> </item>
</layout>
</widget>
</widget>
</item>
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout_6"> <layout class="QVBoxLayout" name="verticalLayout_6">
<item> <item>

View File

@ -24,7 +24,7 @@
#include "ui_configure_per_game.h" #include "ui_configure_per_game.h"
ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString& file_name, ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString& file_name,
Core::System& system_) std::span<const QString> physical_devices, Core::System& system_)
: QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()),
filename{file_name.toStdString()}, title_id{title_id_}, system{system_} { filename{file_name.toStdString()}, title_id{title_id_}, system{system_} {
const auto config_file_name = title_id == 0 ? std::string(FileUtil::GetFilename(filename)) const auto config_file_name = title_id == 0 ? std::string(FileUtil::GetFilename(filename))
@ -35,7 +35,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString
audio_tab = std::make_unique<ConfigureAudio>(is_powered_on, this); audio_tab = std::make_unique<ConfigureAudio>(is_powered_on, this);
general_tab = std::make_unique<ConfigureGeneral>(this); general_tab = std::make_unique<ConfigureGeneral>(this);
enhancements_tab = std::make_unique<ConfigureEnhancements>(this); enhancements_tab = std::make_unique<ConfigureEnhancements>(this);
graphics_tab = std::make_unique<ConfigureGraphics>(is_powered_on, this); graphics_tab = std::make_unique<ConfigureGraphics>(physical_devices, is_powered_on, this);
system_tab = std::make_unique<ConfigureSystem>(system, this); system_tab = std::make_unique<ConfigureSystem>(system, this);
debug_tab = std::make_unique<ConfigureDebug>(is_powered_on, this); debug_tab = std::make_unique<ConfigureDebug>(is_powered_on, this);
cheat_tab = std::make_unique<ConfigureCheats>(system, title_id, this); cheat_tab = std::make_unique<ConfigureCheats>(system, title_id, this);

View File

@ -4,9 +4,11 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <span>
#include <string> #include <string>
#include <QDialog> #include <QDialog>
#include <QList> #include <QList>
#include <QString>
#include "citra_qt/configuration/config.h" #include "citra_qt/configuration/config.h"
namespace Core { namespace Core {
@ -35,9 +37,8 @@ class ConfigurePerGame : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
// Cannot use std::filesystem::path due to https://bugreports.qt.io/browse/QTBUG-73263
explicit ConfigurePerGame(QWidget* parent, u64 title_id_, const QString& file_name, explicit ConfigurePerGame(QWidget* parent, u64 title_id_, const QString& file_name,
Core::System& system_); std::span<const QString> physical_devices, Core::System& system_);
~ConfigurePerGame() override; ~ConfigurePerGame() override;
/// Loads all button configurations to settings file /// Loads all button configurations to settings file

View File

@ -551,7 +551,6 @@ void ConfigureSystem::SetupPerGameUI() {
} }
void ConfigureSystem::DownloadFromNUS() { void ConfigureSystem::DownloadFromNUS() {
#ifdef ENABLE_WEB_SERVICE
ui->button_start_download->setEnabled(false); ui->button_start_download->setEnabled(false);
const auto mode = const auto mode =
@ -590,5 +589,4 @@ void ConfigureSystem::DownloadFromNUS() {
} }
ui->button_start_download->setEnabled(true); ui->button_start_download->setEnabled(true);
#endif
} }

View File

@ -64,15 +64,22 @@ void GPUCommandStreamItemModel::OnGXCommandFinishedInternal(int total_command_co
} }
GPUCommandStreamWidget::GPUCommandStreamWidget(QWidget* parent) GPUCommandStreamWidget::GPUCommandStreamWidget(QWidget* parent)
: QDockWidget(tr("Graphics Debugger"), parent) { : QDockWidget(tr("Graphics Debugger"), parent), model(this) {
setObjectName(QStringLiteral("GraphicsDebugger")); setObjectName(QStringLiteral("GraphicsDebugger"));
GPUCommandStreamItemModel* command_model = new GPUCommandStreamItemModel(this); auto* command_list = new QListView;
g_debugger.RegisterObserver(command_model); command_list->setModel(&model);
QListView* command_list = new QListView;
command_list->setModel(command_model);
command_list->setFont(GetMonospaceFont()); command_list->setFont(GetMonospaceFont());
setWidget(command_list); setWidget(command_list);
} }
void GPUCommandStreamWidget::showEvent(QShowEvent* event) {
g_debugger.RegisterObserver(&model);
QDockWidget::showEvent(event);
}
void GPUCommandStreamWidget::hideEvent(QHideEvent* event) {
g_debugger.UnregisterObserver(&model);
QDockWidget::hideEvent(event);
}

View File

@ -37,5 +37,10 @@ class GPUCommandStreamWidget : public QDockWidget {
public: public:
GPUCommandStreamWidget(QWidget* parent = nullptr); GPUCommandStreamWidget(QWidget* parent = nullptr);
protected:
void showEvent(QShowEvent* event) override;
void hideEvent(QHideEvent* event) override;
private: private:
GPUCommandStreamItemModel model;
}; };

View File

@ -62,6 +62,7 @@
#include "citra_qt/uisettings.h" #include "citra_qt/uisettings.h"
#include "citra_qt/updater/updater.h" #include "citra_qt/updater/updater.h"
#include "citra_qt/util/clickable_label.h" #include "citra_qt/util/clickable_label.h"
#include "citra_qt/util/vk_device_info.h"
#include "common/arch.h" #include "common/arch.h"
#include "common/common_paths.h" #include "common/common_paths.h"
#include "common/detached_tasks.h" #include "common/detached_tasks.h"
@ -263,6 +264,14 @@ GMainWindow::GMainWindow(Core::System& system_)
connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor); connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor);
connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::OnMouseActivity); connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::OnMouseActivity);
physical_devices = GetVulkanPhysicalDevices();
if (physical_devices.empty()) {
QMessageBox::warning(this, tr("No Suitable Vulkan Devices Detected"),
tr("Vulkan initialization failed during boot.<br/>"
"Your GPU may not support Vulkan 1.1, or you do not "
"have the latest graphics driver."));
}
#if ENABLE_QT_UPDATER #if ENABLE_QT_UPDATER
if (UISettings::values.check_for_update_on_start) { if (UISettings::values.check_for_update_on_start) {
CheckForUpdates(); CheckForUpdates();
@ -1071,8 +1080,10 @@ bool GMainWindow::LoadROM(const QString& filename) {
ShutdownGame(); ShutdownGame();
} }
render_window->InitRenderTarget(); if (!render_window->InitRenderTarget() || !secondary_window->InitRenderTarget()) {
secondary_window->InitRenderTarget(); LOG_CRITICAL(Frontend, "Failed to initialize render targets!");
return false;
}
const auto scope = render_window->Acquire(); const auto scope = render_window->Acquire();
@ -1420,10 +1431,17 @@ void GMainWindow::UpdateSaveStates() {
actions_save_state[i]->setText(tr("Slot %1").arg(i + 1)); actions_save_state[i]->setText(tr("Slot %1").arg(i + 1));
} }
for (const auto& savestate : savestates) { for (const auto& savestate : savestates) {
const auto text = tr("Slot %1 - %2") const bool display_name =
savestate.status == Core::SaveStateInfo::ValidationStatus::RevisionDismatch &&
!savestate.build_name.empty();
const auto text =
tr("Slot %1 - %2 %3")
.arg(savestate.slot) .arg(savestate.slot)
.arg(QDateTime::fromSecsSinceEpoch(savestate.time) .arg(QDateTime::fromSecsSinceEpoch(savestate.time)
.toString(QStringLiteral("yyyy-MM-dd hh:mm:ss"))); .toString(QStringLiteral("yyyy-MM-dd hh:mm:ss")))
.arg(display_name ? QString::fromStdString(savestate.build_name) : QLatin1String())
.trimmed();
actions_load_state[savestate.slot - 1]->setEnabled(true); actions_load_state[savestate.slot - 1]->setEnabled(true);
actions_load_state[savestate.slot - 1]->setText(text); actions_load_state[savestate.slot - 1]->setText(text);
actions_save_state[savestate.slot - 1]->setText(text); actions_save_state[savestate.slot - 1]->setText(text);
@ -2001,7 +2019,7 @@ void GMainWindow::OnLoadState() {
void GMainWindow::OnConfigure() { void GMainWindow::OnConfigure() {
game_list->SetDirectoryWatcherEnabled(false); game_list->SetDirectoryWatcherEnabled(false);
Settings::SetConfiguringGlobal(true); Settings::SetConfiguringGlobal(true);
ConfigureDialog configureDialog(this, hotkey_registry, system, ConfigureDialog configureDialog(this, hotkey_registry, system, physical_devices,
!multiplayer_state->IsHostingPublicRoom()); !multiplayer_state->IsHostingPublicRoom());
connect(&configureDialog, &ConfigureDialog::LanguageChanged, this, connect(&configureDialog, &ConfigureDialog::LanguageChanged, this,
&GMainWindow::OnLanguageChanged); &GMainWindow::OnLanguageChanged);
@ -2461,9 +2479,11 @@ void GMainWindow::ShowMouseCursor() {
} }
void GMainWindow::UpdateAPIIndicator(bool update) { void GMainWindow::UpdateAPIIndicator(bool update) {
static std::array graphics_apis = {QStringLiteral("SOFTWARE"), QStringLiteral("OPENGL")}; static std::array graphics_apis = {QStringLiteral("SOFTWARE"), QStringLiteral("OPENGL"),
QStringLiteral("VULKAN")};
static std::array graphics_api_colors = {QStringLiteral("#3ae400"), QStringLiteral("#00ccdd")}; static std::array graphics_api_colors = {QStringLiteral("#3ae400"), QStringLiteral("#00ccdd"),
QStringLiteral("#91242a")};
u32 api_index = static_cast<u32>(Settings::values.graphics_api.GetValue()); u32 api_index = static_cast<u32>(Settings::values.graphics_api.GetValue());
if (update) { if (update) {
@ -2755,7 +2775,7 @@ void GMainWindow::OnConfigurePerGame() {
void GMainWindow::OpenPerGameConfiguration(u64 title_id, const QString& file_name) { void GMainWindow::OpenPerGameConfiguration(u64 title_id, const QString& file_name) {
Settings::SetConfiguringGlobal(false); Settings::SetConfiguringGlobal(false);
ConfigurePerGame dialog(this, title_id, file_name, system); ConfigurePerGame dialog(this, title_id, file_name, physical_devices, system);
const auto result = dialog.exec(); const auto result = dialog.exec();
if (result != QDialog::Accepted) { if (result != QDialog::Accepted) {

View File

@ -6,8 +6,10 @@
#include <array> #include <array>
#include <memory> #include <memory>
#include <vector>
#include <QMainWindow> #include <QMainWindow>
#include <QPushButton> #include <QPushButton>
#include <QString>
#include <QTimer> #include <QTimer>
#include <QTranslator> #include <QTranslator>
#include "citra_qt/compatibility_list.h" #include "citra_qt/compatibility_list.h"
@ -326,6 +328,8 @@ private:
// Whether game was paused due to stopping video dumping // Whether game was paused due to stopping video dumping
bool game_paused_for_dumping = false; bool game_paused_for_dumping = false;
std::vector<QString> physical_devices;
// Debugger panes // Debugger panes
ProfilerWidget* profilerWidget; ProfilerWidget* profilerWidget;
MicroProfileDialog* microProfileDialog; MicroProfileDialog* microProfileDialog;

View File

@ -14,6 +14,7 @@
#include "citra_qt/uisettings.h" #include "citra_qt/uisettings.h"
#include "citra_qt/updater/updater.h" #include "citra_qt/updater/updater.h"
#include "citra_qt/updater/updater_p.h" #include "citra_qt/updater/updater_p.h"
#include "common/file_util.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#ifdef Q_OS_OSX #ifdef Q_OS_OSX
@ -110,9 +111,22 @@ QString UpdaterPrivate::ToSystemExe(QString base_path) {
#endif #endif
} }
QFileInfo UpdaterPrivate::GetMaintenanceTool() const {
#if defined(Q_OS_UNIX) && !defined(Q_OS_OSX)
const auto appimage_path = QProcessEnvironment::systemEnvironment()
.value(QStringLiteral("APPIMAGE"), {})
.toStdString();
if (!appimage_path.empty()) {
const auto appimage_dir = FileUtil::GetParentPath(appimage_path);
LOG_DEBUG(Frontend, "Detected app image directory: {}", appimage_dir);
return QFileInfo(QString::fromStdString(std::string(appimage_dir)), tool_path);
}
#endif
return QFileInfo(QCoreApplication::applicationDirPath(), tool_path);
}
bool UpdaterPrivate::HasUpdater() const { bool UpdaterPrivate::HasUpdater() const {
QFileInfo tool_info(QCoreApplication::applicationDirPath(), tool_path); return GetMaintenanceTool().exists();
return tool_info.exists();
} }
bool UpdaterPrivate::StartUpdateCheck() { bool UpdaterPrivate::StartUpdateCheck() {
@ -125,10 +139,9 @@ bool UpdaterPrivate::StartUpdateCheck() {
last_error_code = EXIT_SUCCESS; last_error_code = EXIT_SUCCESS;
last_error_log.clear(); last_error_log.clear();
QFileInfo tool_info(QCoreApplication::applicationDirPath(), tool_path);
main_process = new QProcess(this); main_process = new QProcess(this);
main_process->setProgram(tool_info.absoluteFilePath()); main_process->setProgram(GetMaintenanceTool().absoluteFilePath());
main_process->setArguments({QStringLiteral("--checkupdates"), QStringLiteral("-v")}); main_process->setArguments({QStringLiteral("--checkupdates")});
connect(main_process, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this, connect(main_process, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this,
&UpdaterPrivate::UpdaterReady, Qt::QueuedConnection); &UpdaterPrivate::UpdaterReady, Qt::QueuedConnection);
@ -271,7 +284,7 @@ void UpdaterPrivate::LaunchWithArguments(const QStringList& args) {
return; return;
} }
QFileInfo tool_info(QCoreApplication::applicationDirPath(), tool_path); QFileInfo tool_info = GetMaintenanceTool();
if (!QProcess::startDetached(tool_info.absoluteFilePath(), args, tool_info.absolutePath())) { if (!QProcess::startDetached(tool_info.absoluteFilePath(), args, tool_info.absolutePath())) {
LOG_WARNING(Frontend, "Unable to start program {}", LOG_WARNING(Frontend, "Unable to start program {}",

View File

@ -26,6 +26,7 @@ public:
static QString ToSystemExe(QString base_path); static QString ToSystemExe(QString base_path);
QFileInfo GetMaintenanceTool() const;
bool HasUpdater() const; bool HasUpdater() const;
bool StartUpdateCheck(); bool StartUpdateCheck();

View File

@ -0,0 +1,23 @@
// Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "citra_qt/util/vk_device_info.h"
#include "video_core/renderer_vulkan/vk_instance.h"
std::vector<QString> GetVulkanPhysicalDevices() {
std::vector<QString> result;
try {
Vulkan::Instance instance{};
const auto physical_devices = instance.GetPhysicalDevices();
for (const vk::PhysicalDevice physical_device : physical_devices) {
const QString name = QString::fromUtf8(physical_device.getProperties().deviceName, -1);
result.push_back(name);
}
} catch (const std::runtime_error& err) {
LOG_ERROR(Frontend, "Error occured while querying for physical devices: {}", err.what());
}
return result;
}

View File

@ -0,0 +1,11 @@
// Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include <QString>
/// Returns a list of all available vulkan GPUs.
std::vector<QString> GetVulkanPhysicalDevices();

View File

@ -167,7 +167,6 @@ create_target_directory_groups(citra_common)
target_link_libraries(citra_common PUBLIC fmt::fmt library-headers microprofile Boost::boost Boost::serialization Boost::iostreams) target_link_libraries(citra_common PUBLIC fmt::fmt library-headers microprofile Boost::boost Boost::serialization Boost::iostreams)
target_link_libraries(citra_common PRIVATE libzstd_static) target_link_libraries(citra_common PRIVATE libzstd_static)
set_target_properties(citra_common PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
if ("x86_64" IN_LIST ARCHITECTURE) if ("x86_64" IN_LIST ARCHITECTURE)
target_link_libraries(citra_common PRIVATE xbyak) target_link_libraries(citra_common PRIVATE xbyak)

View File

@ -12,6 +12,10 @@
namespace Common { namespace Common {
DynamicLibrary::DynamicLibrary() = default;
DynamicLibrary::DynamicLibrary(void* handle_) : handle{handle_} {}
DynamicLibrary::DynamicLibrary(std::string_view name, int major, int minor) { DynamicLibrary::DynamicLibrary(std::string_view name, int major, int minor) {
auto full_name = GetLibraryName(name, major, minor); auto full_name = GetLibraryName(name, major, minor);
void(Load(full_name)); void(Load(full_name));

View File

@ -11,6 +11,7 @@ namespace Common {
class DynamicLibrary { class DynamicLibrary {
public: public:
explicit DynamicLibrary(); explicit DynamicLibrary();
explicit DynamicLibrary(void* handle);
explicit DynamicLibrary(std::string_view name, int major = -1, int minor = -1); explicit DynamicLibrary(std::string_view name, int major = -1, int minor = -1);
~DynamicLibrary(); ~DynamicLibrary();

View File

@ -16,6 +16,7 @@
#include "common/common_paths.h" #include "common/common_paths.h"
#include "common/file_util.h" #include "common/file_util.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scope_exit.h"
#include "common/string_util.h" #include "common/string_util.h"
#ifdef _WIN32 #ifdef _WIN32
@ -324,31 +325,32 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
return AndroidStorage::CopyFile(srcFilename, std::string(GetParentPath(destFilename)), return AndroidStorage::CopyFile(srcFilename, std::string(GetParentPath(destFilename)),
std::string(GetFilename(destFilename))); std::string(GetFilename(destFilename)));
#else #else
using CFilePointer = std::unique_ptr<FILE, decltype(&std::fclose)>;
// Open input file // Open input file
CFilePointer input{fopen(srcFilename.c_str(), "rb"), std::fclose}; FILE* input = fopen(srcFilename.c_str(), "rb");
if (!input) { if (!input) {
LOG_ERROR(Common_Filesystem, "opening input failed {} --> {}: {}", srcFilename, LOG_ERROR(Common_Filesystem, "opening input failed {} --> {}: {}", srcFilename,
destFilename, GetLastErrorMsg()); destFilename, GetLastErrorMsg());
return false; return false;
} }
SCOPE_EXIT({ fclose(input); });
// open output file // open output file
CFilePointer output{fopen(destFilename.c_str(), "wb"), std::fclose}; FILE* output = fopen(destFilename.c_str(), "wb");
if (!output) { if (!output) {
LOG_ERROR(Common_Filesystem, "opening output failed {} --> {}: {}", srcFilename, LOG_ERROR(Common_Filesystem, "opening output failed {} --> {}: {}", srcFilename,
destFilename, GetLastErrorMsg()); destFilename, GetLastErrorMsg());
return false; return false;
} }
SCOPE_EXIT({ fclose(output); });
// copy loop // copy loop
std::array<char, 1024> buffer; std::array<char, 1024> buffer;
while (!feof(input.get())) { while (!feof(input)) {
// read input // read input
std::size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input.get()); std::size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input);
if (rnum != buffer.size()) { if (rnum != buffer.size()) {
if (ferror(input.get()) != 0) { if (ferror(input) != 0) {
LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}", LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}",
srcFilename, destFilename, GetLastErrorMsg()); srcFilename, destFilename, GetLastErrorMsg());
return false; return false;
@ -356,7 +358,7 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
} }
// write output // write output
std::size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output.get()); std::size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output);
if (wnum != rnum) { if (wnum != rnum) {
LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename, LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename,
destFilename, GetLastErrorMsg()); destFilename, GetLastErrorMsg());

View File

@ -37,8 +37,8 @@ static inline u64 ComputeStructHash64(const T& data) noexcept {
* Combines the seed parameter with the provided hash, producing a new unique hash * Combines the seed parameter with the provided hash, producing a new unique hash
* Implementation from: http://boost.sourceforge.net/doc/html/boost/hash_combine.html * Implementation from: http://boost.sourceforge.net/doc/html/boost/hash_combine.html
*/ */
inline u64 HashCombine(std::size_t& seed, const u64 hash) { inline u64 HashCombine(std::size_t seed, const u64 hash) {
return seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2); return seed ^ (hash + 0x9e3779b9 + (seed << 6) + (seed >> 2));
} }
template <typename T> template <typename T>

View File

@ -31,6 +31,8 @@ std::string_view GetGraphicsAPIName(GraphicsAPI api) {
return "Software"; return "Software";
case GraphicsAPI::OpenGL: case GraphicsAPI::OpenGL:
return "OpenGL"; return "OpenGL";
case GraphicsAPI::Vulkan:
return "Vulkan";
default: default:
return "Invalid"; return "Invalid";
} }
@ -72,6 +74,9 @@ void LogSettings() {
log_setting("Core_CPUClockPercentage", values.cpu_clock_percentage.GetValue()); log_setting("Core_CPUClockPercentage", values.cpu_clock_percentage.GetValue());
log_setting("Renderer_UseGLES", values.use_gles.GetValue()); log_setting("Renderer_UseGLES", values.use_gles.GetValue());
log_setting("Renderer_GraphicsAPI", GetGraphicsAPIName(values.graphics_api.GetValue())); log_setting("Renderer_GraphicsAPI", GetGraphicsAPIName(values.graphics_api.GetValue()));
log_setting("Renderer_AsyncShaders", values.async_shader_compilation.GetValue());
log_setting("Renderer_AsyncPresentation", values.async_presentation.GetValue());
log_setting("Renderer_SpirvShaderGen", values.spirv_shader_gen.GetValue());
log_setting("Renderer_Debug", values.renderer_debug.GetValue()); log_setting("Renderer_Debug", values.renderer_debug.GetValue());
log_setting("Renderer_UseHwShader", values.use_hw_shader.GetValue()); log_setting("Renderer_UseHwShader", values.use_hw_shader.GetValue());
log_setting("Renderer_ShadersAccurateMul", values.shaders_accurate_mul.GetValue()); log_setting("Renderer_ShadersAccurateMul", values.shaders_accurate_mul.GetValue());
@ -159,6 +164,10 @@ void RestoreGlobalState(bool is_powered_on) {
// Renderer // Renderer
values.graphics_api.SetGlobal(true); values.graphics_api.SetGlobal(true);
values.physical_device.SetGlobal(true);
values.spirv_shader_gen.SetGlobal(true);
values.async_shader_compilation.SetGlobal(true);
values.async_presentation.SetGlobal(true);
values.use_hw_shader.SetGlobal(true); values.use_hw_shader.SetGlobal(true);
values.use_disk_shader_cache.SetGlobal(true); values.use_disk_shader_cache.SetGlobal(true);
values.shaders_accurate_mul.SetGlobal(true); values.shaders_accurate_mul.SetGlobal(true);

View File

@ -17,10 +17,10 @@
namespace Settings { namespace Settings {
constexpr u32 GraphicsAPICount = 2;
enum class GraphicsAPI { enum class GraphicsAPI {
Software = 0, Software = 0,
OpenGL = 1, OpenGL = 1,
Vulkan = 2,
}; };
enum class InitClock : u32 { enum class InitClock : u32 {
@ -430,12 +430,15 @@ struct Values {
Setting<bool> allow_plugin_loader{true, "allow_plugin_loader"}; Setting<bool> allow_plugin_loader{true, "allow_plugin_loader"};
// Renderer // Renderer
SwitchableSetting<GraphicsAPI, true> graphics_api{ SwitchableSetting<GraphicsAPI, true> graphics_api{GraphicsAPI::OpenGL, GraphicsAPI::Software,
GraphicsAPI::OpenGL, GraphicsAPI::Software, static_cast<GraphicsAPI>(GraphicsAPICount - 1), GraphicsAPI::Vulkan, "graphics_api"};
"graphics_api"}; SwitchableSetting<u32> physical_device{0, "physical_device"};
Setting<bool> use_gles{false, "use_gles"}; Setting<bool> use_gles{false, "use_gles"};
Setting<bool> renderer_debug{false, "renderer_debug"}; Setting<bool> renderer_debug{false, "renderer_debug"};
Setting<bool> dump_command_buffers{false, "dump_command_buffers"}; Setting<bool> dump_command_buffers{false, "dump_command_buffers"};
SwitchableSetting<bool> spirv_shader_gen{true, "spirv_shader_gen"};
SwitchableSetting<bool> async_shader_compilation{false, "async_shader_compilation"};
SwitchableSetting<bool> async_presentation{true, "async_presentation"};
SwitchableSetting<bool> use_hw_shader{true, "use_hw_shader"}; SwitchableSetting<bool> use_hw_shader{true, "use_hw_shader"};
SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
SwitchableSetting<bool> shaders_accurate_mul{true, "shaders_accurate_mul"}; SwitchableSetting<bool> shaders_accurate_mul{true, "shaders_accurate_mul"};

View File

@ -458,11 +458,14 @@ add_library(citra_core STATIC
mmio.h mmio.h
movie.cpp movie.cpp
movie.h movie.h
nus_download.cpp
nus_download.h
perf_stats.cpp perf_stats.cpp
perf_stats.h perf_stats.h
precompiled_headers.h precompiled_headers.h
savestate.cpp savestate.cpp
savestate.h savestate.h
savestate_data.h
system_titles.cpp system_titles.cpp
system_titles.h system_titles.h
telemetry_session.cpp telemetry_session.cpp
@ -477,7 +480,6 @@ create_target_directory_groups(citra_core)
target_link_libraries(citra_core PUBLIC citra_common PRIVATE audio_core network video_core) target_link_libraries(citra_core PUBLIC citra_common PRIVATE audio_core network video_core)
target_link_libraries(citra_core PRIVATE Boost::boost Boost::serialization Boost::iostreams httplib) target_link_libraries(citra_core PRIVATE Boost::boost Boost::serialization Boost::iostreams httplib)
target_link_libraries(citra_core PUBLIC dds-ktx PRIVATE cryptopp fmt::fmt lodepng open_source_archives) target_link_libraries(citra_core PUBLIC dds-ktx PRIVATE cryptopp fmt::fmt lodepng open_source_archives)
set_target_properties(citra_core PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
if (ENABLE_WEB_SERVICE) if (ENABLE_WEB_SERVICE)
target_link_libraries(citra_core PRIVATE web_service) target_link_libraries(citra_core PRIVATE web_service)

View File

@ -365,7 +365,9 @@ void ARM_Dynarmic::ServeBreak() {
std::unique_ptr<Dynarmic::A32::Jit> ARM_Dynarmic::MakeJit() { std::unique_ptr<Dynarmic::A32::Jit> ARM_Dynarmic::MakeJit() {
Dynarmic::A32::UserConfig config; Dynarmic::A32::UserConfig config;
config.callbacks = cb.get(); config.callbacks = cb.get();
if (current_page_table) {
config.page_table = &current_page_table->GetPointerArray(); config.page_table = &current_page_table->GetPointerArray();
}
config.coprocessors[15] = std::make_shared<DynarmicCP15>(cp15_state); config.coprocessors[15] = std::make_shared<DynarmicCP15>(cp15_state);
config.define_unpredictable_behaviour = true; config.define_unpredictable_behaviour = true;

View File

@ -12,6 +12,10 @@
#include "core/3ds.h" #include "core/3ds.h"
#include "core/frontend/framebuffer_layout.h" #include "core/frontend/framebuffer_layout.h"
namespace Common {
class DynamicLibrary;
}
namespace Frontend { namespace Frontend {
/// Information for the Graphics Backends signifying what type of screen pointer is in /// Information for the Graphics Backends signifying what type of screen pointer is in
@ -82,6 +86,11 @@ public:
/// Releases (dunno if this is the "right" word) the context from the caller thread /// Releases (dunno if this is the "right" word) the context from the caller thread
virtual void DoneCurrent(){}; virtual void DoneCurrent(){};
/// Gets the GPU driver library (used by Android only)
virtual std::shared_ptr<Common::DynamicLibrary> GetDriverLibrary() {
return {};
}
class Scoped { class Scoped {
public: public:
explicit Scoped(GraphicsContext& context_) : context(context_) { explicit Scoped(GraphicsContext& context_) : context(context_) {

View File

@ -151,6 +151,17 @@ void Module::Interface::RegisterDisconnectEvent(Kernel::HLERequestContext& ctx)
LOG_WARNING(Service_AC, "(STUBBED) called"); LOG_WARNING(Service_AC, "(STUBBED) called");
} }
void Module::Interface::GetConnectingProxyEnable(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
constexpr bool proxy_enabled = false;
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push(proxy_enabled);
LOG_WARNING(Service_AC, "(STUBBED) called");
}
void Module::Interface::IsConnected(Kernel::HLERequestContext& ctx) { void Module::Interface::IsConnected(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx); IPC::RequestParser rp(ctx);
u32 unk = rp.Pop<u32>(); u32 unk = rp.Pop<u32>();

View File

@ -120,6 +120,14 @@ public:
*/ */
void RegisterDisconnectEvent(Kernel::HLERequestContext& ctx); void RegisterDisconnectEvent(Kernel::HLERequestContext& ctx);
/**
* AC::GetConnectingProxyEnable service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : bool, is proxy enabled
*/
void GetConnectingProxyEnable(Kernel::HLERequestContext& ctx);
/** /**
* AC::IsConnected service function * AC::IsConnected service function
* Outputs: * Outputs:

View File

@ -27,6 +27,7 @@ AC_I::AC_I(std::shared_ptr<Module> ac) : Module::Interface(std::move(ac), "ac:i"
{0x0027, &AC_I::GetInfraPriority, "GetInfraPriority"}, {0x0027, &AC_I::GetInfraPriority, "GetInfraPriority"},
{0x002D, &AC_I::SetRequestEulaVersion, "SetRequestEulaVersion"}, {0x002D, &AC_I::SetRequestEulaVersion, "SetRequestEulaVersion"},
{0x0030, &AC_I::RegisterDisconnectEvent, "RegisterDisconnectEvent"}, {0x0030, &AC_I::RegisterDisconnectEvent, "RegisterDisconnectEvent"},
{0x0036, &AC_I::GetConnectingProxyEnable, "GetConnectingProxyEnable"},
{0x003C, nullptr, "GetAPSSIDList"}, {0x003C, nullptr, "GetAPSSIDList"},
{0x003E, &AC_I::IsConnected, "IsConnected"}, {0x003E, &AC_I::IsConnected, "IsConnected"},
{0x0040, &AC_I::SetClientVersion, "SetClientVersion"}, {0x0040, &AC_I::SetClientVersion, "SetClientVersion"},

View File

@ -27,6 +27,7 @@ AC_U::AC_U(std::shared_ptr<Module> ac) : Module::Interface(std::move(ac), "ac:u"
{0x0027, &AC_U::GetInfraPriority, "GetInfraPriority"}, {0x0027, &AC_U::GetInfraPriority, "GetInfraPriority"},
{0x002D, &AC_U::SetRequestEulaVersion, "SetRequestEulaVersion"}, {0x002D, &AC_U::SetRequestEulaVersion, "SetRequestEulaVersion"},
{0x0030, &AC_U::RegisterDisconnectEvent, "RegisterDisconnectEvent"}, {0x0030, &AC_U::RegisterDisconnectEvent, "RegisterDisconnectEvent"},
{0x0036, &AC_U::GetConnectingProxyEnable, "GetConnectingProxyEnable"},
{0x003C, nullptr, "GetAPSSIDList"}, {0x003C, nullptr, "GetAPSSIDList"},
{0x003E, &AC_U::IsConnected, "IsConnected"}, {0x003E, &AC_U::IsConnected, "IsConnected"},
{0x0040, &AC_U::SetClientVersion, "SetClientVersion"}, {0x0040, &AC_U::SetClientVersion, "SetClientVersion"},

View File

@ -31,9 +31,7 @@
#include "core/hle/service/fs/fs_user.h" #include "core/hle/service/fs/fs_user.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "core/loader/smdh.h" #include "core/loader/smdh.h"
#ifdef ENABLE_WEB_SERVICE #include "core/nus_download.h"
#include "web_service/nus_download.h"
#endif
namespace Service::AM { namespace Service::AM {
@ -463,7 +461,6 @@ InstallStatus InstallCIA(const std::string& path,
} }
InstallStatus InstallFromNus(u64 title_id, int version) { InstallStatus InstallFromNus(u64 title_id, int version) {
#ifdef ENABLE_WEB_SERVICE
LOG_DEBUG(Service_AM, "Downloading {:X}", title_id); LOG_DEBUG(Service_AM, "Downloading {:X}", title_id);
CIAFile install_file{GetTitleMediaType(title_id)}; CIAFile install_file{GetTitleMediaType(title_id)};
@ -472,7 +469,7 @@ InstallStatus InstallFromNus(u64 title_id, int version) {
if (version != -1) { if (version != -1) {
path += fmt::format(".{}", version); path += fmt::format(".{}", version);
} }
auto tmd_response = WebService::NUS::Download(path); auto tmd_response = Core::NUS::Download(path);
if (!tmd_response) { if (!tmd_response) {
LOG_ERROR(Service_AM, "Failed to download tmd for {:016X}", title_id); LOG_ERROR(Service_AM, "Failed to download tmd for {:016X}", title_id);
return InstallStatus::ErrorFileNotFound; return InstallStatus::ErrorFileNotFound;
@ -481,7 +478,7 @@ InstallStatus InstallFromNus(u64 title_id, int version) {
tmd.Load(*tmd_response); tmd.Load(*tmd_response);
path = fmt::format("/ccs/download/{:016X}/cetk", title_id); path = fmt::format("/ccs/download/{:016X}/cetk", title_id);
auto cetk_response = WebService::NUS::Download(path); auto cetk_response = Core::NUS::Download(path);
if (!cetk_response) { if (!cetk_response) {
LOG_ERROR(Service_AM, "Failed to download cetk for {:016X}", title_id); LOG_ERROR(Service_AM, "Failed to download cetk for {:016X}", title_id);
return InstallStatus::ErrorFileNotFound; return InstallStatus::ErrorFileNotFound;
@ -492,7 +489,7 @@ InstallStatus InstallFromNus(u64 title_id, int version) {
for (std::size_t i = 0; i < content_count; ++i) { for (std::size_t i = 0; i < content_count; ++i) {
const std::string filename = fmt::format("{:08x}", tmd.GetContentIDByIndex(i)); const std::string filename = fmt::format("{:08x}", tmd.GetContentIDByIndex(i));
path = fmt::format("/ccs/download/{:016X}/{}", title_id, filename); path = fmt::format("/ccs/download/{:016X}/{}", title_id, filename);
const auto temp_response = WebService::NUS::Download(path); const auto temp_response = Core::NUS::Download(path);
if (!temp_response) { if (!temp_response) {
LOG_ERROR(Service_AM, "Failed to download content for {:016X}", title_id); LOG_ERROR(Service_AM, "Failed to download content for {:016X}", title_id);
return InstallStatus::ErrorFileNotFound; return InstallStatus::ErrorFileNotFound;
@ -550,9 +547,6 @@ InstallStatus InstallFromNus(u64 title_id, int version) {
return result; return result;
} }
return InstallStatus::Success; return InstallStatus::Success;
#else
return InstallStatus::ErrorFileNotFound;
#endif
} }
u64 GetTitleUpdateId(u64 title_id) { u64 GetTitleUpdateId(u64 title_id) {

View File

@ -620,9 +620,9 @@ void Module::Interface::SetData(Kernel::HLERequestContext& ctx) {
void Module::Interface::ReadData(Kernel::HLERequestContext& ctx) { void Module::Interface::ReadData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx); IPC::RequestParser rp(ctx);
const u32 dest_buffer_size = rp.Pop<u32>(); const auto dest_buffer_size = rp.Pop<u32>();
const CecSystemInfoType info_type = rp.PopEnum<CecSystemInfoType>(); const auto info_type = rp.PopEnum<CecSystemInfoType>();
const u32 param_buffer_size = rp.Pop<u32>(); const auto param_buffer_size = rp.Pop<u32>();
auto& param_buffer = rp.PopMappedBuffer(); auto& param_buffer = rp.PopMappedBuffer();
auto& dest_buffer = rp.PopMappedBuffer(); auto& dest_buffer = rp.PopMappedBuffer();
@ -631,22 +631,23 @@ void Module::Interface::ReadData(Kernel::HLERequestContext& ctx) {
std::vector<u8> buffer; std::vector<u8> buffer;
switch (info_type) { switch (info_type) {
case CecSystemInfoType::EulaVersion: { case CecSystemInfoType::EulaVersion: {
auto cfg = Service::CFG::GetModule(cecd->system); const auto cfg = Service::CFG::GetModule(cecd->system);
Service::CFG::EULAVersion version = cfg->GetEULAVersion(); const auto version = cfg->GetEULAVersion();
dest_buffer.Write(&version, 0, sizeof(version)); buffer = {version.minor, version.major};
break; break;
} }
case CecSystemInfoType::Eula: case CecSystemInfoType::Eula:
buffer = {0x01}; // Eula agreed buffer = {true}; // Eula agreed
dest_buffer.Write(buffer.data(), 0, buffer.size());
break; break;
case CecSystemInfoType::ParentControl: case CecSystemInfoType::ParentControl:
buffer = {0x00}; // No parent control buffer = {false}; // No parent control
dest_buffer.Write(buffer.data(), 0, buffer.size());
break; break;
default: default:
LOG_ERROR(Service_CECD, "Unknown system info type={:#x}", info_type); LOG_ERROR(Service_CECD, "Unknown system info type={:#x}", info_type);
buffer = {};
} }
dest_buffer.Write(buffer.data(), 0,
std::min(static_cast<size_t>(dest_buffer_size), buffer.size()));
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
rb.PushMappedBuffer(param_buffer); rb.PushMappedBuffer(param_buffer);

View File

@ -309,7 +309,7 @@ void DSP_DSP::ForceHeadphoneOut(Kernel::HLERequestContext& ctx) {
// that's waiting for an interrupt event. Immediately after this interrupt event, userland // that's waiting for an interrupt event. Immediately after this interrupt event, userland
// normally updates the state in the next region and increments the relevant frame counter by two. // normally updates the state in the next region and increments the relevant frame counter by two.
void DSP_DSP::SignalInterrupt(InterruptType type, DspPipe pipe) { void DSP_DSP::SignalInterrupt(InterruptType type, DspPipe pipe) {
LOG_DEBUG(Service_DSP, "called, type={}, pipe={}", type, pipe); LOG_TRACE(Service_DSP, "called, type={}, pipe={}", type, pipe);
const auto& event = GetInterruptEvent(type, pipe); const auto& event = GetInterruptEvent(type, pipe);
if (event) if (event)
event->Signal(); event->Signal();

View File

@ -336,31 +336,31 @@ void GSP_GPU::SetBufferSwap(Kernel::HLERequestContext& ctx) {
void GSP_GPU::FlushDataCache(Kernel::HLERequestContext& ctx) { void GSP_GPU::FlushDataCache(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx); IPC::RequestParser rp(ctx);
u32 address = rp.Pop<u32>(); [[maybe_unused]] u32 address = rp.Pop<u32>();
u32 size = rp.Pop<u32>(); [[maybe_unused]] u32 size = rp.Pop<u32>();
auto process = rp.PopObject<Kernel::Process>(); [[maybe_unused]] auto process = rp.PopObject<Kernel::Process>();
// TODO(purpasmart96): Verify return header on HW // TODO(purpasmart96): Verify return header on HW
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_GSP, "(STUBBED) called address=0x{:08X}, size=0x{:08X}, process={}", address, LOG_TRACE(Service_GSP, "(STUBBED) called address=0x{:08X}, size=0x{:08X}, process={}", address,
size, process->process_id); size, process->process_id);
} }
void GSP_GPU::InvalidateDataCache(Kernel::HLERequestContext& ctx) { void GSP_GPU::InvalidateDataCache(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx); IPC::RequestParser rp(ctx);
u32 address = rp.Pop<u32>(); [[maybe_unused]] u32 address = rp.Pop<u32>();
u32 size = rp.Pop<u32>(); [[maybe_unused]] u32 size = rp.Pop<u32>();
auto process = rp.PopObject<Kernel::Process>(); [[maybe_unused]] auto process = rp.PopObject<Kernel::Process>();
// TODO(purpasmart96): Verify return header on HW // TODO(purpasmart96): Verify return header on HW
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_GSP, "(STUBBED) called address=0x{:08X}, size=0x{:08X}, process={}", address, LOG_TRACE(Service_GSP, "(STUBBED) called address=0x{:08X}, size=0x{:08X}, process={}", address,
size, process->process_id); size, process->process_id);
} }

View File

@ -267,6 +267,7 @@ void HTTP_C::ReceiveDataImpl(Kernel::HLERequestContext& ctx, bool timeout) {
} else { } else {
LOG_WARNING(Service_HTTP, "(STUBBED) called"); LOG_WARNING(Service_HTTP, "(STUBBED) called");
} }
[[maybe_unused]] Kernel::MappedBuffer& buffer = rp.PopMappedBuffer();
if (!PerformStateChecks(ctx, rp, context_handle)) { if (!PerformStateChecks(ctx, rp, context_handle)) {
return; return;

View File

@ -316,7 +316,7 @@ void Module::Interface::GetTagInfo2(Kernel::HLERequestContext& ctx) {
if (nfc->nfc_mode == CommunicationMode::TrainTag) { if (nfc->nfc_mode == CommunicationMode::TrainTag) {
LOG_ERROR(Service_NFC, "CommunicationMode {} not implemented", nfc->nfc_mode); LOG_ERROR(Service_NFC, "CommunicationMode {} not implemented", nfc->nfc_mode);
IPC::RequestBuilder rb = rp.MakeBuilder(26, 0); IPC::RequestBuilder rb = rp.MakeBuilder(25, 0);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
rb.PushRaw<TagInfo2>({}); rb.PushRaw<TagInfo2>({});
return; return;
@ -324,7 +324,7 @@ void Module::Interface::GetTagInfo2(Kernel::HLERequestContext& ctx) {
TagInfo2 tag_info{}; TagInfo2 tag_info{};
const auto result = nfc->device->GetTagInfo2(tag_info); const auto result = nfc->device->GetTagInfo2(tag_info);
IPC::RequestBuilder rb = rp.MakeBuilder(26, 0); IPC::RequestBuilder rb = rp.MakeBuilder(25, 0);
rb.Push(result); rb.Push(result);
rb.PushRaw<TagInfo2>(tag_info); rb.PushRaw<TagInfo2>(tag_info);
} }
@ -383,10 +383,14 @@ void Module::Interface::OpenApplicationArea(Kernel::HLERequestContext& ctx) {
void Module::Interface::CreateApplicationArea(Kernel::HLERequestContext& ctx) { void Module::Interface::CreateApplicationArea(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx); IPC::RequestParser rp(ctx);
u32 access_id = rp.Pop<u32>(); u32 access_id = rp.Pop<u32>();
[[maybe_unused]] u32 size = rp.Pop<u32>(); u32 size = rp.Pop<u32>();
std::vector<u8> buffer = rp.PopStaticBuffer(); std::vector<u8> buffer = rp.PopStaticBuffer();
LOG_CRITICAL(Service_NFC, "called, size={}", size); LOG_INFO(Service_NFC, "called, size={}", size);
if (buffer.size() > size) {
buffer.resize(size);
}
if (nfc->nfc_mode != CommunicationMode::Amiibo) { if (nfc->nfc_mode != CommunicationMode::Amiibo) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
@ -402,8 +406,9 @@ void Module::Interface::CreateApplicationArea(Kernel::HLERequestContext& ctx) {
void Module::Interface::ReadApplicationArea(Kernel::HLERequestContext& ctx) { void Module::Interface::ReadApplicationArea(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx); IPC::RequestParser rp(ctx);
u32 size = rp.Pop<u32>();
LOG_INFO(Service_NFC, "called"); LOG_INFO(Service_NFC, "called, size={}", size);
nfc->device->RescheduleTagRemoveEvent(); nfc->device->RescheduleTagRemoveEvent();
@ -413,7 +418,7 @@ void Module::Interface::ReadApplicationArea(Kernel::HLERequestContext& ctx) {
return; return;
} }
std::vector<u8> buffer(sizeof(ApplicationArea)); std::vector<u8> buffer(size);
const auto result = nfc->device->GetApplicationArea(buffer); const auto result = nfc->device->GetApplicationArea(buffer);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
@ -423,11 +428,15 @@ void Module::Interface::ReadApplicationArea(Kernel::HLERequestContext& ctx) {
void Module::Interface::WriteApplicationArea(Kernel::HLERequestContext& ctx) { void Module::Interface::WriteApplicationArea(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx); IPC::RequestParser rp(ctx);
[[maybe_unused]] u32 size = rp.Pop<u32>(); u32 size = rp.Pop<u32>();
std::vector<u8> tag_uuid_info = rp.PopStaticBuffer(); std::vector<u8> tag_uuid_info = rp.PopStaticBuffer();
std::vector<u8> buffer = rp.PopStaticBuffer(); std::vector<u8> buffer = rp.PopStaticBuffer();
LOG_CRITICAL(Service_NFC, "called, size={}", size); LOG_INFO(Service_NFC, "called, size={}", size);
if (buffer.size() > size) {
buffer.resize(size);
}
if (nfc->nfc_mode != CommunicationMode::Amiibo) { if (nfc->nfc_mode != CommunicationMode::Amiibo) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
@ -540,7 +549,7 @@ void Module::Interface::GetIdentificationBlock(Kernel::HLERequestContext& ctx) {
ModelInfo model_info{}; ModelInfo model_info{};
const auto result = nfc->device->GetModelInfo(model_info); const auto result = nfc->device->GetModelInfo(model_info);
IPC::RequestBuilder rb = rp.MakeBuilder(0x1F, 0); IPC::RequestBuilder rb = rp.MakeBuilder(14, 0);
rb.Push(result); rb.Push(result);
rb.PushRaw<ModelInfo>(model_info); rb.PushRaw<ModelInfo>(model_info);
} }

View File

@ -1101,8 +1101,8 @@ void NfcDevice::BuildAmiiboWithoutKeys() {
} }
void NfcDevice::RescheduleTagRemoveEvent() { void NfcDevice::RescheduleTagRemoveEvent() {
/// The interval at which the amiibo will be removed automatically 1.5s /// The interval at which the amiibo will be removed automatically 3s
static constexpr u64 amiibo_removal_interval = nsToCycles(1500 * 1000 * 1000); static constexpr u64 amiibo_removal_interval = msToCycles(3 * 1000);
system.CoreTiming().UnscheduleEvent(remove_amiibo_event, 0); system.CoreTiming().UnscheduleEvent(remove_amiibo_event, 0);

View File

@ -128,7 +128,7 @@ static void MemoryFill(const Regs::MemoryFillConfig& config) {
static void DisplayTransfer(const Regs::DisplayTransferConfig& config) { static void DisplayTransfer(const Regs::DisplayTransferConfig& config) {
const PAddr src_addr = config.GetPhysicalInputAddress(); const PAddr src_addr = config.GetPhysicalInputAddress();
const PAddr dst_addr = config.GetPhysicalOutputAddress(); PAddr dst_addr = config.GetPhysicalOutputAddress();
// TODO: do hwtest with these cases // TODO: do hwtest with these cases
if (!g_memory->IsValidPhysicalAddress(src_addr)) { if (!g_memory->IsValidPhysicalAddress(src_addr)) {
@ -164,6 +164,14 @@ static void DisplayTransfer(const Regs::DisplayTransferConfig& config) {
if (VideoCore::g_renderer->Rasterizer()->AccelerateDisplayTransfer(config)) if (VideoCore::g_renderer->Rasterizer()->AccelerateDisplayTransfer(config))
return; return;
// Using flip_vertically alongside crop_input_lines produces skewed output on hardware.
// We have to emulate this because some games rely on this behaviour to render correctly.
if (config.flip_vertically && config.crop_input_lines &&
config.input_width > config.output_width) {
dst_addr += (config.input_width - config.output_width) * (config.output_height - 1) *
GPU::Regs::BytesPerPixel(config.output_format);
}
u8* src_pointer = g_memory->GetPhysicalPointer(src_addr); u8* src_pointer = g_memory->GetPhysicalPointer(src_addr);
u8* dst_pointer = g_memory->GetPhysicalPointer(dst_addr); u8* dst_pointer = g_memory->GetPhysicalPointer(dst_addr);

View File

@ -5,9 +5,9 @@
#include <memory> #include <memory>
#include <httplib.h> #include <httplib.h>
#include "common/logging/log.h" #include "common/logging/log.h"
#include "web_service/nus_download.h" #include "core/nus_download.h"
namespace WebService::NUS { namespace Core::NUS {
std::optional<std::vector<u8>> Download(const std::string& path) { std::optional<std::vector<u8>> Download(const std::string& path) {
constexpr auto HOST = "http://nus.cdn.c.shop.nintendowifi.net"; constexpr auto HOST = "http://nus.cdn.c.shop.nintendowifi.net";
@ -46,4 +46,4 @@ std::optional<std::vector<u8>> Download(const std::string& path) {
return std::vector<u8>(response.body.begin(), response.body.end()); return std::vector<u8>(response.body.begin(), response.body.end());
} }
} // namespace WebService::NUS } // namespace Core::NUS

View File

@ -8,8 +8,8 @@
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
namespace WebService::NUS { namespace Core::NUS {
std::optional<std::vector<u8>> Download(const std::string& path); std::optional<std::vector<u8>> Download(const std::string& path);
} } // namespace Core::NUS

View File

@ -15,6 +15,7 @@
#include "core/core.h" #include "core/core.h"
#include "core/movie.h" #include "core/movie.h"
#include "core/savestate.h" #include "core/savestate.h"
#include "core/savestate_data.h"
#include "network/network.h" #include "network/network.h"
namespace Core { namespace Core {
@ -25,8 +26,10 @@ struct CSTHeader {
u64_le program_id; /// ID of the ROM being executed. Also called title_id u64_le program_id; /// ID of the ROM being executed. Also called title_id
std::array<u8, 20> revision; /// Git hash of the revision this savestate was created with std::array<u8, 20> revision; /// Git hash of the revision this savestate was created with
u64_le time; /// The time when this save state was created u64_le time; /// The time when this save state was created
std::array<u8, 20> build_name; /// The build name (Canary/Nightly) with the version number
u32_le zero = 0; /// Should be zero, just in case.
std::array<u8, 216> reserved{}; /// Make heading 256 bytes so it has consistent size std::array<u8, 192> reserved{}; /// Make heading 256 bytes so it has consistent size
}; };
static_assert(sizeof(CSTHeader) == 256, "CSTHeader should be 256 bytes"); static_assert(sizeof(CSTHeader) == 256, "CSTHeader should be 256 bytes");
#pragma pack(pop) #pragma pack(pop)
@ -58,11 +61,26 @@ static bool ValidateSaveState(const CSTHeader& header, SaveStateInfo& info, u64
return false; return false;
} }
const std::string revision = fmt::format("{:02x}", fmt::join(header.revision, "")); const std::string revision = fmt::format("{:02x}", fmt::join(header.revision, ""));
const std::string build_name =
header.zero == 0 ? reinterpret_cast<const char*>(header.build_name.data()) : "";
if (revision == Common::g_scm_rev) { if (revision == Common::g_scm_rev) {
info.status = SaveStateInfo::ValidationStatus::OK; info.status = SaveStateInfo::ValidationStatus::OK;
} else { } else {
if (!build_name.empty()) {
info.build_name = build_name;
} else if (hash_to_version.find(revision) != hash_to_version.end()) {
info.build_name = hash_to_version.at(revision);
}
if (info.build_name.empty()) {
LOG_WARNING(Core, "Save state file {} created from a different revision {}", path, LOG_WARNING(Core, "Save state file {} created from a different revision {}", path,
revision); revision);
} else {
LOG_WARNING(Core,
"Save state file {} created from a different build {} with revision {}",
path, info.build_name, revision);
}
info.status = SaveStateInfo::ValidationStatus::RevisionDismatch; info.status = SaveStateInfo::ValidationStatus::RevisionDismatch;
} }
return true; return true;
@ -134,6 +152,10 @@ void System::SaveState(u32 slot) const {
header.time = std::chrono::duration_cast<std::chrono::seconds>( header.time = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()) std::chrono::system_clock::now().time_since_epoch())
.count(); .count();
const std::string build_fullname = Common::g_build_fullname;
std::memset(header.build_name.data(), 0, sizeof(header.build_name));
std::memcpy(header.build_name.data(), build_fullname.c_str(),
std::min(build_fullname.length(), sizeof(header.build_name) - 1));
if (file.WriteBytes(&header, sizeof(header)) != sizeof(header) || if (file.WriteBytes(&header, sizeof(header)) != sizeof(header) ||
file.WriteBytes(buffer.data(), buffer.size()) != buffer.size()) { file.WriteBytes(buffer.data(), buffer.size()) != buffer.size()) {

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include <string>
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
@ -16,6 +17,7 @@ struct SaveStateInfo {
OK, OK,
RevisionDismatch, RevisionDismatch,
} status; } status;
std::string build_name;
}; };
constexpr u32 SaveStateSlotCount = 10; // Maximum count of savestate slots constexpr u32 SaveStateSlotCount = 10; // Maximum count of savestate slots

1427
src/core/savestate_data.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -43,7 +43,6 @@ endif()
create_target_directory_groups(input_common) create_target_directory_groups(input_common)
target_link_libraries(input_common PUBLIC citra_core PRIVATE citra_common ${Boost_LIBRARIES}) target_link_libraries(input_common PUBLIC citra_core PRIVATE citra_common ${Boost_LIBRARIES})
set_target_properties(input_common PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
if (CITRA_USE_PRECOMPILED_HEADERS) if (CITRA_USE_PRECOMPILED_HEADERS)
target_precompile_headers(input_common PRIVATE precompiled_headers.h) target_precompile_headers(input_common PRIVATE precompiled_headers.h)

View File

@ -23,7 +23,6 @@ if (ENABLE_WEB_SERVICE)
endif() endif()
target_link_libraries(network PRIVATE citra_common enet Boost::serialization httplib) target_link_libraries(network PRIVATE citra_common enet Boost::serialization httplib)
set_target_properties(network PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
if (CITRA_USE_PRECOMPILED_HEADERS) if (CITRA_USE_PRECOMPILED_HEADERS)
target_precompile_headers(network PRIVATE precompiled_headers.h) target_precompile_headers(network PRIVATE precompiled_headers.h)

View File

@ -101,18 +101,47 @@ add_library(video_core STATIC
renderer_software/sw_texturing.cpp renderer_software/sw_texturing.cpp
renderer_software/sw_texturing.h renderer_software/sw_texturing.h
renderer_vulkan/pica_to_vk.h renderer_vulkan/pica_to_vk.h
renderer_vulkan/renderer_vulkan.cpp
renderer_vulkan/renderer_vulkan.h
renderer_vulkan/vk_blit_helper.cpp
renderer_vulkan/vk_blit_helper.h
renderer_vulkan/vk_common.cpp renderer_vulkan/vk_common.cpp
renderer_vulkan/vk_common.h renderer_vulkan/vk_common.h
renderer_vulkan/vk_descriptor_pool.cpp
renderer_vulkan/vk_descriptor_pool.h
renderer_vulkan/vk_graphics_pipeline.cpp
renderer_vulkan/vk_graphics_pipeline.h
renderer_vulkan/vk_master_semaphore.cpp
renderer_vulkan/vk_master_semaphore.h
renderer_vulkan/vk_rasterizer.cpp
renderer_vulkan/vk_rasterizer.h
renderer_vulkan/vk_rasterizer_cache.cpp
renderer_vulkan/vk_scheduler.cpp
renderer_vulkan/vk_scheduler.h
renderer_vulkan/vk_resource_pool.cpp
renderer_vulkan/vk_resource_pool.h
renderer_vulkan/vk_instance.cpp renderer_vulkan/vk_instance.cpp
renderer_vulkan/vk_instance.h renderer_vulkan/vk_instance.h
renderer_vulkan/vk_pipeline_cache.cpp
renderer_vulkan/vk_pipeline_cache.h
renderer_vulkan/vk_platform.cpp renderer_vulkan/vk_platform.cpp
renderer_vulkan/vk_platform.h renderer_vulkan/vk_platform.h
renderer_vulkan/vk_present_window.cpp
renderer_vulkan/vk_present_window.h
renderer_vulkan/vk_renderpass_cache.cpp
renderer_vulkan/vk_renderpass_cache.h
renderer_vulkan/vk_shader_gen.cpp renderer_vulkan/vk_shader_gen.cpp
renderer_vulkan/vk_shader_gen.h renderer_vulkan/vk_shader_gen.h
renderer_vulkan/vk_shader_gen_spv.cpp renderer_vulkan/vk_shader_gen_spv.cpp
renderer_vulkan/vk_shader_gen_spv.h renderer_vulkan/vk_shader_gen_spv.h
renderer_vulkan/vk_shader_util.cpp renderer_vulkan/vk_shader_util.cpp
renderer_vulkan/vk_shader_util.h renderer_vulkan/vk_shader_util.h
renderer_vulkan/vk_stream_buffer.cpp
renderer_vulkan/vk_stream_buffer.h
renderer_vulkan/vk_swapchain.cpp
renderer_vulkan/vk_swapchain.h
renderer_vulkan/vk_texture_runtime.cpp
renderer_vulkan/vk_texture_runtime.h
shader/debug_data.h shader/debug_data.h
shader/shader.cpp shader/shader.cpp
shader/shader.h shader/shader.h
@ -143,7 +172,6 @@ create_target_directory_groups(video_core)
target_link_libraries(video_core PUBLIC citra_common citra_core) target_link_libraries(video_core PUBLIC citra_common citra_core)
target_link_libraries(video_core PRIVATE Boost::serialization dds-ktx json-headers nihstro-headers tsl::robin_map) target_link_libraries(video_core PRIVATE Boost::serialization dds-ktx json-headers nihstro-headers tsl::robin_map)
target_link_libraries(video_core PRIVATE vulkan-headers vma glad sirit SPIRV glslang) target_link_libraries(video_core PRIVATE vulkan-headers vma glad sirit SPIRV glslang)
set_target_properties(video_core PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
if ("x86_64" IN_LIST ARCHITECTURE) if ("x86_64" IN_LIST ARCHITECTURE)
target_link_libraries(video_core PUBLIC xbyak) target_link_libraries(video_core PUBLIC xbyak)

View File

@ -27,9 +27,12 @@ set(SHADER_FILES
vulkan_blit_depth_stencil.frag vulkan_blit_depth_stencil.frag
) )
find_program(GLSLANGVALIDATOR "glslangValidator") find_program(GLSLANG "glslang")
if ("${GLSLANGVALIDATOR}" STREQUAL "GLSLANGVALIDATOR-NOTFOUND") if ("${GLSLANG}" STREQUAL "GLSLANG-NOTFOUND")
message(FATAL_ERROR "Required program `glslangValidator` not found.") find_program(GLSLANG "glslangValidator")
if ("${GLSLANG}" STREQUAL "GLSLANG-NOTFOUND")
message(FATAL_ERROR "Required program `glslang` (or `glslangValidator`) not found.")
endif()
endif() endif()
set(MACROS "-Dgl_VertexID=gl_VertexIndex") set(MACROS "-Dgl_VertexID=gl_VertexIndex")
@ -42,11 +45,11 @@ set(HOST_SHADERS_INCLUDE ${SHADER_INCLUDE} PARENT_SCOPE)
set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in) set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in)
set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake) set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake)
# Check if `--quiet` is available on host's glslangValidator version # Check if `--quiet` is available on host's glslang version
# glslangValidator prints to STDERR iff an unrecognized flag is passed to it # glslang prints to STDERR iff an unrecognized flag is passed to it
execute_process( execute_process(
COMMAND COMMAND
${GLSLANGVALIDATOR} ${QUIET_FLAG} ${GLSLANG} ${QUIET_FLAG}
ERROR_VARIABLE ERROR_VARIABLE
GLSLANG_ERROR GLSLANG_ERROR
# STDOUT variable defined to silence unnecessary output during CMake configuration # STDOUT variable defined to silence unnecessary output during CMake configuration
@ -55,7 +58,7 @@ execute_process(
) )
if (NOT GLSLANG_ERROR STREQUAL "") if (NOT GLSLANG_ERROR STREQUAL "")
message(WARNING "Refusing to use unavailable flag `${QUIET_FLAG}` on `${GLSLANGVALIDATOR}`") message(WARNING "Refusing to use unavailable flag `${QUIET_FLAG}` on `${GLSLANG}`")
set(QUIET_FLAG "") set(QUIET_FLAG "")
endif() endif()
@ -87,7 +90,7 @@ foreach(FILENAME IN ITEMS ${SHADER_FILES})
OUTPUT OUTPUT
${SPIRV_HEADER_FILE} ${SPIRV_HEADER_FILE}
COMMAND COMMAND
${GLSLANGVALIDATOR} --target-env vulkan1.1 --glsl-version 450 ${QUIET_FLAG} ${MACROS} --variable-name ${SPIRV_VARIABLE_NAME} -o ${SPIRV_HEADER_FILE} ${SOURCE_FILE} ${GLSLANG} --target-env vulkan1.1 --glsl-version 450 ${QUIET_FLAG} ${MACROS} --variable-name ${SPIRV_VARIABLE_NAME} -o ${SPIRV_HEADER_FILE} ${SOURCE_FILE}
MAIN_DEPENDENCY MAIN_DEPENDENCY
${SOURCE_FILE} ${SOURCE_FILE}
) )

View File

@ -12,15 +12,17 @@ layout(set = 0, binding = 2, rgba8) uniform highp writeonly image2D color;
layout(push_constant, std140) uniform ComputeInfo { layout(push_constant, std140) uniform ComputeInfo {
mediump ivec2 src_offset; mediump ivec2 src_offset;
mediump ivec2 dst_offset;
mediump ivec2 extent; mediump ivec2 extent;
}; };
void main() { void main() {
ivec2 tex_coord = src_offset + ivec2(gl_GlobalInvocationID.xy); ivec2 src_coord = src_offset + ivec2(gl_GlobalInvocationID.xy);
ivec2 dst_coord = dst_offset + ivec2(gl_GlobalInvocationID.xy);
highp uint depth_val = highp uint depth_val =
uint(texelFetch(depth, tex_coord, 0).x * (exp2(32.0) - 1.0)); uint(texelFetch(depth, src_coord, 0).x * (exp2(32.0) - 1.0));
lowp uint stencil_val = texelFetch(stencil, tex_coord, 0).x; lowp uint stencil_val = texelFetch(stencil, src_coord, 0).x;
highp uvec4 components = highp uvec4 components =
uvec4(stencil_val, (uvec3(depth_val) >> uvec3(24u, 16u, 8u)) & 0x000000FFu); uvec4(stencil_val, (uvec3(depth_val) >> uvec3(24u, 16u, 8u)) & 0x000000FFu);
imageStore(color, tex_coord, vec4(components) / (exp2(8.0) - 1.0)); imageStore(color, dst_coord, vec4(components) / (exp2(8.0) - 1.0));
} }

View File

@ -14,6 +14,7 @@ layout(binding = 2) writeonly buffer OutputBuffer{
layout(push_constant, std140) uniform ComputeInfo { layout(push_constant, std140) uniform ComputeInfo {
mediump ivec2 src_offset; mediump ivec2 src_offset;
mediump ivec2 dst_offset;
mediump ivec2 extent; mediump ivec2 extent;
}; };

View File

@ -244,6 +244,11 @@ void RasterizerAccelerated::NotifyPicaRegisterChanged(u32 id) {
} }
break; break;
// Fragment operation mode
case PICA_REG_INDEX(framebuffer.output_merger.fragment_operation_mode):
shader_dirty = true;
break;
// Alpha test // Alpha test
case PICA_REG_INDEX(framebuffer.output_merger.alpha_test): case PICA_REG_INDEX(framebuffer.output_merger.alpha_test):
SyncAlphaTest(); SyncAlphaTest();
@ -599,6 +604,17 @@ void RasterizerAccelerated::NotifyPicaRegisterChanged(u32 id) {
SyncTextureLodBias(2); SyncTextureLodBias(2);
break; break;
// Texture borders
case PICA_REG_INDEX(texturing.texture0.border_color):
SyncTextureBorderColor(0);
break;
case PICA_REG_INDEX(texturing.texture1.border_color):
SyncTextureBorderColor(1);
break;
case PICA_REG_INDEX(texturing.texture2.border_color):
SyncTextureBorderColor(2);
break;
// Clipping plane // Clipping plane
case PICA_REG_INDEX(rasterizer.clip_coef[0]): case PICA_REG_INDEX(rasterizer.clip_coef[0]):
case PICA_REG_INDEX(rasterizer.clip_coef[1]): case PICA_REG_INDEX(rasterizer.clip_coef[1]):
@ -606,11 +622,10 @@ void RasterizerAccelerated::NotifyPicaRegisterChanged(u32 id) {
case PICA_REG_INDEX(rasterizer.clip_coef[3]): case PICA_REG_INDEX(rasterizer.clip_coef[3]):
SyncClipCoef(); SyncClipCoef();
break; break;
}
default:
// Forward registers that map to fixed function API features to the video backend // Forward registers that map to fixed function API features to the video backend
NotifyFixedFunctionPicaRegisterChanged(id); NotifyFixedFunctionPicaRegisterChanged(id);
}
} }
void RasterizerAccelerated::SyncDepthScale() { void RasterizerAccelerated::SyncDepthScale() {
@ -821,6 +836,16 @@ void RasterizerAccelerated::SyncTextureLodBias(int tex_index) {
} }
} }
void RasterizerAccelerated::SyncTextureBorderColor(int tex_index) {
const auto pica_textures = regs.texturing.GetTextures();
const auto params = pica_textures[tex_index].config;
const Common::Vec4f border_color = ColorRGBA8(params.border_color.raw);
if (border_color != uniform_block_data.data.tex_border_color[tex_index]) {
uniform_block_data.data.tex_border_color[tex_index] = border_color;
uniform_block_data.dirty = true;
}
}
void RasterizerAccelerated::SyncClipCoef() { void RasterizerAccelerated::SyncClipCoef() {
const auto raw_clip_coef = regs.rasterizer.GetClipCoef(); const auto raw_clip_coef = regs.rasterizer.GetClipCoef();
const Common::Vec4f new_clip_coef = {raw_clip_coef.x.ToFloat32(), raw_clip_coef.y.ToFloat32(), const Common::Vec4f new_clip_coef = {raw_clip_coef.x.ToFloat32(), raw_clip_coef.y.ToFloat32(),

Some files were not shown because too many files have changed in this diff Show More