Compare commits

..

123 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
6d0cd5b00e build: Expose ENABLE_SCRIPTING and ENABLE_WEB_SERVICE flags as public. (#6872) 2023-08-07 03:12:49 -07:00
5b52849f90 rasterizer_cache: Allow custom recycle (#6851)
* surface_params: Allow custom surface recycling

* rasterizer_cache: Cleanup
2023-08-06 22:59:54 +03:00
20f4677f80 qt: Fix dark themes not showing alternating row icons with Qt 6.5.2 (#6862) 2023-08-06 12:24:03 -07:00
0048e61fc7 Fix compilation without ENABLE_WEB_SERVICE (#6856) 2023-08-06 12:23:53 -07:00
aaeba6759e citra_qt: do not pass memory to widget (#6849) 2023-08-04 16:35:52 -07:00
ebac2e4978 custom_tex_manager: Allow old hash in more cases (#6843) 2023-08-03 00:51:10 +03:00
5b9f4d4129 SOC: Fix setting TTL to default value when TTL = 0 (#6835) 2023-08-02 13:06:51 -07:00
b1b6f08926 citra_qt: fix two graphics configuration bugs (#6840) 2023-08-02 21:38:57 +02:00
7e6a761f07 cmake: fix USE_SYSTEM_BOOST behavior ... (#6837) 2023-08-02 12:20:35 -07:00
6f7612f73d Res cache fixes (#6838)
* rasterizer_cache: Dont consider res_scale during recycle

* rasterizer_cache: Switch to plain erase loop

* rasterizer_cache: Fix crash due to memory corruption
2023-08-02 21:38:45 +03:00
88ea66053e Miscallenious fixes to gl backend and qt frontend (#6834)
* renderer_gl: Make rasterizer normal class member

* It doesn't need to be heap allocated anymore

* gl_rasterizer: Remove default_texture

* It's unused

* gl_rasterizer: General cleanup

* gl_rasterizer: Lower case lambdas

* Match style with review comments from vulkan backend

* rasterizer_cache: Prevent memory leak

* Since the switch from shared_ptr these surfaces were no longer being destroyed properly. Use our garbage collector for that purpose to destroy it safely for both backends

* rasterizer_cache: Make temp copy of old surface

* The custom surface would override the memory region of the old region resulting in garbage data, this ensures the custom surface is constructed correctly

* citra_qt: Manually create dialog tabs

* Allows for custom constructors which is very useful. While at it, global state is now eliminated from configuration

* citra_qt: Eliminate global system usage

* core: Remove global system usage in memory and HIO

* citra_qt: Use qOverload

* tests: Run clang format

* gl_texture_runtime: Fix surface scaling
2023-08-02 01:40:39 +03:00
970f2284d8 http/soc: Various implementations and fixes (#6828) 2023-08-02 00:37:56 +02:00
baf3ea4beb custom_tex_manager: Allow old hash in the dumper (#6832) 2023-08-01 20:38:51 +03:00
35e208b447 Move MiiData to its own namespace and add ChecksummedMiiData (#6824)
* Move mii to own namespace and add checksummed mii data

* Fix compile issues

* Make mii classes trivial and add cast operator

* Fix Android side

* Add new line at the end of files.

* Make miidata a struct and crc16 a u32_be as per switch code.

* Apply suggestions

* Change back crc to u16 and set padding to 0.
2023-07-31 20:38:47 -07:00
f8b8b6e53c core: De-globalize movie (#6659) 2023-08-01 02:57:38 +02:00
a955f02771 rasterizer_cache: Remove runtime allocation caching (#6705)
* rasterizer_cache: Sentence surfaces

* gl_texture_runtime: Remove runtime side allocation cache

* rasterizer_cache: Adjust surface scale during reinterpreration

* Fixes pixelated outlines. Also allows to remove the d24s8 specific hack and is more generic in general

* rasterizer_cache: Remove Expand flag

* Begone!

* rasterizer_cache: Cache framebuffers with surface id

* rasterizer_cache: Sentence texture cubes

* renderer_opengl: Move texture mailbox to separate file

* Makes renderer_opengl cleaner overall and allows to report removal threshold from runtime instead of hardcoding. Vulkan requires this

* rasterizer_cache: Dont flush cache on layout change

* rasterizer_cache: Overhaul framebuffer management

* video_core: Remove duplicate

* rasterizer_cache: Sentence custom surfaces

* Vulkan cannot destroy images immediately so this ensures we use our garbage collector for that purpose
2023-08-01 03:35:41 +03:00
3fedc68230 common: Only use libbacktrace if present. (#6827) 2023-07-31 14:24:27 -07:00
335fb78c5c service/am: Clean up and optimize CIA installation. (#6718) 2023-07-30 21:40:35 +02:00
22c4eb86d7 core/frd: Correct FunctionInfo and stub more functions (#6209) 2023-07-30 04:18:52 +02:00
964f9ee3cf service/gsp: Implement saving of framebuffers in SaveVramSysArea. (#6821)
* service/gsp: Implement saving of framebuffers in SaveVramSysArea.

* Address review comments.

* service/apt: Separate capture info and capture buffer info.

The former is used with the RequestForSysApplet message and GetCaptureInfo.
The latter is used with SendCaptureBufferInfo and ReceiveCaptureBufferInfo.
2023-07-29 00:26:24 -07:00
bb364d9bc0 service/apt: Add and implement more service commands. (#6721)
* service/apt: Add and implement more service commands.

* service/apt: Implement power button.

* Address review comments and fix GetApplicationRunningMode bug.
2023-07-29 00:26:16 -07:00
51996c54f0 audio_core\hle\adts_reader.cpp: Use BitField to parse ADTS header (#6719) 2023-07-28 12:15:58 -07:00
539a1a0b6e service/cfg: Separate out config block defaults and create when not found. (#6716) 2023-07-26 10:33:00 -07:00
8b21b902f2 hw/y2r: Templatize input/output formats. (#6717) 2023-07-25 05:51:57 -07:00
19107cec4b citra-qt: update the separate window titles to use full_name and show Primary/Secondary identifier (#6712)
* citra-qt: update the separate window titles to use full_name and Primary/Secondary window identifier

* build_fullname: remove trailing space
2023-07-25 05:51:46 -07:00
a537f56766 services/cfg: Clean up definitions and access flag handling. (#6711) 2023-07-21 18:04:15 -07:00
b5e1a27a7e rasterizer_cache: Handle texture cubes with duplicate faces (#6710) 2023-07-21 01:55:19 +03:00
a9e390b1b1 frontend: Fix clicking primary screen window touching screen. (#6707) 2023-07-20 15:48:50 -07:00
71582a72a4 sdl: Check correct windowID based on event type. (#6703) 2023-07-19 01:29:13 -07:00
e783b0d4a9 rasterizer_cache: Fixes to (unaligned) texture downloads (#6697)
* rasterizer_cache: Header cleanup

* gl_texture_runtime: Fix incorrect stride in single scanline downloads

* texture_codec: Fix unaligned texture downloads
2023-07-18 17:31:31 +03:00
700c00f021 audio_core/hle: Stub binary requests SaveState and LoadState. (#6684)
* audio_core/hle: Stub binary requests SaveState and LoadState.

* audio_core/hle: Fire DSP binary pipe interrupt on request completion instead of tick.
2023-07-16 17:54:40 -07:00
9cb14044ec kernel: Improvements to process cleanup. (#6680)
* kernel: Properly clean up process threads on exit.

* kernel: Track process-owned memory and free on destruction.

* apt: Implement DoApplicationJump via home menu when available.

* kernel: Move TLS allocation management to owning process.
2023-07-16 17:54:29 -07:00
8b6b58a364 y2r: Pass ConversionConfiguration by copying. (#6690) 2023-07-15 01:57:06 -07:00
e043caac27 hle: Eliminate need to specify command headers for IPC. (#6678) 2023-07-14 17:32:59 -07:00
0bedb28bdc rpc: Fix class hierarchy. (#6694) 2023-07-13 16:29:54 -07:00
c10ffda91f android: Restore notification permission request (#6693)
When we targeted API <32, the notification permission would automatically be requested on startup. This restores that behavior temporarily while we work on new UX.
2023-07-12 18:54:21 -07:00
5e8ae4fa8a qt: Fix per-game config loading for 3DSX/ELF. (#6691)
Co-authored-by: Michael Theall <pigman46@gmail.com>
2023-07-12 18:54:11 -07:00
7a7f485640 core: Cleanup RPC (#6674) 2023-07-12 18:54:02 -07:00
bbf833bceb citra_qt\game_list: Make columns hideable (#6467) 2023-07-12 23:17:16 +02:00
8eebb83c2c service: nfc: Keep tag alive while it's being used (#6687) 2023-07-12 14:01:08 -07:00
427 changed files with 22431 additions and 8220 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)
@ -57,6 +59,7 @@ CMAKE_DEPENDENT_OPTION(ENABLE_TESTS "Enable generating tests executable" ON "NOT
CMAKE_DEPENDENT_OPTION(ENABLE_DEDICATED_ROOM "Enable generating dedicated room executable" ON "NOT ANDROID AND NOT IOS" OFF) CMAKE_DEPENDENT_OPTION(ENABLE_DEDICATED_ROOM "Enable generating dedicated room executable" ON "NOT ANDROID AND NOT IOS" OFF)
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
option(ENABLE_SCRIPTING "Enable RPC server for scripting" ON)
CMAKE_DEPENDENT_OPTION(ENABLE_CUBEB "Enables the cubeb audio backend" ON "NOT IOS" OFF) CMAKE_DEPENDENT_OPTION(ENABLE_CUBEB "Enables the cubeb audio backend" ON "NOT IOS" OFF)
option(ENABLE_OPENAL "Enables the OpenAL audio backend" ON) option(ENABLE_OPENAL "Enables the OpenAL audio backend" ON)
@ -68,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)
@ -219,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)
@ -341,13 +346,6 @@ function(get_timestamp _var)
set(${_var} "${timestamp}" PARENT_SCOPE) set(${_var} "${timestamp}" PARENT_SCOPE)
endfunction() endfunction()
# Prevent boost from linking against libs when building
add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY
-DBOOST_SYSTEM_NO_LIB
-DBOOST_DATE_TIME_NO_LIB
-DBOOST_REGEX_NO_LIB
)
# generate git/build information # generate git/build information
include(GetGitRevisionDescription) include(GetGitRevisionDescription)
get_git_head_revision(GIT_REF_SPEC GIT_REV) get_git_head_revision(GIT_REF_SPEC GIT_REV)
@ -355,17 +353,23 @@ git_describe(GIT_DESC --always --long --dirty)
git_branch_name(GIT_BRANCH) git_branch_name(GIT_BRANCH)
get_timestamp(BUILD_DATE) get_timestamp(BUILD_DATE)
if (NOT USE_SYSTEM_BOOST) # Boost
add_definitions( -DBOOST_ALL_NO_LIB ) # Prevent boost from linking against libs when building
add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY
-DBOOST_SYSTEM_NO_LIB
-DBOOST_DATE_TIME_NO_LIB
-DBOOST_REGEX_NO_LIB
)
if (USE_SYSTEM_BOOST)
find_package(Boost 1.70.0 COMPONENTS container locale serialization iostreams REQUIRED)
endif() endif()
enable_testing() enable_testing()
add_subdirectory(externals) add_subdirectory(externals)
# Boost # Boost (bundled)
if (USE_SYSTEM_BOOST) if (NOT USE_SYSTEM_BOOST)
find_package(Boost 1.70.0 COMPONENTS serialization iostreams REQUIRED) add_definitions( -DBOOST_ALL_NO_LIB )
else()
add_library(Boost::boost ALIAS boost) add_library(Boost::boost ALIAS boost)
add_library(Boost::serialization ALIAS boost_serialization) add_library(Boost::serialization ALIAS boost_serialization)
add_library(Boost::iostreams ALIAS boost_iostreams) add_library(Boost::iostreams ALIAS boost_iostreams)
@ -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,31 +1,38 @@
# 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 (MINGW) if (NOT DOWNLOAD_QT_TOOL)
set(arch "win64_mingw") if (MINGW)
set(arch_path "mingw_64") set(arch "win64_mingw")
elseif (MSVC) set(arch_path "mingw_64")
if ("arm64" IN_LIST ARCHITECTURE) elseif (MSVC)
set(arch_path "msvc2019_arm64") if ("arm64" IN_LIST ARCHITECTURE)
elseif ("x86_64" IN_LIST ARCHITECTURE) set(arch_path "msvc2019_arm64")
set(arch_path "msvc2019_64") elseif ("x86_64" IN_LIST ARCHITECTURE)
set(arch_path "msvc2019_64")
else()
message(FATAL_ERROR "Unsupported bundled Qt architecture. Enable USE_SYSTEM_QT and provide your own.")
endif()
set(arch "win64_${arch_path}")
else() else()
message(FATAL_ERROR "Unsupported bundled Qt architecture. 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()
set(arch "win64_${arch_path}")
else()
message(FATAL_ERROR "Unsupported bundled Qt toolchain. Enable USE_SYSTEM_QT and provide your own.")
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

@ -57,9 +57,7 @@ if (DEFINED ENV{CI})
set(BUILD_VERSION ${CMAKE_MATCH_1}) set(BUILD_VERSION ${CMAKE_MATCH_1})
endif() endif()
if (BUILD_VERSION) if (BUILD_VERSION)
# This leaves a trailing space on the last word, but we actually want that set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION}")
# because of how it's styled in the title bar.
set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ")
else() else()
set(BUILD_FULLNAME "") set(BUILD_FULLNAME "")
endif() endif()

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

@ -298,6 +298,11 @@ QAbstractItemView:read-only {
alternate-background-color: #232629; alternate-background-color: #232629;
} }
/* Workaround for https://bugreports.qt.io/browse/QTBUG-115529 */
QAbstractItemView:item {
border: 0px;
}
QWidget:focus { QWidget:focus {
border: 1px solid #3daee9; border: 1px solid #3daee9;
} }

View File

@ -481,6 +481,11 @@ QAbstractItemView QLineEdit {
padding: 2px; padding: 2px;
} }
/* Workaround for https://bugreports.qt.io/browse/QTBUG-115529 */
QAbstractItemView:item {
border: 0px;
}
/* QAbstractScrollArea ---------------------------------------------------- /* QAbstractScrollArea ----------------------------------------------------
https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qabstractscrollarea https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qabstractscrollarea
@ -1067,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

@ -12,27 +12,29 @@ include(DownloadExternals)
include(ExternalProject) include(ExternalProject)
# Boost # Boost
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "") if (NOT USE_SYSTEM_BOOST)
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "") message(STATUS "Including vendored Boost library")
set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "") set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
add_library(boost INTERFACE) set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR}) set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "")
add_library(boost INTERFACE)
target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR})
# Boost::serialization # Boost::serialization
file(GLOB boost_serialization_SRC "${CMAKE_SOURCE_DIR}/externals/boost/libs/serialization/src/*.cpp") file(GLOB boost_serialization_SRC "${CMAKE_SOURCE_DIR}/externals/boost/libs/serialization/src/*.cpp")
add_library(boost_serialization STATIC ${boost_serialization_SRC}) add_library(boost_serialization STATIC ${boost_serialization_SRC})
target_link_libraries(boost_serialization PUBLIC boost) target_link_libraries(boost_serialization PUBLIC boost)
# Boost::iostreams
add_library(
boost_iostreams
STATIC
${CMAKE_SOURCE_DIR}/externals/boost/libs/iostreams/src/file_descriptor.cpp
${CMAKE_SOURCE_DIR}/externals/boost/libs/iostreams/src/mapped_file.cpp
)
target_link_libraries(boost_iostreams PUBLIC boost)
# Boost::iostreams
add_library(
boost_iostreams
STATIC
${CMAKE_SOURCE_DIR}/externals/boost/libs/iostreams/src/file_descriptor.cpp
${CMAKE_SOURCE_DIR}/externals/boost/libs/iostreams/src/mapped_file.cpp
)
target_link_libraries(boost_iostreams PUBLIC boost)
# Add additional boost libs here; remember to ALIAS them in the root CMakeLists! # Add additional boost libs here; remember to ALIAS them in the root CMakeLists!
endif()
# Catch2 # Catch2
set(CATCH_INSTALL_DOCS OFF CACHE BOOL "") set(CATCH_INSTALL_DOCS OFF CACHE BOOL "")
@ -95,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
@ -171,37 +174,39 @@ endif()
add_library(json-headers INTERFACE) add_library(json-headers INTERFACE)
target_include_directories(json-headers INTERFACE ./json) target_include_directories(json-headers INTERFACE ./json)
# OpenSSL
if (USE_SYSTEM_OPENSSL)
find_package(OpenSSL 1.1)
if (OPENSSL_FOUND)
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
endif()
endif()
if (NOT OPENSSL_FOUND)
# LibreSSL
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
set(OPENSSLDIR "/etc/ssl/")
add_subdirectory(libressl EXCLUDE_FROM_ALL)
target_include_directories(ssl INTERFACE ./libressl/include)
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
get_directory_property(OPENSSL_LIBRARIES
DIRECTORY libressl
DEFINITION OPENSSL_LIBS)
endif()
# httplib
add_library(httplib INTERFACE)
target_include_directories(httplib INTERFACE ./httplib)
target_compile_options(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
if(ANDROID)
add_subdirectory(android-ifaddrs)
target_link_libraries(httplib INTERFACE ifaddrs)
endif()
# cpp-jwt
if (ENABLE_WEB_SERVICE) if (ENABLE_WEB_SERVICE)
if (USE_SYSTEM_OPENSSL)
find_package(OpenSSL 1.1)
if (OPENSSL_FOUND)
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
endif()
endif()
if (NOT OPENSSL_FOUND)
# LibreSSL
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
set(OPENSSLDIR "/etc/ssl/")
add_subdirectory(libressl EXCLUDE_FROM_ALL)
target_include_directories(ssl INTERFACE ./libressl/include)
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
get_directory_property(OPENSSL_LIBRARIES
DIRECTORY libressl
DEFINITION OPENSSL_LIBS)
endif()
if(ANDROID)
add_subdirectory(android-ifaddrs)
endif()
# httplib
add_library(httplib INTERFACE)
target_include_directories(httplib INTERFACE ./httplib)
target_compile_options(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
# cpp-jwt
add_library(cpp-jwt INTERFACE) add_library(cpp-jwt INTERFACE)
target_include_directories(cpp-jwt INTERFACE ./cpp-jwt/include) target_include_directories(cpp-jwt INTERFACE ./cpp-jwt/include)
target_compile_definitions(cpp-jwt INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON) target_compile_definitions(cpp-jwt INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON)
@ -237,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

@ -29,6 +29,7 @@
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application <application
android:name="org.citra.citra_emu.CitraApplication" android:name="org.citra.citra_emu.CitraApplication"

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

@ -1,7 +1,10 @@
package org.citra.citra_emu.ui.main; package org.citra.citra_emu.ui.main;
import android.Manifest;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@ -13,6 +16,7 @@ import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.core.splashscreen.SplashScreen; import androidx.core.splashscreen.SplashScreen;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.Collections; import java.util.Collections;
@ -124,6 +128,9 @@ public final class MainActivity extends AppCompatActivity implements MainView {
); );
}); });
private final ActivityResultLauncher<String> requestNotificationPermissionLauncher =
registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> { });
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
SplashScreen splashScreen = SplashScreen.installSplashScreen(this); SplashScreen splashScreen = SplashScreen.installSplashScreen(this);
@ -165,6 +172,12 @@ public final class MainActivity extends AppCompatActivity implements MainView {
EmulationActivity.tryDismissRunningNotification(this); EmulationActivity.tryDismissRunningNotification(this);
setInsets(); setInsets();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
requestNotificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
}
}
} }
@Override @Override

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

@ -53,7 +53,7 @@ void AndroidMiiSelector::Setup(const Frontend::MiiSelectorConfig& config) {
const u32 return_code = static_cast<u32>( const u32 return_code = static_cast<u32>(
env->GetLongField(data, env->GetFieldID(s_mii_selector_data_class, "return_code", "J"))); env->GetLongField(data, env->GetFieldID(s_mii_selector_data_class, "return_code", "J")));
if (return_code == 1) { if (return_code == 1) {
Finalize(return_code, HLE::Applets::MiiData{}); Finalize(return_code, Mii::MiiData{});
return; return;
} }

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"
@ -26,13 +29,15 @@
#include "core/hle/service/nfc/nfc.h" #include "core/hle/service/nfc/nfc.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "core/savestate.h" #include "core/savestate.h"
#include "core/telemetry_session.h"
#include "jni/android_common/android_common.h" #include "jni/android_common/android_common.h"
#include "jni/applets/mii_selector.h" #include "jni/applets/mii_selector.h"
#include "jni/applets/swkbd.h" #include "jni/applets/swkbd.h"
#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"
@ -41,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};
@ -122,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()};
@ -156,7 +169,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
system.RegisterSoftwareKeyboard(std::make_shared<SoftwareKeyboard::AndroidKeyboard>()); system.RegisterSoftwareKeyboard(std::make_shared<SoftwareKeyboard::AndroidKeyboard>());
// Register microphone permission check // Register microphone permission check
Core::System::GetInstance().RegisterMicPermissionCheck(&CheckMicPermission); system.RegisterMicPermissionCheck(&CheckMicPermission);
InputManager::Init(); InputManager::Init();
@ -166,7 +179,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
return load_result; return load_result;
} }
auto& telemetry_session = Core::System::GetInstance().TelemetrySession(); auto& telemetry_session = system.TelemetrySession();
telemetry_session.AddField(Common::Telemetry::FieldType::App, "Frontend", "Android"); telemetry_session.AddField(Common::Telemetry::FieldType::App, "Frontend", "Android");
stop_run = false; stop_run = false;
@ -187,8 +200,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
audio_stretching_event = audio_stretching_event =
system.CoreTiming().RegisterEvent("AudioStretchingEvent", [&](u64, s64 cycles_late) { system.CoreTiming().RegisterEvent("AudioStretchingEvent", [&](u64, s64 cycles_late) {
if (Settings::values.enable_audio_stretching) { if (Settings::values.enable_audio_stretching) {
Core::DSP().EnableStretching( system.DSP().EnableStretching(system.GetAndResetPerfStats().emulation_speed < 0.95);
Core::System::GetInstance().GetAndResetPerfStats().emulation_speed < 0.95);
} }
system.CoreTiming().ScheduleEvent(audio_stretching_ticks - cycles_late, system.CoreTiming().ScheduleEvent(audio_stretching_ticks - cycles_late,
@ -219,7 +231,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
SCOPE_EXIT({ Settings::values.volume = volume; }); SCOPE_EXIT({ Settings::values.volume = volume; });
Settings::values.volume = 0; Settings::values.volume = 0;
std::unique_lock<std::mutex> pause_lock(paused_mutex); std::unique_lock pause_lock{paused_mutex};
running_cv.wait(pause_lock, [] { return !pause_emulation || stop_run; }); running_cv.wait(pause_lock, [] { return !pause_emulation || stop_run; });
window->PollEvents(); window->PollEvents();
} }
@ -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,
@ -621,7 +676,7 @@ jobjectArray Java_org_citra_citra_1emu_NativeLibrary_GetSavestateInfo(
return nullptr; return nullptr;
} }
const auto savestates = Core::ListSaveStates(title_id); const auto savestates = Core::ListSaveStates(title_id, system.Movie().GetCurrentMovieID());
const jobjectArray array = const jobjectArray array =
env->NewObjectArray(static_cast<jsize>(savestates.size()), savestate_info_class, nullptr); env->NewObjectArray(static_cast<jsize>(savestates.size()), savestate_info_class, nullptr);
for (std::size_t i = 0; i < savestates.size(); ++i) { for (std::size_t i = 0; i < savestates.size(); ++i) {

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

@ -5,20 +5,24 @@
#include "common/common_types.h" #include "common/common_types.h"
namespace AudioCore {
struct ADTSData { struct ADTSData {
u8 header_length; u8 header_length = 0;
bool MPEG2; bool mpeg2 = false;
u8 profile; u8 profile = 0;
u8 channels; u8 channels = 0;
u8 channel_idx; u8 channel_idx = 0;
u8 framecount; u8 framecount = 0;
u8 samplerate_idx; u8 samplerate_idx = 0;
u32 length; u32 length = 0;
u32 samplerate; u32 samplerate = 0;
}; };
ADTSData ParseADTS(const char* buffer); ADTSData ParseADTS(const u8* buffer);
// last two bytes of MF AAC decoder user data // last two bytes of MF AAC decoder user data
// see https://docs.microsoft.com/en-us/windows/desktop/medfound/aac-decoder#example-media-types // see https://docs.microsoft.com/en-us/windows/desktop/medfound/aac-decoder#example-media-types
u16 MFGetAACTag(const ADTSData& input); u16 MFGetAACTag(const ADTSData& input);
} // namespace AudioCore

View File

@ -3,44 +3,59 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <array> #include <array>
#include "adts.h" #include "adts.h"
#include "common/bit_field.h"
namespace AudioCore {
constexpr std::array<u32, 16> freq_table = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, constexpr std::array<u32, 16> freq_table = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
16000, 12000, 11025, 8000, 7350, 0, 0, 0}; 16000, 12000, 11025, 8000, 7350, 0, 0, 0};
constexpr std::array<u8, 8> channel_table = {0, 1, 2, 3, 4, 5, 6, 8}; constexpr std::array<u8, 8> channel_table = {0, 1, 2, 3, 4, 5, 6, 8};
ADTSData ParseADTS(const char* buffer) { struct ADTSHeader {
u32 tmp = 0; union {
ADTSData out; std::array<u8, 7> raw{};
BitFieldBE<52, 12, u64> sync_word;
BitFieldBE<51, 1, u64> mpeg2;
BitFieldBE<49, 2, u64> layer;
BitFieldBE<48, 1, u64> protection_absent;
BitFieldBE<46, 2, u64> profile;
BitFieldBE<42, 4, u64> samplerate_idx;
BitFieldBE<41, 1, u64> private_bit;
BitFieldBE<38, 3, u64> channel_idx;
BitFieldBE<37, 1, u64> originality;
BitFieldBE<36, 1, u64> home;
BitFieldBE<35, 1, u64> copyright_id;
BitFieldBE<34, 1, u64> copyright_id_start;
BitFieldBE<21, 13, u64> frame_length;
BitFieldBE<10, 11, u64> buffer_fullness;
BitFieldBE<8, 2, u64> frame_count;
};
};
ADTSData ParseADTS(const u8* buffer) {
ADTSHeader header;
memcpy(header.raw.data(), buffer, sizeof(header.raw));
// sync word 0xfff // sync word 0xfff
tmp = (buffer[0] << 8) | (buffer[1] & 0xf0); if (header.sync_word != 0xfff) {
if ((tmp & 0xffff) != 0xfff0) { return {};
out.length = 0;
return out;
} }
ADTSData out{};
// bit 16 = no CRC // bit 16 = no CRC
out.header_length = (buffer[1] & 0x1) ? 7 : 9; out.header_length = header.protection_absent ? 7 : 9;
out.MPEG2 = (buffer[1] >> 3) & 0x1; out.mpeg2 = static_cast<bool>(header.mpeg2);
// bit 17 to 18 // bit 17 to 18
out.profile = (buffer[2] >> 6) + 1; out.profile = static_cast<u8>(header.profile) + 1;
// bit 19 to 22 // bit 19 to 22
tmp = (buffer[2] >> 2) & 0xf; out.samplerate_idx = static_cast<u8>(header.samplerate_idx);
out.samplerate_idx = tmp; out.samplerate = header.samplerate_idx > 15 ? 0 : freq_table[header.samplerate_idx];
out.samplerate = (tmp > 15) ? 0 : freq_table[tmp];
// bit 24 to 26 // bit 24 to 26
tmp = ((buffer[2] & 0x1) << 2) | ((buffer[3] >> 6) & 0x3); out.channel_idx = static_cast<u8>(header.channel_idx);
out.channel_idx = tmp; out.channels = (header.channel_idx > 7) ? 0 : channel_table[header.channel_idx];
out.channels = (tmp > 7) ? 0 : channel_table[tmp];
// bit 55 to 56 // bit 55 to 56
out.framecount = (buffer[6] & 0x3) + 1; out.framecount = static_cast<u8>(header.frame_count + 1);
// bit 31 to 43 // bit 31 to 43
tmp = (buffer[3] & 0x3) << 11; out.length = static_cast<u32>(header.frame_length);
tmp |= (buffer[4] << 3) & 0x7f8;
tmp |= (buffer[5] >> 5) & 0x7;
out.length = tmp;
return out; return out;
} }
@ -61,3 +76,4 @@ u16 MFGetAACTag(const ADTSData& input) {
return tag; return tag;
} }
} // namespace AudioCore

View File

@ -24,7 +24,7 @@ private:
std::optional<BinaryMessage> Decode(const BinaryMessage& request); std::optional<BinaryMessage> Decode(const BinaryMessage& request);
void Clear(); void Clear();
bool InitializeDecoder(ADTSData& adts_header); bool InitializeDecoder(AudioCore::ADTSData& adts_header);
static OSStatus DataFunc(AudioConverterRef in_audio_converter, u32* io_number_data_packets, static OSStatus DataFunc(AudioConverterRef in_audio_converter, u32* io_number_data_packets,
AudioBufferList* io_data, AudioBufferList* io_data,
@ -33,7 +33,7 @@ private:
Memory::MemorySystem& memory; Memory::MemorySystem& memory;
ADTSData adts_config; AudioCore::ADTSData adts_config;
AudioStreamBasicDescription output_format = {}; AudioStreamBasicDescription output_format = {};
AudioConverterRef converter = nullptr; AudioConverterRef converter = nullptr;
@ -85,7 +85,11 @@ std::optional<BinaryMessage> AudioToolboxDecoder::Impl::ProcessRequest(
case DecoderCommand::EncodeDecode: { case DecoderCommand::EncodeDecode: {
return Decode(request); return Decode(request);
} }
case DecoderCommand::Unknown: { case DecoderCommand::Shutdown:
case DecoderCommand::SaveState:
case DecoderCommand::LoadState: {
LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}",
static_cast<u16>(request.header.cmd));
BinaryMessage response = request; BinaryMessage response = request;
response.header.result = ResultStatus::Success; response.header.result = ResultStatus::Success;
return response; return response;
@ -97,7 +101,7 @@ std::optional<BinaryMessage> AudioToolboxDecoder::Impl::ProcessRequest(
} }
} }
bool AudioToolboxDecoder::Impl::InitializeDecoder(ADTSData& adts_header) { bool AudioToolboxDecoder::Impl::InitializeDecoder(AudioCore::ADTSData& adts_header) {
if (converter) { if (converter) {
if (adts_config.channels == adts_header.channels && if (adts_config.channels == adts_header.channels &&
adts_config.samplerate == adts_header.samplerate) { adts_config.samplerate == adts_header.samplerate) {
@ -179,8 +183,9 @@ std::optional<BinaryMessage> AudioToolboxDecoder::Impl::Decode(const BinaryMessa
return {}; return {};
} }
auto data = memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); const auto data =
auto adts_header = ParseADTS(reinterpret_cast<const char*>(data)); memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR);
auto adts_header = AudioCore::ParseADTS(data);
curr_data = data + adts_header.header_length; curr_data = data + adts_header.header_length;
curr_data_len = request.decode_aac_request.size - adts_header.header_length; curr_data_len = request.decode_aac_request.size - adts_header.header_length;

View File

@ -42,7 +42,9 @@ std::optional<BinaryMessage> NullDecoder::ProcessRequest(const BinaryMessage& re
BinaryMessage response{}; BinaryMessage response{};
switch (request.header.cmd) { switch (request.header.cmd) {
case DecoderCommand::Init: case DecoderCommand::Init:
case DecoderCommand::Unknown: case DecoderCommand::Shutdown:
case DecoderCommand::SaveState:
case DecoderCommand::LoadState:
response = request; response = request;
response.header.result = ResultStatus::Success; response.header.result = ResultStatus::Success;
return response; return response;

View File

@ -14,9 +14,16 @@
namespace AudioCore::HLE { namespace AudioCore::HLE {
enum class DecoderCommand : u16 { enum class DecoderCommand : u16 {
/// Initializes the decoder.
Init = 0, Init = 0,
/// Decodes/encodes a data frame.
EncodeDecode = 1, EncodeDecode = 1,
Unknown = 2, // Probably UnInit /// Shuts down the decoder.
Shutdown = 2,
/// Loads the saved decoder state. Used for DSP wake.
LoadState = 3,
/// Saves the decoder state. Used for DSP sleep.
SaveState = 4,
}; };
enum class DecoderCodec : u16 { enum class DecoderCodec : u16 {

View File

@ -111,7 +111,11 @@ std::optional<BinaryMessage> FDKDecoder::Impl::ProcessRequest(const BinaryMessag
case DecoderCommand::EncodeDecode: { case DecoderCommand::EncodeDecode: {
return Decode(request); return Decode(request);
} }
case DecoderCommand::Unknown: { case DecoderCommand::Shutdown:
case DecoderCommand::SaveState:
case DecoderCommand::LoadState: {
LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}",
static_cast<u16>(request.header.cmd));
BinaryMessage response = request; BinaryMessage response = request;
response.header.result = ResultStatus::Success; response.header.result = ResultStatus::Success;
return response; return response;

View File

@ -80,7 +80,11 @@ std::optional<BinaryMessage> FFMPEGDecoder::Impl::ProcessRequest(const BinaryMes
case DecoderCommand::EncodeDecode: { case DecoderCommand::EncodeDecode: {
return Decode(request); return Decode(request);
} }
case DecoderCommand::Unknown: { case DecoderCommand::Shutdown:
case DecoderCommand::SaveState:
case DecoderCommand::LoadState: {
LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}",
static_cast<u16>(request.header.cmd));
BinaryMessage response = request; BinaryMessage response = request;
response.header.result = ResultStatus::Success; response.header.result = ResultStatus::Success;
return response; return response;

View File

@ -8,6 +8,7 @@
#include <boost/serialization/vector.hpp> #include <boost/serialization/vector.hpp>
#include <boost/serialization/weak_ptr.hpp> #include <boost/serialization/weak_ptr.hpp>
#include "audio_core/audio_types.h" #include "audio_core/audio_types.h"
#include "common/archives.h"
#ifdef HAVE_MF #ifdef HAVE_MF
#include "audio_core/hle/wmf_decoder.h" #include "audio_core/hle/wmf_decoder.h"
#elif HAVE_AUDIOTOOLBOX #elif HAVE_AUDIOTOOLBOX
@ -319,6 +320,10 @@ void DspHle::Impl::PipeWrite(DspPipe pipe_number, std::span<const u8> buffer) {
pipe_data[static_cast<u32>(pipe_number)].resize(sizeof(value)); pipe_data[static_cast<u32>(pipe_number)].resize(sizeof(value));
std::memcpy(pipe_data[static_cast<u32>(pipe_number)].data(), &value, sizeof(value)); std::memcpy(pipe_data[static_cast<u32>(pipe_number)].data(), &value, sizeof(value));
} }
auto dsp = dsp_dsp.lock();
if (dsp) {
dsp->SignalInterrupt(InterruptType::Pipe, DspPipe::Binary);
}
break; break;
} }
default: default:
@ -461,8 +466,6 @@ void DspHle::Impl::AudioTickCallback(s64 cycles_late) {
// TODO(merry): Signal all the other interrupts as appropriate. // TODO(merry): Signal all the other interrupts as appropriate.
if (auto service = dsp_dsp.lock()) { if (auto service = dsp_dsp.lock()) {
service->SignalInterrupt(InterruptType::Pipe, DspPipe::Audio); service->SignalInterrupt(InterruptType::Pipe, DspPipe::Audio);
// HACK(merry): Added to prevent regressions. Will remove soon.
service->SignalInterrupt(InterruptType::Pipe, DspPipe::Binary);
} }
} }

View File

@ -27,7 +27,7 @@ public:
~Impl(); ~Impl();
std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request); std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request);
bool SetMediaType(const ADTSData& adts_data); bool SetMediaType(const AudioCore::ADTSData& adts_data);
private: private:
std::optional<BinaryMessage> Initalize(const BinaryMessage& request); std::optional<BinaryMessage> Initalize(const BinaryMessage& request);
@ -36,8 +36,8 @@ private:
Memory::MemorySystem& memory; Memory::MemorySystem& memory;
std::unique_ptr<AMediaCodec, AMediaCodecRelease> decoder; std::unique_ptr<AMediaCodec, AMediaCodecRelease> decoder;
// default: 2 channles, 48000 samplerate // default: 2 channles, 48000 samplerate
ADTSData mADTSData{ AudioCore::ADTSData mADTSData{
/*header_length*/ 7, /*MPEG2*/ false, /*profile*/ 2, /*header_length*/ 7, /*mpeg2*/ false, /*profile*/ 2,
/*channels*/ 2, /*channel_idx*/ 2, /*framecount*/ 0, /*channels*/ 2, /*channel_idx*/ 2, /*framecount*/ 0,
/*samplerate_idx*/ 3, /*length*/ 0, /*samplerate*/ 48000}; /*samplerate_idx*/ 3, /*length*/ 0, /*samplerate*/ 48000};
}; };
@ -54,7 +54,7 @@ std::optional<BinaryMessage> MediaNDKDecoder::Impl::Initalize(const BinaryMessag
return response; return response;
} }
bool MediaNDKDecoder::Impl::SetMediaType(const ADTSData& adts_data) { bool MediaNDKDecoder::Impl::SetMediaType(const AudioCore::ADTSData& adts_data) {
const char* mime = "audio/mp4a-latm"; const char* mime = "audio/mp4a-latm";
if (decoder && mADTSData.profile == adts_data.profile && if (decoder && mADTSData.profile == adts_data.profile &&
mADTSData.channel_idx == adts_data.channel_idx && mADTSData.channel_idx == adts_data.channel_idx &&
@ -110,7 +110,11 @@ std::optional<BinaryMessage> MediaNDKDecoder::Impl::ProcessRequest(const BinaryM
case DecoderCommand::EncodeDecode: { case DecoderCommand::EncodeDecode: {
return Decode(request); return Decode(request);
} }
case DecoderCommand::Unknown: { case DecoderCommand::Shutdown:
case DecoderCommand::SaveState:
case DecoderCommand::LoadState: {
LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}",
static_cast<u16>(request.header.cmd));
BinaryMessage response = request; BinaryMessage response = request;
response.header.result = ResultStatus::Success; response.header.result = ResultStatus::Success;
return response; return response;
@ -137,8 +141,9 @@ std::optional<BinaryMessage> MediaNDKDecoder::Impl::Decode(const BinaryMessage&
return response; return response;
} }
u8* data = memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); const u8* data =
ADTSData adts_data = ParseADTS(reinterpret_cast<const char*>(data)); memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR);
ADTSData adts_data = AudioCore::ParseADTS(data);
SetMediaType(adts_data); SetMediaType(adts_data);
response.decode_aac_response.sample_rate = GetSampleRateEnum(adts_data.samplerate); response.decode_aac_response.sample_rate = GetSampleRateEnum(adts_data.samplerate);
response.decode_aac_response.num_channels = adts_data.channels; response.decode_aac_response.num_channels = adts_data.channels;

View File

@ -23,7 +23,8 @@ private:
std::optional<BinaryMessage> Decode(const BinaryMessage& request); std::optional<BinaryMessage> Decode(const BinaryMessage& request);
MFOutputState DecodingLoop(ADTSData adts_header, std::array<std::vector<u8>, 2>& out_streams); MFOutputState DecodingLoop(AudioCore::ADTSData adts_header,
std::array<std::vector<u8>, 2>& out_streams);
bool transform_initialized = false; bool transform_initialized = false;
bool format_selected = false; bool format_selected = false;
@ -115,7 +116,11 @@ std::optional<BinaryMessage> WMFDecoder::Impl::ProcessRequest(const BinaryMessag
case DecoderCommand::EncodeDecode: { case DecoderCommand::EncodeDecode: {
return Decode(request); return Decode(request);
} }
case DecoderCommand::Unknown: { case DecoderCommand::Shutdown:
case DecoderCommand::SaveState:
case DecoderCommand::LoadState: {
LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}",
static_cast<u16>(request.header.cmd));
BinaryMessage response = request; BinaryMessage response = request;
response.header.result = ResultStatus::Success; response.header.result = ResultStatus::Success;
return response; return response;
@ -135,7 +140,7 @@ std::optional<BinaryMessage> WMFDecoder::Impl::Initalize(const BinaryMessage& re
return response; return response;
} }
MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, MFOutputState WMFDecoder::Impl::DecodingLoop(AudioCore::ADTSData adts_header,
std::array<std::vector<u8>, 2>& out_streams) { std::array<std::vector<u8>, 2>& out_streams) {
std::optional<std::vector<f32>> output_buffer; std::optional<std::vector<f32>> output_buffer;
@ -206,14 +211,14 @@ std::optional<BinaryMessage> WMFDecoder::Impl::Decode(const BinaryMessage& reque
request.decode_aac_request.src_addr); request.decode_aac_request.src_addr);
return std::nullopt; return std::nullopt;
} }
u8* data = memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); const u8* data =
memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR);
std::array<std::vector<u8>, 2> out_streams; std::array<std::vector<u8>, 2> out_streams;
unique_mfptr<IMFSample> sample; unique_mfptr<IMFSample> sample;
MFInputState input_status = MFInputState::OK; MFInputState input_status = MFInputState::OK;
MFOutputState output_status = MFOutputState::OK; MFOutputState output_status = MFOutputState::OK;
std::optional<ADTSMeta> adts_meta = std::optional<ADTSMeta> adts_meta = DetectMediaType(data, request.decode_aac_request.size);
DetectMediaType((char*)data, request.decode_aac_request.size);
if (!adts_meta) { if (!adts_meta) {
LOG_ERROR(Audio_DSP, "Unable to deduce decoding parameters from ADTS stream"); LOG_ERROR(Audio_DSP, "Unable to deduce decoding parameters from ADTS stream");

View File

@ -110,8 +110,9 @@ unique_mfptr<IMFSample> CreateSample(const void* data, DWORD len, DWORD alignmen
return sample; return sample;
} }
bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts, bool SelectInputMediaType(IMFTransform* transform, int in_stream_id,
const UINT8* user_data, UINT32 user_data_len, GUID audio_format) { const AudioCore::ADTSData& adts, const UINT8* user_data,
UINT32 user_data_len, GUID audio_format) {
HRESULT hr = S_OK; HRESULT hr = S_OK;
unique_mfptr<IMFMediaType> t; unique_mfptr<IMFMediaType> t;
@ -190,12 +191,12 @@ bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audi
return false; return false;
} }
std::optional<ADTSMeta> DetectMediaType(char* buffer, std::size_t len) { std::optional<ADTSMeta> DetectMediaType(const u8* buffer, std::size_t len) {
if (len < 7) { if (len < 7) {
return std::nullopt; return std::nullopt;
} }
ADTSData tmp; AudioCore::ADTSData tmp;
ADTSMeta result; ADTSMeta result;
// see https://docs.microsoft.com/en-us/windows/desktop/api/mmreg/ns-mmreg-heaacwaveinfo_tag // see https://docs.microsoft.com/en-us/windows/desktop/api/mmreg/ns-mmreg-heaacwaveinfo_tag
// for the meaning of the byte array below // for the meaning of the byte array below
@ -207,7 +208,7 @@ std::optional<ADTSMeta> DetectMediaType(char* buffer, std::size_t len) {
UINT8 aac_tmp[] = {0x01, 0x00, 0xfe, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x00, 0x00}; UINT8 aac_tmp[] = {0x01, 0x00, 0xfe, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x00, 0x00};
uint16_t tag = 0; uint16_t tag = 0;
tmp = ParseADTS(buffer); tmp = AudioCore::ParseADTS(buffer);
if (tmp.length == 0) { if (tmp.length == 0) {
return std::nullopt; return std::nullopt;
} }
@ -215,7 +216,7 @@ std::optional<ADTSMeta> DetectMediaType(char* buffer, std::size_t len) {
tag = MFGetAACTag(tmp); tag = MFGetAACTag(tmp);
aac_tmp[12] |= (tag & 0xff00) >> 8; aac_tmp[12] |= (tag & 0xff00) >> 8;
aac_tmp[13] |= (tag & 0x00ff); aac_tmp[13] |= (tag & 0x00ff);
std::memcpy(&(result.ADTSHeader), &tmp, sizeof(ADTSData)); std::memcpy(&(result.ADTSHeader), &tmp, sizeof(AudioCore::ADTSData));
std::memcpy(&(result.AACTag), aac_tmp, 14); std::memcpy(&(result.AACTag), aac_tmp, 14);
return result; return result;
} }

View File

@ -99,7 +99,7 @@ void ReportError(std::string msg, HRESULT hr);
// data type for transferring ADTS metadata between functions // data type for transferring ADTS metadata between functions
struct ADTSMeta { struct ADTSMeta {
ADTSData ADTSHeader; AudioCore::ADTSData ADTSHeader;
u8 AACTag[14]; u8 AACTag[14];
}; };
@ -110,10 +110,10 @@ bool InitMFDLL();
unique_mfptr<IMFTransform> MFDecoderInit(GUID audio_format = MFAudioFormat_AAC); unique_mfptr<IMFTransform> MFDecoderInit(GUID audio_format = MFAudioFormat_AAC);
unique_mfptr<IMFSample> CreateSample(const void* data, DWORD len, DWORD alignment = 1, unique_mfptr<IMFSample> CreateSample(const void* data, DWORD len, DWORD alignment = 1,
LONGLONG duration = 0); LONGLONG duration = 0);
bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts, bool SelectInputMediaType(IMFTransform* transform, int in_stream_id,
const UINT8* user_data, UINT32 user_data_len, const AudioCore::ADTSData& adts, const UINT8* user_data,
GUID audio_format = MFAudioFormat_AAC); UINT32 user_data_len, GUID audio_format = MFAudioFormat_AAC);
std::optional<ADTSMeta> DetectMediaType(char* buffer, std::size_t len); std::optional<ADTSMeta> DetectMediaType(const u8* buffer, std::size_t len);
bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id,
GUID audio_format = MFAudioFormat_PCM); GUID audio_format = MFAudioFormat_PCM);
void MFFlush(IMFTransform* transform); void MFFlush(IMFTransform* transform);

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,11 +15,11 @@
#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"
#include "common/logging/backend.h" #include "common/logging/backend.h"
#include "common/logging/filter.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scm_rev.h" #include "common/scm_rev.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
@ -28,18 +28,15 @@
#include "core/core.h" #include "core/core.h"
#include "core/dumping/backend.h" #include "core/dumping/backend.h"
#include "core/dumping/ffmpeg_backend.h" #include "core/dumping/ffmpeg_backend.h"
#include "core/file_sys/cia_container.h"
#include "core/frontend/applets/default_applets.h" #include "core/frontend/applets/default_applets.h"
#include "core/frontend/framebuffer_layout.h" #include "core/frontend/framebuffer_layout.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/service/am/am.h" #include "core/hle/service/am/am.h"
#include "core/hle/service/cfg/cfg.h" #include "core/hle/service/cfg/cfg.h"
#include "core/loader/loader.h"
#include "core/movie.h" #include "core/movie.h"
#include "core/telemetry_session.h"
#include "input_common/main.h" #include "input_common/main.h"
#include "network/network.h" #include "network/network.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#include "video_core/video_core.h"
#undef _UNICODE #undef _UNICODE
#include <getopt.h> #include <getopt.h>
@ -331,7 +328,7 @@ int main(int argc, char** argv) {
} }
auto& system = Core::System::GetInstance(); auto& system = Core::System::GetInstance();
auto& movie = Core::Movie::GetInstance(); auto& movie = system.Movie();
if (!movie_record.empty()) { if (!movie_record.empty()) {
movie.PrepareForRecording(); movie.PrepareForRecording();
@ -355,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

@ -128,16 +128,54 @@ void EmuWindow_SDL2::InitializeSDL2() {
SDL_SetMainReady(); SDL_SetMainReady();
} }
u32 EmuWindow_SDL2::GetEventWindowId(const SDL_Event& event) const {
switch (event.type) {
case SDL_WINDOWEVENT:
return event.window.windowID;
case SDL_KEYDOWN:
case SDL_KEYUP:
return event.key.windowID;
case SDL_MOUSEMOTION:
return event.motion.windowID;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
return event.button.windowID;
case SDL_MOUSEWHEEL:
return event.wheel.windowID;
case SDL_FINGERDOWN:
case SDL_FINGERMOTION:
case SDL_FINGERUP:
return event.tfinger.windowID;
case SDL_TEXTEDITING:
return event.edit.windowID;
case SDL_TEXTEDITING_EXT:
return event.editExt.windowID;
case SDL_TEXTINPUT:
return event.text.windowID;
case SDL_DROPBEGIN:
case SDL_DROPFILE:
case SDL_DROPTEXT:
case SDL_DROPCOMPLETE:
return event.drop.windowID;
case SDL_USEREVENT:
return event.user.windowID;
default:
// Event is not for any particular window, so we can just pretend it's for this one.
return render_window_id;
}
}
void EmuWindow_SDL2::PollEvents() { void EmuWindow_SDL2::PollEvents() {
SDL_Event event; SDL_Event event;
std::vector<SDL_Event> other_window_events; std::vector<SDL_Event> other_window_events;
// SDL_PollEvent returns 0 when there are no more events in the event queue // SDL_PollEvent returns 0 when there are no more events in the event queue
while (SDL_PollEvent(&event)) { while (SDL_PollEvent(&event)) {
if (event.window.windowID != render_window_id) { if (GetEventWindowId(event) != render_window_id) {
other_window_events.push_back(event); other_window_events.push_back(event);
continue; continue;
} }
switch (event.type) { switch (event.type) {
case SDL_WINDOWEVENT: case SDL_WINDOWEVENT:
switch (event.window.event) { switch (event.window.event) {

View File

@ -8,6 +8,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
union SDL_Event;
struct SDL_Window; struct SDL_Window;
namespace Core { namespace Core {
@ -35,6 +36,9 @@ public:
void RequestClose(); void RequestClose();
protected: protected:
/// Gets the ID of the window an event originated from.
u32 GetEventWindowId(const SDL_Event& event) const;
/// Called by PollEvents when a key is pressed or released. /// Called by PollEvents when a key is pressed or released.
void OnKeyEvent(int key, u8 state); void OnKeyEvent(int key, u8 state);

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
@ -342,7 +344,7 @@ if (USE_DISCORD_PRESENCE)
endif() endif()
if (ENABLE_WEB_SERVICE) if (ENABLE_WEB_SERVICE)
target_compile_definitions(citra-qt PRIVATE -DENABLE_WEB_SERVICE) target_link_libraries(citra-qt PRIVATE web_service)
endif() endif()
if(UNIX AND NOT APPLE) if(UNIX AND NOT APPLE)

View File

@ -67,6 +67,5 @@ void QtMiiSelector::OpenDialog() {
dialog.return_code, index); dialog.return_code, index);
const auto mii_data = dialog.miis.at(index); const auto mii_data = dialog.miis.at(index);
Finalize(dialog.return_code, Finalize(dialog.return_code, dialog.return_code == 0 ? std::move(mii_data) : Mii::MiiData{});
dialog.return_code == 0 ? std::move(mii_data) : HLE::Applets::MiiData{});
} }

View File

@ -24,7 +24,7 @@ private:
QVBoxLayout* layout; QVBoxLayout* layout;
QtMiiSelector* mii_selector; QtMiiSelector* mii_selector;
u32 return_code = 0; u32 return_code = 0;
std::vector<HLE::Applets::MiiData> miis; std::vector<Mii::MiiData> miis;
friend class QtMiiSelector; friend class QtMiiSelector;
}; };

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);
@ -387,10 +403,6 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread_, Core::Sys
bool is_secondary_) bool is_secondary_)
: QWidget(parent_), EmuWindow(is_secondary_), emu_thread(emu_thread_), system{system_} { : QWidget(parent_), EmuWindow(is_secondary_), emu_thread(emu_thread_), system{system_} {
setWindowTitle(QStringLiteral("Citra %1 | %2-%3")
.arg(QString::fromUtf8(Common::g_build_name),
QString::fromUtf8(Common::g_scm_branch),
QString::fromUtf8(Common::g_scm_desc)));
setAttribute(Qt::WA_AcceptTouchEvents); setAttribute(Qt::WA_AcceptTouchEvents);
auto layout = new QHBoxLayout(this); auto layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
@ -605,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
@ -690,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

@ -9,6 +9,7 @@
#include "citra_qt/compatdb.h" #include "citra_qt/compatdb.h"
#include "common/telemetry.h" #include "common/telemetry.h"
#include "core/core.h" #include "core/core.h"
#include "core/telemetry_session.h"
#include "ui_compatdb.h" #include "ui_compatdb.h"
CompatDB::CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent) CompatDB::CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent)

View File

@ -29,7 +29,7 @@ Config::~Config() {
const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_T, Qt::Key_G, Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_T, Qt::Key_G,
Qt::Key_F, Qt::Key_H, Qt::Key_Q, Qt::Key_W, Qt::Key_M, Qt::Key_N, Qt::Key_F, Qt::Key_H, Qt::Key_Q, Qt::Key_W, Qt::Key_M, Qt::Key_N,
Qt::Key_O, Qt::Key_P, Qt::Key_1, Qt::Key_2, Qt::Key_B, Qt::Key_O, Qt::Key_P, Qt::Key_1, Qt::Key_2, Qt::Key_B, Qt::Key_V,
}; };
const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
@ -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);
@ -772,6 +777,11 @@ void Config::ReadUIGameListValues() {
ReadBasicSetting(UISettings::values.game_list_hide_no_icon); ReadBasicSetting(UISettings::values.game_list_hide_no_icon);
ReadBasicSetting(UISettings::values.game_list_single_line_mode); ReadBasicSetting(UISettings::values.game_list_single_line_mode);
ReadBasicSetting(UISettings::values.show_compat_column);
ReadBasicSetting(UISettings::values.show_region_column);
ReadBasicSetting(UISettings::values.show_type_column);
ReadBasicSetting(UISettings::values.show_size_column);
qt_config->endGroup(); qt_config->endGroup();
} }
@ -1102,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);
@ -1230,6 +1244,11 @@ void Config::SaveUIGameListValues() {
WriteBasicSetting(UISettings::values.game_list_hide_no_icon); WriteBasicSetting(UISettings::values.game_list_hide_no_icon);
WriteBasicSetting(UISettings::values.game_list_single_line_mode); WriteBasicSetting(UISettings::values.game_list_single_line_mode);
WriteBasicSetting(UISettings::values.show_compat_column);
WriteBasicSetting(UISettings::values.show_region_column);
WriteBasicSetting(UISettings::values.show_type_column);
WriteBasicSetting(UISettings::values.show_size_column);
qt_config->endGroup(); qt_config->endGroup();
} }

View File

@ -21,68 +21,8 @@
<item> <item>
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>-1</number>
</property> </property>
<widget class="ConfigureGeneral" name="generalTab">
<attribute name="title">
<string>General</string>
</attribute>
</widget>
<widget class="ConfigureSystem" name="systemTab">
<attribute name="title">
<string>System</string>
</attribute>
</widget>
<widget class="ConfigureInput" name="inputTab">
<attribute name="title">
<string>Input</string>
</attribute>
</widget>
<widget class="ConfigureHotkeys" name="hotkeysTab">
<attribute name="title">
<string>Hotkeys</string>
</attribute>
</widget>
<widget class="ConfigureGraphics" name="graphicsTab">
<attribute name="title">
<string>Graphics</string>
</attribute>
</widget>
<widget class="ConfigureEnhancements" name="enhancementsTab">
<attribute name="title">
<string>Enhancements</string>
</attribute>
</widget>
<widget class="ConfigureAudio" name="audioTab">
<attribute name="title">
<string>Audio</string>
</attribute>
</widget>
<widget class="ConfigureCamera" name="cameraTab">
<attribute name="title">
<string>Camera</string>
</attribute>
</widget>
<widget class="ConfigureDebug" name="debugTab">
<attribute name="title">
<string>Debug</string>
</attribute>
</widget>
<widget class="ConfigureStorage" name="storageTab">
<attribute name="title">
<string>Storage</string>
</attribute>
</widget>
<widget class="ConfigureWeb" name="webTab">
<attribute name="title">
<string>Web</string>
</attribute>
</widget>
<widget class="ConfigureUi" name="uiTab">
<attribute name="title">
<string>UI</string>
</attribute>
</widget>
</widget> </widget>
</item> </item>
</layout> </layout>

View File

@ -10,14 +10,13 @@
#include "citra_qt/configuration/configuration_shared.h" #include "citra_qt/configuration/configuration_shared.h"
#include "citra_qt/configuration/configure_audio.h" #include "citra_qt/configuration/configure_audio.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h"
#include "ui_configure_audio.h" #include "ui_configure_audio.h"
#if defined(__APPLE__) #if defined(__APPLE__)
#include "common/apple_authorization.h" #include "common/apple_authorization.h"
#endif #endif
ConfigureAudio::ConfigureAudio(QWidget* parent) ConfigureAudio::ConfigureAudio(bool is_powered_on, QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureAudio>()) { : QWidget(parent), ui(std::make_unique<Ui::ConfigureAudio>()) {
ui->setupUi(this); ui->setupUi(this);
@ -27,8 +26,7 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
AudioCore::GetSinkName(static_cast<AudioCore::SinkType>(type)).data())); AudioCore::GetSinkName(static_cast<AudioCore::SinkType>(type)).data()));
} }
const bool is_running = Core::System::GetInstance().IsPoweredOn(); ui->emulation_combo_box->setEnabled(!is_powered_on);
ui->emulation_combo_box->setEnabled(!is_running);
connect(ui->volume_slider, &QSlider::valueChanged, this, connect(ui->volume_slider, &QSlider::valueChanged, this,
&ConfigureAudio::SetVolumeIndicatorText); &ConfigureAudio::SetVolumeIndicatorText);

View File

@ -19,7 +19,7 @@ class ConfigureAudio : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureAudio(QWidget* parent = nullptr); explicit ConfigureAudio(bool is_powered_on, QWidget* parent = nullptr);
~ConfigureAudio() override; ~ConfigureAudio() override;
void ApplyConfiguration(); void ApplyConfiguration();

View File

@ -47,8 +47,7 @@ ConfigureCamera::~ConfigureCamera() {
} }
void ConfigureCamera::ConnectEvents() { void ConfigureCamera::ConnectEvents() {
connect(ui->image_source, connect(ui->image_source, qOverload<int>(&QComboBox::currentIndexChanged), this,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
[this](int index) { [this](int index) {
StopPreviewing(); StopPreviewing();
UpdateImageSourceUI(); UpdateImageSourceUI();
@ -58,36 +57,33 @@ void ConfigureCamera::ConnectEvents() {
} }
#endif #endif
}); });
connect(ui->camera_selection, connect(ui->camera_selection, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this] { StopPreviewing();
StopPreviewing(); if (GetCameraSelection() != current_selected) {
if (GetCameraSelection() != current_selected) { RecordConfig();
RecordConfig(); }
} if (ui->camera_selection->currentIndex() == 1) {
if (ui->camera_selection->currentIndex() == 1) { ui->camera_mode->setCurrentIndex(1); // Double
ui->camera_mode->setCurrentIndex(1); // Double if (camera_name[0] == camera_name[2] && camera_config[0] == camera_config[2]) {
if (camera_name[0] == camera_name[2] && camera_config[0] == camera_config[2]) { ui->camera_mode->setCurrentIndex(0); // Single
ui->camera_mode->setCurrentIndex(0); // Single }
} }
} UpdateCameraMode();
UpdateCameraMode(); SetConfiguration();
SetConfiguration(); });
}); connect(ui->camera_mode, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
connect(ui->camera_mode, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), StopPreviewing();
this, [this] { ui->camera_position_label->setVisible(ui->camera_mode->currentIndex() == 1);
StopPreviewing(); ui->camera_position->setVisible(ui->camera_mode->currentIndex() == 1);
ui->camera_position_label->setVisible(ui->camera_mode->currentIndex() == 1); current_selected = GetCameraSelection();
ui->camera_position->setVisible(ui->camera_mode->currentIndex() == 1); });
current_selected = GetCameraSelection(); connect(ui->camera_position, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
}); StopPreviewing();
connect(ui->camera_position, if (GetCameraSelection() != current_selected) {
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this] { RecordConfig();
StopPreviewing(); }
if (GetCameraSelection() != current_selected) { SetConfiguration();
RecordConfig(); });
}
SetConfiguration();
});
connect(ui->toolButton, &QToolButton::clicked, this, &ConfigureCamera::OnToolButtonClicked); connect(ui->toolButton, &QToolButton::clicked, this, &ConfigureCamera::OnToolButtonClicked);
connect(ui->preview_button, &QPushButton::clicked, this, [this] { StartPreviewing(); }); connect(ui->preview_button, &QPushButton::clicked, this, [this] { StartPreviewing(); });
connect(ui->prompt_before_load, &QCheckBox::stateChanged, this, [this](int state) { connect(ui->prompt_before_load, &QCheckBox::stateChanged, this, [this](int state) {

View File

@ -9,11 +9,9 @@
#include "core/cheats/cheat_base.h" #include "core/cheats/cheat_base.h"
#include "core/cheats/cheats.h" #include "core/cheats/cheats.h"
#include "core/cheats/gateway_cheat.h" #include "core/cheats/gateway_cheat.h"
#include "core/core.h"
#include "core/hle/kernel/process.h"
#include "ui_configure_cheats.h" #include "ui_configure_cheats.h"
ConfigureCheats::ConfigureCheats(u64 title_id_, QWidget* parent) ConfigureCheats::ConfigureCheats(Core::System& system, u64 title_id_, QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureCheats>()), title_id{title_id_} { : QWidget(parent), ui(std::make_unique<Ui::ConfigureCheats>()), title_id{title_id_} {
// Setup gui control settings // Setup gui control settings
ui->setupUi(this); ui->setupUi(this);
@ -36,7 +34,7 @@ ConfigureCheats::ConfigureCheats(u64 title_id_, QWidget* parent)
[this] { SaveCheat(ui->tableCheats->currentRow()); }); [this] { SaveCheat(ui->tableCheats->currentRow()); });
connect(ui->buttonDelete, &QPushButton::clicked, this, &ConfigureCheats::OnDeleteCheat); connect(ui->buttonDelete, &QPushButton::clicked, this, &ConfigureCheats::OnDeleteCheat);
cheat_engine = std::make_unique<Cheats::CheatEngine>(title_id, Core::System::GetInstance()); cheat_engine = std::make_unique<Cheats::CheatEngine>(title_id, system);
LoadCheats(); LoadCheats();
} }

View File

@ -5,6 +5,7 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <QWidget>
#include "common/common_types.h" #include "common/common_types.h"
namespace Cheats { namespace Cheats {
@ -12,6 +13,10 @@ class CheatBase;
class CheatEngine; class CheatEngine;
} // namespace Cheats } // namespace Cheats
namespace Core {
class System;
}
namespace Ui { namespace Ui {
class ConfigureCheats; class ConfigureCheats;
} // namespace Ui } // namespace Ui
@ -20,7 +25,7 @@ class ConfigureCheats : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureCheats(u64 title_id_, QWidget* parent = nullptr); explicit ConfigureCheats(Core::System& system, u64 title_id, QWidget* parent = nullptr);
~ConfigureCheats(); ~ConfigureCheats();
bool ApplyConfiguration(); bool ApplyConfiguration();

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"
@ -11,8 +12,8 @@
#include "common/file_util.h" #include "common/file_util.h"
#include "common/logging/backend.h" #include "common/logging/backend.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/core.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
@ -25,8 +26,8 @@ static constexpr int SettingsToSlider(int value) {
return (value - 5) / 5; return (value - 5) / 5;
} }
ConfigureDebug::ConfigureDebug(QWidget* parent) ConfigureDebug::ConfigureDebug(bool is_powered_on_, QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureDebug>()) { : QWidget(parent), ui(std::make_unique<Ui::ConfigureDebug>()), is_powered_on{is_powered_on_} {
ui->setupUi(this); ui->setupUi(this);
SetConfiguration(); SetConfiguration();
@ -35,9 +36,39 @@ ConfigureDebug::ConfigureDebug(QWidget* parent)
QDesktopServices::openUrl(QUrl::fromLocalFile(path)); QDesktopServices::openUrl(QUrl::fromLocalFile(path));
}); });
const bool is_powered_on = Core::System::GetInstance().IsPoweredOn(); 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%")
@ -59,11 +90,12 @@ void ConfigureDebug::SetConfiguration() {
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub.GetValue()); ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub.GetValue());
ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub.GetValue()); ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub.GetValue());
ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port.GetValue()); ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port.GetValue());
ui->toggle_console->setEnabled(!Core::System::GetInstance().IsPoweredOn()); ui->toggle_console->setEnabled(!is_powered_on);
ui->toggle_console->setChecked(UISettings::values.show_console.GetValue()); ui->toggle_console->setChecked(UISettings::values.show_console.GetValue());
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()) {
@ -94,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

@ -15,7 +15,7 @@ class ConfigureDebug : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureDebug(QWidget* parent = nullptr); explicit ConfigureDebug(bool is_powered_on, QWidget* parent = nullptr);
~ConfigureDebug() override; ~ConfigureDebug() override;
void ApplyConfiguration(); void ApplyConfiguration();
@ -25,4 +25,5 @@ public:
private: private:
std::unique_ptr<Ui::ConfigureDebug> ui; std::unique_ptr<Ui::ConfigureDebug> ui;
bool is_powered_on;
}; };

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

@ -4,26 +4,62 @@
#include <map> #include <map>
#include <QListWidgetItem> #include <QListWidgetItem>
#include "citra_qt/configuration/config.h" #include "citra_qt/configuration/configure_audio.h"
#include "citra_qt/configuration/configure_camera.h"
#include "citra_qt/configuration/configure_debug.h"
#include "citra_qt/configuration/configure_dialog.h" #include "citra_qt/configuration/configure_dialog.h"
#include "citra_qt/configuration/configure_enhancements.h"
#include "citra_qt/configuration/configure_general.h"
#include "citra_qt/configuration/configure_graphics.h"
#include "citra_qt/configuration/configure_hotkeys.h"
#include "citra_qt/configuration/configure_input.h"
#include "citra_qt/configuration/configure_storage.h"
#include "citra_qt/configuration/configure_system.h"
#include "citra_qt/configuration/configure_ui.h"
#include "citra_qt/configuration/configure_web.h"
#include "citra_qt/hotkeys.h" #include "citra_qt/hotkeys.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h" #include "core/core.h"
#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_} { system{system_}, is_powered_on{system.IsPoweredOn()},
general_tab{std::make_unique<ConfigureGeneral>(this)},
system_tab{std::make_unique<ConfigureSystem>(system, this)},
input_tab{std::make_unique<ConfigureInput>(this)},
hotkeys_tab{std::make_unique<ConfigureHotkeys>(this)},
graphics_tab{std::make_unique<ConfigureGraphics>(physical_devices, is_powered_on, this)},
enhancements_tab{std::make_unique<ConfigureEnhancements>(this)},
audio_tab{std::make_unique<ConfigureAudio>(is_powered_on, this)},
camera_tab{std::make_unique<ConfigureCamera>(this)},
debug_tab{std::make_unique<ConfigureDebug>(is_powered_on, this)},
storage_tab{std::make_unique<ConfigureStorage>(is_powered_on, this)},
web_tab{std::make_unique<ConfigureWeb>(this)}, ui_tab{std::make_unique<ConfigureUi>(this)} {
Settings::SetConfiguringGlobal(true); Settings::SetConfiguringGlobal(true);
ui->setupUi(this); ui->setupUi(this);
ui->hotkeysTab->Populate(registry);
ui->webTab->SetWebServiceConfigEnabled(enable_web_config); ui->tabWidget->addTab(general_tab.get(), tr("General"));
ui->tabWidget->addTab(system_tab.get(), tr("System"));
ui->tabWidget->addTab(input_tab.get(), tr("Input"));
ui->tabWidget->addTab(hotkeys_tab.get(), tr("Hotkeys"));
ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics"));
ui->tabWidget->addTab(enhancements_tab.get(), tr("Enhancements"));
ui->tabWidget->addTab(audio_tab.get(), tr("Audio"));
ui->tabWidget->addTab(camera_tab.get(), tr("Camera"));
ui->tabWidget->addTab(debug_tab.get(), tr("Debug"));
ui->tabWidget->addTab(storage_tab.get(), tr("Storage"));
ui->tabWidget->addTab(web_tab.get(), tr("Web"));
ui->tabWidget->addTab(ui_tab.get(), tr("UI"));
hotkeys_tab->Populate(registry);
web_tab->SetWebServiceConfigEnabled(enable_web_config);
PopulateSelectionList(); PopulateSelectionList();
connect(ui->uiTab, &ConfigureUi::LanguageChanged, this, &ConfigureDialog::OnLanguageChanged); connect(ui_tab.get(), &ConfigureUi::LanguageChanged, this, &ConfigureDialog::OnLanguageChanged);
connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, connect(ui->selectorList, &QListWidget::itemSelectionChanged, this,
&ConfigureDialog::UpdateVisibleTabs); &ConfigureDialog::UpdateVisibleTabs);
@ -31,46 +67,46 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Cor
ui->selectorList->setCurrentRow(0); ui->selectorList->setCurrentRow(0);
// Set up used key list synchronisation // Set up used key list synchronisation
connect(ui->inputTab, &ConfigureInput::InputKeysChanged, ui->hotkeysTab, connect(input_tab.get(), &ConfigureInput::InputKeysChanged, hotkeys_tab.get(),
&ConfigureHotkeys::OnInputKeysChanged); &ConfigureHotkeys::OnInputKeysChanged);
connect(ui->hotkeysTab, &ConfigureHotkeys::HotkeysChanged, ui->inputTab, connect(hotkeys_tab.get(), &ConfigureHotkeys::HotkeysChanged, input_tab.get(),
&ConfigureInput::OnHotkeysChanged); &ConfigureInput::OnHotkeysChanged);
// Synchronise lists upon initialisation // Synchronise lists upon initialisation
ui->inputTab->EmitInputKeysChanged(); input_tab->EmitInputKeysChanged();
ui->hotkeysTab->EmitHotkeysChanged(); hotkeys_tab->EmitHotkeysChanged();
} }
ConfigureDialog::~ConfigureDialog() = default; ConfigureDialog::~ConfigureDialog() = default;
void ConfigureDialog::SetConfiguration() { void ConfigureDialog::SetConfiguration() {
ui->generalTab->SetConfiguration(); general_tab->SetConfiguration();
ui->systemTab->SetConfiguration(); system_tab->SetConfiguration();
ui->inputTab->LoadConfiguration(); input_tab->LoadConfiguration();
ui->graphicsTab->SetConfiguration(); graphics_tab->SetConfiguration();
ui->enhancementsTab->SetConfiguration(); enhancements_tab->SetConfiguration();
ui->audioTab->SetConfiguration(); audio_tab->SetConfiguration();
ui->cameraTab->SetConfiguration(); camera_tab->SetConfiguration();
ui->debugTab->SetConfiguration(); debug_tab->SetConfiguration();
ui->webTab->SetConfiguration(); web_tab->SetConfiguration();
ui->uiTab->SetConfiguration(); ui_tab->SetConfiguration();
ui->storageTab->SetConfiguration(); storage_tab->SetConfiguration();
} }
void ConfigureDialog::ApplyConfiguration() { void ConfigureDialog::ApplyConfiguration() {
ui->generalTab->ApplyConfiguration(); general_tab->ApplyConfiguration();
ui->systemTab->ApplyConfiguration(); system_tab->ApplyConfiguration();
ui->inputTab->ApplyConfiguration(); input_tab->ApplyConfiguration();
ui->inputTab->ApplyProfile(); input_tab->ApplyProfile();
ui->hotkeysTab->ApplyConfiguration(registry); hotkeys_tab->ApplyConfiguration(registry);
ui->graphicsTab->ApplyConfiguration(); graphics_tab->ApplyConfiguration();
ui->enhancementsTab->ApplyConfiguration(); enhancements_tab->ApplyConfiguration();
ui->audioTab->ApplyConfiguration(); audio_tab->ApplyConfiguration();
ui->cameraTab->ApplyConfiguration(); camera_tab->ApplyConfiguration();
ui->debugTab->ApplyConfiguration(); debug_tab->ApplyConfiguration();
ui->webTab->ApplyConfiguration(); web_tab->ApplyConfiguration();
ui->uiTab->ApplyConfiguration(); ui_tab->ApplyConfiguration();
ui->storageTab->ApplyConfiguration(); storage_tab->ApplyConfiguration();
system.ApplySettings(); system.ApplySettings();
Settings::LogSettings(); Settings::LogSettings();
} }
@ -81,11 +117,11 @@ void ConfigureDialog::PopulateSelectionList() {
ui->selectorList->clear(); ui->selectorList->clear();
const std::array<std::pair<QString, QList<QWidget*>>, 5> items{ const std::array<std::pair<QString, QList<QWidget*>>, 5> items{
{{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}}, {{tr("General"), {general_tab.get(), web_tab.get(), debug_tab.get(), ui_tab.get()}},
{tr("System"), {ui->systemTab, ui->cameraTab, ui->storageTab}}, {tr("System"), {system_tab.get(), camera_tab.get(), storage_tab.get()}},
{tr("Graphics"), {ui->enhancementsTab, ui->graphicsTab}}, {tr("Graphics"), {enhancements_tab.get(), graphics_tab.get()}},
{tr("Audio"), {ui->audioTab}}, {tr("Audio"), {audio_tab.get()}},
{tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}}; {tr("Controls"), {input_tab.get(), hotkeys_tab.get()}}}};
for (const auto& entry : items) { for (const auto& entry : items) {
auto* const item = new QListWidgetItem(entry.first); auto* const item = new QListWidgetItem(entry.first);
@ -112,18 +148,18 @@ void ConfigureDialog::RetranslateUI() {
ui->selectorList->setCurrentRow(old_row); ui->selectorList->setCurrentRow(old_row);
ui->tabWidget->setCurrentIndex(old_index); ui->tabWidget->setCurrentIndex(old_index);
ui->generalTab->RetranslateUI(); general_tab->RetranslateUI();
ui->systemTab->RetranslateUI(); system_tab->RetranslateUI();
ui->inputTab->RetranslateUI(); input_tab->RetranslateUI();
ui->hotkeysTab->RetranslateUI(); hotkeys_tab->RetranslateUI();
ui->graphicsTab->RetranslateUI(); graphics_tab->RetranslateUI();
ui->enhancementsTab->RetranslateUI(); enhancements_tab->RetranslateUI();
ui->audioTab->RetranslateUI(); audio_tab->RetranslateUI();
ui->cameraTab->RetranslateUI(); camera_tab->RetranslateUI();
ui->debugTab->RetranslateUI(); debug_tab->RetranslateUI();
ui->webTab->RetranslateUI(); web_tab->RetranslateUI();
ui->uiTab->RetranslateUI(); ui_tab->RetranslateUI();
ui->storageTab->RetranslateUI(); storage_tab->RetranslateUI();
} }
void ConfigureDialog::UpdateVisibleTabs() { void ConfigureDialog::UpdateVisibleTabs() {
@ -131,18 +167,18 @@ void ConfigureDialog::UpdateVisibleTabs() {
if (items.isEmpty()) if (items.isEmpty())
return; return;
const std::map<QWidget*, QString> widgets = {{ui->generalTab, tr("General")}, const std::map<QWidget*, QString> widgets = {{general_tab.get(), tr("General")},
{ui->systemTab, tr("System")}, {system_tab.get(), tr("System")},
{ui->inputTab, tr("Input")}, {input_tab.get(), tr("Input")},
{ui->hotkeysTab, tr("Hotkeys")}, {hotkeys_tab.get(), tr("Hotkeys")},
{ui->enhancementsTab, tr("Enhancements")}, {enhancements_tab.get(), tr("Enhancements")},
{ui->graphicsTab, tr("Advanced")}, {graphics_tab.get(), tr("Advanced")},
{ui->audioTab, tr("Audio")}, {audio_tab.get(), tr("Audio")},
{ui->cameraTab, tr("Camera")}, {camera_tab.get(), tr("Camera")},
{ui->debugTab, tr("Debug")}, {debug_tab.get(), tr("Debug")},
{ui->storageTab, tr("Storage")}, {storage_tab.get(), tr("Storage")},
{ui->webTab, tr("Web")}, {web_tab.get(), tr("Web")},
{ui->uiTab, tr("UI")}}; {ui_tab.get(), tr("UI")}};
ui->tabWidget->clear(); ui->tabWidget->clear();

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;
@ -17,11 +19,25 @@ namespace Core {
class System; class System;
} }
class ConfigureGeneral;
class ConfigureSystem;
class ConfigureInput;
class ConfigureHotkeys;
class ConfigureGraphics;
class ConfigureEnhancements;
class ConfigureAudio;
class ConfigureCamera;
class ConfigureDebug;
class ConfigureStorage;
class ConfigureWeb;
class ConfigureUi;
class ConfigureDialog : public QDialog { class ConfigureDialog : public QDialog {
Q_OBJECT Q_OBJECT
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;
@ -42,4 +58,18 @@ private:
std::unique_ptr<Ui::ConfigureDialog> ui; std::unique_ptr<Ui::ConfigureDialog> ui;
HotkeyRegistry& registry; HotkeyRegistry& registry;
Core::System& system; Core::System& system;
bool is_powered_on;
std::unique_ptr<ConfigureGeneral> general_tab;
std::unique_ptr<ConfigureSystem> system_tab;
std::unique_ptr<ConfigureInput> input_tab;
std::unique_ptr<ConfigureHotkeys> hotkeys_tab;
std::unique_ptr<ConfigureGraphics> graphics_tab;
std::unique_ptr<ConfigureEnhancements> enhancements_tab;
std::unique_ptr<ConfigureAudio> audio_tab;
std::unique_ptr<ConfigureCamera> camera_tab;
std::unique_ptr<ConfigureDebug> debug_tab;
std::unique_ptr<ConfigureStorage> storage_tab;
std::unique_ptr<ConfigureWeb> web_tab;
std::unique_ptr<ConfigureUi> ui_tab;
}; };

View File

@ -22,8 +22,7 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent)
const bool res_scale_enabled = graphics_api != Settings::GraphicsAPI::Software; const bool res_scale_enabled = graphics_api != Settings::GraphicsAPI::Software;
ui->resolution_factor_combobox->setEnabled(res_scale_enabled); ui->resolution_factor_combobox->setEnabled(res_scale_enabled);
connect(ui->render_3d_combobox, connect(ui->render_3d_combobox, qOverload<int>(&QComboBox::currentIndexChanged), this,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
[this](int currentIndex) { [this](int currentIndex) {
updateShaders(static_cast<Settings::StereoRenderOption>(currentIndex)); updateShaders(static_cast<Settings::StereoRenderOption>(currentIndex));
}); });

View File

@ -6,17 +6,35 @@
#include "citra_qt/configuration/configuration_shared.h" #include "citra_qt/configuration/configuration_shared.h"
#include "citra_qt/configuration/configure_graphics.h" #include "citra_qt/configuration/configure_graphics.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h"
#include "ui_configure_graphics.h" #include "ui_configure_graphics.h"
#include "video_core/renderer_vulkan/vk_instance.h"
ConfigureGraphics::ConfigureGraphics(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);
ui->toggle_vsync_new->setEnabled(!Core::System::GetInstance().IsPoweredOn()); SetupPerGameUI();
for (const QString& name : physical_devices) {
ui->physical_device_combo->addItem(name);
}
ui->toggle_vsync_new->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 =
@ -29,12 +47,15 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
}); });
connect(ui->toggle_hw_shader, &QCheckBox::toggled, this, [this] { connect(ui->toggle_hw_shader, &QCheckBox::toggled, this, [this] {
const bool enabled = ui->toggle_hw_shader->isEnabled();
const bool checked = ui->toggle_hw_shader->isChecked(); const bool checked = ui->toggle_hw_shader->isChecked();
ui->hw_shader_group->setEnabled(checked); ui->hw_shader_group->setEnabled(checked && enabled);
ui->toggle_disk_shader_cache->setEnabled(checked); ui->toggle_disk_shader_cache->setEnabled(checked && enabled);
}); });
SetupPerGameUI(); connect(ui->graphics_api_combo, qOverload<int>(&QComboBox::currentIndexChanged), this,
&ConfigureGraphics::SetPhysicalDeviceComboVisibility);
SetConfiguration(); SetConfiguration();
} }
@ -46,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());
@ -64,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,
@ -92,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;
} }
@ -101,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(
@ -110,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(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">
@ -19,6 +19,9 @@
<property name="windowTitle"> <property name="windowTitle">
<string>Form</string> <string>Form</string>
</property> </property>
<property name="accessibleName">
<string>Graphics</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QGroupBox" name="apiBox"> <widget class="QGroupBox" name="apiBox">
@ -60,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>
@ -92,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>
@ -140,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

@ -9,7 +9,6 @@
#include "citra_qt/configuration/configure_hotkeys.h" #include "citra_qt/configuration/configure_hotkeys.h"
#include "citra_qt/hotkeys.h" #include "citra_qt/hotkeys.h"
#include "citra_qt/util/sequence_dialog/sequence_dialog.h" #include "citra_qt/util/sequence_dialog/sequence_dialog.h"
#include "common/settings.h"
#include "ui_configure_hotkeys.h" #include "ui_configure_hotkeys.h"
constexpr int name_column = 0; constexpr int name_column = 0;
@ -189,9 +188,9 @@ void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) {
QAction* clear = context_menu.addAction(tr("Clear")); QAction* clear = context_menu.addAction(tr("Clear"));
const auto hotkey_index = index.sibling(index.row(), hotkey_column); const auto hotkey_index = index.sibling(index.row(), hotkey_column);
connect(restore_default, &QAction::triggered, connect(restore_default, &QAction::triggered, this,
[this, hotkey_index] { RestoreHotkey(hotkey_index); }); [this, hotkey_index] { RestoreHotkey(hotkey_index); });
connect(clear, &QAction::triggered, connect(clear, &QAction::triggered, this,
[this, hotkey_index] { model->setData(hotkey_index, QString{}); }); [this, hotkey_index] { model->setData(hotkey_index, QString{}); });
context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location)); context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location));

View File

@ -162,7 +162,7 @@ ConfigureInput::ConfigureInput(QWidget* parent)
ui->buttonDpadUp, ui->buttonDpadDown, ui->buttonDpadLeft, ui->buttonDpadRight, ui->buttonDpadUp, ui->buttonDpadDown, ui->buttonDpadLeft, ui->buttonDpadRight,
ui->buttonL, ui->buttonR, ui->buttonStart, ui->buttonSelect, ui->buttonL, ui->buttonR, ui->buttonStart, ui->buttonSelect,
ui->buttonDebug, ui->buttonGpio14, ui->buttonZL, ui->buttonZR, ui->buttonDebug, ui->buttonGpio14, ui->buttonZL, ui->buttonZR,
ui->buttonHome, ui->buttonHome, ui->buttonPower,
}; };
analog_map_buttons = {{ analog_map_buttons = {{

File diff suppressed because it is too large Load Diff

View File

@ -24,19 +24,21 @@
#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 ? filename : fmt::format("{:016X}", title_id); const auto config_file_name = title_id == 0 ? std::string(FileUtil::GetFilename(filename))
: fmt::format("{:016X}", title_id);
game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig); game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig);
audio_tab = std::make_unique<ConfigureAudio>(this); const bool is_powered_on = system.IsPoweredOn();
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>(this); graphics_tab = std::make_unique<ConfigureGraphics>(physical_devices, is_powered_on, this);
system_tab = std::make_unique<ConfigureSystem>(this); system_tab = std::make_unique<ConfigureSystem>(system, this);
debug_tab = std::make_unique<ConfigureDebug>(this); debug_tab = std::make_unique<ConfigureDebug>(is_powered_on, this);
cheat_tab = std::make_unique<ConfigureCheats>(title_id, this); cheat_tab = std::make_unique<ConfigureCheats>(system, title_id, this);
ui->setupUi(this); ui->setupUi(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

@ -6,12 +6,12 @@
#include <QFileDialog> #include <QFileDialog>
#include <QUrl> #include <QUrl>
#include "citra_qt/configuration/configure_storage.h" #include "citra_qt/configuration/configure_storage.h"
#include "common/file_util.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h"
#include "ui_configure_storage.h" #include "ui_configure_storage.h"
ConfigureStorage::ConfigureStorage(QWidget* parent) ConfigureStorage::ConfigureStorage(bool is_powered_on_, QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureStorage>()) { : QWidget(parent), ui(std::make_unique<Ui::ConfigureStorage>()), is_powered_on{is_powered_on_} {
ui->setupUi(this); ui->setupUi(this);
SetConfiguration(); SetConfiguration();
@ -74,7 +74,7 @@ void ConfigureStorage::SetConfiguration() {
ui->toggle_virtual_sd->setChecked(Settings::values.use_virtual_sd.GetValue()); ui->toggle_virtual_sd->setChecked(Settings::values.use_virtual_sd.GetValue());
ui->toggle_custom_storage->setChecked(Settings::values.use_custom_storage.GetValue()); ui->toggle_custom_storage->setChecked(Settings::values.use_custom_storage.GetValue());
ui->storage_group->setEnabled(!Core::System::GetInstance().IsPoweredOn()); ui->storage_group->setEnabled(!is_powered_on);
} }
void ConfigureStorage::ApplyConfiguration() { void ConfigureStorage::ApplyConfiguration() {

View File

@ -15,7 +15,7 @@ class ConfigureStorage : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureStorage(QWidget* parent = nullptr); explicit ConfigureStorage(bool is_powered_on, QWidget* parent = nullptr);
~ConfigureStorage() override; ~ConfigureStorage() override;
void ApplyConfiguration(); void ApplyConfiguration();
@ -23,4 +23,5 @@ public:
void SetConfiguration(); void SetConfiguration();
std::unique_ptr<Ui::ConfigureStorage> ui; std::unique_ptr<Ui::ConfigureStorage> ui;
bool is_powered_on;
}; };

View File

@ -223,14 +223,12 @@ static const std::array<const char*, 187> country_names = {
QT_TRANSLATE_NOOP("ConfigureSystem", "Bermuda"), // 180-186 QT_TRANSLATE_NOOP("ConfigureSystem", "Bermuda"), // 180-186
}; };
ConfigureSystem::ConfigureSystem(QWidget* parent) ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureSystem>()) { : QWidget(parent), ui(std::make_unique<Ui::ConfigureSystem>()), system{system_} {
ui->setupUi(this); ui->setupUi(this);
connect(ui->combo_birthmonth, connect(ui->combo_birthmonth, qOverload<int>(&QComboBox::currentIndexChanged), this,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
&ConfigureSystem::UpdateBirthdayComboBox); &ConfigureSystem::UpdateBirthdayComboBox);
connect(ui->combo_init_clock, connect(ui->combo_init_clock, qOverload<int>(&QComboBox::currentIndexChanged), this,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
&ConfigureSystem::UpdateInitTime); &ConfigureSystem::UpdateInitTime);
connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,
&ConfigureSystem::RefreshConsoleID); &ConfigureSystem::RefreshConsoleID);
@ -280,7 +278,7 @@ ConfigureSystem::ConfigureSystem(QWidget* parent)
ConfigureSystem::~ConfigureSystem() = default; ConfigureSystem::~ConfigureSystem() = default;
void ConfigureSystem::SetConfiguration() { void ConfigureSystem::SetConfiguration() {
enabled = !Core::System::GetInstance().IsPoweredOn(); enabled = !system.IsPoweredOn();
ui->combo_init_clock->setCurrentIndex(static_cast<u8>(Settings::values.init_clock.GetValue())); ui->combo_init_clock->setCurrentIndex(static_cast<u8>(Settings::values.init_clock.GetValue()));
QDateTime date_time; QDateTime date_time;
@ -296,7 +294,7 @@ void ConfigureSystem::SetConfiguration() {
ui->edit_init_time_offset_time->setTime(time); ui->edit_init_time_offset_time->setTime(time);
if (!enabled) { if (!enabled) {
cfg = Service::CFG::GetModule(Core::System::GetInstance()); cfg = Service::CFG::GetModule(system);
ASSERT_MSG(cfg, "CFG Module missing!"); ASSERT_MSG(cfg, "CFG Module missing!");
ReadSystemSettings(); ReadSystemSettings();
ui->group_system_settings->setEnabled(false); ui->group_system_settings->setEnabled(false);
@ -553,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 =
@ -592,5 +589,4 @@ void ConfigureSystem::DownloadFromNUS() {
} }
ui->button_start_download->setEnabled(true); ui->button_start_download->setEnabled(true);
#endif
} }

View File

@ -16,6 +16,10 @@ namespace ConfigurationShared {
enum class CheckState; enum class CheckState;
} }
namespace Core {
class System;
}
namespace Service { namespace Service {
namespace CFG { namespace CFG {
class Module; class Module;
@ -26,7 +30,7 @@ class ConfigureSystem : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureSystem(QWidget* parent = nullptr); explicit ConfigureSystem(Core::System& system, QWidget* parent = nullptr);
~ConfigureSystem() override; ~ConfigureSystem() override;
void ApplyConfiguration(); void ApplyConfiguration();
@ -45,8 +49,10 @@ private:
void DownloadFromNUS(); void DownloadFromNUS();
ConfigurationShared::CheckState is_new_3ds; private:
std::unique_ptr<Ui::ConfigureSystem> ui; std::unique_ptr<Ui::ConfigureSystem> ui;
Core::System& system;
ConfigurationShared::CheckState is_new_3ds;
bool enabled = false; bool enabled = false;
std::shared_ptr<Service::CFG::Module> cfg; std::shared_ptr<Service::CFG::Module> cfg;

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

@ -167,8 +167,7 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
const auto format = texture.format; const auto format = texture.format;
const auto info = Pica::Texture::TextureInfo::FromPicaRegister(config, format); const auto info = Pica::Texture::TextureInfo::FromPicaRegister(config, format);
const u8* src = const u8* src = system.Memory().GetPhysicalPointer(config.GetPhysicalAddress());
Core::System::GetInstance().Memory().GetPhysicalPointer(config.GetPhysicalAddress());
new_info_widget = new TextureInfoWidget(src, info); new_info_widget = new TextureInfoWidget(src, info);
} }
if (command_info_widget) { if (command_info_widget) {
@ -182,8 +181,8 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
} }
#undef COMMAND_IN_RANGE #undef COMMAND_IN_RANGE
GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) GPUCommandListWidget::GPUCommandListWidget(Core::System& system_, QWidget* parent)
: QDockWidget(tr("Pica Command List"), parent) { : QDockWidget(tr("Pica Command List"), parent), system{system_} {
setObjectName(QStringLiteral("Pica Command List")); setObjectName(QStringLiteral("Pica Command List"));
GPUCommandListModel* model = new GPUCommandListModel(this); GPUCommandListModel* model = new GPUCommandListModel(this);

View File

@ -7,11 +7,14 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QDockWidget> #include <QDockWidget>
#include "video_core/debug_utils/debug_utils.h" #include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu_debugger.h"
class QPushButton; class QPushButton;
class QTreeView; class QTreeView;
namespace Core {
class System;
}
class GPUCommandListModel : public QAbstractListModel { class GPUCommandListModel : public QAbstractListModel {
Q_OBJECT Q_OBJECT
@ -39,7 +42,7 @@ class GPUCommandListWidget : public QDockWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit GPUCommandListWidget(QWidget* parent = nullptr); explicit GPUCommandListWidget(Core::System& system, QWidget* parent = nullptr);
public slots: public slots:
void OnToggleTracing(); void OnToggleTracing();
@ -54,7 +57,7 @@ signals:
private: private:
std::unique_ptr<Pica::DebugUtils::PicaTrace> pica_trace; std::unique_ptr<Pica::DebugUtils::PicaTrace> pica_trace;
Core::System& system;
QTreeView* list_widget; QTreeView* list_widget;
QWidget* command_info_widget; QWidget* command_info_widget;
QPushButton* toggle_tracing; QPushButton* toggle_tracing;

View File

@ -15,8 +15,6 @@
#include "citra_qt/debugger/graphics/graphics_surface.h" #include "citra_qt/debugger/graphics/graphics_surface.h"
#include "citra_qt/util/spinbox.h" #include "citra_qt/util/spinbox.h"
#include "common/color.h" #include "common/color.h"
#include "core/core.h"
#include "core/hw/gpu.h"
#include "core/memory.h" #include "core/memory.h"
#include "video_core/pica_state.h" #include "video_core/pica_state.h"
#include "video_core/regs_framebuffer.h" #include "video_core/regs_framebuffer.h"
@ -51,9 +49,10 @@ void SurfacePicture::mouseMoveEvent(QMouseEvent* event) {
mousePressEvent(event); mousePressEvent(event);
} }
GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, GraphicsSurfaceWidget::GraphicsSurfaceWidget(Memory::MemorySystem& memory_,
std::shared_ptr<Pica::DebugContext> debug_context,
QWidget* parent) QWidget* parent)
: BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent), : BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent), memory{memory_},
surface_source(Source::ColorBuffer) { surface_source(Source::ColorBuffer) {
setObjectName(QStringLiteral("PicaSurface")); setObjectName(QStringLiteral("PicaSurface"));
@ -290,57 +289,57 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
return; return;
} }
u8* buffer = Core::System::GetInstance().Memory().GetPhysicalPointer(surface_address); const u8* buffer = memory.GetPhysicalPointer(surface_address);
if (buffer == nullptr) { if (!buffer) {
surface_info_label->setText(tr("(unable to access pixel data)")); surface_info_label->setText(tr("(unable to access pixel data)"));
surface_info_label->setAlignment(Qt::AlignCenter); surface_info_label->setAlignment(Qt::AlignCenter);
return; return;
} }
unsigned nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format); const u32 nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format);
unsigned stride = nibbles_per_pixel * surface_width / 2; const u32 stride = nibbles_per_pixel * surface_width / 2;
const bool nibble_mode = (nibbles_per_pixel == 1);
unsigned bytes_per_pixel; const u32 bytes_per_pixel = [&] {
bool nibble_mode = (nibbles_per_pixel == 1); if (nibble_mode) {
if (nibble_mode) { // As nibbles are contained in a byte we still need to access one byte per nibble
// As nibbles are contained in a byte we still need to access one byte per nibble return 1u;
bytes_per_pixel = 1; } else {
} else { return nibbles_per_pixel / 2;
bytes_per_pixel = nibbles_per_pixel / 2; }
} }();
const u32 coarse_y = y & ~7; const u32 coarse_y = y & ~7;
u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride; const u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
const u8* pixel = buffer + (nibble_mode ? (offset / 2) : offset); const u8* pixel = buffer + (nibble_mode ? (offset / 2) : offset);
auto GetText = [offset](Format format, const u8* pixel) { const auto get_text = [offset](Format format, const u8* pixel) {
switch (format) { switch (format) {
case Format::RGBA8: { case Format::RGBA8: {
auto value = Common::Color::DecodeRGBA8(pixel) / 255.0f; const auto value = Common::Color::DecodeRGBA8(pixel) / 255.0f;
return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4") return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4")
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2), .arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
QString::number(value.b(), 'f', 2), QString::number(value.a(), 'f', 2)); QString::number(value.b(), 'f', 2), QString::number(value.a(), 'f', 2));
} }
case Format::RGB8: { case Format::RGB8: {
auto value = Common::Color::DecodeRGB8(pixel) / 255.0f; const auto value = Common::Color::DecodeRGB8(pixel) / 255.0f;
return QStringLiteral("Red: %1, Green: %2, Blue: %3") return QStringLiteral("Red: %1, Green: %2, Blue: %3")
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2), .arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
QString::number(value.b(), 'f', 2)); QString::number(value.b(), 'f', 2));
} }
case Format::RGB5A1: { case Format::RGB5A1: {
auto value = Common::Color::DecodeRGB5A1(pixel) / 255.0f; const auto value = Common::Color::DecodeRGB5A1(pixel) / 255.0f;
return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4") return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4")
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2), .arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
QString::number(value.b(), 'f', 2), QString::number(value.a(), 'f', 2)); QString::number(value.b(), 'f', 2), QString::number(value.a(), 'f', 2));
} }
case Format::RGB565: { case Format::RGB565: {
auto value = Common::Color::DecodeRGB565(pixel) / 255.0f; const auto value = Common::Color::DecodeRGB565(pixel) / 255.0f;
return QStringLiteral("Red: %1, Green: %2, Blue: %3") return QStringLiteral("Red: %1, Green: %2, Blue: %3")
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2), .arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
QString::number(value.b(), 'f', 2)); QString::number(value.b(), 'f', 2));
} }
case Format::RGBA4: { case Format::RGBA4: {
auto value = Common::Color::DecodeRGBA4(pixel) / 255.0f; const auto value = Common::Color::DecodeRGBA4(pixel) / 255.0f;
return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4") return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4")
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2), .arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
QString::number(value.b(), 'f', 2), QString::number(value.a(), 'f', 2)); QString::number(value.b(), 'f', 2), QString::number(value.a(), 'f', 2));
@ -348,7 +347,7 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
case Format::IA8: case Format::IA8:
return QStringLiteral("Index: %1, Alpha: %2").arg(pixel[0], pixel[1]); return QStringLiteral("Index: %1, Alpha: %2").arg(pixel[0], pixel[1]);
case Format::RG8: { case Format::RG8: {
auto value = Common::Color::DecodeRG8(pixel) / 255.0f; const auto value = Common::Color::DecodeRG8(pixel) / 255.0f;
return QStringLiteral("Red: %1, Green: %2") return QStringLiteral("Red: %1, Green: %2")
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2)); .arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2));
} }
@ -359,11 +358,11 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
case Format::IA4: case Format::IA4:
return QStringLiteral("Index: %1, Alpha: %2").arg(*pixel & 0xF, (*pixel & 0xF0) >> 4); return QStringLiteral("Index: %1, Alpha: %2").arg(*pixel & 0xF, (*pixel & 0xF0) >> 4);
case Format::I4: { case Format::I4: {
u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF; const u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
return QStringLiteral("Index: %1").arg(i); return QStringLiteral("Index: %1").arg(i);
} }
case Format::A4: { case Format::A4: {
u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF; const u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
return QStringLiteral("Alpha: %1").arg(QString::number(a / 15.0f, 'f', 2)); return QStringLiteral("Alpha: %1").arg(QString::number(a / 15.0f, 'f', 2));
} }
case Format::ETC1: case Format::ETC1:
@ -371,17 +370,17 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
// TODO: Display block information or channel values? // TODO: Display block information or channel values?
return QStringLiteral("Compressed data"); return QStringLiteral("Compressed data");
case Format::D16: { case Format::D16: {
auto value = Common::Color::DecodeD16(pixel); const auto value = Common::Color::DecodeD16(pixel);
return QStringLiteral("Depth: %1").arg(QString::number(value / (float)0xFFFF, 'f', 4)); return QStringLiteral("Depth: %1").arg(QString::number(value / (float)0xFFFF, 'f', 4));
} }
case Format::D24: { case Format::D24: {
auto value = Common::Color::DecodeD24(pixel); const auto value = Common::Color::DecodeD24(pixel);
return QStringLiteral("Depth: %1") return QStringLiteral("Depth: %1")
.arg(QString::number(value / (float)0xFFFFFF, 'f', 4)); .arg(QString::number(value / (float)0xFFFFFF, 'f', 4));
} }
case Format::D24X8: case Format::D24X8:
case Format::X24S8: { case Format::X24S8: {
auto values = Common::Color::DecodeD24S8(pixel); const auto values = Common::Color::DecodeD24S8(pixel);
return QStringLiteral("Depth: %1, Stencil: %2") return QStringLiteral("Depth: %1, Stencil: %2")
.arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4), values[1]); .arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4), values[1]);
} }
@ -398,13 +397,13 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
if (nibble_mode) { if (nibble_mode) {
nibble_index += (offset % 2) ? 0 : 1; nibble_index += (offset % 2) ? 0 : 1;
} }
u8 byte = pixel[nibble_index / 2]; const u8 byte = pixel[nibble_index / 2];
u8 nibble = (byte >> ((nibble_index % 2) ? 0 : 4)) & 0xF; const u8 nibble = (byte >> ((nibble_index % 2) ? 0 : 4)) & 0xF;
nibbles.append(QString::number(nibble, 16).toUpper()); nibbles.append(QString::number(nibble, 16).toUpper());
} }
surface_info_label->setText( surface_info_label->setText(
QStringLiteral("Raw: 0x%3\n(%4)").arg(nibbles, GetText(surface_format, pixel))); QStringLiteral("Raw: 0x%3\n(%4)").arg(nibbles, get_text(surface_format, pixel)));
surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
} }
@ -546,9 +545,9 @@ void GraphicsSurfaceWidget::OnUpdate() {
// TODO: Implement a good way to visualize alpha components! // TODO: Implement a good way to visualize alpha components!
QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32); QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
u8* buffer = Core::System::GetInstance().Memory().GetPhysicalPointer(surface_address); const u8* buffer = memory.GetPhysicalPointer(surface_address);
if (buffer == nullptr) { if (!buffer) {
surface_picture_label->hide(); surface_picture_label->hide();
surface_info_label->setText(tr("(invalid surface address)")); surface_info_label->setText(tr("(invalid surface address)"));
surface_info_label->setAlignment(Qt::AlignCenter); surface_info_label->setAlignment(Qt::AlignCenter);
@ -682,9 +681,8 @@ void GraphicsSurfaceWidget::SaveSurface() {
tr("Failed to save surface data to file '%1'").arg(filename)); tr("Failed to save surface data to file '%1'").arg(filename));
} }
} else if (selected_filter == bin_filter) { } else if (selected_filter == bin_filter) {
const u8* const buffer = const u8* const buffer = memory.GetPhysicalPointer(surface_address);
Core::System::GetInstance().Memory().GetPhysicalPointer(surface_address); ASSERT_MSG(buffer, "Memory not accessible");
ASSERT_MSG(buffer != nullptr, "Memory not accessible");
QFile file{filename}; QFile file{filename};
if (!file.open(QIODevice::WriteOnly)) { if (!file.open(QIODevice::WriteOnly)) {

View File

@ -14,6 +14,10 @@ class CSpinBox;
class GraphicsSurfaceWidget; class GraphicsSurfaceWidget;
namespace Memory {
class MemorySystem;
}
class SurfacePicture : public QLabel { class SurfacePicture : public QLabel {
Q_OBJECT Q_OBJECT
@ -72,7 +76,8 @@ class GraphicsSurfaceWidget : public BreakPointObserverDock {
static unsigned int NibblesPerPixel(Format format); static unsigned int NibblesPerPixel(Format format);
public: public:
explicit GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, explicit GraphicsSurfaceWidget(Memory::MemorySystem& memory,
std::shared_ptr<Pica::DebugContext> debug_context,
QWidget* parent = nullptr); QWidget* parent = nullptr);
void Pick(int x, int y); void Pick(int x, int y);
@ -95,6 +100,7 @@ private:
void SaveSurface(); void SaveSurface();
Memory::MemorySystem& memory;
QComboBox* surface_source_list; QComboBox* surface_source_list;
CSpinBox* surface_address_control; CSpinBox* surface_address_control;
QSpinBox* surface_width_control; QSpinBox* surface_width_control;

View File

@ -16,13 +16,13 @@
#include "core/hle/service/sm/sm.h" #include "core/hle/service/sm/sm.h"
#include "ui_recorder.h" #include "ui_recorder.h"
IPCRecorderWidget::IPCRecorderWidget(QWidget* parent) IPCRecorderWidget::IPCRecorderWidget(Core::System& system_, QWidget* parent)
: QDockWidget(parent), ui(std::make_unique<Ui::IPCRecorder>()) { : QDockWidget(parent), ui(std::make_unique<Ui::IPCRecorder>()), system{system_} {
ui->setupUi(this); ui->setupUi(this);
qRegisterMetaType<IPCDebugger::RequestRecord>(); qRegisterMetaType<IPCDebugger::RequestRecord>();
connect(ui->enabled, &QCheckBox::stateChanged, connect(ui->enabled, &QCheckBox::stateChanged, this,
[this](int new_state) { SetEnabled(new_state == Qt::Checked); }); [this](int new_state) { SetEnabled(new_state == Qt::Checked); });
connect(ui->clearButton, &QPushButton::clicked, this, &IPCRecorderWidget::Clear); connect(ui->clearButton, &QPushButton::clicked, this, &IPCRecorderWidget::Clear);
connect(ui->filter, &QLineEdit::textChanged, this, &IPCRecorderWidget::ApplyFilterToAll); connect(ui->filter, &QLineEdit::textChanged, this, &IPCRecorderWidget::ApplyFilterToAll);
@ -90,7 +90,7 @@ void IPCRecorderWidget::OnEntryUpdated(IPCDebugger::RequestRecord record) {
(record.status == IPCDebugger::RequestStatus::Handled && (record.status == IPCDebugger::RequestStatus::Handled &&
record.translated_reply_cmdbuf[1] != RESULT_SUCCESS.raw)) { // Unimplemented / Error record.translated_reply_cmdbuf[1] != RESULT_SUCCESS.raw)) { // Unimplemented / Error
auto* item = ui->main->invisibleRootItem()->child(row_id); auto item = ui->main->invisibleRootItem()->child(row_id);
for (int column = 0; column < item->columnCount(); ++column) { for (int column = 0; column < item->columnCount(); ++column) {
item->setBackground(column, QBrush(QColor::fromRgb(255, 0, 0))); item->setBackground(column, QBrush(QColor::fromRgb(255, 0, 0)));
} }
@ -100,11 +100,11 @@ void IPCRecorderWidget::OnEntryUpdated(IPCDebugger::RequestRecord record) {
} }
void IPCRecorderWidget::SetEnabled(bool enabled) { void IPCRecorderWidget::SetEnabled(bool enabled) {
if (!Core::System::GetInstance().IsPoweredOn()) { if (!system.IsPoweredOn()) {
return; return;
} }
auto& ipc_recorder = Core::System::GetInstance().Kernel().GetIPCRecorder(); auto& ipc_recorder = system.Kernel().GetIPCRecorder();
ipc_recorder.SetEnabled(enabled); ipc_recorder.SetEnabled(enabled);
if (enabled) { if (enabled) {
@ -123,10 +123,10 @@ void IPCRecorderWidget::Clear() {
} }
QString IPCRecorderWidget::GetServiceName(const IPCDebugger::RequestRecord& record) const { QString IPCRecorderWidget::GetServiceName(const IPCDebugger::RequestRecord& record) const {
if (Core::System::GetInstance().IsPoweredOn() && record.client_port.id != -1) { if (system.IsPoweredOn() && record.client_port.id != -1) {
const auto service_name = const Service::SM::ServiceManager& sm = system.ServiceManager();
Core::System::GetInstance().ServiceManager().GetServiceNameByPortId( const u32 port_id = static_cast<u32>(record.client_port.id);
static_cast<u32>(record.client_port.id)); const auto service_name = sm.GetServiceNameByPortId(port_id);
if (!service_name.empty()) { if (!service_name.empty()) {
return QString::fromStdString(service_name); return QString::fromStdString(service_name);

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