Compare commits

..

70 Commits

Author SHA1 Message Date
Lioncash
25dda06f49 producer_listener: Add virtual destructor to IProducerListener
Several member variables are shared_ptr's to this base class. Even
though producer listeners are still unimplemented, this ensures we
always have consistent deletion behavior once this ends up being used
polymorphically.
2022-11-28 19:39:13 -05:00
liamwhite
6291eec700 Merge pull request #9325 from german77/default_by_default
yuzu-cmd: Fix default config value
2022-11-28 08:14:32 -05:00
german77
aaada241dc yuzu-cmd: Fix default config value 2022-11-27 20:43:34 -06:00
liamwhite
6b64557ad6 Merge pull request #8829 from Docteh/qt6_0002
CMake: rework for Qt6 support
2022-11-27 17:56:22 -05:00
liamwhite
a2fde04da9 Merge pull request #9317 from german77/input-crash
yuzu-cmd: Fix input callback crash on close
2022-11-27 15:39:24 -05:00
liamwhite
71f2b4ba8f Merge pull request #9323 from german77/intructions
yuzu-cmd: Update configuration file description
2022-11-27 12:54:01 -05:00
german77
a67e776af9 yuzu-cmd: Fix input callback crash on close 2022-11-27 10:53:22 -06:00
bunnei
3ab8d9ac7c Merge pull request #9276 from goldenx86/fsrSlider
FSR Sharpening Slider
2022-11-27 00:13:15 -08:00
Morph
ef6144bf48 Merge pull request #9324 from v1993/master
crypto: use user-provided keys whenever possible
2022-11-26 21:20:05 -05:00
Valeri
278c0e6e8d crypto: use user-provided keys whenever possible
Solves an issue where autogenerated title keys would take precedence over those provided by user.
2022-11-27 05:08:25 +03:00
german77
4a46da6fb5 yuzu-cmd: Update configuration file description 2022-11-26 19:48:22 -06:00
bunnei
eabe45346f Merge pull request #9318 from goldenx86/glsl-ftw
Replace GLSL as the default OpenGL shader backend
2022-11-26 15:57:37 -08:00
Matías Locatti
701ca96827 Oops 2022-11-26 17:39:43 -03:00
Matías Locatti
26211ac339 Replace GLSL as the default OpenGL shader backend
GLASM is not very compatible with the latest games, and too many people have the special superpower to break their Vulkan support.
2022-11-26 17:27:04 -03:00
liamwhite
3e53d8138c Merge pull request #9288 from vonchenplus/deferred_draw
video_core: Fine tune maxwell drawing trigger mechanism
2022-11-26 09:35:45 -05:00
liamwhite
ddca512f3f Merge pull request #9307 from Morph1984/not-used-correctly
maxwell_to_vk: Fix format usage bits and add R16_SINT
2022-11-26 09:08:55 -05:00
Matías Locatti
972bd6cb54 Sharpness instead of Sharpening 2022-11-26 02:14:10 -03:00
Matías Locatti
c8d84cb6cb Merge pull request #1 from lat9nq/fsrSlider
configure_graphics: Implement custom setting for FSR Sharpening
2022-11-26 00:51:08 -03:00
lat9nq
19a640286c configure_graphics: Implement custom FSR Sharpening setting 2022-11-25 22:42:54 -05:00
lat9nq
b670c350e4 settings: Reset FSR sharpening global state with the others 2022-11-25 22:42:24 -05:00
liamwhite
e16d1b85f1 Merge pull request #9297 from Kelebek1/sink_oob
[audio_core] Fix an OoB with sample sinking
2022-11-25 12:53:29 -05:00
bunnei
2572b0a5ea Merge pull request #9302 from liamwhite/why-are-we-still-using-ado
externals: always use LibreSSL on Windows
2022-11-25 00:39:16 -08:00
bunnei
e8cbc3b4c5 Merge pull request #9304 from liamwhite/menu-roll
Qt: assign menuRole properties for actions
2022-11-25 00:38:50 -08:00
bunnei
64965cc658 Merge pull request #9305 from lioncash/request
hle_ipc: Add helper function for determining element counts
2022-11-25 00:38:17 -08:00
liamwhite
20b62dbd30 Merge pull request #9194 from FernandoS27/yfc-fermi2d
YFC - Fermi2D: Rework blit engine and add a software blitter.
2022-11-24 21:48:41 -05:00
Morph
9d081a8729 Merge pull request #9312 from FernandoS27/pokemomma
GPU: Fix buffer cache issue, engine upload not inlining memory in multiple lines, etc
2022-11-24 18:24:07 -05:00
Fernando Sahmkow
826e0785bf Fermi2D: Cleanup and address feedback. 2022-11-24 21:00:48 +01:00
Fernando Sahmkow
3b582d5fb2 GPU: Fix buffer cache issue, engine upload not inlining memory in multiline and pessismistic invalidation. 2022-11-24 20:57:16 +01:00
Fernando Sahmkow
7356ab1de6 GPU: Implement additional render target formats. 2022-11-24 20:35:44 +01:00
Fernando Sahmkow
daf2ef8f1c MaxwellDMA: Implement BlockLinear to BlockLinear copies. 2022-11-24 20:35:44 +01:00
Fernando Sahmkow
5fbd6954ef Fermi2D: Implement Bilinear software filtering and address feedback. 2022-11-24 20:35:44 +01:00
Fernando Sahmkow
957840be91 Fermi2D: Rework blit engine and add a software blitter. 2022-11-24 20:35:44 +01:00
Kyle Kienapfel
a75542ad2d CMake: rework for Qt6 support
This PR rearranges things in the CMake system to make compiling with Qt6 possible

1. Camera API has changed in Qt6, so the camera feature is disabled
2. A previous fix involving QLocale is now version gated.
3. QRegExp replaced with QRegularExpression, see #5343
4. Qt6_LOCATION option added to specify a location to search for Qt6
  (see examples below)
5. windeployqt is used to copy Qt6 files into the build directory on Windows

Notes for Arch Linux
Arch install happened to have qt6-base qt6-declarative qt6-translations installed

mkdir build && cd build
cmake .. -GNinja -DYUZU_USE_BUNDLED_VCPKG=ON -DYUZU_TESTS=OFF -DENABLE_QT6=YES -DYUZU_USE_BUNDLED_QT=NO

Windows (MSVC)
Qt wants users to download precompiled libraries via an online installer,
it is worth noting that the GPL/LGPL takes precendence over any ...

In the Qt Maintenance tool, under a version, such as 6.3.1
Select "MSVC 2019 64-bit"
Under Additional Libraries Qt Multimedia may be of use for Camera support

For the Web Applet I had to select the following:
PDF Positioning WebChannel WebEngine

mkdir build && cd build
cmake -G "Visual Studio 16 2019" -DQt6_LOCATION=C:/Qt/6.4.0/msvc2019_64/ \
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=YES -DYUZU_USE_BUNDLED_QT=NO \
-DENABLE_QT_TRANSLATION=YES -DENABLE_QT6=YES ..

Some numbers for reference (msvc2019_64)
Qt5 (slimmed down) 508 MB
Qt5.15.2 all in    929 MB
Qt6.3.1           1.71 GB
Qt6.3.2           1.73 GB
Qt6.4.0-beta3     1.83 GB
Qt6.4.0           1.67 GB
2022-11-24 06:28:42 -08:00
Matías Locatti
f209e976f4 FSR Sharpening Slider part 1 - only a global slider 2022-11-24 04:22:13 -05:00
Morph
852de7a771 maxwell_to_vk: Add R16_SINT
This was somehow missed when the format was added to GL
2022-11-23 21:30:58 -05:00
Morph
ca154d466a maxwell_to_vk: Fix format usage bits
- VK_FORMAT_B8G8R8A8_UNORM supports the STORAGE_IMAGE_BIT
- VK_FORMAT_R4G4B4A4_UNORM_PACK16 does not support the COLOR_ATTACHMENT_BIT
2022-11-23 21:29:43 -05:00
Lioncash
97f273e94e service: Make use of buffer element count helpers 2022-11-23 13:43:20 -05:00
Lioncash
59335f6796 hle_ipc: Add helper functions for getting number of buffer elements 2022-11-23 13:15:19 -05:00
Lioncash
c31f19b6d1 hle_ipc: Mark relevant member functions as [[nodiscard]]
Will allow the compiler to complain about cases where ignoring the
return value would be a bug.
2022-11-23 13:08:52 -05:00
Liam
9abceaed61 Qt: assign menuRole properties for actions 2022-11-23 12:41:56 -05:00
Liam
cdb2e4eaff externals: always use LibreSSL on Windows 2022-11-23 10:24:25 -05:00
liamwhite
168c9ee341 Merge pull request #9299 from lioncash/cast
k_handle_table: Remove cast to void* in GetObjectForIpc
2022-11-22 17:47:53 -05:00
Lioncash
8d99aae45b k_handle_table: Remove cast to void* in GetObjectForIpc
This was used to get around the KProcess class being incomplete. We can
just move this to the cpp file and eliminate the cast entirely, letting
the compiler do its work.
2022-11-22 13:58:42 -05:00
bunnei
f047f376d4 Merge pull request #9219 from german77/nfc_impl
service: nfc: Implement NFC IUser service
2022-11-22 10:28:38 -08:00
Kelebek1
84d4da89a5 Use the maximum input index for samples buffer span size, not just the input count 2022-11-22 15:32:11 +00:00
FengChen
1d57851fc7 video_core: Optimize maxwell drawing trigger mechanism 2022-11-22 17:53:26 +08:00
Narr the Reg
3027917f39 Merge pull request #9292 from Morph1984/amiibo-web-service
qt_amiibo_settings: Use WebClient only if ENABLE_WEB_SERVICE is enabled
2022-11-21 17:24:59 -06:00
Morph
7e0f70e5a1 qt_amiibo_settings: Use WebClient only if ENABLE_WEB_SERVICE is enabled
Resolves compilation errors when ENABLE_WEB_SERVICE is disabled in CMake configuration
2022-11-21 15:22:17 -05:00
Morph
aab68674c0 Merge pull request #9279 from liamwhite/this-would-have-never-happened-in-rust
dmnt:cht: fix copy-paste error
2022-11-20 13:30:35 -05:00
liamwhite
7f1c6def1f Merge pull request #9216 from vonchenplus/reimp_inline_index_buffer
video_core: Reimplement inline index buffer binding
2022-11-20 12:08:08 -05:00
Narr the Reg
db7bcd51ae Merge branch 'master' into nfc_impl 2022-11-20 09:31:20 -06:00
Liam
eb0713f781 dmnt:cht: fix copy-paste error 2022-11-20 10:14:22 -05:00
bunnei
57a05b1653 Merge pull request #9238 from german77/cabinet_applet
service: am: Implement cabinet applet
2022-11-20 00:48:39 -08:00
Morph
69c92b8156 Merge pull request #9249 from goldenx86/available-vram
Add available Vulkan VRAM to log files
2022-11-20 00:21:29 -05:00
Morph
4acbf3a193 Merge pull request #9274 from Morph1984/issue-forms
github: Add new issue form templates
2022-11-20 00:21:24 -05:00
Morph
54d6273975 github: Add blank issue template
This is meant to only be used by developers.
2022-11-20 00:18:53 -05:00
Morph
98a6cd02c8 github: Remove old markdown form
Replaced by the newer issue forms template
2022-11-19 17:41:26 -05:00
Morph
52acdafa95 github: Add new feature request issue form 2022-11-19 17:41:26 -05:00
Morph
975e17aa13 github: Add new bug report issue form 2022-11-19 17:41:26 -05:00
Narr the Reg
327d225c3e service: nfc: Implement nfc user 2022-11-19 08:51:59 -06:00
Kyle Kienapfel
d23a35dfbd qt: Add Qt version to LogRuntimes 2022-11-17 19:14:14 -08:00
Kyle Kienapfel
ad3ee5c52b Qt6: Disable IR Sensor when compiling with Qt6
Gating the IR Sensor code behind a macro like so
`#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA`

The YUZU_USE_QT_MULTIMEDIA flag is implemented in later commit

Also the locale fix in src/yuzu/main.cpp is now gated against Qt6,
as it causes compilation error
2022-11-17 19:14:14 -08:00
Matías Locatti
7c50a916c7 Update renderer_vulkan.cpp 2022-11-16 05:53:42 -03:00
Feng Chen
cb971ad654 video_core: Reimplement inline index buffer binding 2022-11-15 12:10:44 +08:00
german77
75e6ec85e1 general: Address review comments 2022-11-13 17:13:43 -06:00
german77
a253d1557d service: am: Fix cabinet applet result 2022-11-13 14:25:00 -06:00
german77
9afadca5dc yuzu: Implement cabinet applet frontend 2022-11-13 13:58:19 -06:00
german77
fb57cd26a1 service: am: Implement cabinet applet backend 2022-11-13 11:07:48 -06:00
german77
b193d40d22 input_common: Add amiibo applet functions 2022-11-13 10:56:54 -06:00
german77
6c045c9beb service: nfc: fix tagprotocol and implement GetApplicationAreaId 2022-11-13 10:52:48 -06:00
103 changed files with 4675 additions and 525 deletions

View File

@@ -0,0 +1,10 @@
name: New Issue (Developers Only)
description: A blank issue template for developers only. If you are not a developer, do not use this issue template. Your issue WILL BE CLOSED if you do not use the appropriate issue template.
body:
- type: markdown
attributes:
value: |
**If you are not a developer, do not use this issue template. Your issue WILL BE CLOSED if you do not use the appropriate issue template.**
- type: textarea
attributes:
label: "Issue"

View File

@@ -1,39 +0,0 @@
---
name: Bug Report / Feature Request
about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu or you are requesting a feature you believe would make yuzu better.
title: ''
labels: ''
assignees: ''
---
<!---
Please keep in mind yuzu is EXPERIMENTAL SOFTWARE.
Please read the FAQ:
https://yuzu-emu.org/wiki/faq/
THIS IS NOT A SUPPORT FORUM, FOR SUPPORT GO TO:
https://community.citra-emu.org/
If the FAQ does not answer your question, please go to:
https://community.citra-emu.org/
When submitting an issue, please check the following:
- You have read the above.
- You have provided the version (commit hash) of yuzu you are using.
- You have provided sufficient detail for the issue to be reproduced.
- You have provided system specs (if relevant).
- Please also provide:
- For any issues, a log file
- For crashes, a backtrace.
- For graphical issues, comparison screenshots with real hardware.
- For emulation inaccuracies, a test-case (if able).
-->

64
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,64 @@
name: Bug Report
description: File a bug report
body:
- type: markdown
attributes:
value: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu.
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the bug you encountered.
options:
- label: I have searched the existing issues
required: true
- type: input
attributes:
label: Affected Build(s)
description: List the affected build(s) that this issue applies to.
placeholder: Mainline 1234 / Early Access 1234
validations:
required: true
- type: textarea
id: issue-desc
attributes:
label: Description of Issue
description: A brief description of the issue encountered along with any images and/or videos.
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: Expected Behavior
description: A brief description of how it is expected to work along with any images and/or videos.
validations:
required: true
- type: textarea
id: reproduction-steps
attributes:
label: Reproduction Steps
description: A brief explanation of how to reproduce this issue. If possible, provide a save file to aid in reproducing the issue.
validations:
required: true
- type: textarea
id: log
attributes:
label: Log File
description: A log file will help our developers to better diagnose and fix the issue.
validations:
required: true
- type: textarea
id: system-config
attributes:
label: System Configuration
placeholder: |
CPU: Intel i5-10400 / AMD Ryzen 5 3600
GPU/Driver: NVIDIA GeForce GTX 1060 (Driver 512.95)
RAM: 16GB DDR4-3200
OS: Windows 11 22H2 (Build 22621.819)
value: |
CPU:
GPU/Driver:
RAM:
OS:
validations:
required: true

View File

@@ -0,0 +1,28 @@
name: Feature Request
description: File a feature request
labels: "request"
body:
- type: markdown
attributes:
value: Tech support does not belong here. You should only file an issue here if you are requesting a feature you believe would make yuzu better.
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the feature you are requesting.
options:
- label: I have searched the existing issues
required: true
- type: textarea
id: what-feature
attributes:
label: What feature are you suggesting?
description: A brief description of the requested feature.
validations:
required: true
- type: textarea
id: why-feature
attributes:
label: Why would this feature be useful?
description: A brief description of why this feature would make yuzu better.
validations:
required: true

View File

@@ -132,10 +132,6 @@ Files: vcpkg.json
Copyright: 2022 yuzu Emulator Project
License: GPL-3.0-or-later
Files: .github/ISSUE_TEMPLATE/config.yml
Copyright: 2020 tgsm <doodrabbit@hotmail.com>
License: GPL-2.0-or-later
Files: .github/ISSUE_TEMPLATE/bug-report-feature-request.md
Copyright: 2016 MerryMage
Files: .github/ISSUE_TEMPLATE/*
Copyright: 2022 yuzu Emulator Project
License: GPL-2.0-or-later

View File

@@ -19,6 +19,9 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON
CMAKE_DEPENDENT_OPTION(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" ON "ENABLE_SDL2;NOT MSVC" OFF)
option(ENABLE_QT "Enable the Qt frontend" ON)
option(ENABLE_QT6 "Allow usage of Qt6 to be attempted" OFF)
set(QT6_LOCATION "" CACHE PATH "Additional Location to search for Qt6 libraries like C:/Qt/6.3.1/msvc2019_64/")
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF)
@@ -28,6 +31,8 @@ option(YUZU_USE_BUNDLED_LIBUSB "Compile bundled libusb" OFF)
option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" "${WIN32}")
option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF)
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
@@ -213,128 +218,166 @@ if (MINGW)
find_library(MSWSOCK_LIBRARY mswsock REQUIRED)
endif()
# Please consider this as a stub
if(ENABLE_QT6 AND Qt6_LOCATION)
list(APPEND CMAKE_PREFIX_PATH "${Qt6_LOCATION}")
endif()
function(set_yuzu_qt_components)
# Best practice is to ask for all components at once, so they are from the same version
set(YUZU_QT_COMPONENTS2 Core Widgets Concurrent)
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
list(APPEND YUZU_QT_COMPONENTS2 DBus)
endif()
if (YUZU_USE_QT_MULTIMEDIA)
list(APPEND YUZU_QT_COMPONENTS2 Multimedia)
endif()
if (YUZU_USE_QT_WEB_ENGINE)
list(APPEND YUZU_QT_COMPONENTS2 WebEngineCore WebEngineWidgets)
endif()
if (ENABLE_QT_TRANSLATION)
list(APPEND YUZU_QT_COMPONENTS2 LinguistTools)
endif()
set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE)
endfunction(set_yuzu_qt_components)
# Qt5 requires that we find components, so it doesn't fit our pretty little find package function
if(ENABLE_QT)
set(QT_VERSION 5.15)
# These are used to specify minimum versions
set(QT5_VERSION 5.15)
set(QT6_VERSION 6.3.1)
# Check for system Qt on Linux, fallback to bundled Qt
if (UNIX AND NOT APPLE)
if (NOT YUZU_USE_BUNDLED_QT)
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus Multimedia)
endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT))
# Check for dependencies, then enable bundled Qt download
# Check that the system GLIBCXX version is compatible
find_program(OBJDUMP objdump)
if ("${OBJDUMP}" STREQUAL "OBJDUMP-NOTFOUND")
message(FATAL_ERROR "Required program `objdump` not found.")
endif()
find_library(LIBSTDCXX libstdc++.so.6)
execute_process(
COMMAND
${OBJDUMP} -T ${LIBSTDCXX}
COMMAND
grep GLIBCXX_3.4.28
COMMAND
sed "s/[0-9a-f]*.* //"
COMMAND
sed "s/ .*//"
COMMAND
sort -u
OUTPUT_VARIABLE
GLIBCXX_MET
)
if (NOT GLIBCXX_MET)
message(FATAL_ERROR "Qt too old or not found, and bundled Qt package is not \
compatible with this system. Either install Qt ${QT_VERSION}, or provide the path \
to Qt by setting the variable Qt5_ROOT.")
endif()
# Check for headers
find_package(PkgConfig REQUIRED)
pkg_check_modules(QT_DEP_GLU QUIET glu>=9.0.0)
if (NOT QT_DEP_GLU_FOUND)
message(FATAL_ERROR "Qt bundled pacakge dependency `glu` not found. \
Perhaps `libglu1-mesa-dev` needs to be installed?")
endif()
pkg_check_modules(QT_DEP_MESA QUIET dri>=20.0.8)
if (NOT QT_DEP_MESA_FOUND)
message(FATAL_ERROR "Qt bundled pacakge dependency `dri` not found. \
Perhaps `mesa-common-dev` needs to be installed?")
endif()
# Check for X libraries
set(BUNDLED_QT_REQUIREMENTS
libxcb-icccm.so.4
libxcb-image.so.0
libxcb-keysyms.so.1
libxcb-randr.so.0
libxcb-render-util.so.0
libxcb-render.so.0
libxcb-shape.so.0
libxcb-shm.so.0
libxcb-sync.so.1
libxcb-xfixes.so.0
libxcb-xinerama.so.0
libxcb-xkb.so.1
libxcb.so.1
libxkbcommon-x11.so.0
libxkbcommon.so.0
)
set(UNRESOLVED_QT_DEPS "")
foreach (REQUIREMENT ${BUNDLED_QT_REQUIREMENTS})
find_library(BUNDLED_QT_${REQUIREMENT} ${REQUIREMENT})
if ("${BUNDLED_QT_${REQUIREMENT}}" STREQUAL "BUNDLED_QT_${REQUIREMENT}-NOTFOUND")
set(UNRESOLVED_QT_DEPS ${UNRESOLVED_QT_DEPS} ${REQUIREMENT})
endif()
unset(BUNDLED_QT_${REQUIREMENT})
endforeach()
unset(BUNDLED_QT_REQUIREMENTS)
if (NOT "${UNRESOLVED_QT_DEPS}" STREQUAL "")
message(FATAL_ERROR "Bundled Qt package missing required dependencies: ${UNRESOLVED_QT_DEPS}")
endif()
set(YUZU_USE_BUNDLED_QT ON CACHE BOOL "Download bundled Qt" FORCE)
endif()
if (YUZU_USE_BUNDLED_QT)
# Binary package currently does not support Qt webengine, so make sure it's disabled
set(YUZU_USE_QT_WEB_ENGINE OFF CACHE BOOL "Use Qt Webengine" FORCE)
endif()
set_yuzu_qt_components()
if (ENABLE_QT6)
find_package(Qt6 ${QT6_VERSION} COMPONENTS ${YUZU_QT_COMPONENTS})
endif()
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH)
if(YUZU_USE_BUNDLED_QT)
if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
set(QT_BUILD qt-5.15.2-msvc2019_64)
elseif ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND NOT MINGW AND ARCHITECTURE_x86_64)
set(QT_BUILD qt5_5_15_2)
else()
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
endif()
if (DEFINED QT_BUILD)
download_bundled_external("qt/" ${QT_BUILD} QT_PREFIX)
endif()
set(QT_PREFIX_HINT HINTS "${QT_PREFIX}")
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH")
endif()
if (UNIX AND NOT APPLE AND YUZU_USE_BUNDLED_QT)
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia DBus ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
if (Qt6_FOUND)
message(STATUS "yuzu/CMakeLists.txt: Qt6Widgets_VERSION ${Qt6Widgets_VERSION}, setting QT_VERSION")
set(QT_VERSION ${Qt6Widgets_VERSION})
set(QT_MAJOR_VERSION 6)
# Qt6 sets cxx_std_17 and we need to undo that
set_target_properties(Qt6::Platform PROPERTIES INTERFACE_COMPILE_FEATURES "")
else()
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
endif()
if (YUZU_USE_QT_WEB_ENGINE)
find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets)
message(STATUS "yuzu/CMakeLists.txt: Qt6 not found/not selected, trying for Qt5")
# When Qt6 partially found, need this set to use Qt5 when not specifying version
set(QT_DEFAULT_MAJOR_VERSION 5)
set(QT_MAJOR_VERSION 5)
set(YUZU_USE_QT_MULTIMEDIA ON)
# Check for system Qt on Linux, fallback to bundled Qt
if (UNIX AND NOT APPLE)
if (NOT YUZU_USE_BUNDLED_QT)
find_package(Qt5 ${QT5_VERSION} COMPONENTS Widgets DBus Multimedia)
endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT))
# Check for dependencies, then enable bundled Qt download
# Check that the system GLIBCXX version is compatible
find_program(OBJDUMP objdump)
if (NOT OBJDUMP)
message(FATAL_ERROR "Required program `objdump` not found.")
endif()
find_library(LIBSTDCXX libstdc++.so.6)
execute_process(
COMMAND
${OBJDUMP} -T ${LIBSTDCXX}
COMMAND
grep GLIBCXX_3.4.28
COMMAND
sed "s/[0-9a-f]*.* //"
COMMAND
sed "s/ .*//"
COMMAND
sort -u
OUTPUT_VARIABLE
GLIBCXX_MET
)
if (NOT GLIBCXX_MET)
message(FATAL_ERROR "Qt too old or not found, and bundled Qt package is not \
compatible with this system. Either install Qt ${QT_VERSION}, or provide the path \
to Qt by setting the variable Qt5_ROOT.")
endif()
# Check for headers
find_package(PkgConfig REQUIRED)
pkg_check_modules(QT_DEP_GLU QUIET glu>=9.0.0)
if (NOT QT_DEP_GLU_FOUND)
message(FATAL_ERROR "Qt bundled pacakge dependency `glu` not found. \
Perhaps `libglu1-mesa-dev` needs to be installed?")
endif()
pkg_check_modules(QT_DEP_MESA QUIET dri>=20.0.8)
if (NOT QT_DEP_MESA_FOUND)
message(FATAL_ERROR "Qt bundled pacakge dependency `dri` not found. \
Perhaps `mesa-common-dev` needs to be installed?")
endif()
# Check for X libraries
set(BUNDLED_QT_REQUIREMENTS
libxcb-icccm.so.4
libxcb-image.so.0
libxcb-keysyms.so.1
libxcb-randr.so.0
libxcb-render-util.so.0
libxcb-render.so.0
libxcb-shape.so.0
libxcb-shm.so.0
libxcb-sync.so.1
libxcb-xfixes.so.0
libxcb-xinerama.so.0
libxcb-xkb.so.1
libxcb.so.1
libxkbcommon-x11.so.0
libxkbcommon.so.0
)
set(UNRESOLVED_QT_DEPS "")
foreach (REQUIREMENT ${BUNDLED_QT_REQUIREMENTS})
find_library(BUNDLED_QT_${REQUIREMENT} ${REQUIREMENT})
if (NOT BUNDLED_QT_${REQUIREMENT})
set(UNRESOLVED_QT_DEPS ${UNRESOLVED_QT_DEPS} ${REQUIREMENT})
endif()
unset(BUNDLED_QT_${REQUIREMENT})
endforeach()
unset(BUNDLED_QT_REQUIREMENTS)
if (NOT "${UNRESOLVED_QT_DEPS}" STREQUAL "")
message(FATAL_ERROR "Bundled Qt package missing required dependencies: ${UNRESOLVED_QT_DEPS}")
endif()
set(YUZU_USE_BUNDLED_QT ON CACHE BOOL "Download bundled Qt" FORCE)
endif()
if (YUZU_USE_BUNDLED_QT)
# Binary package currently does not support Qt webengine, so make sure it's disabled
set(YUZU_USE_QT_WEB_ENGINE OFF CACHE BOOL "Use Qt Webengine" FORCE)
endif()
endif()
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH)
if(YUZU_USE_BUNDLED_QT)
if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
set(QT_BUILD qt-5.15.2-msvc2019_64)
elseif ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND NOT MINGW AND ARCHITECTURE_x86_64)
set(QT_BUILD qt5_5_15_2)
else()
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
endif()
if (DEFINED QT_BUILD)
download_bundled_external("qt/" ${QT_BUILD} QT_PREFIX)
endif()
set(QT_PREFIX_HINT HINTS "${QT_PREFIX}")
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH")
# Binary package for Qt5 has Qt Multimedia
set(YUZU_USE_QT_MULTIMEDIA ON CACHE BOOL "Use Qt Multimedia" FORCE)
endif()
set_yuzu_qt_components()
find_package(Qt5 ${QT5_VERSION} COMPONENTS ${YUZU_QT_COMPONENTS} ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
endif()
if (ENABLE_QT_TRANSLATION)
find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT})
endif()
endif()
# find SDL2 exports a bunch of variables that are needed, so its easier to do this outside of the yuzu_find_package

View File

@@ -27,10 +27,13 @@ function(copy_yuzu_Qt5_deps target_dir)
Qt5Core$<$<CONFIG:Debug>:d>.*
Qt5Gui$<$<CONFIG:Debug>:d>.*
Qt5Widgets$<$<CONFIG:Debug>:d>.*
Qt5Multimedia$<$<CONFIG:Debug>:d>.*
Qt5Network$<$<CONFIG:Debug>:d>.*
)
if (YUZU_USE_QT_MULTIMEDIA)
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
Qt5Multimedia$<$<CONFIG:Debug>:d>.*
)
endif()
if (YUZU_USE_QT_WEB_ENGINE)
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
Qt5Network$<$<CONFIG:Debug>:d>.*

View File

@@ -92,10 +92,14 @@ endif()
add_subdirectory(sirit)
if (ENABLE_WEB_SERVICE)
find_package(OpenSSL 1.1)
if (OPENSSL_FOUND)
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
else()
if (NOT WIN32)
find_package(OpenSSL 1.1)
if (OPENSSL_FOUND)
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
endif()
endif()
if (WIN32 OR NOT OPENSSL_FOUND)
# LibreSSL
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
set(OPENSSLDIR "/etc/ssl/")

View File

@@ -460,21 +460,23 @@ void CommandBuffer::GenerateDeviceSinkCommand(const s32 node_id, const s16 buffe
cmd.session_id = session_id;
cmd.input_count = parameter.input_count;
s16 max_input{0};
for (u32 i = 0; i < parameter.input_count; i++) {
cmd.inputs[i] = buffer_offset + parameter.inputs[i];
max_input = std::max(max_input, cmd.inputs[i]);
}
if (state.upsampler_info != nullptr) {
const auto size_{state.upsampler_info->sample_count * parameter.input_count};
const auto size_bytes{size_ * sizeof(s32)};
const auto addr{memory_pool->Translate(state.upsampler_info->samples_pos, size_bytes)};
cmd.sample_buffer = {reinterpret_cast<s32*>(addr),
parameter.input_count * state.upsampler_info->sample_count};
(max_input + 1) * state.upsampler_info->sample_count};
} else {
cmd.sample_buffer = samples_buffer;
}
cmd.input_count = parameter.input_count;
for (u32 i = 0; i < parameter.input_count; i++) {
cmd.inputs[i] = buffer_offset + parameter.inputs[i];
}
GenerateEnd<DeviceSinkCommand>(cmd);
}

View File

@@ -48,6 +48,7 @@ void LogSettings() {
log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue());
log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue());
log_setting("Renderer_ScalingFilter", values.scaling_filter.GetValue());
log_setting("Renderer_FSRSlider", values.fsr_sharpening_slider.GetValue());
log_setting("Renderer_AntiAliasing", values.anti_aliasing.GetValue());
log_setting("Renderer_UseSpeedLimit", values.use_speed_limit.GetValue());
log_setting("Renderer_SpeedLimit", values.speed_limit.GetValue());
@@ -181,6 +182,7 @@ void RestoreGlobalState(bool is_powered_on) {
values.cpuopt_unsafe_ignore_global_monitor.SetGlobal(true);
// Renderer
values.fsr_sharpening_slider.SetGlobal(true);
values.renderer_backend.SetGlobal(true);
values.vulkan_device.SetGlobal(true);
values.aspect_ratio.SetGlobal(true);

View File

@@ -421,6 +421,7 @@ struct Values {
ResolutionScalingInfo resolution_info{};
SwitchableSetting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"};
SwitchableSetting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"};
SwitchableSetting<int, true> fsr_sharpening_slider{25, 0, 200, "fsr_sharpening_slider"};
SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"};
// *nix platforms may have issues with the borderless windowed fullscreen mode.
// Default to exclusive fullscreen on these platforms for now.
@@ -442,7 +443,7 @@ struct Values {
SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"};
SwitchableSetting<bool> use_vsync{true, "use_vsync"};
SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLSL, ShaderBackend::GLSL,
ShaderBackend::SPIRV, "shader_backend"};
SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};

View File

@@ -120,6 +120,8 @@ add_library(core STATIC
file_sys/vfs_vector.h
file_sys/xts_archive.cpp
file_sys/xts_archive.h
frontend/applets/cabinet.cpp
frontend/applets/cabinet.h
frontend/applets/controller.cpp
frontend/applets/controller.h
frontend/applets/error.cpp
@@ -312,6 +314,8 @@ add_library(core STATIC
hle/service/am/applet_ae.h
hle/service/am/applet_oe.cpp
hle/service/am/applet_oe.h
hle/service/am/applets/applet_cabinet.cpp
hle/service/am/applets/applet_cabinet.h
hle/service/am/applets/applet_controller.cpp
hle/service/am/applets/applet_controller.h
hle/service/am/applets/applet_error.cpp
@@ -526,6 +530,11 @@ add_library(core STATIC
hle/service/ncm/ncm.h
hle/service/nfc/nfc.cpp
hle/service/nfc/nfc.h
hle/service/nfc/nfc_device.cpp
hle/service/nfc/nfc_device.h
hle/service/nfc/nfc_result.h
hle/service/nfc/nfc_user.cpp
hle/service/nfc/nfc_user.h
hle/service/nfp/amiibo_crypto.cpp
hle/service/nfp/amiibo_crypto.h
hle/service/nfp/nfp.cpp

View File

@@ -578,18 +578,18 @@ KeyManager::KeyManager() {
if (Settings::values.use_dev_keys) {
dev_mode = true;
LoadFromFile(yuzu_keys_dir / "dev.keys", false);
LoadFromFile(yuzu_keys_dir / "dev.keys_autogenerated", false);
LoadFromFile(yuzu_keys_dir / "dev.keys", false);
} else {
dev_mode = false;
LoadFromFile(yuzu_keys_dir / "prod.keys", false);
LoadFromFile(yuzu_keys_dir / "prod.keys_autogenerated", false);
LoadFromFile(yuzu_keys_dir / "prod.keys", false);
}
LoadFromFile(yuzu_keys_dir / "title.keys", true);
LoadFromFile(yuzu_keys_dir / "title.keys_autogenerated", true);
LoadFromFile(yuzu_keys_dir / "console.keys", false);
LoadFromFile(yuzu_keys_dir / "title.keys", true);
LoadFromFile(yuzu_keys_dir / "console.keys_autogenerated", false);
LoadFromFile(yuzu_keys_dir / "console.keys", false);
}
static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) {

View File

@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/frontend/applets/cabinet.h"
#include <thread>
namespace Core::Frontend {
CabinetApplet::~CabinetApplet() = default;
void DefaultCabinetApplet::ShowCabinetApplet(
const CabinetCallback& callback, const CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const {
LOG_WARNING(Service_AM, "(STUBBED) called");
callback(false, {});
}
} // namespace Core::Frontend

View File

@@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <functional>
#include "core/hle/service/nfp/nfp_types.h"
namespace Service::NFP {
class NfpDevice;
} // namespace Service::NFP
namespace Core::Frontend {
struct CabinetParameters {
Service::NFP::TagInfo tag_info;
Service::NFP::RegisterInfo register_info;
Service::NFP::CabinetMode mode;
};
using CabinetCallback = std::function<void(bool, const std::string&)>;
class CabinetApplet {
public:
virtual ~CabinetApplet();
virtual void ShowCabinetApplet(const CabinetCallback& callback,
const CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const = 0;
};
class DefaultCabinetApplet final : public CabinetApplet {
public:
void ShowCabinetApplet(const CabinetCallback& callback, const CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override;
};
} // namespace Core::Frontend

View File

@@ -145,6 +145,7 @@ void EmulatedDevices::UnloadInput() {
for (auto& button : keyboard_modifier_devices) {
button.reset();
}
ring_analog_device.reset();
}
void EmulatedDevices::EnableConfiguration() {

View File

@@ -199,7 +199,7 @@ public:
~HLERequestContext();
/// Returns a pointer to the IPC command buffer for this request.
u32* CommandBuffer() {
[[nodiscard]] u32* CommandBuffer() {
return cmd_buf.data();
}
@@ -207,7 +207,7 @@ public:
* Returns the session through which this request was made. This can be used as a map key to
* access per-client data on services.
*/
Kernel::KServerSession* Session() {
[[nodiscard]] Kernel::KServerSession* Session() {
return server_session;
}
@@ -217,61 +217,61 @@ public:
/// Writes data from this context back to the requesting process/thread.
Result WriteToOutgoingCommandBuffer(KThread& requesting_thread);
u32_le GetHipcCommand() const {
[[nodiscard]] u32_le GetHipcCommand() const {
return command;
}
u32_le GetTipcCommand() const {
[[nodiscard]] u32_le GetTipcCommand() const {
return static_cast<u32_le>(command_header->type.Value()) -
static_cast<u32_le>(IPC::CommandType::TIPC_CommandRegion);
}
u32_le GetCommand() const {
[[nodiscard]] u32_le GetCommand() const {
return command_header->IsTipc() ? GetTipcCommand() : GetHipcCommand();
}
bool IsTipc() const {
[[nodiscard]] bool IsTipc() const {
return command_header->IsTipc();
}
IPC::CommandType GetCommandType() const {
[[nodiscard]] IPC::CommandType GetCommandType() const {
return command_header->type;
}
u64 GetPID() const {
[[nodiscard]] u64 GetPID() const {
return pid;
}
u32 GetDataPayloadOffset() const {
[[nodiscard]] u32 GetDataPayloadOffset() const {
return data_payload_offset;
}
const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const {
[[nodiscard]] const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const {
return buffer_x_desciptors;
}
const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const {
[[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const {
return buffer_a_desciptors;
}
const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const {
[[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const {
return buffer_b_desciptors;
}
const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const {
[[nodiscard]] const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const {
return buffer_c_desciptors;
}
const IPC::DomainMessageHeader& GetDomainMessageHeader() const {
[[nodiscard]] const IPC::DomainMessageHeader& GetDomainMessageHeader() const {
return domain_message_header.value();
}
bool HasDomainMessageHeader() const {
[[nodiscard]] bool HasDomainMessageHeader() const {
return domain_message_header.has_value();
}
/// Helper function to read a buffer using the appropriate buffer descriptor
std::vector<u8> ReadBuffer(std::size_t buffer_index = 0) const;
[[nodiscard]] std::vector<u8> ReadBuffer(std::size_t buffer_index = 0) const;
/// Helper function to write a buffer using the appropriate buffer descriptor
std::size_t WriteBuffer(const void* buffer, std::size_t size,
@@ -308,22 +308,34 @@ public:
}
/// Helper function to get the size of the input buffer
std::size_t GetReadBufferSize(std::size_t buffer_index = 0) const;
[[nodiscard]] std::size_t GetReadBufferSize(std::size_t buffer_index = 0) const;
/// Helper function to get the size of the output buffer
std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const;
[[nodiscard]] std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const;
/// Helper function to derive the number of elements able to be contained in the read buffer
template <typename T>
[[nodiscard]] std::size_t GetReadBufferNumElements(std::size_t buffer_index = 0) const {
return GetReadBufferSize(buffer_index) / sizeof(T);
}
/// Helper function to derive the number of elements able to be contained in the write buffer
template <typename T>
[[nodiscard]] std::size_t GetWriteBufferNumElements(std::size_t buffer_index = 0) const {
return GetWriteBufferSize(buffer_index) / sizeof(T);
}
/// Helper function to test whether the input buffer at buffer_index can be read
bool CanReadBuffer(std::size_t buffer_index = 0) const;
[[nodiscard]] bool CanReadBuffer(std::size_t buffer_index = 0) const;
/// Helper function to test whether the output buffer at buffer_index can be written
bool CanWriteBuffer(std::size_t buffer_index = 0) const;
[[nodiscard]] bool CanWriteBuffer(std::size_t buffer_index = 0) const;
Handle GetCopyHandle(std::size_t index) const {
[[nodiscard]] Handle GetCopyHandle(std::size_t index) const {
return incoming_copy_handles.at(index);
}
Handle GetMoveHandle(std::size_t index) const {
[[nodiscard]] Handle GetMoveHandle(std::size_t index) const {
return incoming_move_handles.at(index);
}
@@ -348,13 +360,13 @@ public:
manager = manager_;
}
std::string Description() const;
[[nodiscard]] std::string Description() const;
KThread& GetThread() {
[[nodiscard]] KThread& GetThread() {
return *thread;
}
std::shared_ptr<SessionRequestManager> GetManager() const {
[[nodiscard]] std::shared_ptr<SessionRequestManager> GetManager() const {
return manager.lock();
}

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_process.h"
namespace Kernel {
@@ -82,6 +83,22 @@ Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
R_SUCCEED();
}
KScopedAutoObject<KAutoObject> KHandleTable::GetObjectForIpc(Handle handle,
KThread* cur_thread) const {
// Handle pseudo-handles.
ASSERT(cur_thread != nullptr);
if (handle == Svc::PseudoHandle::CurrentProcess) {
auto* const cur_process = cur_thread->GetOwnerProcess();
ASSERT(cur_process != nullptr);
return cur_process;
}
if (handle == Svc::PseudoHandle::CurrentThread) {
return cur_thread;
}
return GetObjectForIpcWithoutPseudoHandle(handle);
}
Result KHandleTable::Reserve(Handle* out_handle) {
KScopedDisableDispatch dd{m_kernel};
KScopedSpinLock lk(m_lock);

View File

@@ -113,21 +113,7 @@ public:
return this->GetObjectImpl(handle);
}
KScopedAutoObject<KAutoObject> GetObjectForIpc(Handle handle, KThread* cur_thread) const {
// Handle pseudo-handles.
ASSERT(cur_thread != nullptr);
if (handle == Svc::PseudoHandle::CurrentProcess) {
auto* const cur_process =
static_cast<KAutoObject*>(static_cast<void*>(cur_thread->GetOwnerProcess()));
ASSERT(cur_process != nullptr);
return cur_process;
}
if (handle == Svc::PseudoHandle::CurrentThread) {
return static_cast<KAutoObject*>(cur_thread);
}
return GetObjectForIpcWithoutPseudoHandle(handle);
}
KScopedAutoObject<KAutoObject> GetObjectForIpc(Handle handle, KThread* cur_thread) const;
KScopedAutoObject<KAutoObject> GetObjectByIndex(Handle* out_handle, size_t index) const {
KScopedDisableDispatch dd{m_kernel};

View File

@@ -0,0 +1,177 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/frontend/applets/cabinet.h"
#include "core/hid/hid_core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applet_cabinet.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/nfp/nfp_device.h"
namespace Service::AM::Applets {
Cabinet::Cabinet(Core::System& system_, LibraryAppletMode applet_mode_,
const Core::Frontend::CabinetApplet& frontend_)
: Applet{system_, applet_mode_}, frontend{frontend_}, system{system_}, service_context{
system_,
"CabinetApplet"} {
availability_change_event =
service_context.CreateEvent("CabinetApplet:AvailabilityChangeEvent");
}
Cabinet::~Cabinet() = default;
void Cabinet::Initialize() {
Applet::Initialize();
LOG_INFO(Service_HID, "Initializing Cabinet Applet.");
LOG_DEBUG(Service_HID,
"Initializing Applet with common_args: arg_version={}, lib_version={}, "
"play_startup_sound={}, size={}, system_tick={}, theme_color={}",
common_args.arguments_version, common_args.library_version,
common_args.play_startup_sound, common_args.size, common_args.system_tick,
common_args.theme_color);
const auto storage = broker.PopNormalDataToApplet();
ASSERT(storage != nullptr);
const auto applet_input_data = storage->GetData();
ASSERT(applet_input_data.size() >= sizeof(StartParamForAmiiboSettings));
std::memcpy(&applet_input_common, applet_input_data.data(),
sizeof(StartParamForAmiiboSettings));
}
bool Cabinet::TransactionComplete() const {
return is_complete;
}
Result Cabinet::GetStatus() const {
return ResultSuccess;
}
void Cabinet::ExecuteInteractive() {
ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
}
void Cabinet::Execute() {
if (is_complete) {
return;
}
const auto callback = [this](bool apply_changes, const std::string& amiibo_name) {
DisplayCompleted(apply_changes, amiibo_name);
};
// TODO: listen on all controllers
if (nfp_device == nullptr) {
nfp_device = std::make_shared<Service::NFP::NfpDevice>(
system.HIDCore().GetFirstNpadId(), system, service_context, availability_change_event);
nfp_device->Initialize();
nfp_device->StartDetection(Service::NFP::TagProtocol::All);
}
const Core::Frontend::CabinetParameters parameters{
.tag_info = applet_input_common.tag_info,
.register_info = applet_input_common.register_info,
.mode = applet_input_common.applet_mode,
};
switch (applet_input_common.applet_mode) {
case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings:
case Service::NFP::CabinetMode::StartGameDataEraser:
case Service::NFP::CabinetMode::StartRestorer:
case Service::NFP::CabinetMode::StartFormatter:
frontend.ShowCabinetApplet(callback, parameters, nfp_device);
break;
default:
UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode);
DisplayCompleted(false, {});
break;
}
}
void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) {
Service::Mii::MiiManager manager;
ReturnValueForAmiiboSettings applet_output{};
if (!apply_changes) {
Cancel();
}
if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound &&
nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) {
Cancel();
}
if (nfp_device->GetCurrentState() == Service::NFP::DeviceState::TagFound) {
nfp_device->Mount(Service::NFP::MountTarget::All);
}
switch (applet_input_common.applet_mode) {
case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: {
Service::NFP::AmiiboName name{};
std::memcpy(name.data(), amiibo_name.data(), std::min(amiibo_name.size(), name.size() - 1));
nfp_device->SetNicknameAndOwner(name);
break;
}
case Service::NFP::CabinetMode::StartGameDataEraser:
nfp_device->DeleteApplicationArea();
break;
case Service::NFP::CabinetMode::StartRestorer:
nfp_device->RestoreAmiibo();
break;
case Service::NFP::CabinetMode::StartFormatter:
nfp_device->DeleteAllData();
break;
default:
UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode);
break;
}
applet_output.device_handle = applet_input_common.device_handle;
applet_output.result = CabinetResult::Cancel;
const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info);
const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info);
nfp_device->Finalize();
if (reg_result.IsSuccess()) {
applet_output.result |= CabinetResult::RegisterInfo;
}
if (tag_result.IsSuccess()) {
applet_output.result |= CabinetResult::TagInfo;
}
std::vector<u8> out_data(sizeof(ReturnValueForAmiiboSettings));
std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings));
is_complete = true;
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
broker.SignalStateChanged();
}
void Cabinet::Cancel() {
ReturnValueForAmiiboSettings applet_output{};
applet_output.device_handle = applet_input_common.device_handle;
applet_output.result = CabinetResult::Cancel;
nfp_device->Finalize();
std::vector<u8> out_data(sizeof(ReturnValueForAmiiboSettings));
std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings));
is_complete = true;
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
broker.SignalStateChanged();
}
} // namespace Service::AM::Applets

View File

@@ -0,0 +1,104 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "core/hle/result.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfp/nfp_types.h"
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Core {
class System;
} // namespace Core
namespace Service::NFP {
class NfpDevice;
}
namespace Service::AM::Applets {
enum class CabinetAppletVersion : u32 {
Version1 = 0x1,
};
enum class CabinetResult : u8 {
Cancel = 0,
TagInfo = 1 << 1,
RegisterInfo = 1 << 2,
All = TagInfo | RegisterInfo,
};
DECLARE_ENUM_FLAG_OPERATORS(CabinetResult)
// This is nn::nfp::AmiiboSettingsStartParam
struct AmiiboSettingsStartParam {
u64 device_handle;
std::array<u8, 0x20> param_1;
u8 param_2;
};
static_assert(sizeof(AmiiboSettingsStartParam) == 0x30,
"AmiiboSettingsStartParam is an invalid size");
#pragma pack(push, 1)
// This is nn::nfp::StartParamForAmiiboSettings
struct StartParamForAmiiboSettings {
u8 param_1;
Service::NFP::CabinetMode applet_mode;
u8 flags;
u8 amiibo_settings_1;
u64 device_handle;
Service::NFP::TagInfo tag_info;
Service::NFP::RegisterInfo register_info;
std::array<u8, 0x20> amiibo_settings_3;
INSERT_PADDING_BYTES(0x24);
};
static_assert(sizeof(StartParamForAmiiboSettings) == 0x1A8,
"StartParamForAmiiboSettings is an invalid size");
// This is nn::nfp::ReturnValueForAmiiboSettings
struct ReturnValueForAmiiboSettings {
CabinetResult result;
INSERT_PADDING_BYTES(0x3);
u64 device_handle;
Service::NFP::TagInfo tag_info;
Service::NFP::RegisterInfo register_info;
INSERT_PADDING_BYTES(0x24);
};
static_assert(sizeof(ReturnValueForAmiiboSettings) == 0x188,
"ReturnValueForAmiiboSettings is an invalid size");
#pragma pack(pop)
class Cabinet final : public Applet {
public:
explicit Cabinet(Core::System& system_, LibraryAppletMode applet_mode_,
const Core::Frontend::CabinetApplet& frontend_);
~Cabinet() override;
void Initialize() override;
bool TransactionComplete() const override;
Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
void DisplayCompleted(bool apply_changes, std::string_view amiibo_name);
void Cancel();
private:
const Core::Frontend::CabinetApplet& frontend;
Core::System& system;
bool is_complete{false};
std::shared_ptr<Service::NFP::NfpDevice> nfp_device;
Kernel::KEvent* availability_change_event;
KernelHelpers::ServiceContext service_context;
StartParamForAmiiboSettings applet_input_common{};
};
} // namespace Service::AM::Applets

View File

@@ -5,6 +5,7 @@
#include "common/assert.h"
#include "core/core.h"
#include "core/frontend/applets/cabinet.h"
#include "core/frontend/applets/controller.h"
#include "core/frontend/applets/error.h"
#include "core/frontend/applets/general_frontend.h"
@@ -16,6 +17,7 @@
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/applets/applet_cabinet.h"
#include "core/hle/service/am/applets/applet_controller.h"
#include "core/hle/service/am/applets/applet_error.h"
#include "core/hle/service/am/applets/applet_general_backend.h"
@@ -171,13 +173,15 @@ void Applet::Initialize() {
AppletFrontendSet::AppletFrontendSet() = default;
AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
AppletFrontendSet::AppletFrontendSet(CabinetApplet cabinet_applet,
ControllerApplet controller_applet, ErrorApplet error_applet,
MiiEdit mii_edit_,
ParentalControlsApplet parental_controls_applet,
PhotoViewer photo_viewer_, ProfileSelect profile_select_,
SoftwareKeyboard software_keyboard_, WebBrowser web_browser_)
: controller{std::move(controller_applet)}, error{std::move(error_applet)},
mii_edit{std::move(mii_edit_)}, parental_controls{std::move(parental_controls_applet)},
: cabinet{std::move(cabinet_applet)}, controller{std::move(controller_applet)},
error{std::move(error_applet)}, mii_edit{std::move(mii_edit_)},
parental_controls{std::move(parental_controls_applet)},
photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)},
software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {}
@@ -196,6 +200,10 @@ const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const {
}
void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
if (set.cabinet != nullptr) {
frontend.cabinet = std::move(set.cabinet);
}
if (set.controller != nullptr) {
frontend.controller = std::move(set.controller);
}
@@ -235,6 +243,10 @@ void AppletManager::SetDefaultAppletFrontendSet() {
}
void AppletManager::SetDefaultAppletsIfMissing() {
if (frontend.cabinet == nullptr) {
frontend.cabinet = std::make_unique<Core::Frontend::DefaultCabinetApplet>();
}
if (frontend.controller == nullptr) {
frontend.controller =
std::make_unique<Core::Frontend::DefaultControllerApplet>(system.HIDCore());
@@ -279,6 +291,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, LibraryAppletMode
switch (id) {
case AppletId::Auth:
return std::make_shared<Auth>(system, mode, *frontend.parental_controls);
case AppletId::Cabinet:
return std::make_shared<Cabinet>(system, mode, *frontend.cabinet);
case AppletId::Controller:
return std::make_shared<Controller>(system, mode, *frontend.controller);
case AppletId::Error:

View File

@@ -16,6 +16,7 @@ class System;
}
namespace Core::Frontend {
class CabinetApplet;
class ControllerApplet;
class ECommerceApplet;
class ErrorApplet;
@@ -176,6 +177,7 @@ protected:
};
struct AppletFrontendSet {
using CabinetApplet = std::unique_ptr<Core::Frontend::CabinetApplet>;
using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>;
@@ -186,10 +188,11 @@ struct AppletFrontendSet {
using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
AppletFrontendSet();
AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
MiiEdit mii_edit_, ParentalControlsApplet parental_controls_applet,
PhotoViewer photo_viewer_, ProfileSelect profile_select_,
SoftwareKeyboard software_keyboard_, WebBrowser web_browser_);
AppletFrontendSet(CabinetApplet cabinet_applet, ControllerApplet controller_applet,
ErrorApplet error_applet, MiiEdit mii_edit_,
ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_,
ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_,
WebBrowser web_browser_);
~AppletFrontendSet();
AppletFrontendSet(const AppletFrontendSet&) = delete;
@@ -198,6 +201,7 @@ struct AppletFrontendSet {
AppletFrontendSet(AppletFrontendSet&&) noexcept;
AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
CabinetApplet cabinet;
ControllerApplet controller;
ErrorApplet error;
MiiEdit mii_edit;

View File

@@ -122,10 +122,10 @@ private:
}
void GetReleasedAudioInBuffer(Kernel::HLERequestContext& ctx) {
auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64);
std::vector<u64> released_buffers(write_buffer_size, 0);
const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>();
std::vector<u64> released_buffers(write_buffer_size);
auto count = impl->GetReleasedBuffers(released_buffers);
const auto count = impl->GetReleasedBuffers(released_buffers);
[[maybe_unused]] std::string tags{};
for (u32 i = 0; i < count; i++) {
@@ -228,7 +228,7 @@ void AudInU::ListAudioIns(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
const auto write_count =
static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
static_cast<u32>(ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>());
std::vector<AudioDevice::AudioDeviceName> device_names{};
u32 out_count{0};
@@ -248,7 +248,7 @@ void AudInU::ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
const auto write_count =
static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
static_cast<u32>(ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>());
std::vector<AudioDevice::AudioDeviceName> device_names{};
u32 out_count{0};

View File

@@ -129,16 +129,16 @@ private:
}
void GetReleasedAudioOutBuffers(Kernel::HLERequestContext& ctx) {
auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64);
std::vector<u64> released_buffers(write_buffer_size, 0);
const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>();
std::vector<u64> released_buffers(write_buffer_size);
auto count = impl->GetReleasedBuffers(released_buffers);
const auto count = impl->GetReleasedBuffers(released_buffers);
[[maybe_unused]] std::string tags{};
for (u32 i = 0; i < count; i++) {
tags += fmt::format("{:08X}, ", released_buffers[i]);
}
[[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
[[maybe_unused]] const auto sessionid{impl->GetSystem().GetSessionId()};
LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count,
tags);
@@ -244,7 +244,7 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
std::scoped_lock l{impl->mutex};
const auto write_count =
static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
static_cast<u32>(ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>());
std::vector<AudioDevice::AudioDeviceName> device_names{};
if (write_count > 0) {
device_names.emplace_back("DeviceOut");

View File

@@ -274,7 +274,7 @@ public:
private:
void ListAudioDeviceName(Kernel::HLERequestContext& ctx) {
const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName);
const size_t in_count = ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>();
std::vector<AudioDevice::AudioDeviceName> out_names{};
@@ -335,7 +335,7 @@ private:
}
void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
const auto write_size = ctx.GetWriteBufferSize() / sizeof(char);
const auto write_size = ctx.GetWriteBufferSize();
std::string out_name{"AudioTvOutput"};
LOG_DEBUG(Service_Audio, "(STUBBED) called. Name={}", out_name);
@@ -387,7 +387,7 @@ private:
}
void ListAudioOutputDeviceName(Kernel::HLERequestContext& ctx) {
const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName);
const size_t in_count = ctx.GetWriteBufferNumElements<AudioDevice::AudioDeviceName>();
std::vector<AudioDevice::AudioDeviceName> out_names{};

View File

@@ -68,7 +68,7 @@ private:
ExtraBehavior extra_behavior) {
u32 consumed = 0;
u32 sample_count = 0;
std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16));
std::vector<opus_int16> samples(ctx.GetWriteBufferNumElements<opus_int16>());
if (extra_behavior == ExtraBehavior::ResetContext) {
ResetDecoderContext();

View File

@@ -443,7 +443,7 @@ private:
}
void Read(Kernel::HLERequestContext& ctx) {
auto write_size = ctx.GetWriteBufferSize() / sizeof(DeliveryCacheDirectoryEntry);
auto write_size = ctx.GetWriteBufferNumElements<DeliveryCacheDirectoryEntry>();
LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", write_size);
@@ -533,7 +533,7 @@ private:
}
void EnumerateDeliveryCacheDirectory(Kernel::HLERequestContext& ctx) {
auto size = ctx.GetWriteBufferSize() / sizeof(DirectoryName);
auto size = ctx.GetWriteBufferNumElements<DirectoryName>();
LOG_DEBUG(Service_BCAT, "called, size={:016X}", size);

View File

@@ -192,12 +192,10 @@ private:
}
void ListCommonTicketRightsIds(Kernel::HLERequestContext& ctx) {
u32 out_entries;
if (keys.GetCommonTickets().empty())
out_entries = 0;
else
out_entries = static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(u128));
size_t out_entries = 0;
if (!keys.GetCommonTickets().empty()) {
out_entries = ctx.GetWriteBufferNumElements<u128>();
}
LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries);
keys.PopulateTickets();
@@ -206,20 +204,19 @@ private:
std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids),
[](const auto& pair) { return pair.first; });
out_entries = static_cast<u32>(std::min<std::size_t>(ids.size(), out_entries));
out_entries = std::min(ids.size(), out_entries);
ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128));
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(out_entries);
rb.Push<u32>(static_cast<u32>(out_entries));
}
void ListPersonalizedTicketRightsIds(Kernel::HLERequestContext& ctx) {
u32 out_entries;
if (keys.GetPersonalizedTickets().empty())
out_entries = 0;
else
out_entries = static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(u128));
size_t out_entries = 0;
if (!keys.GetPersonalizedTickets().empty()) {
out_entries = ctx.GetWriteBufferNumElements<u128>();
}
LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries);
@@ -229,12 +226,12 @@ private:
std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids),
[](const auto& pair) { return pair.first; });
out_entries = static_cast<u32>(std::min<std::size_t>(ids.size(), out_entries));
out_entries = std::min(ids.size(), out_entries);
ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128));
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(out_entries);
rb.Push<u32>(static_cast<u32>(out_entries));
}
void GetCommonTicketSize(Kernel::HLERequestContext& ctx) {

View File

@@ -277,7 +277,7 @@ private:
LOG_DEBUG(Service_FS, "called.");
// Calculate how many entries we can fit in the output buffer
const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry);
const u64 count_entries = ctx.GetWriteBufferNumElements<FileSys::Entry>();
// Cap at total number of entries.
const u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index);
@@ -543,7 +543,7 @@ public:
LOG_DEBUG(Service_FS, "called");
// Calculate how many entries we can fit in the output buffer
const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(SaveDataInfo);
const u64 count_entries = ctx.GetWriteBufferNumElements<SaveDataInfo>();
// Cap at total number of entries.
const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index);

View File

@@ -292,7 +292,7 @@ public:
void GetNetworkInfoLatestUpdate(Kernel::HLERequestContext& ctx) {
const std::size_t network_buffer_size = ctx.GetWriteBufferSize(0);
const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate);
const std::size_t node_buffer_count = ctx.GetWriteBufferNumElements<NodeLatestUpdate>(1);
if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) {
LOG_ERROR(Service_LDN, "Invalid buffer, size = {}, count = {}", network_buffer_size,
@@ -333,7 +333,7 @@ public:
const auto channel{rp.PopEnum<WifiChannel>()};
const auto scan_filter{rp.PopRaw<ScanFilter>()};
const std::size_t network_info_size = ctx.GetWriteBufferSize() / sizeof(NetworkInfo);
const std::size_t network_info_size = ctx.GetWriteBufferNumElements<NetworkInfo>();
if (network_info_size == 0) {
LOG_ERROR(Service_LDN, "Invalid buffer size {}", network_info_size);

View File

@@ -7,6 +7,7 @@
#include "common/settings.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/nfc/nfc.h"
#include "core/hle/service/nfc/nfc_user.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
@@ -97,76 +98,6 @@ private:
}
};
class IUser final : public ServiceFramework<IUser> {
public:
explicit IUser(Core::System& system_) : ServiceFramework{system_, "NFC::IUser"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IUser::InitializeOld, "InitializeOld"},
{1, &IUser::FinalizeOld, "FinalizeOld"},
{2, &IUser::GetStateOld, "GetStateOld"},
{3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"},
{400, &IUser::InitializeOld, "Initialize"},
{401, &IUser::FinalizeOld, "Finalize"},
{402, &IUser::GetStateOld, "GetState"},
{403, &IUser::IsNfcEnabledOld, "IsNfcEnabled"},
{404, nullptr, "ListDevices"},
{405, nullptr, "GetDeviceState"},
{406, nullptr, "GetNpadId"},
{407, nullptr, "AttachAvailabilityChangeEvent"},
{408, nullptr, "StartDetection"},
{409, nullptr, "StopDetection"},
{410, nullptr, "GetTagInfo"},
{411, nullptr, "AttachActivateEvent"},
{412, nullptr, "AttachDeactivateEvent"},
{1000, nullptr, "ReadMifare"},
{1001, nullptr, "WriteMifare"},
{1300, nullptr, "SendCommandByPassThrough"},
{1301, nullptr, "KeepPassThroughSession"},
{1302, nullptr, "ReleasePassThroughSession"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
enum class NfcStates : u32 {
Finalized = 6,
};
void InitializeOld(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 2, 0};
rb.Push(ResultSuccess);
// We don't deal with hardware initialization so we can just stub this.
}
void IsNfcEnabledOld(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "IsNfcEnabledOld");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw<u8>(true);
}
void GetStateOld(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NFC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(NfcStates::Finalized); // TODO(ogniK): Figure out if this matches nfp
}
void FinalizeOld(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NFC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
};
class NFC_U final : public ServiceFramework<NFC_U> {
public:
explicit NFC_U(Core::System& system_) : ServiceFramework{system_, "nfc:user"} {

View File

@@ -0,0 +1,197 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/input.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/nfc/nfc_device.h"
#include "core/hle/service/nfc/nfc_result.h"
#include "core/hle/service/nfc/nfc_user.h"
namespace Service::NFC {
NfcDevice::NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
KernelHelpers::ServiceContext& service_context_,
Kernel::KEvent* availability_change_event_)
: npad_id{npad_id_}, system{system_}, service_context{service_context_},
availability_change_event{availability_change_event_} {
activate_event = service_context.CreateEvent("IUser:NFCActivateEvent");
deactivate_event = service_context.CreateEvent("IUser:NFCDeactivateEvent");
npad_device = system.HIDCore().GetEmulatedController(npad_id);
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); },
.is_npad_service = false,
};
is_controller_set = true;
callback_key = npad_device->SetCallback(engine_callback);
}
NfcDevice::~NfcDevice() {
activate_event->Close();
deactivate_event->Close();
if (!is_controller_set) {
return;
}
npad_device->DeleteCallback(callback_key);
is_controller_set = false;
};
void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
if (type == Core::HID::ControllerTriggerType::Connected ||
type == Core::HID::ControllerTriggerType::Disconnected) {
availability_change_event->Signal();
return;
}
if (type != Core::HID::ControllerTriggerType::Nfc) {
return;
}
if (!npad_device->IsConnected()) {
return;
}
const auto nfc_status = npad_device->GetNfc();
switch (nfc_status.state) {
case Common::Input::NfcState::NewAmiibo:
LoadNfcTag(nfc_status.data);
break;
case Common::Input::NfcState::AmiiboRemoved:
if (device_state != NFP::DeviceState::SearchingForTag) {
CloseNfcTag();
}
break;
default:
break;
}
}
bool NfcDevice::LoadNfcTag(std::span<const u8> data) {
if (device_state != NFP::DeviceState::SearchingForTag) {
LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state);
return false;
}
if (data.size() != sizeof(NFP::EncryptedNTAG215File)) {
LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size());
return false;
}
memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
device_state = NFP::DeviceState::TagFound;
deactivate_event->GetReadableEvent().Clear();
activate_event->Signal();
return true;
}
void NfcDevice::CloseNfcTag() {
LOG_INFO(Service_NFC, "Remove nfc tag");
device_state = NFP::DeviceState::TagRemoved;
encrypted_tag_data = {};
activate_event->GetReadableEvent().Clear();
deactivate_event->Signal();
}
Kernel::KReadableEvent& NfcDevice::GetActivateEvent() const {
return activate_event->GetReadableEvent();
}
Kernel::KReadableEvent& NfcDevice::GetDeactivateEvent() const {
return deactivate_event->GetReadableEvent();
}
void NfcDevice::Initialize() {
device_state =
npad_device->HasNfc() ? NFP::DeviceState::Initialized : NFP::DeviceState::Unavailable;
encrypted_tag_data = {};
}
void NfcDevice::Finalize() {
if (device_state == NFP::DeviceState::SearchingForTag ||
device_state == NFP::DeviceState::TagRemoved) {
StopDetection();
}
device_state = NFP::DeviceState::Unavailable;
}
Result NfcDevice::StartDetection(s32 protocol_) {
if (device_state != NFP::DeviceState::Initialized &&
device_state != NFP::DeviceState::TagRemoved) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
return WrongDeviceState;
}
if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) {
LOG_ERROR(Service_NFC, "Nfc not supported");
return NfcDisabled;
}
device_state = NFP::DeviceState::SearchingForTag;
protocol = protocol_;
return ResultSuccess;
}
Result NfcDevice::StopDetection() {
npad_device->SetPollingMode(Common::Input::PollingMode::Active);
if (device_state == NFP::DeviceState::Initialized) {
return ResultSuccess;
}
if (device_state == NFP::DeviceState::TagFound ||
device_state == NFP::DeviceState::TagMounted) {
CloseNfcTag();
return ResultSuccess;
}
if (device_state == NFP::DeviceState::SearchingForTag ||
device_state == NFP::DeviceState::TagRemoved) {
device_state = NFP::DeviceState::Initialized;
return ResultSuccess;
}
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
return WrongDeviceState;
}
Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const {
if (device_state != NFP::DeviceState::TagFound &&
device_state != NFP::DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
if (device_state == NFP::DeviceState::TagRemoved) {
return TagRemoved;
}
return WrongDeviceState;
}
// Protocol and tag type may change here
tag_info = {
.uuid = encrypted_tag_data.uuid.uid,
.uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
.protocol = NFP::TagProtocol::TypeA,
.tag_type = NFP::TagType::Type2,
};
return ResultSuccess;
}
u64 NfcDevice::GetHandle() const {
// Generate a handle based of the npad id
return static_cast<u64>(npad_id);
}
NFP::DeviceState NfcDevice::GetCurrentState() const {
return device_state;
}
Core::HID::NpadIdType NfcDevice::GetNpadId() const {
return npad_id;
}
} // namespace Service::NFC

View File

@@ -0,0 +1,70 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "core/hle/service/service.h"
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Core {
class System;
} // namespace Core
namespace Core::HID {
class EmulatedController;
enum class ControllerTriggerType;
enum class NpadIdType : u32;
} // namespace Core::HID
namespace Service::NFC {
class NfcDevice {
public:
NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
KernelHelpers::ServiceContext& service_context_,
Kernel::KEvent* availability_change_event_);
~NfcDevice();
void Initialize();
void Finalize();
Result StartDetection(s32 protocol_);
Result StopDetection();
Result GetTagInfo(NFP::TagInfo& tag_info) const;
u64 GetHandle() const;
NFP::DeviceState GetCurrentState() const;
Core::HID::NpadIdType GetNpadId() const;
Kernel::KReadableEvent& GetActivateEvent() const;
Kernel::KReadableEvent& GetDeactivateEvent() const;
private:
void NpadUpdate(Core::HID::ControllerTriggerType type);
bool LoadNfcTag(std::span<const u8> data);
void CloseNfcTag();
bool is_controller_set{};
int callback_key;
const Core::HID::NpadIdType npad_id;
Core::System& system;
Core::HID::EmulatedController* npad_device = nullptr;
KernelHelpers::ServiceContext& service_context;
Kernel::KEvent* activate_event = nullptr;
Kernel::KEvent* deactivate_event = nullptr;
Kernel::KEvent* availability_change_event = nullptr;
s32 protocol{};
NFP::DeviceState device_state{NFP::DeviceState::Unavailable};
NFP::EncryptedNTAG215File encrypted_tag_data{};
};
} // namespace Service::NFC

View File

@@ -0,0 +1,17 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/result.h"
namespace Service::NFC {
constexpr Result DeviceNotFound(ErrorModule::NFC, 64);
constexpr Result InvalidArgument(ErrorModule::NFC, 65);
constexpr Result WrongDeviceState(ErrorModule::NFC, 73);
constexpr Result NfcDisabled(ErrorModule::NFC, 80);
constexpr Result TagRemoved(ErrorModule::NFC, 97);
constexpr Result CorruptedData(ErrorModule::NFC, 144);
} // namespace Service::NFC

View File

@@ -0,0 +1,365 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/hid_types.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/nfc/nfc_device.h"
#include "core/hle/service/nfc/nfc_result.h"
#include "core/hle/service/nfc/nfc_user.h"
#include "core/hle/service/time/clock_types.h"
namespace Service::NFC {
IUser::IUser(Core::System& system_)
: ServiceFramework{system_, "NFC::IUser"}, service_context{system_, service_name} {
static const FunctionInfo functions[] = {
{0, &IUser::Initialize, "InitializeOld"},
{1, &IUser::Finalize, "FinalizeOld"},
{2, &IUser::GetState, "GetStateOld"},
{3, &IUser::IsNfcEnabled, "IsNfcEnabledOld"},
{400, &IUser::Initialize, "Initialize"},
{401, &IUser::Finalize, "Finalize"},
{402, &IUser::GetState, "GetState"},
{403, &IUser::IsNfcEnabled, "IsNfcEnabled"},
{404, &IUser::ListDevices, "ListDevices"},
{405, &IUser::GetDeviceState, "GetDeviceState"},
{406, &IUser::GetNpadId, "GetNpadId"},
{407, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
{408, &IUser::StartDetection, "StartDetection"},
{409, &IUser::StopDetection, "StopDetection"},
{410, &IUser::GetTagInfo, "GetTagInfo"},
{411, &IUser::AttachActivateEvent, "AttachActivateEvent"},
{412, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
{1000, nullptr, "ReadMifare"},
{1001, nullptr, "WriteMifare"},
{1300, &IUser::SendCommandByPassThrough, "SendCommandByPassThrough"},
{1301, nullptr, "KeepPassThroughSession"},
{1302, nullptr, "ReleasePassThroughSession"},
};
RegisterHandlers(functions);
availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent");
for (u32 device_index = 0; device_index < 10; device_index++) {
devices[device_index] =
std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system,
service_context, availability_change_event);
}
}
IUser ::~IUser() {
availability_change_event->Close();
}
void IUser::Initialize(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
state = State::Initialized;
for (auto& device : devices) {
device->Initialize();
}
IPC::ResponseBuilder rb{ctx, 2, 0};
rb.Push(ResultSuccess);
}
void IUser::Finalize(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
state = State::NonInitialized;
for (auto& device : devices) {
device->Finalize();
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IUser::GetState(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(state);
}
void IUser::IsNfcEnabled(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(state != State::NonInitialized);
}
void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
if (!ctx.CanWriteBuffer()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(InvalidArgument);
return;
}
if (ctx.GetWriteBufferSize() == 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(InvalidArgument);
return;
}
std::vector<u64> nfp_devices;
const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
for (auto& device : devices) {
if (nfp_devices.size() >= max_allowed_devices) {
continue;
}
if (device->GetCurrentState() != NFP::DeviceState::Unavailable) {
nfp_devices.push_back(device->GetHandle());
}
}
if (nfp_devices.empty()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
ctx.WriteBuffer(nfp_devices);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<s32>(nfp_devices.size()));
}
void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(device.value()->GetCurrentState());
}
void IUser::GetNpadId(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(device.value()->GetNpadId());
}
void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(availability_change_event->GetReadableEvent());
}
void IUser::StartDetection(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto nfp_protocol{rp.Pop<s32>()};
LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
const auto result = device.value()->StartDetection(nfp_protocol);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IUser::StopDetection(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
const auto result = device.value()->StopDetection();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
NFP::TagInfo tag_info{};
const auto result = device.value()->GetTagInfo(tag_info);
ctx.WriteBuffer(tag_info);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device.value()->GetActivateEvent());
}
void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device.value()->GetDeactivateEvent());
}
void IUser::SendCommandByPassThrough(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto timeout{rp.PopRaw<Time::Clock::TimeSpanType>()};
const auto command_data{ctx.ReadBuffer()};
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}",
device_handle, timeout.ToSeconds(), command_data.size());
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
std::vector<u8> out_data(1);
// TODO: Request data from nfc device
ctx.WriteBuffer(out_data);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<u32>(out_data.size()));
}
std::optional<std::shared_ptr<NfcDevice>> IUser::GetNfcDevice(u64 handle) {
for (auto& device : devices) {
if (device->GetHandle() == handle) {
return device;
}
}
return std::nullopt;
}
} // namespace Service::NFC

View File

@@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <memory>
#include <optional>
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Service::NFC {
class NfcDevice;
class IUser final : public ServiceFramework<IUser> {
public:
explicit IUser(Core::System& system_);
~IUser();
private:
enum class State : u32 {
NonInitialized,
Initialized,
};
void Initialize(Kernel::HLERequestContext& ctx);
void Finalize(Kernel::HLERequestContext& ctx);
void GetState(Kernel::HLERequestContext& ctx);
void IsNfcEnabled(Kernel::HLERequestContext& ctx);
void ListDevices(Kernel::HLERequestContext& ctx);
void GetDeviceState(Kernel::HLERequestContext& ctx);
void GetNpadId(Kernel::HLERequestContext& ctx);
void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx);
void StartDetection(Kernel::HLERequestContext& ctx);
void StopDetection(Kernel::HLERequestContext& ctx);
void GetTagInfo(Kernel::HLERequestContext& ctx);
void AttachActivateEvent(Kernel::HLERequestContext& ctx);
void AttachDeactivateEvent(Kernel::HLERequestContext& ctx);
void SendCommandByPassThrough(Kernel::HLERequestContext& ctx);
std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle);
KernelHelpers::ServiceContext service_context;
std::array<std::shared_ptr<NfcDevice>, 10> devices{};
State state{State::NonInitialized};
Kernel::KEvent* availability_change_event;
};
} // namespace Service::NFC

View File

@@ -12,7 +12,6 @@
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/nfp/amiibo_crypto.h"
namespace Service::NFP::AmiiboCrypto {

View File

@@ -2,10 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <atomic>
#include "common/fs/file.h"
#include "common/fs/path_util.h"
#include "common/input.h"
#include "common/logging/log.h"
#include "common/string_util.h"
@@ -19,7 +16,6 @@
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/mii/types.h"
#include "core/hle/service/nfp/amiibo_crypto.h"
#include "core/hle/service/nfp/nfp.h"
#include "core/hle/service/nfp/nfp_device.h"
#include "core/hle/service/nfp/nfp_result.h"
#include "core/hle/service/nfp/nfp_user.h"
@@ -49,6 +45,8 @@ NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
}
NfpDevice::~NfpDevice() {
activate_event->Close();
deactivate_event->Close();
if (!is_controller_set) {
return;
}
@@ -77,6 +75,9 @@ void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
LoadAmiibo(nfc_status.data);
break;
case Common::Input::NfcState::AmiiboRemoved:
if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
break;
}
if (device_state != DeviceState::SearchingForTag) {
CloseAmiibo();
}
@@ -97,6 +98,8 @@ bool NfpDevice::LoadAmiibo(std::span<const u8> data) {
return false;
}
// TODO: Filter by allowed_protocols here
memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File));
device_state = DeviceState::TagFound;
@@ -143,7 +146,7 @@ void NfpDevice::Finalize() {
device_state = DeviceState::Unavailable;
}
Result NfpDevice::StartDetection(s32 protocol_) {
Result NfpDevice::StartDetection(TagProtocol allowed_protocol) {
if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) {
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
return WrongDeviceState;
@@ -155,7 +158,7 @@ Result NfpDevice::StartDetection(s32 protocol_) {
}
device_state = DeviceState::SearchingForTag;
protocol = protocol_;
allowed_protocols = allowed_protocol;
return ResultSuccess;
}
@@ -469,6 +472,32 @@ Result NfpDevice::OpenApplicationArea(u32 access_id) {
return ResultSuccess;
}
Result NfpDevice::GetApplicationAreaId(u32& application_area_id) const {
application_area_id = {};
if (device_state != DeviceState::TagMounted) {
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
if (device_state == DeviceState::TagRemoved) {
return TagRemoved;
}
return WrongDeviceState;
}
if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
return WrongDeviceState;
}
if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
LOG_WARNING(Service_NFP, "Application area is not initialized");
return ApplicationAreaIsNotInitialized;
}
application_area_id = tag_data.application_area_id;
return ResultSuccess;
}
Result NfpDevice::GetApplicationArea(std::vector<u8>& data) const {
if (device_state != DeviceState::TagMounted) {
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);

View File

@@ -3,10 +3,10 @@
#pragma once
#include <array>
#include <span>
#include <vector>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "core/hle/service/service.h"
@@ -37,7 +37,7 @@ public:
void Initialize();
void Finalize();
Result StartDetection(s32 protocol_);
Result StartDetection(TagProtocol allowed_protocol);
Result StopDetection();
Result Mount(MountTarget mount_target);
Result Unmount();
@@ -53,6 +53,7 @@ public:
Result DeleteAllData();
Result OpenApplicationArea(u32 access_id);
Result GetApplicationAreaId(u32& application_area_id) const;
Result GetApplicationArea(std::vector<u8>& data) const;
Result SetApplicationArea(std::span<const u8> data);
Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
@@ -88,7 +89,7 @@ private:
bool is_data_moddified{};
bool is_app_area_open{};
s32 protocol{};
TagProtocol allowed_protocols{};
s64 current_posix_time{};
MountTarget mount_target{MountTarget::None};
DeviceState device_state{DeviceState::Unavailable};

View File

@@ -88,11 +88,22 @@ enum class PackedTagType : u8 {
Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
};
// Verify this enum. It might be completely wrong default protocol is 0x48
enum class TagProtocol : u32 {
None,
TypeA, // ISO14443A
TypeB, // ISO14443B
TypeF, // Sony Felica
TypeA = 1U << 0, // ISO14443A
TypeB = 1U << 1, // ISO14443B
TypeF = 1U << 2, // Sony Felica
Unknown1 = 1U << 3,
Unknown2 = 1U << 5,
All = 0xFFFFFFFFU,
};
enum class CabinetMode : u8 {
StartNicknameAndOwnerSettings,
StartGameDataEraser,
StartRestorer,
StartFormatter,
};
using UniqueSerialNumber = std::array<u8, 7>;

View File

@@ -1,9 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <atomic>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/hid_types.h"
@@ -55,8 +52,12 @@ IUser::IUser(Core::System& system_)
}
}
IUser ::~IUser() {
availability_change_event->Close();
}
void IUser::Initialize(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
LOG_INFO(Service_NFP, "called");
state = State::Initialized;
@@ -64,7 +65,7 @@ void IUser::Initialize(Kernel::HLERequestContext& ctx) {
device->Initialize();
}
IPC::ResponseBuilder rb{ctx, 2, 0};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
@@ -103,9 +104,9 @@ void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
}
std::vector<u64> nfp_devices;
const std::size_t max_allowed_devices = ctx.GetWriteBufferSize() / sizeof(u64);
const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
for (auto& device : devices) {
for (const auto& device : devices) {
if (nfp_devices.size() >= max_allowed_devices) {
continue;
}
@@ -114,7 +115,7 @@ void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
}
}
if (nfp_devices.size() == 0) {
if (nfp_devices.empty()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
@@ -130,7 +131,7 @@ void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
void IUser::StartDetection(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto nfp_protocol{rp.Pop<s32>()};
const auto nfp_protocol{rp.PopEnum<TagProtocol>()};
LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
if (state == State::NonInitialized) {
@@ -551,9 +552,9 @@ void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
}
void IUser::GetState(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
LOG_DEBUG(Service_NFP, "called");
IPC::ResponseBuilder rb{ctx, 3, 0};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(state);
}

View File

@@ -3,6 +3,10 @@
#pragma once
#include <array>
#include <memory>
#include <optional>
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
@@ -12,6 +16,7 @@ class NfpDevice;
class IUser final : public ServiceFramework<IUser> {
public:
explicit IUser(Core::System& system_);
~IUser();
private:
enum class State : u32 {

View File

@@ -279,13 +279,10 @@ void IPlatformServiceManager::GetSharedFontInOrderOfPriority(Kernel::HLERequestC
font_sizes.push_back(region.size);
}
// Resize buffers if game requests smaller size output.
font_codes.resize(
std::min<std::size_t>(font_codes.size(), ctx.GetWriteBufferSize(0) / sizeof(u32)));
font_offsets.resize(
std::min<std::size_t>(font_offsets.size(), ctx.GetWriteBufferSize(1) / sizeof(u32)));
font_sizes.resize(
std::min<std::size_t>(font_sizes.size(), ctx.GetWriteBufferSize(2) / sizeof(u32)));
// Resize buffers if game requests smaller size output
font_codes.resize(std::min(font_codes.size(), ctx.GetWriteBufferNumElements<u32>(0)));
font_offsets.resize(std::min(font_offsets.size(), ctx.GetWriteBufferNumElements<u32>(1)));
font_sizes.resize(std::min(font_sizes.size(), ctx.GetWriteBufferNumElements<u32>(2)));
ctx.WriteBuffer(font_codes, 0);
ctx.WriteBuffer(font_offsets, 1);

View File

@@ -10,6 +10,7 @@ namespace Service::android {
class IProducerListener {
public:
virtual ~IProducerListener() = default;
virtual void OnBufferReleased() = 0;
};

View File

@@ -83,7 +83,7 @@ void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_la
}
void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_entries) {
const std::size_t requested_amount = ctx.GetWriteBufferSize() / sizeof(LanguageCode);
const std::size_t requested_amount = ctx.GetWriteBufferNumElements<LanguageCode>();
const std::size_t max_amount = std::min(requested_amount, max_entries);
const std::size_t copy_amount = std::min(available_language_codes.size(), max_amount);
const std::size_t copy_size = copy_amount * sizeof(LanguageCode);

View File

@@ -761,7 +761,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
u64 src_address =
GetCheatProcessAddress(metadata, begin_cond->mem_type, begin_cond->rel_address);
u64 src_value = 0;
switch (store_static->bit_width) {
switch (begin_cond->bit_width) {
case 1:
case 2:
case 4:

View File

@@ -60,6 +60,8 @@ Common::Input::NfcState VirtualAmiibo::WriteNfcData(
return Common::Input::NfcState::WriteFailed;
}
amiibo_data = data;
return Common::Input::NfcState::Success;
}
@@ -91,6 +93,15 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
return Info::Success;
}
VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
if (state == State::AmiiboIsOpen) {
SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data});
return Info::Success;
}
return LoadAmiibo(file_path);
}
VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo
: State::Initialized;
@@ -98,4 +109,8 @@ VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
return Info::Success;
}
std::string VirtualAmiibo::GetLastFilePath() const {
return file_path;
}
} // namespace InputCommon

View File

@@ -47,8 +47,11 @@ public:
State GetCurrentState() const;
Info LoadAmiibo(const std::string& amiibo_file);
Info ReloadAmiibo();
Info CloseAmiibo();
std::string GetLastFilePath() const;
private:
static constexpr std::size_t amiibo_size = 0x21C;
static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8;

View File

@@ -133,7 +133,7 @@ public:
return Common::Input::CameraError::NotSupported;
}
// Request nfc data from a controller
// Returns success if nfc is supported
virtual Common::Input::NfcState SupportsNfc(
[[maybe_unused]] const PadIdentifier& identifier) const {
return Common::Input::NfcState::NotSupported;

View File

@@ -138,6 +138,16 @@ struct InputSubsystem::Impl {
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName());
tas_input.reset();
Common::Input::UnregisterFactory<Common::Input::InputDevice>(camera->GetEngineName());
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(camera->GetEngineName());
camera.reset();
Common::Input::UnregisterFactory<Common::Input::InputDevice>(
virtual_amiibo->GetEngineName());
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(
virtual_amiibo->GetEngineName());
virtual_amiibo.reset();
#ifdef HAVE_SDL2
Common::Input::UnregisterFactory<Common::Input::InputDevice>(sdl->GetEngineName());
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName());

View File

@@ -28,6 +28,10 @@ add_library(video_core STATIC
dirty_flags.h
dma_pusher.cpp
dma_pusher.h
engines/sw_blitter/blitter.cpp
engines/sw_blitter/blitter.h
engines/sw_blitter/converter.cpp
engines/sw_blitter/converter.h
engines/const_buffer_info.h
engines/engine_interface.h
engines/engine_upload.cpp

View File

@@ -992,7 +992,20 @@ void BufferCache<P>::BindHostIndexBuffer() {
TouchBuffer(buffer, index_buffer.buffer_id);
const u32 offset = buffer.Offset(index_buffer.cpu_addr);
const u32 size = index_buffer.size;
SynchronizeBuffer(buffer, index_buffer.cpu_addr, size);
if (maxwell3d->inline_index_draw_indexes.size()) {
if constexpr (USE_MEMORY_MAPS) {
auto upload_staging = runtime.UploadStagingBuffer(size);
std::array<BufferCopy, 1> copies{
{BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}};
std::memcpy(upload_staging.mapped_span.data(),
maxwell3d->inline_index_draw_indexes.data(), size);
runtime.CopyBuffer(buffer, upload_staging.buffer, copies);
} else {
buffer.ImmediateUpload(0, maxwell3d->inline_index_draw_indexes);
}
} else {
SynchronizeBuffer(buffer, index_buffer.cpu_addr, size);
}
if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
const u32 new_offset = offset + maxwell3d->regs.index_buffer.first *
maxwell3d->regs.index_buffer.FormatSizeInBytes();
@@ -1275,7 +1288,15 @@ void BufferCache<P>::UpdateIndexBuffer() {
}
flags[Dirty::IndexBuffer] = false;
last_index_count = index_array.count;
if (maxwell3d->inline_index_draw_indexes.size()) {
auto inline_index_size = static_cast<u32>(maxwell3d->inline_index_draw_indexes.size());
index_buffer = Binding{
.cpu_addr = 0,
.size = inline_index_size,
.buffer_id = CreateBuffer(0, inline_index_size),
};
return;
}
const GPUVAddr gpu_addr_begin = index_array.StartAddress();
const GPUVAddr gpu_addr_end = index_array.EndAddress();
const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
@@ -1491,6 +1512,14 @@ typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu
VAddr end = cpu_addr + wanted_size;
int stream_score = 0;
bool has_stream_leap = false;
if (begin == 0) {
return OverlapResult{
.ids = std::move(overlap_ids),
.begin = begin,
.end = end,
.has_stream_leap = has_stream_leap,
};
}
for (; cpu_addr >> YUZU_PAGEBITS < Common::DivCeil(end, YUZU_PAGESIZE);
cpu_addr += YUZU_PAGESIZE) {
const BufferId overlap_id = page_table[cpu_addr >> YUZU_PAGEBITS];
@@ -1713,12 +1742,12 @@ bool BufferCache<P>::InlineMemory(VAddr dest_address, size_t copy_size,
SynchronizeBuffer(buffer, dest_address, static_cast<u32>(copy_size));
if constexpr (USE_MEMORY_MAPS) {
auto upload_staging = runtime.UploadStagingBuffer(copy_size);
std::array copies{BufferCopy{
.src_offset = 0,
.src_offset = upload_staging.offset,
.dst_offset = buffer.Offset(dest_address),
.size = copy_size,
}};
auto upload_staging = runtime.UploadStagingBuffer(copy_size);
u8* const src_pointer = upload_staging.mapped_span.data();
std::memcpy(src_pointer, inlined_buffer.data(), copy_size);
runtime.CopyBuffer(buffer, upload_staging.buffer, copies);

View File

@@ -20,7 +20,7 @@ void ChannelState::Init(Core::System& system, GPU& gpu) {
ASSERT(memory_manager);
dma_pusher = std::make_unique<Tegra::DmaPusher>(system, gpu, *memory_manager, *this);
maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, *memory_manager);
fermi_2d = std::make_unique<Engines::Fermi2D>();
fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager);
kepler_compute = std::make_unique<Engines::KeplerCompute>(system, *memory_manager);
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, *memory_manager);
kepler_memory = std::make_unique<Engines::KeplerMemory>(system, *memory_manager);

View File

@@ -51,11 +51,11 @@ void State::ProcessData(std::span<const u8> read_buffer) {
} else {
for (u32 line = 0; line < regs.line_count; ++line) {
const GPUVAddr dest_line = address + static_cast<size_t>(line) * regs.dest.pitch;
memory_manager.WriteBlockUnsafe(
dest_line, read_buffer.data() + static_cast<size_t>(line) * regs.line_length_in,
regs.line_length_in);
std::span<const u8> buffer(read_buffer.data() +
static_cast<size_t>(line) * regs.line_length_in,
regs.line_length_in);
rasterizer->AccelerateInlineToMemory(dest_line, regs.line_length_in, buffer);
}
memory_manager.InvalidateRegion(address, regs.dest.pitch * regs.line_count);
}
} else {
u32 width = regs.dest.width;

View File

@@ -3,17 +3,25 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "video_core/engines/fermi_2d.h"
#include "video_core/memory_manager.h"
#include "video_core/engines/sw_blitter/blitter.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/surface.h"
#include "video_core/textures/decoders.h"
MICROPROFILE_DECLARE(GPU_BlitEngine);
MICROPROFILE_DEFINE(GPU_BlitEngine, "GPU", "Blit Engine", MP_RGB(224, 224, 128));
using VideoCore::Surface::BytesPerBlock;
using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
namespace Tegra::Engines {
Fermi2D::Fermi2D() {
using namespace Texture;
Fermi2D::Fermi2D(MemoryManager& memory_manager_) {
sw_blitter = std::make_unique<Blitter::SoftwareBlitEngine>(memory_manager_);
// Nvidia's OpenGL driver seems to assume these values
regs.src.depth = 1;
regs.dst.depth = 1;
@@ -42,6 +50,7 @@ void Fermi2D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32
}
void Fermi2D::Blit() {
MICROPROFILE_SCOPE(GPU_BlitEngine);
LOG_DEBUG(HW_GPU, "called. source address=0x{:x}, destination address=0x{:x}",
regs.src.Address(), regs.dst.Address());
@@ -52,9 +61,16 @@ void Fermi2D::Blit() {
UNIMPLEMENTED_IF_MSG(regs.clip_enable != 0, "Clipped blit enabled");
const auto& args = regs.pixels_from_memory;
constexpr s64 null_derivate = 1ULL << 32;
Surface src = regs.src;
const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
const bool delegate_to_gpu = src.width > 512 && src.height > 512 && bytes_per_pixel <= 8 &&
src.format != regs.dst.format;
Config config{
.operation = regs.operation,
.filter = args.sample_mode.filter,
.must_accelerate =
args.du_dx != null_derivate || args.dv_dy != null_derivate || delegate_to_gpu,
.dst_x0 = args.dst_x0,
.dst_y0 = args.dst_y0,
.dst_x1 = args.dst_x0 + args.dst_width,
@@ -64,8 +80,7 @@ void Fermi2D::Blit() {
.src_x1 = static_cast<s32>((args.du_dx * args.dst_width + args.src_x0) >> 32),
.src_y1 = static_cast<s32>((args.dv_dy * args.dst_height + args.src_y0) >> 32),
};
Surface src = regs.src;
const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
const auto need_align_to_pitch =
src.linear == Tegra::Engines::Fermi2D::MemoryLayout::Pitch &&
static_cast<s32>(src.width) == config.src_x1 &&
@@ -78,8 +93,9 @@ void Fermi2D::Blit() {
config.src_x1 -= config.src_x0;
config.src_x0 = 0;
}
if (!rasterizer->AccelerateSurfaceCopy(src, regs.dst, config)) {
UNIMPLEMENTED();
sw_blitter->Blit(src, regs.dst, config);
}
}

View File

@@ -5,6 +5,7 @@
#include <array>
#include <cstddef>
#include <memory>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
@@ -21,6 +22,10 @@ class RasterizerInterface;
namespace Tegra::Engines {
namespace Blitter {
class SoftwareBlitEngine;
}
/**
* This Engine is known as G80_2D. Documentation can be found in:
* https://github.com/envytools/envytools/blob/master/rnndb/graph/g80_2d.xml
@@ -32,7 +37,7 @@ namespace Tegra::Engines {
class Fermi2D final : public EngineInterface {
public:
explicit Fermi2D();
explicit Fermi2D(MemoryManager& memory_manager_);
~Fermi2D() override;
/// Binds a rasterizer to this engine.
@@ -286,6 +291,7 @@ public:
struct Config {
Operation operation;
Filter filter;
bool must_accelerate;
s32 dst_x0;
s32 dst_y0;
s32 dst_x1;
@@ -298,6 +304,7 @@ public:
private:
VideoCore::RasterizerInterface* rasterizer = nullptr;
std::unique_ptr<Blitter::SoftwareBlitEngine> sw_blitter;
/// Performs the copy from the source surface to the destination surface as configured in the
/// registers.

View File

@@ -126,6 +126,7 @@ void Maxwell3D::InitializeRegisterDefaults() {
draw_command[MAXWELL3D_REG_INDEX(draw_inline_index)] = true;
draw_command[MAXWELL3D_REG_INDEX(inline_index_2x16.even)] = true;
draw_command[MAXWELL3D_REG_INDEX(inline_index_4x8.index0)] = true;
draw_command[MAXWELL3D_REG_INDEX(draw.instance_id)] = true;
}
void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
@@ -249,9 +250,6 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
return;
case MAXWELL3D_REG_INDEX(fragment_barrier):
return rasterizer->FragmentBarrier();
case MAXWELL3D_REG_INDEX(invalidate_texture_data_cache):
rasterizer->InvalidateGPUCache();
return rasterizer->WaitForIdle();
case MAXWELL3D_REG_INDEX(tiled_cache_barrier):
return rasterizer->TiledCacheBarrier();
}
@@ -288,31 +286,58 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
ASSERT_MSG(method < Regs::NUM_REGS,
"Invalid Maxwell3D register, increase the size of the Regs structure");
const u32 argument = ProcessShadowRam(method, method_argument);
ProcessDirtyRegisters(method, argument);
if (draw_command[method]) {
regs.reg_array[method] = method_argument;
deferred_draw_method.push_back(method);
auto u32_to_u8 = [&](const u32 argument) {
inline_index_draw_indexes.push_back(static_cast<u8>(argument & 0x000000ff));
inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0x0000ff00) >> 8));
inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0x00ff0000) >> 16));
inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0xff000000) >> 24));
auto update_inline_index = [&](const u32 index) {
inline_index_draw_indexes.push_back(static_cast<u8>(index & 0x000000ff));
inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x0000ff00) >> 8));
inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x00ff0000) >> 16));
inline_index_draw_indexes.push_back(static_cast<u8>((index & 0xff000000) >> 24));
draw_mode = DrawMode::InlineIndex;
};
if (MAXWELL3D_REG_INDEX(draw_inline_index) == method) {
u32_to_u8(method_argument);
} else if (MAXWELL3D_REG_INDEX(inline_index_2x16.even) == method) {
u32_to_u8(regs.inline_index_2x16.even);
u32_to_u8(regs.inline_index_2x16.odd);
} else if (MAXWELL3D_REG_INDEX(inline_index_4x8.index0) == method) {
u32_to_u8(regs.inline_index_4x8.index0);
u32_to_u8(regs.inline_index_4x8.index1);
u32_to_u8(regs.inline_index_4x8.index2);
u32_to_u8(regs.inline_index_4x8.index3);
switch (method) {
case MAXWELL3D_REG_INDEX(draw.end):
switch (draw_mode) {
case DrawMode::General:
ProcessDraw(1);
break;
case DrawMode::InlineIndex:
regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4);
regs.index_buffer.format = Regs::IndexFormat::UnsignedInt;
ProcessDraw(1);
inline_index_draw_indexes.clear();
break;
case DrawMode::Instance:
break;
}
break;
case MAXWELL3D_REG_INDEX(draw_inline_index):
update_inline_index(method_argument);
break;
case MAXWELL3D_REG_INDEX(inline_index_2x16.even):
update_inline_index(regs.inline_index_2x16.even);
update_inline_index(regs.inline_index_2x16.odd);
break;
case MAXWELL3D_REG_INDEX(inline_index_4x8.index0):
update_inline_index(regs.inline_index_4x8.index0);
update_inline_index(regs.inline_index_4x8.index1);
update_inline_index(regs.inline_index_4x8.index2);
update_inline_index(regs.inline_index_4x8.index3);
break;
case MAXWELL3D_REG_INDEX(draw.instance_id):
draw_mode =
(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) ||
(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged)
? DrawMode::Instance
: DrawMode::General;
break;
}
} else {
ProcessDeferredDraw();
const u32 argument = ProcessShadowRam(method, method_argument);
ProcessDirtyRegisters(method, argument);
ProcessMethodCall(method, argument, method_argument, is_last_call);
}
}
@@ -511,10 +536,7 @@ void Maxwell3D::ProcessCounterReset() {
void Maxwell3D::ProcessSyncPoint() {
const u32 sync_point = regs.sync_info.sync_point.Value();
const u32 cache_flush = regs.sync_info.clean_l2.Value();
if (cache_flush != 0) {
rasterizer->InvalidateGPUCache();
}
[[maybe_unused]] const u32 cache_flush = regs.sync_info.clean_l2.Value();
rasterizer->SignalSyncPoint(sync_point);
}
@@ -626,57 +648,27 @@ void Maxwell3D::ProcessDraw(u32 instance_count) {
}
void Maxwell3D::ProcessDeferredDraw() {
if (deferred_draw_method.empty()) {
if (draw_mode != DrawMode::Instance || deferred_draw_method.empty()) {
return;
}
enum class DrawMode {
Undefined,
General,
Instance,
};
DrawMode draw_mode{DrawMode::Undefined};
u32 method_count = static_cast<u32>(deferred_draw_method.size());
u32 method = deferred_draw_method[method_count - 1];
if (MAXWELL3D_REG_INDEX(draw.end) != method) {
return;
}
draw_mode = (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) ||
(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged)
? DrawMode::Instance
: DrawMode::General;
u32 instance_count = 0;
if (draw_mode == DrawMode::Instance) {
u32 vertex_buffer_count = 0;
u32 index_buffer_count = 0;
for (u32 index = 0; index < method_count; ++index) {
method = deferred_draw_method[index];
if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count)) {
instance_count = ++vertex_buffer_count;
} else if (method == MAXWELL3D_REG_INDEX(index_buffer.count)) {
instance_count = ++index_buffer_count;
}
}
ASSERT_MSG(!(vertex_buffer_count && index_buffer_count),
"Instance both indexed and direct?");
} else {
instance_count = 1;
for (u32 index = 0; index < method_count; ++index) {
method = deferred_draw_method[index];
if (MAXWELL3D_REG_INDEX(draw_inline_index) == method ||
MAXWELL3D_REG_INDEX(inline_index_2x16.even) == method ||
MAXWELL3D_REG_INDEX(inline_index_4x8.index0) == method) {
regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4);
regs.index_buffer.format = Regs::IndexFormat::UnsignedInt;
break;
}
u32 instance_count = 1;
u32 vertex_buffer_count = 0;
u32 index_buffer_count = 0;
for (u32 index = 0; index < method_count; ++index) {
u32 method = deferred_draw_method[index];
if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count)) {
instance_count = ++vertex_buffer_count;
} else if (method == MAXWELL3D_REG_INDEX(index_buffer.count)) {
instance_count = ++index_buffer_count;
}
}
ASSERT_MSG(!(vertex_buffer_count && index_buffer_count), "Instance both indexed and direct?");
ProcessDraw(instance_count);
deferred_draw_method.clear();
inline_index_draw_indexes.clear();
}
} // namespace Tegra::Engines

View File

@@ -3148,10 +3148,12 @@ private:
/// Handles use of topology overrides (e.g., to avoid using a topology assigned from a macro)
void ProcessTopologyOverride();
void ProcessDraw(u32 instance_count = 1);
/// Handles deferred draw(e.g., instance draw).
void ProcessDeferredDraw();
/// Handles a draw.
void ProcessDraw(u32 instance_count = 1);
/// Returns a query's value or an empty object if the value will be deferred through a cache.
std::optional<u64> GetQueryResult();
@@ -3178,6 +3180,8 @@ private:
std::array<bool, Regs::NUM_REGS> draw_command{};
std::vector<u32> deferred_draw_method;
enum class DrawMode : u32 { General = 0, Instance, InlineIndex };
DrawMode draw_mode{DrawMode::General};
};
#define ASSERT_REG_POSITION(field_name, position) \

View File

@@ -62,7 +62,8 @@ void MaxwellDMA::Launch() {
if (!is_src_pitch && !is_dst_pitch) {
// If both the source and the destination are in block layout, assert.
UNIMPLEMENTED_MSG("Tiled->Tiled DMA transfers are not yet implemented");
CopyBlockLinearToBlockLinear();
ReleaseSemaphore();
return;
}
@@ -291,6 +292,70 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() {
memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
}
void MaxwellDMA::CopyBlockLinearToBlockLinear() {
UNIMPLEMENTED_IF(regs.src_params.block_size.width != 0);
const bool is_remapping = regs.launch_dma.remap_enable != 0;
// Deswizzle the input and copy it over.
const Parameters& src = regs.src_params;
const Parameters& dst = regs.dst_params;
const u32 num_remap_components = regs.remap_const.num_dst_components_minus_one + 1;
const u32 remap_components_size = regs.remap_const.component_size_minus_one + 1;
const u32 base_bpp = !is_remapping ? 1U : num_remap_components * remap_components_size;
u32 src_width = src.width;
u32 dst_width = dst.width;
u32 x_elements = regs.line_length_in;
u32 src_x_offset = src.origin.x;
u32 dst_x_offset = dst.origin.x;
u32 bpp_shift = 0U;
if (!is_remapping) {
bpp_shift = Common::FoldRight(
4U, [](u32 x, u32 y) { return std::min(x, static_cast<u32>(std::countr_zero(y))); },
src_width, dst_width, x_elements, src_x_offset, dst_x_offset,
static_cast<u32>(regs.offset_in), static_cast<u32>(regs.offset_out));
src_width >>= bpp_shift;
dst_width >>= bpp_shift;
x_elements >>= bpp_shift;
src_x_offset >>= bpp_shift;
dst_x_offset >>= bpp_shift;
}
const u32 bytes_per_pixel = base_bpp << bpp_shift;
const size_t src_size = CalculateSize(true, bytes_per_pixel, src_width, src.height, src.depth,
src.block_size.height, src.block_size.depth);
const size_t dst_size = CalculateSize(true, bytes_per_pixel, dst_width, dst.height, dst.depth,
dst.block_size.height, dst.block_size.depth);
const u32 pitch = x_elements * bytes_per_pixel;
const size_t mid_buffer_size = pitch * regs.line_count;
if (read_buffer.size() < src_size) {
read_buffer.resize(src_size);
}
if (write_buffer.size() < dst_size) {
write_buffer.resize(dst_size);
}
intermediate_buffer.resize(mid_buffer_size);
memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
UnswizzleSubrect(intermediate_buffer, read_buffer, bytes_per_pixel, src_width, src.height,
src.depth, src_x_offset, src.origin.y, x_elements, regs.line_count,
src.block_size.height, src.block_size.depth, pitch);
SwizzleSubrect(write_buffer, intermediate_buffer, bytes_per_pixel, dst_width, dst.height,
dst.depth, dst_x_offset, dst.origin.y, x_elements, regs.line_count,
dst.block_size.height, dst.block_size.depth, pitch);
memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
}
void MaxwellDMA::ReleaseSemaphore() {
const auto type = regs.launch_dma.semaphore_type;
const GPUVAddr address = regs.semaphore.address;

View File

@@ -223,6 +223,8 @@ private:
void CopyPitchToBlockLinear();
void CopyBlockLinearToBlockLinear();
void FastCopyBlockLinearToPitch();
void ReleaseSemaphore();
@@ -234,6 +236,7 @@ private:
std::vector<u8> read_buffer;
std::vector<u8> write_buffer;
std::vector<u8> intermediate_buffer;
static constexpr std::size_t NUM_REGS = 0x800;
struct Regs {

View File

@@ -118,7 +118,7 @@ void Puller::ProcessSemaphoreRelease() {
std::function<void()> operation([this, sequence_address, payload] {
memory_manager.Write<u32>(sequence_address, payload);
});
rasterizer->SyncOperation(std::move(operation));
rasterizer->SignalFence(std::move(operation));
}
void Puller::ProcessSemaphoreAcquire() {
@@ -151,8 +151,8 @@ void Puller::CallPullerMethod(const MethodCall& method_call) {
case BufferMethods::SemaphoreAddressLow:
case BufferMethods::SemaphoreSequencePayload:
case BufferMethods::SyncpointPayload:
break;
case BufferMethods::WrcacheFlush:
break;
case BufferMethods::RefCnt:
rasterizer->SignalReference();
break;

View File

@@ -0,0 +1,238 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
#include <cmath>
#include <vector>
#include "video_core/engines/sw_blitter/blitter.h"
#include "video_core/engines/sw_blitter/converter.h"
#include "video_core/memory_manager.h"
#include "video_core/surface.h"
#include "video_core/textures/decoders.h"
namespace Tegra {
class MemoryManager;
}
using VideoCore::Surface::BytesPerBlock;
using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
namespace Tegra::Engines::Blitter {
using namespace Texture;
namespace {
constexpr size_t ir_components = 4;
void NearestNeighbor(std::span<const u8> input, std::span<u8> output, u32 src_width, u32 src_height,
u32 dst_width, u32 dst_height, size_t bpp) {
const size_t dx_du = std::llround((static_cast<f64>(src_width) / dst_width) * (1ULL << 32));
const size_t dy_dv = std::llround((static_cast<f64>(src_height) / dst_height) * (1ULL << 32));
size_t src_y = 0;
for (u32 y = 0; y < dst_height; y++) {
size_t src_x = 0;
for (u32 x = 0; x < dst_width; x++) {
const size_t read_from = ((src_y * src_width + src_x) >> 32) * bpp;
const size_t write_to = (y * dst_width + x) * bpp;
std::memcpy(&output[write_to], &input[read_from], bpp);
src_x += dx_du;
}
src_y += dy_dv;
}
}
void NearestNeighborFast(std::span<const f32> input, std::span<f32> output, u32 src_width,
u32 src_height, u32 dst_width, u32 dst_height) {
const size_t dx_du = std::llround((static_cast<f64>(src_width) / dst_width) * (1ULL << 32));
const size_t dy_dv = std::llround((static_cast<f64>(src_height) / dst_height) * (1ULL << 32));
size_t src_y = 0;
for (u32 y = 0; y < dst_height; y++) {
size_t src_x = 0;
for (u32 x = 0; x < dst_width; x++) {
const size_t read_from = ((src_y * src_width + src_x) >> 32) * ir_components;
const size_t write_to = (y * dst_width + x) * ir_components;
std::memcpy(&output[write_to], &input[read_from], sizeof(f32) * ir_components);
src_x += dx_du;
}
src_y += dy_dv;
}
}
void Bilinear(std::span<const f32> input, std::span<f32> output, size_t src_width,
size_t src_height, size_t dst_width, size_t dst_height) {
const auto bilinear_sample = [](std::span<const f32> x0_y0, std::span<const f32> x1_y0,
std::span<const f32> x0_y1, std::span<const f32> x1_y1,
f32 weight_x, f32 weight_y) {
std::array<f32, ir_components> result{};
for (size_t i = 0; i < ir_components; i++) {
const f32 a = std::lerp(x0_y0[i], x1_y0[i], weight_x);
const f32 b = std::lerp(x0_y1[i], x1_y1[i], weight_x);
result[i] = std::lerp(a, b, weight_y);
}
return result;
};
const f32 dx_du =
dst_width > 1 ? static_cast<f32>(src_width - 1) / static_cast<f32>(dst_width - 1) : 0.f;
const f32 dy_dv =
dst_height > 1 ? static_cast<f32>(src_height - 1) / static_cast<f32>(dst_height - 1) : 0.f;
for (u32 y = 0; y < dst_height; y++) {
for (u32 x = 0; x < dst_width; x++) {
const f32 x_low = std::floor(static_cast<f32>(x) * dx_du);
const f32 y_low = std::floor(static_cast<f32>(y) * dy_dv);
const f32 x_high = std::ceil(static_cast<f32>(x) * dx_du);
const f32 y_high = std::ceil(static_cast<f32>(y) * dy_dv);
const f32 weight_x = (static_cast<f32>(x) * dx_du) - x_low;
const f32 weight_y = (static_cast<f32>(y) * dy_dv) - y_low;
const auto read_src = [&](f32 in_x, f32 in_y) {
const size_t read_from =
((static_cast<size_t>(in_x) * src_width + static_cast<size_t>(in_y)) >> 32) *
ir_components;
return std::span<const f32>(&input[read_from], ir_components);
};
auto x0_y0 = read_src(x_low, y_low);
auto x1_y0 = read_src(x_high, y_low);
auto x0_y1 = read_src(x_low, y_high);
auto x1_y1 = read_src(x_high, y_high);
const auto result = bilinear_sample(x0_y0, x1_y0, x0_y1, x1_y1, weight_x, weight_y);
const size_t write_to = (y * dst_width + x) * ir_components;
std::memcpy(&output[write_to], &result, sizeof(f32) * ir_components);
}
}
}
} // namespace
struct SoftwareBlitEngine::BlitEngineImpl {
std::vector<u8> tmp_buffer;
std::vector<u8> src_buffer;
std::vector<u8> dst_buffer;
std::vector<f32> intermediate_src;
std::vector<f32> intermediate_dst;
ConverterFactory converter_factory;
};
SoftwareBlitEngine::SoftwareBlitEngine(MemoryManager& memory_manager_)
: memory_manager{memory_manager_} {
impl = std::make_unique<BlitEngineImpl>();
}
SoftwareBlitEngine::~SoftwareBlitEngine() = default;
bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
Fermi2D::Config& config) {
const auto get_surface_size = [](Fermi2D::Surface& surface, u32 bytes_per_pixel) {
if (surface.linear == Fermi2D::MemoryLayout::BlockLinear) {
return CalculateSize(true, bytes_per_pixel, surface.width, surface.height,
surface.depth, surface.block_height, surface.block_depth);
}
return static_cast<size_t>(surface.pitch * surface.height);
};
const auto process_pitch_linear = [](bool unpack, std::span<const u8> input,
std::span<u8> output, u32 extent_x, u32 extent_y,
u32 pitch, u32 x0, u32 y0, size_t bpp) {
const size_t base_offset = x0 * bpp;
const size_t copy_size = extent_x * bpp;
for (u32 y = y0; y < extent_y; y++) {
const size_t first_offset = y * pitch + base_offset;
const size_t second_offset = y * extent_x * bpp;
u8* write_to = unpack ? &output[first_offset] : &output[second_offset];
const u8* read_from = unpack ? &input[second_offset] : &input[first_offset];
std::memcpy(write_to, read_from, copy_size);
}
};
const u32 src_extent_x = config.src_x1 - config.src_x0;
const u32 src_extent_y = config.src_y1 - config.src_y0;
const u32 dst_extent_x = config.dst_x1 - config.dst_x0;
const u32 dst_extent_y = config.dst_y1 - config.dst_y0;
const auto src_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
const auto dst_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(dst.format));
const size_t src_size = get_surface_size(src, src_bytes_per_pixel);
impl->tmp_buffer.resize(src_size);
memory_manager.ReadBlock(src.Address(), impl->tmp_buffer.data(), src_size);
const size_t src_copy_size = src_extent_x * src_extent_y * src_bytes_per_pixel;
const size_t dst_copy_size = dst_extent_x * dst_extent_y * dst_bytes_per_pixel;
impl->src_buffer.resize(src_copy_size);
const bool no_passthrough =
src.format != dst.format || src_extent_x != dst_extent_x || src_extent_y != dst_extent_y;
const auto convertion_phase_same_format = [&]() {
NearestNeighbor(impl->src_buffer, impl->dst_buffer, src_extent_x, src_extent_y,
dst_extent_x, dst_extent_y, dst_bytes_per_pixel);
};
const auto convertion_phase_ir = [&]() {
auto* input_converter = impl->converter_factory.GetFormatConverter(src.format);
impl->intermediate_src.resize((src_copy_size / src_bytes_per_pixel) * ir_components);
impl->intermediate_dst.resize((dst_copy_size / dst_bytes_per_pixel) * ir_components);
input_converter->ConvertTo(impl->src_buffer, impl->intermediate_src);
if (config.filter != Fermi2D::Filter::Bilinear) {
NearestNeighborFast(impl->intermediate_src, impl->intermediate_dst, src_extent_x,
src_extent_y, dst_extent_x, dst_extent_y);
} else {
Bilinear(impl->intermediate_src, impl->intermediate_dst, src_extent_x, src_extent_y,
dst_extent_x, dst_extent_y);
}
auto* output_converter = impl->converter_factory.GetFormatConverter(dst.format);
output_converter->ConvertFrom(impl->intermediate_dst, impl->dst_buffer);
};
// Do actuall Blit
impl->dst_buffer.resize(dst_copy_size);
if (src.linear == Fermi2D::MemoryLayout::BlockLinear) {
UnswizzleSubrect(impl->src_buffer, impl->tmp_buffer, src_bytes_per_pixel, src.width,
src.height, src.depth, config.src_x0, config.src_y0, src_extent_x,
src_extent_y, src.block_height, src.block_depth,
src_extent_x * src_bytes_per_pixel);
} else {
process_pitch_linear(false, impl->tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y,
src.pitch, config.src_x0, config.src_y0, src_bytes_per_pixel);
}
// Conversion Phase
if (no_passthrough) {
if (src.format != dst.format || config.filter == Fermi2D::Filter::Bilinear) {
convertion_phase_ir();
} else {
convertion_phase_same_format();
}
} else {
impl->dst_buffer.swap(impl->src_buffer);
}
const size_t dst_size = get_surface_size(dst, dst_bytes_per_pixel);
impl->tmp_buffer.resize(dst_size);
memory_manager.ReadBlock(dst.Address(), impl->tmp_buffer.data(), dst_size);
if (dst.linear == Fermi2D::MemoryLayout::BlockLinear) {
SwizzleSubrect(impl->tmp_buffer, impl->dst_buffer, dst_bytes_per_pixel, dst.width,
dst.height, dst.depth, config.dst_x0, config.dst_y0, dst_extent_x,
dst_extent_y, dst.block_height, dst.block_depth,
dst_extent_x * dst_bytes_per_pixel);
} else {
process_pitch_linear(true, impl->dst_buffer, impl->tmp_buffer, dst_extent_x, dst_extent_y,
dst.pitch, config.dst_x0, config.dst_y0,
static_cast<size_t>(dst_bytes_per_pixel));
}
memory_manager.WriteBlock(dst.Address(), impl->tmp_buffer.data(), dst_size);
return true;
}
} // namespace Tegra::Engines::Blitter

View File

@@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "video_core/engines/fermi_2d.h"
namespace Tegra {
class MemoryManager;
}
namespace Tegra::Engines::Blitter {
class SoftwareBlitEngine {
public:
explicit SoftwareBlitEngine(MemoryManager& memory_manager_);
~SoftwareBlitEngine();
bool Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst, Fermi2D::Config& copy_config);
private:
MemoryManager& memory_manager;
struct BlitEngineImpl;
std::unique_ptr<BlitEngineImpl> impl;
};
} // namespace Tegra::Engines::Blitter

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <memory>
#include <span>
#include "common/common_types.h"
#include "video_core/gpu.h"
namespace Tegra::Engines::Blitter {
class Converter {
public:
virtual void ConvertTo(std::span<const u8> input, std::span<f32> output) = 0;
virtual void ConvertFrom(std::span<const f32> input, std::span<u8> output) = 0;
virtual ~Converter() = default;
};
class ConverterFactory {
public:
ConverterFactory();
~ConverterFactory();
Converter* GetFormatConverter(RenderTargetFormat format);
private:
Converter* BuildConverter(RenderTargetFormat format);
struct ConverterFactoryImpl;
std::unique_ptr<ConverterFactoryImpl> impl;
};
} // namespace Tegra::Engines::Blitter

View File

@@ -0,0 +1,136 @@
# SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
import re
class Format:
def __init__(self, string_value):
self.name = string_value
tmp = string_value.split('_')
self.component_type = tmp[1]
component_data = re.findall(r"\w\d+", tmp[0])
self.num_components = len(component_data)
sizes = []
swizzle = []
for data in component_data:
swizzle.append(data[0])
sizes.append(int(data[1:]))
self.sizes = sizes
self.swizzle = swizzle
def build_component_type_array(self):
result = "{ "
b = False
for i in range(0, self.num_components):
if b:
result += ", "
b = True
result += "ComponentType::" + self.component_type
result += " }"
return result
def build_component_sizes_array(self):
result = "{ "
b = False
for i in range(0, self.num_components):
if b:
result += ", "
b = True
result += str(self.sizes[i])
result += " }"
return result
def build_component_swizzle_array(self):
result = "{ "
b = False
for i in range(0, self.num_components):
if b:
result += ", "
b = True
swizzle = self.swizzle[i]
if swizzle == "X":
swizzle = "None"
result += "Swizzle::" + swizzle
result += " }"
return result
def print_declaration(self):
print("struct " + self.name + "Traits {")
print(" static constexpr size_t num_components = " + str(self.num_components) + ";")
print(" static constexpr std::array<ComponentType, num_components> component_types = " + self.build_component_type_array() + ";")
print(" static constexpr std::array<size_t, num_components> component_sizes = " + self.build_component_sizes_array() + ";")
print(" static constexpr std::array<Swizzle, num_components> component_swizzle = " + self.build_component_swizzle_array() + ";")
print("};\n")
def print_case(self):
print("case RenderTargetFormat::" + self.name + ":")
print(" return impl->converters_cache")
print(" .emplace(format, std::make_unique<ConverterImpl<" + self.name + "Traits>>())")
print(" .first->second.get();")
print(" break;")
txt = """
R32G32B32A32_FLOAT
R32G32B32A32_SINT
R32G32B32A32_UINT
R32G32B32X32_FLOAT
R32G32B32X32_SINT
R32G32B32X32_UINT
R16G16B16A16_UNORM
R16G16B16A16_SNORM
R16G16B16A16_SINT
R16G16B16A16_UINT
R16G16B16A16_FLOAT
R32G32_FLOAT
R32G32_SINT
R32G32_UINT
R16G16B16X16_FLOAT
A8R8G8B8_UNORM
A8R8G8B8_SRGB
A2B10G10R10_UNORM
A2B10G10R10_UINT
A2R10G10B10_UNORM
A8B8G8R8_UNORM
A8B8G8R8_SRGB
A8B8G8R8_SNORM
A8B8G8R8_SINT
A8B8G8R8_UINT
R16G16_UNORM
R16G16_SNORM
R16G16_SINT
R16G16_UINT
R16G16_FLOAT
B10G11R11_FLOAT
R32_SINT
R32_UINT
R32_FLOAT
X8R8G8B8_UNORM
X8R8G8B8_SRGB
R5G6B5_UNORM
A1R5G5B5_UNORM
R8G8_UNORM
R8G8_SNORM
R8G8_SINT
R8G8_UINT
R16_UNORM
R16_SNORM
R16_SINT
R16_UINT
R16_FLOAT
R8_UNORM
R8_SNORM
R8_SINT
R8_UINT
X1R5G5B5_UNORM
X8B8G8R8_UNORM
X8B8G8R8_SRGB
"""
x = txt.split()
y = list(map(lambda a: Format(a), x))
formats = list(y)
for format in formats:
format.print_declaration()
for format in formats:
format.print_case()

View File

@@ -27,12 +27,12 @@ struct CommandList;
// TODO: Implement the commented ones
enum class RenderTargetFormat : u32 {
NONE = 0x0,
R32B32G32A32_FLOAT = 0xC0,
R32G32B32A32_FLOAT = 0xC0,
R32G32B32A32_SINT = 0xC1,
R32G32B32A32_UINT = 0xC2,
// R32G32B32X32_FLOAT = 0xC3,
// R32G32B32X32_SINT = 0xC4,
// R32G32B32X32_UINT = 0xC5,
R32G32B32X32_FLOAT = 0xC3,
R32G32B32X32_SINT = 0xC4,
R32G32B32X32_UINT = 0xC5,
R16G16B16A16_UNORM = 0xC6,
R16G16B16A16_SNORM = 0xC7,
R16G16B16A16_SINT = 0xC8,
@@ -56,13 +56,13 @@ enum class RenderTargetFormat : u32 {
R16G16_SINT = 0xDC,
R16G16_UINT = 0xDD,
R16G16_FLOAT = 0xDE,
// A2R10G10B10_UNORM = 0xDF,
A2R10G10B10_UNORM = 0xDF,
B10G11R11_FLOAT = 0xE0,
R32_SINT = 0xE3,
R32_UINT = 0xE4,
R32_FLOAT = 0xE5,
// X8R8G8B8_UNORM = 0xE6,
// X8R8G8B8_SRGB = 0xE7,
X8R8G8B8_UNORM = 0xE6,
X8R8G8B8_SRGB = 0xE7,
R5G6B5_UNORM = 0xE8,
A1R5G5B5_UNORM = 0xE9,
R8G8_UNORM = 0xEA,
@@ -79,11 +79,11 @@ enum class RenderTargetFormat : u32 {
R8_SINT = 0xF5,
R8_UINT = 0xF6,
/*
A8_UNORM = 0xF7,
// A8_UNORM = 0xF7,
X1R5G5B5_UNORM = 0xF8,
X8B8G8R8_UNORM = 0xF9,
X8B8G8R8_SRGB = 0xFA,
/*
Z1R5G5B5_UNORM = 0xFB,
O1R5G5B5_UNORM = 0xFC,
Z8R8G8B8_UNORM = 0xFD,

View File

@@ -222,8 +222,6 @@ void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
pipeline->SetEngine(maxwell3d, gpu_memory);
pipeline->Configure(is_indexed);
BindInlineIndexBuffer();
SyncState();
const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d->regs.draw.topology);
@@ -468,8 +466,7 @@ bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surf
const Tegra::Engines::Fermi2D::Config& copy_config) {
MICROPROFILE_SCOPE(OpenGL_Blits);
std::scoped_lock lock{texture_cache.mutex};
texture_cache.BlitImage(dst, src, copy_config);
return true;
return texture_cache.BlitImage(dst, src, copy_config);
}
Tegra::Engines::AccelerateDMAInterface& RasterizerOpenGL::AccessAccelerateDMA() {
@@ -1140,16 +1137,6 @@ void RasterizerOpenGL::ReleaseChannel(s32 channel_id) {
query_cache.EraseChannel(channel_id);
}
void RasterizerOpenGL::BindInlineIndexBuffer() {
if (maxwell3d->inline_index_draw_indexes.empty()) {
return;
}
const auto data_count = static_cast<u32>(maxwell3d->inline_index_draw_indexes.size());
auto buffer = Buffer(buffer_cache_runtime, *this, 0, data_count);
buffer.ImmediateUpload(0, maxwell3d->inline_index_draw_indexes);
buffer_cache_runtime.BindIndexBuffer(buffer, 0, data_count);
}
AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {}
bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) {

View File

@@ -199,8 +199,6 @@ private:
/// End a transform feedback
void EndTransformFeedback();
void BindInlineIndexBuffer();
Tegra::GPU& gpu;
const Device& device;

View File

@@ -28,6 +28,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
{GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1R5G5B5_UNORM
{GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UNORM
{GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UINT
{GL_RGB10_A2, GL_BGRA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2R10G10B10_UNORM
{GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1B5G5R5_UNORM
{GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // A5B5G5R1_UNORM
{GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8_UNORM

View File

@@ -125,6 +125,7 @@ struct FormatTuple {
{VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1R5G5B5_UNORM
{VK_FORMAT_A2B10G10R10_UNORM_PACK32, Attachable | Storage}, // A2B10G10R10_UNORM
{VK_FORMAT_A2B10G10R10_UINT_PACK32, Attachable | Storage}, // A2B10G10R10_UINT
{VK_FORMAT_A2R10G10B10_UNORM_PACK32, Attachable | Storage}, // A2R10G10B10_UNORM
{VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1B5G5R5_UNORM (flipped with swizzle)
{VK_FORMAT_R5G5B5A1_UNORM_PACK16}, // A5B5G5R1_UNORM (specially swizzled)
{VK_FORMAT_R8_UNORM, Attachable | Storage}, // R8_UNORM
@@ -149,7 +150,7 @@ struct FormatTuple {
{VK_FORMAT_BC6H_UFLOAT_BLOCK}, // BC6H_UFLOAT
{VK_FORMAT_BC6H_SFLOAT_BLOCK}, // BC6H_SFLOAT
{VK_FORMAT_ASTC_4x4_UNORM_BLOCK}, // ASTC_2D_4X4_UNORM
{VK_FORMAT_B8G8R8A8_UNORM, Attachable}, // B8G8R8A8_UNORM
{VK_FORMAT_B8G8R8A8_UNORM, Attachable | Storage}, // B8G8R8A8_UNORM
{VK_FORMAT_R32G32B32A32_SFLOAT, Attachable | Storage}, // R32G32B32A32_FLOAT
{VK_FORMAT_R32G32B32A32_SINT, Attachable | Storage}, // R32G32B32A32_SINT
{VK_FORMAT_R32G32_SFLOAT, Attachable | Storage}, // R32G32_FLOAT
@@ -159,7 +160,7 @@ struct FormatTuple {
{VK_FORMAT_R16_UNORM, Attachable | Storage}, // R16_UNORM
{VK_FORMAT_R16_SNORM, Attachable | Storage}, // R16_SNORM
{VK_FORMAT_R16_UINT, Attachable | Storage}, // R16_UINT
{VK_FORMAT_UNDEFINED}, // R16_SINT
{VK_FORMAT_R16_SINT, Attachable | Storage}, // R16_SINT
{VK_FORMAT_R16G16_UNORM, Attachable | Storage}, // R16G16_UNORM
{VK_FORMAT_R16G16_SFLOAT, Attachable | Storage}, // R16G16_FLOAT
{VK_FORMAT_R16G16_UINT, Attachable | Storage}, // R16G16_UINT
@@ -183,7 +184,7 @@ struct FormatTuple {
{VK_FORMAT_BC2_SRGB_BLOCK}, // BC2_SRGB
{VK_FORMAT_BC3_SRGB_BLOCK}, // BC3_SRGB
{VK_FORMAT_BC7_SRGB_BLOCK}, // BC7_SRGB
{VK_FORMAT_R4G4B4A4_UNORM_PACK16, Attachable}, // A4B4G4R4_UNORM
{VK_FORMAT_R4G4B4A4_UNORM_PACK16}, // A4B4G4R4_UNORM
{VK_FORMAT_R4G4_UNORM_PACK8}, // G4R4_UNORM
{VK_FORMAT_ASTC_4x4_SRGB_BLOCK}, // ASTC_2D_4X4_SRGB
{VK_FORMAT_ASTC_8x8_SRGB_BLOCK}, // ASTC_2D_8X8_SRGB

View File

@@ -172,6 +172,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
}
void RendererVulkan::Report() const {
using namespace Common::Literals;
const std::string vendor_name{device.GetVendorName()};
const std::string model_name{device.GetModelName()};
const std::string driver_version = GetDriverVersion(device);
@@ -181,9 +182,12 @@ void RendererVulkan::Report() const {
const std::string extensions = BuildCommaSeparatedExtensions(device.GetAvailableExtensions());
const auto available_vram = static_cast<f64>(device.GetDeviceLocalMemory()) / f64{1_GiB};
LOG_INFO(Render_Vulkan, "Driver: {}", driver_name);
LOG_INFO(Render_Vulkan, "Device: {}", model_name);
LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version);
LOG_INFO(Render_Vulkan, "Available VRAM: {:.2f} GiB", available_vram);
static constexpr auto field = Common::Telemetry::FieldType::UserSystem;
telemetry_session.AddField(field, "GPU_Vendor", vendor_name);

View File

@@ -5,6 +5,7 @@
#include "common/bit_cast.h"
#include "common/common_types.h"
#include "common/div_ceil.h"
#include "common/settings.h"
#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_comp_spv.h"
#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_comp_spv.h"
@@ -227,7 +228,10 @@ VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImageView imag
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *rcas_pipeline);
FsrRcasCon(push_constants.data(), 0.25f);
const float sharpening =
static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
FsrRcasCon(push_constants.data(), sharpening);
cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants);
{

View File

@@ -191,8 +191,6 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
pipeline->SetEngine(maxwell3d, gpu_memory);
pipeline->Configure(is_indexed);
BindInlineIndexBuffer();
BeginTransformFeedback();
UpdateDynamicStates();
@@ -544,8 +542,7 @@ bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surf
const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Config& copy_config) {
std::scoped_lock lock{texture_cache.mutex};
texture_cache.BlitImage(dst, src, copy_config);
return true;
return texture_cache.BlitImage(dst, src, copy_config);
}
Tegra::Engines::AccelerateDMAInterface& RasterizerVulkan::AccessAccelerateDMA() {
@@ -1029,17 +1026,4 @@ void RasterizerVulkan::ReleaseChannel(s32 channel_id) {
query_cache.EraseChannel(channel_id);
}
void RasterizerVulkan::BindInlineIndexBuffer() {
if (maxwell3d->inline_index_draw_indexes.empty()) {
return;
}
const auto data_count = static_cast<u32>(maxwell3d->inline_index_draw_indexes.size());
auto buffer = buffer_cache_runtime.UploadStagingBuffer(data_count);
std::memcpy(buffer.mapped_span.data(), maxwell3d->inline_index_draw_indexes.data(), data_count);
buffer_cache_runtime.BindIndexBuffer(
maxwell3d->regs.draw.topology, maxwell3d->regs.index_buffer.format,
maxwell3d->regs.index_buffer.first, maxwell3d->regs.index_buffer.count, buffer.buffer,
static_cast<u32>(buffer.offset), data_count);
}
} // namespace Vulkan

View File

@@ -141,8 +141,6 @@ private:
void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs);
void BindInlineIndexBuffer();
Tegra::GPU& gpu;
ScreenInfo& screen_info;

View File

@@ -93,11 +93,14 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) {
switch (format) {
case Tegra::RenderTargetFormat::R32B32G32A32_FLOAT:
case Tegra::RenderTargetFormat::R32G32B32A32_FLOAT:
case Tegra::RenderTargetFormat::R32G32B32X32_FLOAT:
return PixelFormat::R32G32B32A32_FLOAT;
case Tegra::RenderTargetFormat::R32G32B32A32_SINT:
case Tegra::RenderTargetFormat::R32G32B32X32_SINT:
return PixelFormat::R32G32B32A32_SINT;
case Tegra::RenderTargetFormat::R32G32B32A32_UINT:
case Tegra::RenderTargetFormat::R32G32B32X32_UINT:
return PixelFormat::R32G32B32A32_UINT;
case Tegra::RenderTargetFormat::R16G16B16A16_UNORM:
return PixelFormat::R16G16B16A16_UNORM;
@@ -118,16 +121,22 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format)
case Tegra::RenderTargetFormat::R16G16B16X16_FLOAT:
return PixelFormat::R16G16B16X16_FLOAT;
case Tegra::RenderTargetFormat::A8R8G8B8_UNORM:
case Tegra::RenderTargetFormat::X8R8G8B8_UNORM:
return PixelFormat::B8G8R8A8_UNORM;
case Tegra::RenderTargetFormat::A8R8G8B8_SRGB:
case Tegra::RenderTargetFormat::X8R8G8B8_SRGB:
return PixelFormat::B8G8R8A8_SRGB;
case Tegra::RenderTargetFormat::A2B10G10R10_UNORM:
return PixelFormat::A2B10G10R10_UNORM;
case Tegra::RenderTargetFormat::A2B10G10R10_UINT:
return PixelFormat::A2B10G10R10_UINT;
case Tegra::RenderTargetFormat::A2R10G10B10_UNORM:
return PixelFormat::A2R10G10B10_UNORM;
case Tegra::RenderTargetFormat::A8B8G8R8_UNORM:
case Tegra::RenderTargetFormat::X8B8G8R8_UNORM:
return PixelFormat::A8B8G8R8_UNORM;
case Tegra::RenderTargetFormat::A8B8G8R8_SRGB:
case Tegra::RenderTargetFormat::X8B8G8R8_SRGB:
return PixelFormat::A8B8G8R8_SRGB;
case Tegra::RenderTargetFormat::A8B8G8R8_SNORM:
return PixelFormat::A8B8G8R8_SNORM;
@@ -156,6 +165,7 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format)
case Tegra::RenderTargetFormat::R5G6B5_UNORM:
return PixelFormat::R5G6B5_UNORM;
case Tegra::RenderTargetFormat::A1R5G5B5_UNORM:
case Tegra::RenderTargetFormat::X1R5G5B5_UNORM:
return PixelFormat::A1R5G5B5_UNORM;
case Tegra::RenderTargetFormat::R8G8_UNORM:
return PixelFormat::R8G8_UNORM;

View File

@@ -23,6 +23,7 @@ enum class PixelFormat {
A1R5G5B5_UNORM,
A2B10G10R10_UNORM,
A2B10G10R10_UINT,
A2R10G10B10_UNORM,
A1B5G5R5_UNORM,
A5B5G5R1_UNORM,
R8_UNORM,
@@ -159,6 +160,7 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
1, // A1R5G5B5_UNORM
1, // A2B10G10R10_UNORM
1, // A2B10G10R10_UINT
1, // A2R10G10B10_UNORM
1, // A1B5G5R5_UNORM
1, // A5B5G5R1_UNORM
1, // R8_UNORM
@@ -264,6 +266,7 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
1, // A1R5G5B5_UNORM
1, // A2B10G10R10_UNORM
1, // A2B10G10R10_UINT
1, // A2R10G10B10_UNORM
1, // A1B5G5R5_UNORM
1, // A5B5G5R1_UNORM
1, // R8_UNORM
@@ -369,6 +372,7 @@ constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
16, // A1R5G5B5_UNORM
32, // A2B10G10R10_UNORM
32, // A2B10G10R10_UINT
32, // A2R10G10B10_UNORM
16, // A1B5G5R5_UNORM
16, // A5B5G5R1_UNORM
8, // R8_UNORM

View File

@@ -35,6 +35,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
return "A2B10G10R10_UNORM";
case PixelFormat::A2B10G10R10_UINT:
return "A2B10G10R10_UINT";
case PixelFormat::A2R10G10B10_UNORM:
return "A2R10G10B10_UNORM";
case PixelFormat::A1B5G5R5_UNORM:
return "A1B5G5R5_UNORM";
case PixelFormat::A5B5G5R1_UNORM:

View File

@@ -506,10 +506,14 @@ void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t siz
}
template <class P>
void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
bool TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Config& copy) {
const BlitImages images = GetBlitImages(dst, src, copy);
const auto result = GetBlitImages(dst, src, copy);
if (!result) {
return false;
}
const BlitImages images = *result;
const ImageId dst_id = images.dst_id;
const ImageId src_id = images.src_id;
@@ -596,6 +600,7 @@ void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
runtime.BlitImage(dst_framebuffer, dst_view, src_view, dst_region, src_region, copy.filter,
copy.operation);
}
return true;
}
template <class P>
@@ -1133,7 +1138,7 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
}
template <class P>
typename TextureCache<P>::BlitImages TextureCache<P>::GetBlitImages(
std::optional<typename TextureCache<P>::BlitImages> TextureCache<P>::GetBlitImages(
const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Config& copy) {
@@ -1154,6 +1159,20 @@ typename TextureCache<P>::BlitImages TextureCache<P>::GetBlitImages(
has_deleted_images = false;
src_id = FindImage(src_info, src_addr, try_options);
dst_id = FindImage(dst_info, dst_addr, try_options);
if (!copy.must_accelerate) {
do {
if (!src_id && !dst_id) {
return std::nullopt;
}
if (src_id && True(slot_images[src_id].flags & ImageFlagBits::GpuModified)) {
break;
}
if (dst_id && True(slot_images[dst_id].flags & ImageFlagBits::GpuModified)) {
break;
}
return std::nullopt;
} while (false);
}
const ImageBase* const src_image = src_id ? &slot_images[src_id] : nullptr;
if (src_image && src_image->info.num_samples > 1) {
RelaxedOptions find_options{FIND_OPTIONS | RelaxedOptions::ForceBrokenViews};
@@ -1194,12 +1213,12 @@ typename TextureCache<P>::BlitImages TextureCache<P>::GetBlitImages(
dst_id = FindOrInsertImage(dst_info, dst_addr, RelaxedOptions{});
} while (has_deleted_images);
}
return BlitImages{
return {BlitImages{
.dst_id = dst_id,
.src_id = src_id,
.dst_format = dst_info.format,
.src_format = src_info.format,
};
}};
}
template <class P>

View File

@@ -174,7 +174,7 @@ public:
void UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size);
/// Blit an image with the given parameters
void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
bool BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Config& copy);
@@ -285,9 +285,9 @@ private:
[[nodiscard]] ImageId JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr);
/// Return a blit image pair from the given guest blit parameters
[[nodiscard]] BlitImages GetBlitImages(const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Config& copy);
[[nodiscard]] std::optional<BlitImages> GetBlitImages(
const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Config& copy);
/// Find or create a sampler from a guest descriptor sampler
[[nodiscard]] SamplerId FindSampler(const TSCEntry& config);

View File

@@ -18,6 +18,9 @@ add_executable(yuzu
about_dialog.cpp
about_dialog.h
aboutdialog.ui
applets/qt_amiibo_settings.cpp
applets/qt_amiibo_settings.h
applets/qt_amiibo_settings.ui
applets/qt_controller.cpp
applets/qt_controller.h
applets/qt_controller.ui
@@ -292,7 +295,7 @@ if (APPLE)
set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
elseif(WIN32)
# compile as a win32 gui application instead of a console application
if (QT_VERSION VERSION_GREATER 6)
if (QT_VERSION VERSION_GREATER_EQUAL 6)
target_link_libraries(yuzu PRIVATE Qt6::EntryPointPrivate)
else()
target_link_libraries(yuzu PRIVATE Qt5::WinMain)
@@ -308,15 +311,15 @@ endif()
create_target_directory_groups(yuzu)
target_link_libraries(yuzu PRIVATE common core input_common network video_core)
target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets Qt::Multimedia)
target_link_libraries(yuzu PRIVATE Boost::boost glad Qt${QT_MAJOR_VERSION}::Widgets)
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
if (NOT WIN32)
target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
target_include_directories(yuzu PRIVATE ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
endif()
if (UNIX AND NOT APPLE)
target_link_libraries(yuzu PRIVATE Qt::DBus)
target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::DBus)
endif()
target_compile_definitions(yuzu PRIVATE
@@ -355,8 +358,13 @@ if (ENABLE_WEB_SERVICE)
target_compile_definitions(yuzu PRIVATE -DENABLE_WEB_SERVICE)
endif()
if (YUZU_USE_QT_MULTIMEDIA)
target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::Multimedia)
target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_MULTIMEDIA)
endif ()
if (YUZU_USE_QT_WEB_ENGINE)
target_link_libraries(yuzu PRIVATE Qt::WebEngineCore Qt::WebEngineWidgets)
target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::WebEngineCore Qt${QT_MAJOR_VERSION}::WebEngineWidgets)
target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE)
endif ()
@@ -364,7 +372,16 @@ if(UNIX AND NOT APPLE)
install(TARGETS yuzu)
endif()
if (YUZU_USE_BUNDLED_QT)
if (WIN32 AND QT_VERSION VERSION_GREATER_EQUAL 6)
if (MSVC AND NOT ${CMAKE_GENERATOR} STREQUAL "Ninja")
set(YUZU_EXE_DIR "${CMAKE_BINARY_DIR}/bin/$<CONFIG>")
else()
set(YUZU_EXE_DIR "${CMAKE_BINARY_DIR}/bin")
endif()
add_custom_command(TARGET yuzu POST_BUILD COMMAND ${WINDEPLOYQT_EXECUTABLE} "${YUZU_EXE_DIR}/yuzu.exe" --dir "${YUZU_EXE_DIR}" --libdir "${YUZU_EXE_DIR}" --plugindir "${YUZU_EXE_DIR}/plugins" --no-compiler-runtime --no-opengl-sw --no-system-d3d-compiler --no-translations --verbose 0)
endif()
if (YUZU_USE_BUNDLED_QT AND QT_VERSION VERSION_LESS 6)
include(CopyYuzuQt5Deps)
copy_yuzu_Qt5_deps(yuzu)
endif()

View File

@@ -0,0 +1,264 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <thread>
#include <fmt/format.h>
#include <nlohmann/json.hpp>
#include "common/assert.h"
#include "common/string_util.h"
#include "core/hle/service/nfp/nfp_device.h"
#include "core/hle/service/nfp/nfp_result.h"
#include "input_common/drivers/virtual_amiibo.h"
#include "input_common/main.h"
#include "ui_qt_amiibo_settings.h"
#ifdef ENABLE_WEB_SERVICE
#include "web_service/web_backend.h"
#endif
#include "yuzu/applets/qt_amiibo_settings.h"
#include "yuzu/main.h"
QtAmiiboSettingsDialog::QtAmiiboSettingsDialog(QWidget* parent,
Core::Frontend::CabinetParameters parameters_,
InputCommon::InputSubsystem* input_subsystem_,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device_)
: QDialog(parent), ui(std::make_unique<Ui::QtAmiiboSettingsDialog>()),
input_subsystem{input_subsystem_}, nfp_device{std::move(nfp_device_)},
parameters(std::move(parameters_)) {
ui->setupUi(this);
LoadInfo();
resize(0, 0);
}
QtAmiiboSettingsDialog::~QtAmiiboSettingsDialog() = default;
int QtAmiiboSettingsDialog::exec() {
if (!is_initalized) {
return QDialog::Rejected;
}
return QDialog::exec();
}
std::string QtAmiiboSettingsDialog::GetName() const {
return ui->amiiboCustomNameValue->text().toStdString();
}
void QtAmiiboSettingsDialog::LoadInfo() {
if (input_subsystem->GetVirtualAmiibo()->ReloadAmiibo() !=
InputCommon::VirtualAmiibo::Info::Success) {
return;
}
if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound &&
nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) {
return;
}
nfp_device->Mount(Service::NFP::MountTarget::All);
LoadAmiiboInfo();
LoadAmiiboData();
LoadAmiiboGameInfo();
ui->amiiboDirectoryValue->setText(
QString::fromStdString(input_subsystem->GetVirtualAmiibo()->GetLastFilePath()));
SetSettingsDescription();
is_initalized = true;
}
void QtAmiiboSettingsDialog::LoadAmiiboInfo() {
Service::NFP::ModelInfo model_info{};
const auto model_result = nfp_device->GetModelInfo(model_info);
if (model_result.IsFailure()) {
ui->amiiboImageLabel->setVisible(false);
ui->amiiboInfoGroup->setVisible(false);
return;
}
const auto amiibo_id =
fmt::format("{:04x}{:02x}{:02x}{:04x}{:02x}02", Common::swap16(model_info.character_id),
model_info.character_variant, model_info.amiibo_type, model_info.model_number,
model_info.series);
LOG_DEBUG(Frontend, "Loading amiibo id {}", amiibo_id);
// Note: This function is not being used until we host the images on our server
// LoadAmiiboApiInfo(amiibo_id);
ui->amiiboImageLabel->setVisible(false);
ui->amiiboInfoGroup->setVisible(false);
}
void QtAmiiboSettingsDialog::LoadAmiiboApiInfo(std::string_view amiibo_id) {
#ifdef ENABLE_WEB_SERVICE
// TODO: Host this data on our website
WebService::Client client{"https://amiiboapi.com", {}, {}};
WebService::Client image_client{"https://raw.githubusercontent.com", {}, {}};
const auto url_path = fmt::format("/api/amiibo/?id={}", amiibo_id);
const auto amiibo_json = client.GetJson(url_path, true).returned_data;
if (amiibo_json.empty()) {
ui->amiiboImageLabel->setVisible(false);
ui->amiiboInfoGroup->setVisible(false);
return;
}
std::string amiibo_series{};
std::string amiibo_name{};
std::string amiibo_image_url{};
std::string amiibo_type{};
const auto parsed_amiibo_json_json = nlohmann::json::parse(amiibo_json).at("amiibo");
parsed_amiibo_json_json.at("amiiboSeries").get_to(amiibo_series);
parsed_amiibo_json_json.at("name").get_to(amiibo_name);
parsed_amiibo_json_json.at("image").get_to(amiibo_image_url);
parsed_amiibo_json_json.at("type").get_to(amiibo_type);
ui->amiiboSeriesValue->setText(QString::fromStdString(amiibo_series));
ui->amiiboNameValue->setText(QString::fromStdString(amiibo_name));
ui->amiiboTypeValue->setText(QString::fromStdString(amiibo_type));
if (amiibo_image_url.size() < 34) {
ui->amiiboImageLabel->setVisible(false);
}
const auto image_url_path = amiibo_image_url.substr(34, amiibo_image_url.size() - 34);
const auto image_data = image_client.GetImage(image_url_path, true).returned_data;
if (image_data.empty()) {
ui->amiiboImageLabel->setVisible(false);
}
QPixmap pixmap;
pixmap.loadFromData(reinterpret_cast<const u8*>(image_data.data()),
static_cast<uint>(image_data.size()));
pixmap = pixmap.scaled(250, 350, Qt::AspectRatioMode::KeepAspectRatio,
Qt::TransformationMode::SmoothTransformation);
ui->amiiboImageLabel->setPixmap(pixmap);
#endif
}
void QtAmiiboSettingsDialog::LoadAmiiboData() {
Service::NFP::RegisterInfo register_info{};
Service::NFP::CommonInfo common_info{};
const auto register_result = nfp_device->GetRegisterInfo(register_info);
const auto common_result = nfp_device->GetCommonInfo(common_info);
if (register_result.IsFailure()) {
ui->creationDateValue->setDisabled(true);
ui->modificationDateValue->setDisabled(true);
ui->amiiboCustomNameValue->setReadOnly(false);
ui->amiiboOwnerValue->setReadOnly(false);
return;
}
if (parameters.mode == Service::NFP::CabinetMode::StartNicknameAndOwnerSettings) {
ui->creationDateValue->setDisabled(true);
ui->modificationDateValue->setDisabled(true);
}
const auto amiibo_name = std::string(register_info.amiibo_name.data());
const auto owner_name = Common::UTF16ToUTF8(register_info.mii_char_info.name.data());
const auto creation_date =
QDate(register_info.creation_date.year, register_info.creation_date.month,
register_info.creation_date.day);
ui->amiiboCustomNameValue->setText(QString::fromStdString(amiibo_name));
ui->amiiboOwnerValue->setText(QString::fromStdString(owner_name));
ui->amiiboCustomNameValue->setReadOnly(true);
ui->amiiboOwnerValue->setReadOnly(true);
ui->creationDateValue->setDate(creation_date);
if (common_result.IsFailure()) {
ui->modificationDateValue->setDisabled(true);
return;
}
const auto modification_date =
QDate(common_info.last_write_date.year, common_info.last_write_date.month,
common_info.last_write_date.day);
ui->modificationDateValue->setDate(modification_date);
}
void QtAmiiboSettingsDialog::LoadAmiiboGameInfo() {
u32 application_area_id{};
const auto application_result = nfp_device->GetApplicationAreaId(application_area_id);
if (application_result.IsFailure()) {
ui->gameIdValue->setVisible(false);
ui->gameIdLabel->setText(tr("No game data present"));
return;
}
SetGameDataName(application_area_id);
}
void QtAmiiboSettingsDialog::SetGameDataName(u32 application_area_id) {
static constexpr std::array<std::pair<u32, const char*>, 12> game_name_list = {
// 3ds, wii u
std::pair<u32, const char*>{0x10110E00, "Super Smash Bros (3DS/WiiU)"},
{0x00132600, "Mario & Luigi: Paper Jam"},
{0x0014F000, "Animal Crossing: Happy Home Designer"},
{0x00152600, "Chibi-Robo!: Zip Lash"},
{0x10161f00, "Mario Party 10"},
{0x1019C800, "The Legend of Zelda: Twilight Princess HD"},
// switch
{0x10162B00, "Splatoon 2"},
{0x1016e100, "Shovel Knight: Treasure Trove"},
{0x1019C800, "The Legend of Zelda: Breath of the Wild"},
{0x34F80200, "Super Smash Bros. Ultimate"},
{0x38600500, "Splatoon 3"},
{0x3B440400, "The Legend of Zelda: Link's Awakening"},
};
for (const auto& [game_id, game_name] : game_name_list) {
if (application_area_id == game_id) {
ui->gameIdValue->setText(QString::fromStdString(game_name));
return;
}
}
const auto application_area_string = fmt::format("{:016x}", application_area_id);
ui->gameIdValue->setText(QString::fromStdString(application_area_string));
}
void QtAmiiboSettingsDialog::SetSettingsDescription() {
switch (parameters.mode) {
case Service::NFP::CabinetMode::StartFormatter:
ui->cabinetActionDescriptionLabel->setText(
tr("The following amiibo data will be formatted:"));
break;
case Service::NFP::CabinetMode::StartGameDataEraser:
ui->cabinetActionDescriptionLabel->setText(tr("The following game data will removed:"));
break;
case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings:
ui->cabinetActionDescriptionLabel->setText(tr("Set nickname and owner:"));
break;
case Service::NFP::CabinetMode::StartRestorer:
ui->cabinetActionDescriptionLabel->setText(tr("Do you wish to restore this amiibo?"));
break;
}
}
QtAmiiboSettings::QtAmiiboSettings(GMainWindow& parent) {
connect(this, &QtAmiiboSettings::MainWindowShowAmiiboSettings, &parent,
&GMainWindow::AmiiboSettingsShowDialog, Qt::QueuedConnection);
connect(&parent, &GMainWindow::AmiiboSettingsFinished, this,
&QtAmiiboSettings::MainWindowFinished, Qt::QueuedConnection);
}
QtAmiiboSettings::~QtAmiiboSettings() = default;
void QtAmiiboSettings::ShowCabinetApplet(
const Core::Frontend::CabinetCallback& callback_,
const Core::Frontend::CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const {
callback = std::move(callback_);
emit MainWindowShowAmiiboSettings(parameters, nfp_device);
}
void QtAmiiboSettings::MainWindowFinished(bool is_success, const std::string& name) {
callback(is_success, name);
}

View File

@@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <memory>
#include <QDialog>
#include "core/frontend/applets/cabinet.h"
class GMainWindow;
class QCheckBox;
class QComboBox;
class QDialogButtonBox;
class QGroupBox;
class QLabel;
namespace InputCommon {
class InputSubsystem;
}
namespace Ui {
class QtAmiiboSettingsDialog;
}
namespace Service::NFP {
class NfpDevice;
} // namespace Service::NFP
class QtAmiiboSettingsDialog final : public QDialog {
Q_OBJECT
public:
explicit QtAmiiboSettingsDialog(QWidget* parent, Core::Frontend::CabinetParameters parameters_,
InputCommon::InputSubsystem* input_subsystem_,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device_);
~QtAmiiboSettingsDialog() override;
int exec() override;
std::string GetName() const;
private:
void LoadInfo();
void LoadAmiiboInfo();
void LoadAmiiboApiInfo(std::string_view amiibo_id);
void LoadAmiiboData();
void LoadAmiiboGameInfo();
void SetGameDataName(u32 application_area_id);
void SetSettingsDescription();
std::unique_ptr<Ui::QtAmiiboSettingsDialog> ui;
InputCommon::InputSubsystem* input_subsystem;
std::shared_ptr<Service::NFP::NfpDevice> nfp_device;
// Parameters sent in from the backend HLE applet.
Core::Frontend::CabinetParameters parameters;
// If false amiibo settings failed to load
bool is_initalized{};
};
class QtAmiiboSettings final : public QObject, public Core::Frontend::CabinetApplet {
Q_OBJECT
public:
explicit QtAmiiboSettings(GMainWindow& parent);
~QtAmiiboSettings() override;
void ShowCabinetApplet(const Core::Frontend::CabinetCallback& callback_,
const Core::Frontend::CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override;
signals:
void MainWindowShowAmiiboSettings(const Core::Frontend::CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const;
private:
void MainWindowFinished(bool is_success, const std::string& name);
mutable Core::Frontend::CabinetCallback callback;
};

View File

@@ -0,0 +1,494 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QtAmiiboSettingsDialog</class>
<widget class="QDialog" name="QtAmiiboSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>839</width>
<height>500</height>
</rect>
</property>
<property name="windowTitle">
<string>Amiibo Settings</string>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0">
<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="QWidget" name="mainControllerApplet" native="true">
<layout class="QVBoxLayout" name="verticalLayout_1" stretch="0,3,0">
<property name="spacing">
<number>0</number>
</property>
<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="QWidget" name="topControllerApplet" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>10</number>
</property>
<property name="leftMargin">
<number>20</number>
</property>
<property name="topMargin">
<number>15</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>15</number>
</property>
<item>
<widget class="QLabel" name="cabinetActionDescriptionLabel">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="middleControllerApplet" native="true">
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<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>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>20</number>
</property>
<property name="leftMargin">
<number>15</number>
</property>
<property name="rightMargin">
<number>15</number>
</property>
<item>
<widget class="QLabel" name="amiiboImageLabel">
<property name="minimumSize">
<size>
<width>250</width>
<height>350</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>236</width>
<height>350</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>15</number>
</property>
<item>
<widget class="QGroupBox" name="amiiboInfoGroup">
<property name="title">
<string>Amiibo Info</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QGridLayout" name="gridLayout_1">
<item row="0" column="0">
<widget class="QLabel" name="amiiboSeriesLabel">
<property name="text">
<string>Series</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="amiiboSeriesValue">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="amiiboTypeLabel">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="amiiboTypeValue">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="amiiboNameLabel">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="amiiboNameValue">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="amiiboDataGroup">
<property name="title">
<string>Amiibo Data</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="amiiboCustomNameLabel">
<property name="text">
<string>Custom Name</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="amiiboCustomNameValue">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maxLength">
<number>10</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="amiiboOwnerLabel">
<property name="text">
<string>Owner</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="amiiboOwnerValue">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maxLength">
<number>10</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="creationDateLabel">
<property name="text">
<string>Creation Date</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDateTimeEdit" name="creationDateValue">
<property name="readOnly">
<bool>true</bool>
</property>
<property name="minimumDate">
<date>
<year>1970</year>
<month>1</month>
<day>1</day>
</date>
</property>
<property name="displayFormat">
<string>dd/MM/yyyy</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="modificationDateLabel">
<property name="text">
<string>Modification Date</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDateTimeEdit" name="modificationDateValue">
<property name="readOnly">
<bool>true</bool>
</property>
<property name="minimumDate">
<date>
<year>1970</year>
<month>1</month>
<day>1</day>
</date>
</property>
<property name="displayFormat">
<string>dd/MM/yyyy </string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gameDataGroup">
<property name="minimumSize">
<size>
<width>500</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Game Data</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="gameIdLabel">
<property name="text">
<string>Game Id</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="gameIdValue">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="MountAmiiboGroup">
<property name="minimumSize">
<size>
<width>500</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Mount Amiibo</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="3">
<widget class="QToolButton" name="amiiboDirectoryButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Maximum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>60</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QLabel" name="amiiboDirectoryLabel">
<property name="text">
<string>File Path</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="amiiboDirectoryValue"/>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="bottomControllerApplet" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="spacing">
<number>15</number>
</property>
<property name="leftMargin">
<number>15</number>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>20</number>
</property>
<property name="bottomMargin">
<number>8</number>
</property>
<item alignment="Qt::AlignBottom">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>QtAmiiboSettingsDialog</receiver>
<slot>accept()</slot>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>QtAmiiboSettingsDialog</receiver>
<slot>reject()</slot>
</connection>
</connections>
</ui>

View File

@@ -4,8 +4,10 @@
#include <glad/glad.h>
#include <QApplication>
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
#include <QCameraImageCapture>
#include <QCameraInfo>
#endif
#include <QHBoxLayout>
#include <QMessageBox>
#include <QPainter>
@@ -707,6 +709,7 @@ void GRenderWindow::TouchEndEvent() {
}
void GRenderWindow::InitializeCamera() {
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
constexpr auto camera_update_ms = std::chrono::milliseconds{50}; // (50ms, 20Hz)
if (!Settings::values.enable_ir_sensor) {
return;
@@ -760,18 +763,22 @@ void GRenderWindow::InitializeCamera() {
connect(camera_timer.get(), &QTimer::timeout, [this] { RequestCameraCapture(); });
// This timer should be dependent of camera resolution 5ms for every 100 pixels
camera_timer->start(camera_update_ms);
#endif
}
void GRenderWindow::FinalizeCamera() {
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
if (camera_timer) {
camera_timer->stop();
}
if (camera) {
camera->unload();
}
#endif
}
void GRenderWindow::RequestCameraCapture() {
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
if (!Settings::values.enable_ir_sensor) {
return;
}
@@ -788,6 +795,7 @@ void GRenderWindow::RequestCameraCapture() {
pending_camera_snapshots++;
camera_capture->capture();
#endif
}
void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) {

View File

@@ -241,8 +241,10 @@ private:
bool is_virtual_camera;
int pending_camera_snapshots;
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
std::unique_ptr<QCamera> camera;
std::unique_ptr<QCameraImageCapture> camera_capture;
#endif
std::unique_ptr<QTimer> camera_timer;
Core::System& system;

View File

@@ -672,6 +672,7 @@ void Config::ReadRendererValues() {
ReadGlobalSetting(Settings::values.aspect_ratio);
ReadGlobalSetting(Settings::values.resolution_setup);
ReadGlobalSetting(Settings::values.scaling_filter);
ReadGlobalSetting(Settings::values.fsr_sharpening_slider);
ReadGlobalSetting(Settings::values.anti_aliasing);
ReadGlobalSetting(Settings::values.max_anisotropy);
ReadGlobalSetting(Settings::values.use_speed_limit);
@@ -1282,6 +1283,10 @@ void Config::SaveRendererValues() {
static_cast<u32>(Settings::values.scaling_filter.GetValue(global)),
static_cast<u32>(Settings::values.scaling_filter.GetDefault()),
Settings::values.scaling_filter.UsingGlobal());
WriteSetting(QString::fromStdString(Settings::values.fsr_sharpening_slider.GetLabel()),
static_cast<u32>(Settings::values.fsr_sharpening_slider.GetValue(global)),
static_cast<u32>(Settings::values.fsr_sharpening_slider.GetDefault()),
Settings::values.fsr_sharpening_slider.UsingGlobal());
WriteSetting(QString::fromStdString(Settings::values.anti_aliasing.GetLabel()),
static_cast<u32>(Settings::values.anti_aliasing.GetValue(global)),
static_cast<u32>(Settings::values.anti_aliasing.GetDefault()),

View File

@@ -2,8 +2,11 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include <memory>
#include <QtCore>
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
#include <QCameraImageCapture>
#include <QCameraInfo>
#endif
#include <QStandardItemModel>
#include <QTimer>
@@ -33,6 +36,7 @@ ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* i
ConfigureCamera::~ConfigureCamera() = default;
void ConfigureCamera::PreviewCamera() {
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
const auto index = ui->ir_sensor_combo_box->currentIndex();
bool camera_found = false;
const QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
@@ -101,6 +105,7 @@ void ConfigureCamera::PreviewCamera() {
});
camera_timer->start(250);
#endif
}
void ConfigureCamera::DisplayCapturedFrame(int requestId, const QImage& img) {
@@ -133,11 +138,13 @@ void ConfigureCamera::LoadConfiguration() {
ui->ir_sensor_combo_box->clear();
input_devices.push_back("Auto");
ui->ir_sensor_combo_box->addItem(tr("Auto"));
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
const auto cameras = QCameraInfo::availableCameras();
for (const QCameraInfo& cameraInfo : cameras) {
input_devices.push_back(cameraInfo.deviceName().toStdString());
ui->ir_sensor_combo_box->addItem(cameraInfo.description());
}
#endif
const auto current_device = Settings::values.ir_sensor_device.GetValue();

View File

@@ -46,8 +46,10 @@ private:
bool is_virtual_camera;
int pending_snapshots;
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
std::unique_ptr<QCamera> camera;
std::unique_ptr<QCameraImageCapture> camera_capture;
#endif
std::unique_ptr<QTimer> camera_timer;
std::vector<std::string> input_devices;
std::unique_ptr<Ui::ConfigureCamera> ui;

View File

@@ -63,6 +63,11 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren
ui->api_widget->isEnabled());
ui->bg_label->setVisible(Settings::IsConfiguringGlobal());
ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal());
connect(ui->fsr_sharpening_slider, &QSlider::valueChanged, this,
&ConfigureGraphics::SetFSRIndicatorText);
ui->fsr_sharpening_combobox->setVisible(!Settings::IsConfiguringGlobal());
ui->fsr_sharpening_label->setVisible(Settings::IsConfiguringGlobal());
}
void ConfigureGraphics::UpdateDeviceSelection(int device) {
@@ -110,6 +115,7 @@ void ConfigureGraphics::SetConfiguration() {
static_cast<int>(Settings::values.resolution_setup.GetValue()));
ui->scaling_filter_combobox->setCurrentIndex(
static_cast<int>(Settings::values.scaling_filter.GetValue()));
ui->fsr_sharpening_slider->setValue(Settings::values.fsr_sharpening_slider.GetValue());
ui->anti_aliasing_combobox->setCurrentIndex(
static_cast<int>(Settings::values.anti_aliasing.GetValue()));
} else {
@@ -147,6 +153,15 @@ void ConfigureGraphics::SetConfiguration() {
ConfigurationShared::SetHighlight(ui->anti_aliasing_label,
!Settings::values.anti_aliasing.UsingGlobal());
ui->fsr_sharpening_combobox->setCurrentIndex(
Settings::values.fsr_sharpening_slider.UsingGlobal() ? 0 : 1);
ui->fsr_sharpening_slider->setEnabled(
!Settings::values.fsr_sharpening_slider.UsingGlobal());
ui->fsr_sharpening_value->setEnabled(!Settings::values.fsr_sharpening_slider.UsingGlobal());
ConfigurationShared::SetHighlight(ui->fsr_sharpening_layout,
!Settings::values.fsr_sharpening_slider.UsingGlobal());
ui->fsr_sharpening_slider->setValue(Settings::values.fsr_sharpening_slider.GetValue());
ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1);
ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal());
ConfigurationShared::SetHighlight(ui->bg_layout, !Settings::values.bg_red.UsingGlobal());
@@ -155,6 +170,12 @@ void ConfigureGraphics::SetConfiguration() {
Settings::values.bg_green.GetValue(),
Settings::values.bg_blue.GetValue()));
UpdateAPILayout();
SetFSRIndicatorText(ui->fsr_sharpening_slider->sliderPosition());
}
void ConfigureGraphics::SetFSRIndicatorText(int percentage) {
ui->fsr_sharpening_value->setText(
tr("%1%", "FSR sharpening percentage (e.g. 50%)").arg(100 - (percentage / 2)));
}
void ConfigureGraphics::ApplyConfiguration() {
@@ -210,6 +231,7 @@ void ConfigureGraphics::ApplyConfiguration() {
if (Settings::values.anti_aliasing.UsingGlobal()) {
Settings::values.anti_aliasing.SetValue(anti_aliasing);
}
Settings::values.fsr_sharpening_slider.SetValue(ui->fsr_sharpening_slider->value());
} else {
if (ui->resolution_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
Settings::values.resolution_setup.SetGlobal(true);
@@ -269,6 +291,13 @@ void ConfigureGraphics::ApplyConfiguration() {
Settings::values.bg_green.SetValue(static_cast<u8>(bg_color.green()));
Settings::values.bg_blue.SetValue(static_cast<u8>(bg_color.blue()));
}
if (ui->fsr_sharpening_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
Settings::values.fsr_sharpening_slider.SetGlobal(true);
} else {
Settings::values.fsr_sharpening_slider.SetGlobal(false);
Settings::values.fsr_sharpening_slider.SetValue(ui->fsr_sharpening_slider->value());
}
}
}
@@ -380,6 +409,7 @@ void ConfigureGraphics::SetupPerGameUI() {
ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal());
ui->resolution_combobox->setEnabled(Settings::values.resolution_setup.UsingGlobal());
ui->scaling_filter_combobox->setEnabled(Settings::values.scaling_filter.UsingGlobal());
ui->fsr_sharpening_slider->setEnabled(Settings::values.fsr_sharpening_slider.UsingGlobal());
ui->anti_aliasing_combobox->setEnabled(Settings::values.anti_aliasing.UsingGlobal());
ui->use_asynchronous_gpu_emulation->setEnabled(
Settings::values.use_asynchronous_gpu_emulation.UsingGlobal());
@@ -387,6 +417,7 @@ void ConfigureGraphics::SetupPerGameUI() {
ui->accelerate_astc->setEnabled(Settings::values.accelerate_astc.UsingGlobal());
ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal());
ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal());
ui->fsr_slider_layout->setEnabled(Settings::values.fsr_sharpening_slider.UsingGlobal());
return;
}
@@ -396,6 +427,13 @@ void ConfigureGraphics::SetupPerGameUI() {
ConfigurationShared::SetHighlight(ui->bg_layout, index == 1);
});
connect(ui->fsr_sharpening_combobox, qOverload<int>(&QComboBox::activated), this,
[this](int index) {
ui->fsr_sharpening_slider->setEnabled(index == 1);
ui->fsr_sharpening_value->setEnabled(index == 1);
ConfigurationShared::SetHighlight(ui->fsr_sharpening_layout, index == 1);
});
ConfigurationShared::SetColoredTristate(
ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache);
ConfigurationShared::SetColoredTristate(ui->accelerate_astc, Settings::values.accelerate_astc,

View File

@@ -42,6 +42,8 @@ private:
void RetrieveVulkanDevices();
void SetFSRIndicatorText(int percentage);
void SetupPerGameUI();
Settings::RendererBackend GetCurrentGraphicsBackend() const;

View File

@@ -152,6 +152,12 @@
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="title">
<string>Graphics Settings</string>
</property>
@@ -481,6 +487,146 @@
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="fsr_sharpening_layout" native="true">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<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>
<layout class="QHBoxLayout" name="fsr_sharpening_label_group">
<item>
<widget class="QComboBox" name="fsr_sharpening_combobox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Use global FSR Sharpness</string>
</property>
</item>
<item>
<property name="text">
<string>Set FSR Sharpness</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="fsr_sharpening_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>FSR Sharpness:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="fsr_slider_layout">
<property name="spacing">
<number>6</number>
</property>
<item>
<widget class="QSlider" name="fsr_sharpening_slider">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximum">
<number>200</number>
</property>
<property name="sliderPosition">
<number>25</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="invertedAppearance">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="fsr_sharpening_value">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>100%</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="bg_layout" native="true">
<property name="sizePolicy">

View File

@@ -194,4 +194,8 @@ void ConfigureInputAdvanced::UpdateUIEnabled() {
ui->mouse_panning->setEnabled(!ui->mouse_enabled->isChecked());
ui->mouse_panning_sensitivity->setEnabled(!ui->mouse_enabled->isChecked());
ui->ring_controller_configure->setEnabled(ui->enable_ring_controller->isChecked());
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) || !defined(YUZU_USE_QT_MULTIMEDIA)
ui->enable_ir_sensor->setEnabled(false);
ui->camera_configure->setEnabled(false);
#endif
}

View File

@@ -736,6 +736,9 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
emulated_controller->SetNpadStyleIndex(type);
});
connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this,
&ConfigureInputPlayer::UpdateMappingWithDefaults);
ui->comboDevices->setCurrentIndex(-1);
ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));

View File

@@ -15,6 +15,7 @@
#endif
// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
#include "applets/qt_amiibo_settings.h"
#include "applets/qt_controller.h"
#include "applets/qt_error.h"
#include "applets/qt_profile_select.h"
@@ -26,6 +27,7 @@
#include "configuration/configure_tas.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_real.h"
#include "core/frontend/applets/cabinet.h"
#include "core/frontend/applets/controller.h"
#include "core/frontend/applets/general_frontend.h"
#include "core/frontend/applets/mii_edit.h"
@@ -235,6 +237,7 @@ static void LogRuntimes() {
LOG_INFO(Frontend, "Unable to inspect {}", runtime_dll_name);
}
#endif
LOG_INFO(Frontend, "Qt Compile: {} Runtime: {}", QT_VERSION_STR, qVersion());
}
static QString PrettyProductName() {
@@ -548,6 +551,11 @@ void GMainWindow::RegisterMetaTypes() {
// Register applet types
// Cabinet Applet
qRegisterMetaType<Core::Frontend::CabinetParameters>("Core::Frontend::CabinetParameters");
qRegisterMetaType<std::shared_ptr<Service::NFP::NfpDevice>>(
"std::shared_ptr<Service::NFP::NfpDevice>");
// Controller Applet
qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters");
@@ -569,6 +577,21 @@ void GMainWindow::RegisterMetaTypes() {
qRegisterMetaType<Core::SystemResultStatus>("Core::SystemResultStatus");
}
void GMainWindow::AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) {
QtAmiiboSettingsDialog dialog(this, parameters, input_subsystem.get(), nfp_device);
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |
Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
dialog.setWindowModality(Qt::WindowModal);
if (dialog.exec() == QDialog::Rejected) {
emit AmiiboSettingsFinished(false, {});
return;
}
emit AmiiboSettingsFinished(true, dialog.GetName());
}
void GMainWindow::ControllerSelectorReconfigureControllers(
const Core::Frontend::ControllerParameters& parameters) {
QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get(), *system);
@@ -1546,6 +1569,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
system->SetFilesystem(vfs);
system->SetAppletFrontendSet({
std::make_unique<QtAmiiboSettings>(*this), // Amiibo Settings
std::make_unique<QtControllerSelector>(*this), // Controller Selector
std::make_unique<QtErrorDisplay>(*this), // Error Display
nullptr, // Mii Editor
@@ -4194,10 +4218,12 @@ int main(int argc, char* argv[]) {
// so we can see if we get \u3008 instead
// TL;DR all other number formats are consecutive in unicode code points
// This bug is fixed in Qt6, specifically 6.0.0-alpha1
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
const QLocale locale = QLocale::system();
if (QStringLiteral("\u3008") == locale.toString(1)) {
QLocale::setDefault(QLocale::system().name());
}
#endif
// Qt changes the locale and causes issues in float conversion using std::to_string() when
// generating shaders

View File

@@ -55,6 +55,7 @@ class System;
} // namespace Core
namespace Core::Frontend {
struct CabinetParameters;
struct ControllerParameters;
struct InlineAppearParameters;
struct InlineTextParameters;
@@ -82,6 +83,10 @@ enum class SwkbdReplyType : u32;
enum class WebExitReason : u32;
} // namespace Service::AM::Applets
namespace Service::NFP {
class NfpDevice;
} // namespace Service::NFP
namespace Ui {
class MainWindow;
}
@@ -149,6 +154,8 @@ signals:
void UpdateInstallProgress();
void AmiiboSettingsFinished(bool is_success, const std::string& name);
void ControllerSelectorReconfigureFinished();
void ErrorDisplayFinished();
@@ -170,6 +177,8 @@ public slots:
void OnExecuteProgram(std::size_t program_index);
void OnExit();
void OnSaveConfig();
void AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device);
void ControllerSelectorReconfigureControllers(
const Core::Frontend::ControllerParameters& parameters);
void SoftwareKeyboardInitialize(

View File

@@ -231,6 +231,9 @@
<property name="text">
<string>Con&amp;figure...</string>
</property>
<property name="menuRole">
<enum>QAction::PreferencesRole</enum>
</property>
</action>
<action name="action_Display_Dock_Widget_Headers">
<property name="checkable">
@@ -363,6 +366,9 @@
<property name="text">
<string>&amp;Configure TAS...</string>
</property>
<property name="menuRole">
<enum>QAction::NoRole</enum>
</property>
</action>
<action name="action_Configure_Current_Game">
<property name="enabled">
@@ -371,6 +377,9 @@
<property name="text">
<string>Configure C&amp;urrent Game...</string>
</property>
<property name="menuRole">
<enum>QAction::NoRole</enum>
</property>
</action>
<action name="action_TAS_Start">
<property name="enabled">

View File

@@ -4,7 +4,7 @@
#include <QComboBox>
#include <QFuture>
#include <QIntValidator>
#include <QRegExpValidator>
#include <QRegularExpressionValidator>
#include <QString>
#include <QtConcurrent/QtConcurrentRun>
#include "common/settings.h"

View File

@@ -3,7 +3,7 @@
#pragma once
#include <QRegExp>
#include <QRegularExpression>
#include <QString>
#include <QValidator>
@@ -29,19 +29,21 @@ public:
private:
/// room name can be alphanumeric and " " "_" "." and "-" and must have a size of 4-20
QRegExp room_name_regex = QRegExp(QStringLiteral("^[a-zA-Z0-9._- ]{4,20}$"));
QRegExpValidator room_name;
QRegularExpression room_name_regex =
QRegularExpression(QStringLiteral("^[a-zA-Z0-9._ -]{4,20}"));
QRegularExpressionValidator room_name;
/// nickname can be alphanumeric and " " "_" "." and "-" and must have a size of 4-20
QRegExp nickname_regex = QRegExp(QStringLiteral("^[a-zA-Z0-9._- ]{4,20}$"));
QRegExpValidator nickname;
const QRegularExpression nickname_regex =
QRegularExpression(QStringLiteral("^[a-zA-Z0-9._ -]{4,20}"));
QRegularExpressionValidator nickname;
/// ipv4 address only
// TODO remove this when we support hostnames in direct connect
QRegExp ip_regex = QRegExp(QStringLiteral(
QRegularExpression ip_regex = QRegularExpression(QStringLiteral(
"(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|"
"2[0-4][0-9]|25[0-5])"));
QRegExpValidator ip;
QRegularExpressionValidator ip;
/// port must be between 0 and 65535
QIntValidator port;

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