Compare commits

..

38 Commits

Author SHA1 Message Date
Feng Chen
cb5400b34d video_core: Fine tuning the index drawing judgment logic 2022-12-01 19:14:58 +08: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
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
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
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
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
59 changed files with 2284 additions and 347 deletions

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

@@ -443,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

@@ -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

@@ -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

@@ -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

@@ -118,7 +118,7 @@ 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) {
if (nfp_devices.size() >= max_allowed_devices) {

View File

@@ -104,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;
}
@@ -115,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;

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

@@ -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

@@ -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

@@ -1742,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

@@ -217,16 +217,19 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
regs.index_buffer.count = regs.index_buffer32_first.count;
regs.index_buffer.first = regs.index_buffer32_first.first;
dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
draw_indexed = true;
return ProcessDraw();
case MAXWELL3D_REG_INDEX(index_buffer16_first):
regs.index_buffer.count = regs.index_buffer16_first.count;
regs.index_buffer.first = regs.index_buffer16_first.first;
dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
draw_indexed = true;
return ProcessDraw();
case MAXWELL3D_REG_INDEX(index_buffer8_first):
regs.index_buffer.count = regs.index_buffer8_first.count;
regs.index_buffer.first = regs.index_buffer8_first.first;
dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
draw_indexed = true;
return ProcessDraw();
case MAXWELL3D_REG_INDEX(topology_override):
use_topology_override = true;
@@ -249,9 +252,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 +288,63 @@ 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.begin): {
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;
}
case MAXWELL3D_REG_INDEX(draw.end):
switch (draw_mode) {
case DrawMode::General:
ProcessDraw();
break;
case DrawMode::InlineIndex:
regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4);
regs.index_buffer.format = Regs::IndexFormat::UnsignedInt;
draw_indexed = true;
ProcessDraw();
inline_index_draw_indexes.clear();
break;
case DrawMode::Instance:
break;
}
break;
case MAXWELL3D_REG_INDEX(index_buffer.count):
draw_indexed = true;
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;
}
} else {
ProcessDeferredDraw();
const u32 argument = ProcessShadowRam(method, method_argument);
ProcessDirtyRegisters(method, argument);
ProcessMethodCall(method, argument, method_argument, is_last_call);
}
}
@@ -511,10 +543,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);
}
@@ -602,81 +631,38 @@ void Maxwell3D::ProcessClearBuffers(u32 layer_count) {
void Maxwell3D::ProcessDraw(u32 instance_count) {
LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
regs.vertex_buffer.count);
ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?");
// Both instance configuration registers can not be set at the same time.
ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First ||
regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged,
"Illegal combination of instancing parameters");
draw_indexed ? regs.index_buffer.count : regs.vertex_buffer.count);
ProcessTopologyOverride();
const bool is_indexed = regs.index_buffer.count && !regs.vertex_buffer.count;
if (ShouldExecute()) {
rasterizer->Draw(is_indexed, instance_count);
rasterizer->Draw(draw_indexed, instance_count);
}
if (is_indexed) {
regs.index_buffer.count = 0;
} else {
regs.vertex_buffer.count = 0;
}
draw_indexed = false;
deferred_draw_method.clear();
}
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,9 @@ 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};
bool draw_indexed{};
};
#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

@@ -466,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() {

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

@@ -542,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() {

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

@@ -295,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)
@@ -311,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
@@ -358,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 ()
@@ -367,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

@@ -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

@@ -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

@@ -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

@@ -237,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() {
@@ -4217,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

@@ -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;

View File

@@ -6,16 +6,22 @@
namespace DefaultINI {
const char* sdl2_config_file = R"(
[ControlsGeneral]
[ControlsP0]
# The input devices and parameters for each Switch native input
# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ...
# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."
# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
# Indicates if this player should be connected at boot
connected=
# for button input, the following devices are available:
# - "keyboard" (default) for keyboard input. Required parameters:
# - "code": the code of the key to bind
# - "sdl" for joystick input using SDL. Required parameters:
# - "joystick": the index of the joystick to bind
# - "guid": SDL identification GUID of the joystick
# - "port": the index of the joystick to bind
# - "button"(optional): the index of the button to bind
# - "hat"(optional): the index of the hat to bind as direction buttons
# - "axis"(optional): the index of the axis to bind
@@ -58,12 +64,29 @@ button_screenshot=
# - "modifier_scale": a float number representing the applied modifier scale to the analog input.
# Must be in range of 0.0-1.0. Defaults to 0.5
# - "sdl" for joystick input using SDL. Required parameters:
# - "joystick": the index of the joystick to bind
# - "guid": SDL identification GUID of the joystick
# - "port": the index of the joystick to bind
# - "axis_x": the index of the axis to bind as x-axis (default to 0)
# - "axis_y": the index of the axis to bind as y-axis (default to 1)
lstick=
rstick=
# for motion input, the following devices are available:
# - "keyboard" (default) for emulating random motion input from buttons. Required parameters:
# - "code": the code of the key to bind
# - "sdl" for motion input using SDL. Required parameters:
# - "guid": SDL identification GUID of the joystick
# - "port": the index of the joystick to bind
# - "motion": the index of the motion sensor to bind
# - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters:
# - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001"
# - "port": the port of the cemu hook server
# - "pad": the index of the joystick
# - "motion": the index of the motion sensor of the joystick to bind
motionleft=
motionright=
[ControlsGeneral]
# To use the debug_pad, prepend `debug_pad_` before each button setting above.
# i.e. debug_pad_button_a=

View File

@@ -7,6 +7,7 @@
#include "common/scm_rev.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/hid/hid_core.h"
#include "core/perf_stats.h"
#include "input_common/drivers/keyboard.h"
#include "input_common/drivers/mouse.h"
@@ -26,6 +27,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Co
}
EmuWindow_SDL2::~EmuWindow_SDL2() {
system.HIDCore().UnloadInputDevices();
input_subsystem->Shutdown();
SDL_Quit();
}