Compare commits
1 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bc3c788f5 |
@@ -518,10 +518,6 @@ set(FFmpeg_COMPONENTS
|
||||
avutil
|
||||
swscale)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
Include(FindPkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBVA libva)
|
||||
endif()
|
||||
if (NOT YUZU_USE_BUNDLED_FFMPEG)
|
||||
# Use system installed FFmpeg
|
||||
find_package(FFmpeg QUIET COMPONENTS ${FFmpeg_COMPONENTS})
|
||||
@@ -544,9 +540,6 @@ endif()
|
||||
|
||||
if (YUZU_USE_BUNDLED_FFMPEG)
|
||||
if (NOT WIN32)
|
||||
# TODO(lat9nq): Move this to externals/ffmpeg/CMakeLists.txt (and move externals/ffmpeg to
|
||||
# externals/ffmpeg/ffmpeg)
|
||||
|
||||
# Build FFmpeg from externals
|
||||
message(STATUS "Using FFmpeg from externals")
|
||||
|
||||
@@ -586,23 +579,20 @@ if (YUZU_USE_BUNDLED_FFMPEG)
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
endforeach()
|
||||
|
||||
Include(FindPkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBVA libva)
|
||||
pkg_check_modules(CUDA cuda)
|
||||
pkg_check_modules(FFNVCODEC ffnvcodec)
|
||||
pkg_check_modules(VDPAU vdpau)
|
||||
|
||||
set(FFmpeg_HWACCEL_LIBRARIES)
|
||||
set(FFmpeg_HWACCEL_FLAGS)
|
||||
set(FFmpeg_HWACCEL_INCLUDE_DIRS)
|
||||
set(FFmpeg_HWACCEL_LDFLAGS)
|
||||
set(FFmpeg_INCLUDE_DIR
|
||||
"${FFmpeg_PREFIX};${FFmpeg_BUILD_DIR}"
|
||||
CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
Include(FindPkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBVA libva)
|
||||
endif()
|
||||
if(LIBVA_FOUND)
|
||||
pkg_check_modules(LIBDRM libdrm REQUIRED)
|
||||
find_package(X11 REQUIRED)
|
||||
pkg_check_modules(LIBVA-DRM libva-drm REQUIRED)
|
||||
pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED)
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES
|
||||
set(FFmpeg_LIBVA_LIBRARIES
|
||||
${LIBDRM_LIBRARIES}
|
||||
${X11_LIBRARIES}
|
||||
${LIBVA-DRM_LIBRARIES}
|
||||
@@ -612,56 +602,11 @@ if (YUZU_USE_BUNDLED_FFMPEG)
|
||||
--enable-hwaccel=h264_vaapi
|
||||
--enable-hwaccel=vp9_vaapi
|
||||
--enable-libdrm)
|
||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
|
||||
${LIBDRM_INCLUDE_DIRS}
|
||||
${X11_INCLUDE_DIRS}
|
||||
${LIBVA-DRM_INCLUDE_DIRS}
|
||||
${LIBVA-X11_INCLUDE_DIRS}
|
||||
${LIBVA_INCLUDE_DIRS}
|
||||
)
|
||||
message(STATUS "VA-API found")
|
||||
else()
|
||||
set(FFmpeg_HWACCEL_FLAGS --disable-vaapi)
|
||||
endif()
|
||||
|
||||
if (FFNVCODEC_FOUND AND CUDA_FOUND)
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS
|
||||
--enable-cuvid
|
||||
--enable-ffnvcodec
|
||||
--enable-nvdec
|
||||
--enable-hwaccel=h264_nvdec
|
||||
--enable-hwaccel=vp9_nvdec
|
||||
--extra-cflags=-I${CUDA_INCLUDE_DIRS}
|
||||
)
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES
|
||||
${FFNVCODEC_LIBRARIES}
|
||||
${CUDA_LIBRARIES}
|
||||
)
|
||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
|
||||
${FFNVCODEC_INCLUDE_DIRS}
|
||||
${CUDA_INCLUDE_DIRS}
|
||||
)
|
||||
list(APPEND FFmpeg_HWACCEL_LDFLAGS
|
||||
${FFNVCODEC_LDFLAGS}
|
||||
${CUDA_LDFLAGS}
|
||||
)
|
||||
message(STATUS "ffnvcodec libraries version ${FFNVCODEC_VERSION} found")
|
||||
endif()
|
||||
|
||||
if (VDPAU_FOUND)
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS
|
||||
--enable-vdpau
|
||||
--enable-hwaccel=h264_vdpau
|
||||
--enable-hwaccel=vp9_vdpau
|
||||
)
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${VDPAU_LIBRARIES})
|
||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${VDPAU_INCLUDE_DIRS})
|
||||
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${VDPAU_LDFLAGS})
|
||||
message(STATUS "vdpau libraries version ${VDPAU_VERSION} found")
|
||||
else()
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vdpau)
|
||||
endif()
|
||||
|
||||
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
|
||||
# `--disable-vdpau` is needed to avoid linking issues
|
||||
add_custom_command(
|
||||
@@ -679,6 +624,7 @@ if (YUZU_USE_BUNDLED_FFMPEG)
|
||||
--disable-network
|
||||
--disable-postproc
|
||||
--disable-swresample
|
||||
--disable-vdpau
|
||||
--enable-decoder=h264
|
||||
--enable-decoder=vp9
|
||||
--cc="${CMAKE_C_COMPILER}"
|
||||
@@ -707,26 +653,15 @@ if (YUZU_USE_BUNDLED_FFMPEG)
|
||||
${FFmpeg_BUILD_DIR}
|
||||
)
|
||||
|
||||
set(FFmpeg_INCLUDE_DIR
|
||||
"${FFmpeg_PREFIX};${FFmpeg_BUILD_DIR};${FFmpeg_HWACCEL_INCLUDE_DIRS}"
|
||||
CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||
|
||||
set(FFmpeg_LDFLAGS
|
||||
"${FFmpeg_HWACCEL_LDFLAGS}"
|
||||
CACHE STRING "FFmpeg linker flags" FORCE)
|
||||
|
||||
# ALL makes this custom target build every time
|
||||
# but it won't actually build if the DEPENDS parameter is up to date
|
||||
add_custom_target(ffmpeg-configure ALL DEPENDS ${FFmpeg_MAKEFILE})
|
||||
add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_BUILD_LIBRARIES} ffmpeg-configure)
|
||||
link_libraries(${FFmpeg_LIBVA_LIBRARIES})
|
||||
set(FFmpeg_LIBRARIES ${FFmpeg_BUILD_LIBRARIES} ${FFmpeg_HWACCEL_LIBRARIES}
|
||||
set(FFmpeg_LIBRARIES ${FFmpeg_LIBVA_LIBRARIES} ${FFmpeg_BUILD_LIBRARIES}
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
unset(FFmpeg_BUILD_LIBRARIES)
|
||||
unset(FFmpeg_HWACCEL_FLAGS)
|
||||
unset(FFmpeg_HWACCEL_INCLUDE_DIRS)
|
||||
unset(FFmpeg_HWACCEL_LDFLAGS)
|
||||
unset(FFmpeg_HWACCEL_LIBRARIES)
|
||||
unset(FFmpeg_LIBVA_LIBRARIES)
|
||||
|
||||
if (FFmpeg_FOUND)
|
||||
message(STATUS "Found FFmpeg version ${FFmpeg_VERSION}")
|
||||
@@ -735,13 +670,12 @@ if (YUZU_USE_BUNDLED_FFMPEG)
|
||||
endif()
|
||||
else() # WIN32
|
||||
# Use yuzu FFmpeg binaries
|
||||
set(FFmpeg_EXT_NAME "ffmpeg-4.4")
|
||||
set(FFmpeg_EXT_NAME "ffmpeg-4.3.1")
|
||||
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
|
||||
download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
|
||||
set(FFmpeg_FOUND YES)
|
||||
set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||
set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg library directory" FORCE)
|
||||
set(FFmpeg_LDFLAGS "" CACHE STRING "FFmpeg linker flags" FORCE)
|
||||
set(FFmpeg_DLL_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
|
||||
set(FFmpeg_LIBRARIES
|
||||
${FFmpeg_LIBRARY_DIR}/swscale.lib
|
||||
|
||||
@@ -53,8 +53,6 @@ add_library(common STATIC
|
||||
div_ceil.h
|
||||
dynamic_library.cpp
|
||||
dynamic_library.h
|
||||
error.cpp
|
||||
error.h
|
||||
fiber.cpp
|
||||
fiber.h
|
||||
fs/file.cpp
|
||||
@@ -90,6 +88,7 @@ add_library(common STATIC
|
||||
microprofile.cpp
|
||||
microprofile.h
|
||||
microprofileui.h
|
||||
misc.cpp
|
||||
nvidia_flags.cpp
|
||||
nvidia_flags.h
|
||||
page_table.cpp
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
|
||||
#if !defined(ARCHITECTURE_x86_64)
|
||||
#include <cstdlib> // for exit
|
||||
@@ -48,6 +49,16 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
|
||||
|
||||
#endif // _MSC_VER ndef
|
||||
|
||||
// Generic function to get last error message.
|
||||
// Call directly after the command or use the error num.
|
||||
// This function might change the error code.
|
||||
// Defined in misc.cpp.
|
||||
[[nodiscard]] std::string GetLastErrorMsg();
|
||||
|
||||
// Like GetLastErrorMsg(), but passing an explicit error code.
|
||||
// Defined in misc.cpp.
|
||||
[[nodiscard]] std::string NativeErrorToString(int e);
|
||||
|
||||
#define DECLARE_ENUM_FLAG_OPERATORS(type) \
|
||||
[[nodiscard]] constexpr type operator|(type a, type b) noexcept { \
|
||||
using T = std::underlying_type_t<type>; \
|
||||
@@ -61,14 +72,6 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
|
||||
using T = std::underlying_type_t<type>; \
|
||||
return static_cast<type>(static_cast<T>(a) ^ static_cast<T>(b)); \
|
||||
} \
|
||||
[[nodiscard]] constexpr type operator<<(type a, type b) noexcept { \
|
||||
using T = std::underlying_type_t<type>; \
|
||||
return static_cast<type>(static_cast<T>(a) << static_cast<T>(b)); \
|
||||
} \
|
||||
[[nodiscard]] constexpr type operator>>(type a, type b) noexcept { \
|
||||
using T = std::underlying_type_t<type>; \
|
||||
return static_cast<type>(static_cast<T>(a) >> static_cast<T>(b)); \
|
||||
} \
|
||||
constexpr type& operator|=(type& a, type b) noexcept { \
|
||||
a = a | b; \
|
||||
return a; \
|
||||
@@ -81,14 +84,6 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
|
||||
a = a ^ b; \
|
||||
return a; \
|
||||
} \
|
||||
constexpr type& operator<<=(type& a, type b) noexcept { \
|
||||
a = a << b; \
|
||||
return a; \
|
||||
} \
|
||||
constexpr type& operator>>=(type& a, type b) noexcept { \
|
||||
a = a >> b; \
|
||||
return a; \
|
||||
} \
|
||||
[[nodiscard]] constexpr type operator~(type key) noexcept { \
|
||||
using T = std::underlying_type_t<type>; \
|
||||
return static_cast<type>(~static_cast<T>(key)); \
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Common {
|
||||
|
||||
// Generic function to get last error message.
|
||||
// Call directly after the command or use the error num.
|
||||
// This function might change the error code.
|
||||
// Defined in error.cpp.
|
||||
[[nodiscard]] std::string GetLastErrorMsg();
|
||||
|
||||
// Like GetLastErrorMsg(), but passing an explicit error code.
|
||||
// Defined in error.cpp.
|
||||
[[nodiscard]] std::string NativeErrorToString(int e);
|
||||
|
||||
} // namespace Common
|
||||
@@ -21,7 +21,6 @@
|
||||
#define SCREENSHOTS_DIR "screenshots"
|
||||
#define SDMC_DIR "sdmc"
|
||||
#define SHADER_DIR "shader"
|
||||
#define TAS_DIR "tas"
|
||||
|
||||
// yuzu-specific files
|
||||
|
||||
|
||||
@@ -116,7 +116,6 @@ private:
|
||||
GenerateYuzuPath(YuzuPath::ScreenshotsDir, yuzu_path / SCREENSHOTS_DIR);
|
||||
GenerateYuzuPath(YuzuPath::SDMCDir, yuzu_path / SDMC_DIR);
|
||||
GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR);
|
||||
GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR);
|
||||
}
|
||||
|
||||
~PathManagerImpl() = default;
|
||||
|
||||
@@ -23,7 +23,6 @@ enum class YuzuPath {
|
||||
ScreenshotsDir, // Where yuzu screenshots are stored.
|
||||
SDMCDir, // Where the emulated SDMC is stored.
|
||||
ShaderDir, // Where shaders are stored.
|
||||
TASDir, // Where TAS scripts are stored.
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,9 +10,7 @@
|
||||
#include <cstring>
|
||||
#endif
|
||||
|
||||
#include "common/error.h"
|
||||
|
||||
namespace Common {
|
||||
#include "common/common_funcs.h"
|
||||
|
||||
std::string NativeErrorToString(int e) {
|
||||
#ifdef _WIN32
|
||||
@@ -52,5 +50,3 @@ std::string GetLastErrorMsg() {
|
||||
return NativeErrorToString(errno);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -54,13 +54,14 @@ void LogSettings() {
|
||||
log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue());
|
||||
log_setting("Renderer_UseAsynchronousGpuEmulation",
|
||||
values.use_asynchronous_gpu_emulation.GetValue());
|
||||
log_setting("Renderer_NvdecEmulation", values.nvdec_emulation.GetValue());
|
||||
log_setting("Renderer_UseNvdecEmulation", values.use_nvdec_emulation.GetValue());
|
||||
log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue());
|
||||
log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
|
||||
log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
|
||||
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
|
||||
log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
|
||||
log_setting("Audio_OutputEngine", values.sink_id.GetValue());
|
||||
log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
|
||||
log_setting("Audio_OutputDevice", values.audio_device_id.GetValue());
|
||||
log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd.GetValue());
|
||||
log_path("DataStorage_CacheDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir));
|
||||
@@ -71,9 +72,6 @@ void LogSettings() {
|
||||
log_setting("Debugging_ProgramArgs", values.program_args.GetValue());
|
||||
log_setting("Services_BCATBackend", values.bcat_backend.GetValue());
|
||||
log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local.GetValue());
|
||||
log_setting("Input_EnableMotion", values.motion_enabled.GetValue());
|
||||
log_setting("Input_EnableVibration", values.vibration_enabled.GetValue());
|
||||
log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue());
|
||||
}
|
||||
|
||||
bool IsConfiguringGlobal() {
|
||||
@@ -114,6 +112,7 @@ void RestoreGlobalState(bool is_powered_on) {
|
||||
}
|
||||
|
||||
// Audio
|
||||
values.enable_audio_stretching.SetGlobal(true);
|
||||
values.volume.SetGlobal(true);
|
||||
|
||||
// Core
|
||||
@@ -137,7 +136,7 @@ void RestoreGlobalState(bool is_powered_on) {
|
||||
values.use_disk_shader_cache.SetGlobal(true);
|
||||
values.gpu_accuracy.SetGlobal(true);
|
||||
values.use_asynchronous_gpu_emulation.SetGlobal(true);
|
||||
values.nvdec_emulation.SetGlobal(true);
|
||||
values.use_nvdec_emulation.SetGlobal(true);
|
||||
values.accelerate_astc.SetGlobal(true);
|
||||
values.use_vsync.SetGlobal(true);
|
||||
values.shader_backend.SetGlobal(true);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings_input.h"
|
||||
#include "input_common/udp/client.h"
|
||||
|
||||
namespace Settings {
|
||||
|
||||
@@ -47,12 +48,6 @@ enum class FullscreenMode : u32 {
|
||||
Exclusive = 1,
|
||||
};
|
||||
|
||||
enum class NvdecEmulation : u32 {
|
||||
Off = 0,
|
||||
CPU = 1,
|
||||
GPU = 2,
|
||||
};
|
||||
|
||||
/** The BasicSetting class is a simple resource manager. It defines a label and default value
|
||||
* alongside the actual value of the setting for simpler and less-error prone use with frontend
|
||||
* configurations. Setting a default value and label is required, though subclasses may deviate from
|
||||
@@ -414,6 +409,7 @@ struct Values {
|
||||
BasicSetting<std::string> audio_device_id{"auto", "output_device"};
|
||||
BasicSetting<std::string> sink_id{"auto", "output_engine"};
|
||||
BasicSetting<bool> audio_muted{false, "audio_muted"};
|
||||
Setting<bool> enable_audio_stretching{true, "enable_audio_stretching"};
|
||||
RangedSetting<u8> volume{100, 0, 100, "volume"};
|
||||
|
||||
// Core
|
||||
@@ -470,7 +466,7 @@ struct Values {
|
||||
RangedSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal,
|
||||
GPUAccuracy::Extreme, "gpu_accuracy"};
|
||||
Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
|
||||
Setting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
|
||||
Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"};
|
||||
Setting<bool> accelerate_astc{true, "accelerate_astc"};
|
||||
Setting<bool> use_vsync{true, "use_vsync"};
|
||||
BasicRangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"};
|
||||
@@ -502,20 +498,14 @@ struct Values {
|
||||
|
||||
Setting<bool> use_docked_mode{true, "use_docked_mode"};
|
||||
|
||||
BasicSetting<bool> enable_raw_input{false, "enable_raw_input"};
|
||||
|
||||
Setting<bool> vibration_enabled{true, "vibration_enabled"};
|
||||
Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"};
|
||||
|
||||
Setting<bool> motion_enabled{true, "motion_enabled"};
|
||||
BasicSetting<std::string> motion_device{"engine:motion_emu,update_period:100,sensitivity:0.01",
|
||||
"motion_device"};
|
||||
BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"};
|
||||
|
||||
BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"};
|
||||
BasicSetting<bool> tas_enable{false, "tas_enable"};
|
||||
BasicSetting<bool> tas_loop{false, "tas_loop"};
|
||||
BasicSetting<bool> tas_swap_controllers{true, "tas_swap_controllers"};
|
||||
BasicSetting<std::string> udp_input_servers{InputCommon::CemuhookUDP::DEFAULT_SRV,
|
||||
"udp_input_servers"};
|
||||
|
||||
BasicSetting<bool> mouse_panning{false, "mouse_panning"};
|
||||
BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/error.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/thread.h"
|
||||
#ifdef __APPLE__
|
||||
@@ -23,6 +21,8 @@
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#define cpu_set_t cpuset_t
|
||||
#endif
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include <utility>
|
||||
|
||||
namespace Common {
|
||||
template <typename T, bool with_stop_token = false>
|
||||
template <typename T>
|
||||
class SPSCQueue {
|
||||
public:
|
||||
SPSCQueue() {
|
||||
@@ -84,7 +84,7 @@ public:
|
||||
void Wait() {
|
||||
if (Empty()) {
|
||||
std::unique_lock lock{cv_mutex};
|
||||
cv.wait(lock, [this] { return !Empty(); });
|
||||
cv.wait(lock, [this]() { return !Empty(); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,19 +95,6 @@ public:
|
||||
return t;
|
||||
}
|
||||
|
||||
T PopWait(std::stop_token stop_token) {
|
||||
if (Empty()) {
|
||||
std::unique_lock lock{cv_mutex};
|
||||
cv.wait(lock, stop_token, [this] { return !Empty(); });
|
||||
}
|
||||
if (stop_token.stop_requested()) {
|
||||
return T{};
|
||||
}
|
||||
T t;
|
||||
Pop(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
// not thread-safe
|
||||
void Clear() {
|
||||
size.store(0);
|
||||
@@ -136,13 +123,13 @@ private:
|
||||
ElementPtr* read_ptr;
|
||||
std::atomic_size_t size{0};
|
||||
std::mutex cv_mutex;
|
||||
std::conditional_t<with_stop_token, std::condition_variable_any, std::condition_variable> cv;
|
||||
std::condition_variable cv;
|
||||
};
|
||||
|
||||
// a simple thread-safe,
|
||||
// single reader, multiple writer queue
|
||||
|
||||
template <typename T, bool with_stop_token = false>
|
||||
template <typename T>
|
||||
class MPSCQueue {
|
||||
public:
|
||||
[[nodiscard]] std::size_t Size() const {
|
||||
@@ -179,17 +166,13 @@ public:
|
||||
return spsc_queue.PopWait();
|
||||
}
|
||||
|
||||
T PopWait(std::stop_token stop_token) {
|
||||
return spsc_queue.PopWait(stop_token);
|
||||
}
|
||||
|
||||
// not thread-safe
|
||||
void Clear() {
|
||||
spsc_queue.Clear();
|
||||
}
|
||||
|
||||
private:
|
||||
SPSCQueue<T, with_stop_token> spsc_queue;
|
||||
SPSCQueue<T> spsc_queue;
|
||||
std::mutex write_lock;
|
||||
};
|
||||
} // namespace Common
|
||||
|
||||
@@ -263,8 +263,6 @@ add_library(core STATIC
|
||||
hle/service/acc/acc_u0.h
|
||||
hle/service/acc/acc_u1.cpp
|
||||
hle/service/acc/acc_u1.h
|
||||
hle/service/acc/async_context.cpp
|
||||
hle/service/acc/async_context.h
|
||||
hle/service/acc/errors.h
|
||||
hle/service/acc/profile_manager.cpp
|
||||
hle/service/acc/profile_manager.h
|
||||
|
||||
@@ -83,12 +83,6 @@ FileSys::StorageId GetStorageIdForFrontendSlot(
|
||||
}
|
||||
}
|
||||
|
||||
void KProcessDeleter(Kernel::KProcess* process) {
|
||||
process->Destroy();
|
||||
}
|
||||
|
||||
using KProcessPtr = std::unique_ptr<Kernel::KProcess, decltype(&KProcessDeleter)>;
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
@@ -239,8 +233,8 @@ struct System::Impl {
|
||||
}
|
||||
|
||||
telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
|
||||
main_process = KProcessPtr{Kernel::KProcess::Create(system.Kernel()), KProcessDeleter};
|
||||
ASSERT(Kernel::KProcess::Initialize(main_process.get(), system, "main",
|
||||
auto main_process = Kernel::KProcess::Create(system.Kernel());
|
||||
ASSERT(Kernel::KProcess::Initialize(main_process, system, "main",
|
||||
Kernel::KProcess::ProcessType::Userland)
|
||||
.IsSuccess());
|
||||
main_process->Open();
|
||||
@@ -253,7 +247,7 @@ struct System::Impl {
|
||||
static_cast<u32>(load_result));
|
||||
}
|
||||
AddGlueRegistrationForProcess(*app_loader, *main_process);
|
||||
kernel.MakeCurrentProcess(main_process.get());
|
||||
kernel.MakeCurrentProcess(main_process);
|
||||
kernel.InitializeCores();
|
||||
|
||||
// Initialize cheat engine
|
||||
@@ -305,7 +299,10 @@ struct System::Impl {
|
||||
is_powered_on = false;
|
||||
exit_lock = false;
|
||||
|
||||
gpu_core.reset();
|
||||
if (gpu_core) {
|
||||
gpu_core->ShutDown();
|
||||
}
|
||||
|
||||
services.reset();
|
||||
service_manager.reset();
|
||||
cheat_engine.reset();
|
||||
@@ -314,12 +311,11 @@ struct System::Impl {
|
||||
time_manager.Shutdown();
|
||||
core_timing.Shutdown();
|
||||
app_loader.reset();
|
||||
gpu_core.reset();
|
||||
perf_stats.reset();
|
||||
kernel.Shutdown();
|
||||
memory.Reset();
|
||||
applet_manager.ClearAll();
|
||||
// TODO: The main process should be freed based on KAutoObject ref counting.
|
||||
main_process.reset();
|
||||
|
||||
LOG_DEBUG(Core, "Shutdown OK");
|
||||
}
|
||||
@@ -378,7 +374,6 @@ struct System::Impl {
|
||||
std::unique_ptr<Tegra::GPU> gpu_core;
|
||||
std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
|
||||
std::unique_ptr<Core::DeviceMemory> device_memory;
|
||||
KProcessPtr main_process{nullptr, KProcessDeleter};
|
||||
Core::Memory::Memory memory;
|
||||
CpuManager cpu_manager;
|
||||
std::atomic_bool is_powered_on{};
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
|
||||
@@ -273,10 +273,6 @@ VirtualFile VfsDirectory::GetFile(std::string_view name) const {
|
||||
return iter == files.end() ? nullptr : *iter;
|
||||
}
|
||||
|
||||
FileTimeStampRaw VfsDirectory::GetFileTimeStamp([[maybe_unused]] std::string_view path) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
VirtualDir VfsDirectory::GetSubdirectory(std::string_view name) const {
|
||||
const auto& subs = GetSubdirectories();
|
||||
const auto iter = std::find_if(subs.begin(), subs.end(),
|
||||
|
||||
@@ -199,9 +199,6 @@ public:
|
||||
// file with name.
|
||||
virtual VirtualFile GetFile(std::string_view name) const;
|
||||
|
||||
// Returns a struct containing the file's timestamp.
|
||||
virtual FileTimeStampRaw GetFileTimeStamp(std::string_view path) const;
|
||||
|
||||
// Returns a vector containing all of the subdirectories in this directory.
|
||||
virtual std::vector<VirtualDir> GetSubdirectories() const = 0;
|
||||
// Returns the directory with name matching name. Returns nullptr if directory dosen't have a
|
||||
|
||||
@@ -13,13 +13,6 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
|
||||
// For FileTimeStampRaw
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define stat _stat64
|
||||
#endif
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
namespace FS = Common::FS;
|
||||
@@ -399,28 +392,6 @@ std::vector<VirtualFile> RealVfsDirectory::GetFiles() const {
|
||||
return IterateEntries<RealVfsFile, VfsFile>();
|
||||
}
|
||||
|
||||
FileTimeStampRaw RealVfsDirectory::GetFileTimeStamp(std::string_view path_) const {
|
||||
const auto full_path = FS::SanitizePath(path + '/' + std::string(path_));
|
||||
const auto fs_path = std::filesystem::path{FS::ToU8String(full_path)};
|
||||
struct stat file_status;
|
||||
|
||||
#ifdef _WIN32
|
||||
const auto stat_result = _wstat64(fs_path.c_str(), &file_status);
|
||||
#else
|
||||
const auto stat_result = stat(fs_path.c_str(), &file_status);
|
||||
#endif
|
||||
|
||||
if (stat_result != 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
.created{static_cast<u64>(file_status.st_ctime)},
|
||||
.accessed{static_cast<u64>(file_status.st_atime)},
|
||||
.modified{static_cast<u64>(file_status.st_mtime)},
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<VirtualDir> RealVfsDirectory::GetSubdirectories() const {
|
||||
return IterateEntries<RealVfsDirectory, VfsDirectory>();
|
||||
}
|
||||
|
||||
@@ -86,7 +86,6 @@ public:
|
||||
VirtualDir CreateDirectoryRelative(std::string_view relative_path) override;
|
||||
bool DeleteSubdirectoryRecursive(std::string_view name) override;
|
||||
std::vector<VirtualFile> GetFiles() const override;
|
||||
FileTimeStampRaw GetFileTimeStamp(std::string_view path) const override;
|
||||
std::vector<VirtualDir> GetSubdirectories() const override;
|
||||
bool IsWritable() const override;
|
||||
bool IsReadable() const override;
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
class VfsDirectory;
|
||||
@@ -20,11 +18,4 @@ using VirtualDir = std::shared_ptr<VfsDirectory>;
|
||||
using VirtualFile = std::shared_ptr<VfsFile>;
|
||||
using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
|
||||
|
||||
struct FileTimeStampRaw {
|
||||
u64 created{};
|
||||
u64 accessed{};
|
||||
u64 modified{};
|
||||
u64 padding{};
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -28,20 +28,13 @@ constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 12.1.0-1.0";
|
||||
|
||||
// Atmosphere version constants.
|
||||
|
||||
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 1;
|
||||
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 0;
|
||||
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 0;
|
||||
|
||||
constexpr u32 AtmosphereTargetFirmwareWithRevision(u8 major, u8 minor, u8 micro, u8 rev) {
|
||||
return u32{major} << 24 | u32{minor} << 16 | u32{micro} << 8 | u32{rev};
|
||||
}
|
||||
|
||||
constexpr u32 AtmosphereTargetFirmware(u8 major, u8 minor, u8 micro) {
|
||||
return AtmosphereTargetFirmwareWithRevision(major, minor, micro, 0);
|
||||
}
|
||||
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 0;
|
||||
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 19;
|
||||
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 5;
|
||||
|
||||
constexpr u32 GetTargetFirmware() {
|
||||
return AtmosphereTargetFirmware(HOS_VERSION_MAJOR, HOS_VERSION_MINOR, HOS_VERSION_MICRO);
|
||||
return u32{HOS_VERSION_MAJOR} << 24 | u32{HOS_VERSION_MINOR} << 16 |
|
||||
u32{HOS_VERSION_MICRO} << 8 | 0U;
|
||||
}
|
||||
|
||||
} // namespace HLE::ApiVersion
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "core/hle/service/acc/acc_su.h"
|
||||
#include "core/hle/service/acc/acc_u0.h"
|
||||
#include "core/hle/service/acc/acc_u1.h"
|
||||
#include "core/hle/service/acc/async_context.h"
|
||||
#include "core/hle/service/acc/errors.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/glue/arp.h"
|
||||
@@ -455,6 +454,22 @@ public:
|
||||
: IProfileCommon{system_, "IProfileEditor", true, user_id_, profile_manager_} {}
|
||||
};
|
||||
|
||||
class IAsyncContext final : public ServiceFramework<IAsyncContext> {
|
||||
public:
|
||||
explicit IAsyncContext(Core::System& system_) : ServiceFramework{system_, "IAsyncContext"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetSystemEvent"},
|
||||
{1, nullptr, "Cancel"},
|
||||
{2, nullptr, "HasDone"},
|
||||
{3, nullptr, "GetResult"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class ISessionObject final : public ServiceFramework<ISessionObject> {
|
||||
public:
|
||||
explicit ISessionObject(Core::System& system_, Common::UUID)
|
||||
@@ -489,44 +504,16 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class EnsureTokenIdCacheAsyncInterface final : public IAsyncContext {
|
||||
public:
|
||||
explicit EnsureTokenIdCacheAsyncInterface(Core::System& system_) : IAsyncContext{system_} {
|
||||
MarkComplete();
|
||||
}
|
||||
~EnsureTokenIdCacheAsyncInterface() = default;
|
||||
|
||||
void LoadIdTokenCache(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool IsComplete() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Cancel() override {}
|
||||
|
||||
ResultCode GetResult() const override {
|
||||
return ResultSuccess;
|
||||
}
|
||||
};
|
||||
|
||||
class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
|
||||
public:
|
||||
explicit IManagerForApplication(Core::System& system_, Common::UUID user_id_)
|
||||
: ServiceFramework{system_, "IManagerForApplication"},
|
||||
ensure_token_id{std::make_shared<EnsureTokenIdCacheAsyncInterface>(system)},
|
||||
user_id{user_id_} {
|
||||
: ServiceFramework{system_, "IManagerForApplication"}, user_id{user_id_} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IManagerForApplication::CheckAvailability, "CheckAvailability"},
|
||||
{1, &IManagerForApplication::GetAccountId, "GetAccountId"},
|
||||
{2, &IManagerForApplication::EnsureIdTokenCacheAsync, "EnsureIdTokenCacheAsync"},
|
||||
{3, &IManagerForApplication::LoadIdTokenCache, "LoadIdTokenCache"},
|
||||
{2, nullptr, "EnsureIdTokenCacheAsync"},
|
||||
{3, nullptr, "LoadIdTokenCache"},
|
||||
{130, &IManagerForApplication::GetNintendoAccountUserResourceCacheForApplication, "GetNintendoAccountUserResourceCacheForApplication"},
|
||||
{150, nullptr, "CreateAuthorizationRequest"},
|
||||
{160, &IManagerForApplication::StoreOpenContext, "StoreOpenContext"},
|
||||
@@ -553,20 +540,6 @@ private:
|
||||
rb.PushRaw<u64>(user_id.GetNintendoID());
|
||||
}
|
||||
|
||||
void EnsureIdTokenCacheAsync(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface(ensure_token_id);
|
||||
}
|
||||
|
||||
void LoadIdTokenCache(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
|
||||
ensure_token_id->LoadIdTokenCache(ctx);
|
||||
}
|
||||
|
||||
void GetNintendoAccountUserResourceCacheForApplication(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
|
||||
@@ -589,7 +562,6 @@ private:
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
std::shared_ptr<EnsureTokenIdCacheAsyncInterface> ensure_token_id{};
|
||||
Common::UUID user_id{Common::INVALID_UUID};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright 2021 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/acc/async_context.h"
|
||||
|
||||
namespace Service::Account {
|
||||
IAsyncContext::IAsyncContext(Core::System& system_)
|
||||
: ServiceFramework{system_, "IAsyncContext"}, compeletion_event{system_.Kernel()} {
|
||||
|
||||
Kernel::KAutoObject::Create(std::addressof(compeletion_event));
|
||||
compeletion_event.Initialize("IAsyncContext:CompletionEvent");
|
||||
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAsyncContext::GetSystemEvent, "GetSystemEvent"},
|
||||
{1, &IAsyncContext::Cancel, "Cancel"},
|
||||
{2, &IAsyncContext::HasDone, "HasDone"},
|
||||
{3, &IAsyncContext::GetResult, "GetResult"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
void IAsyncContext::GetSystemEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(compeletion_event.GetReadableEvent());
|
||||
}
|
||||
|
||||
void IAsyncContext::Cancel(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
|
||||
Cancel();
|
||||
MarkComplete();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IAsyncContext::HasDone(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
|
||||
is_complete.store(IsComplete());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(is_complete.load());
|
||||
}
|
||||
|
||||
void IAsyncContext::GetResult(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(GetResult());
|
||||
}
|
||||
|
||||
void IAsyncContext::MarkComplete() {
|
||||
is_complete.store(true);
|
||||
compeletion_event.GetWritableEvent().Signal();
|
||||
}
|
||||
|
||||
} // namespace Service::Account
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright 2021 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
class IAsyncContext : public ServiceFramework<IAsyncContext> {
|
||||
public:
|
||||
explicit IAsyncContext(Core::System& system_);
|
||||
|
||||
void GetSystemEvent(Kernel::HLERequestContext& ctx);
|
||||
void Cancel(Kernel::HLERequestContext& ctx);
|
||||
void HasDone(Kernel::HLERequestContext& ctx);
|
||||
void GetResult(Kernel::HLERequestContext& ctx);
|
||||
|
||||
protected:
|
||||
virtual bool IsComplete() const = 0;
|
||||
virtual void Cancel() = 0;
|
||||
virtual ResultCode GetResult() const = 0;
|
||||
|
||||
void MarkComplete();
|
||||
|
||||
std::atomic<bool> is_complete{false};
|
||||
Kernel::KEvent compeletion_event;
|
||||
};
|
||||
|
||||
} // namespace Service::Account
|
||||
@@ -1270,8 +1270,7 @@ void ILibraryAppletCreator::CreateHandleStorage(Kernel::HLERequestContext& ctx)
|
||||
IApplicationFunctions::IApplicationFunctions(Core::System& system_)
|
||||
: ServiceFramework{system_, "IApplicationFunctions"}, gpu_error_detected_event{system.Kernel()},
|
||||
friend_invitation_storage_channel_event{system.Kernel()},
|
||||
notification_storage_channel_event{system.Kernel()}, health_warning_disappeared_system_event{
|
||||
system.Kernel()} {
|
||||
health_warning_disappeared_system_event{system.Kernel()} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
|
||||
@@ -1323,7 +1322,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
|
||||
{131, nullptr, "SetDelayTimeToAbortOnGpuError"},
|
||||
{140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"},
|
||||
{141, &IApplicationFunctions::TryPopFromFriendInvitationStorageChannel, "TryPopFromFriendInvitationStorageChannel"},
|
||||
{150, &IApplicationFunctions::GetNotificationStorageChannelEvent, "GetNotificationStorageChannelEvent"},
|
||||
{150, nullptr, "GetNotificationStorageChannelEvent"},
|
||||
{151, nullptr, "TryPopFromNotificationStorageChannel"},
|
||||
{160, &IApplicationFunctions::GetHealthWarningDisappearedSystemEvent, "GetHealthWarningDisappearedSystemEvent"},
|
||||
{170, nullptr, "SetHdcpAuthenticationActivated"},
|
||||
@@ -1341,14 +1340,11 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
|
||||
|
||||
Kernel::KAutoObject::Create(std::addressof(gpu_error_detected_event));
|
||||
Kernel::KAutoObject::Create(std::addressof(friend_invitation_storage_channel_event));
|
||||
Kernel::KAutoObject::Create(std::addressof(notification_storage_channel_event));
|
||||
Kernel::KAutoObject::Create(std::addressof(health_warning_disappeared_system_event));
|
||||
|
||||
gpu_error_detected_event.Initialize("IApplicationFunctions:GpuErrorDetectedSystemEvent");
|
||||
friend_invitation_storage_channel_event.Initialize(
|
||||
"IApplicationFunctions:FriendInvitationStorageChannelEvent");
|
||||
notification_storage_channel_event.Initialize(
|
||||
"IApplicationFunctions:NotificationStorageChannelEvent");
|
||||
health_warning_disappeared_system_event.Initialize(
|
||||
"IApplicationFunctions:HealthWarningDisappearedSystemEvent");
|
||||
}
|
||||
@@ -1766,14 +1762,6 @@ void IApplicationFunctions::TryPopFromFriendInvitationStorageChannel(
|
||||
rb.Push(ERR_NO_DATA_IN_CHANNEL);
|
||||
}
|
||||
|
||||
void IApplicationFunctions::GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(notification_storage_channel_event.GetReadableEvent());
|
||||
}
|
||||
|
||||
void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
|
||||
@@ -295,7 +295,6 @@ private:
|
||||
void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
|
||||
void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx);
|
||||
void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx);
|
||||
void GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx);
|
||||
void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx);
|
||||
|
||||
bool launch_popped_application_specific = false;
|
||||
@@ -303,7 +302,6 @@ private:
|
||||
s32 previous_program_index{-1};
|
||||
Kernel::KEvent gpu_error_detected_event;
|
||||
Kernel::KEvent friend_invitation_storage_channel_event;
|
||||
Kernel::KEvent notification_storage_channel_event;
|
||||
Kernel::KEvent health_warning_disappeared_system_event;
|
||||
};
|
||||
|
||||
|
||||
@@ -97,24 +97,14 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons
|
||||
|
||||
ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
|
||||
std::string path(Common::FS::SanitizePath(path_));
|
||||
|
||||
// NOTE: This is inaccurate behavior. CreateDirectory is not recursive.
|
||||
// CreateDirectory should return PathNotFound if the parent directory does not exist.
|
||||
// This is here temporarily in order to have UMM "work" in the meantime.
|
||||
// TODO (Morph): Remove this when a hardware test verifies the correct behavior.
|
||||
const auto components = Common::FS::SplitPathComponents(path);
|
||||
std::string relative_path;
|
||||
for (const auto& component : components) {
|
||||
// Skip empty path components
|
||||
if (component.empty()) {
|
||||
continue;
|
||||
}
|
||||
relative_path = Common::FS::SanitizePath(relative_path + '/' + component);
|
||||
auto new_dir = backing->CreateSubdirectory(relative_path);
|
||||
if (new_dir == nullptr) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultUnknown;
|
||||
}
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
|
||||
if (dir == nullptr || Common::FS::GetFilename(Common::FS::GetParentPath(path)).empty()) {
|
||||
dir = backing;
|
||||
}
|
||||
auto new_dir = dir->CreateSubdirectory(Common::FS::GetFilename(path));
|
||||
if (new_dir == nullptr) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultUnknown;
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
@@ -261,18 +251,6 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
|
||||
ResultVal<FileSys::FileTimeStampRaw> VfsDirectoryServiceWrapper::GetFileTimeStampRaw(
|
||||
const std::string& path) const {
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
|
||||
if (dir == nullptr) {
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
if (GetEntryType(path).Failed()) {
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
return MakeResult(dir->GetFileTimeStamp(Common::FS::GetFilename(path)));
|
||||
}
|
||||
|
||||
FileSystemController::FileSystemController(Core::System& system_) : system{system_} {}
|
||||
|
||||
FileSystemController::~FileSystemController() = default;
|
||||
|
||||
@@ -240,12 +240,6 @@ public:
|
||||
*/
|
||||
ResultVal<FileSys::EntryType> GetEntryType(const std::string& path) const;
|
||||
|
||||
/**
|
||||
* Get the timestamp of the specified path
|
||||
* @return The timestamp of the specified path or error code
|
||||
*/
|
||||
ResultVal<FileSys::FileTimeStampRaw> GetFileTimeStampRaw(const std::string& path) const;
|
||||
|
||||
private:
|
||||
FileSys::VirtualDir backing;
|
||||
};
|
||||
|
||||
@@ -326,7 +326,7 @@ public:
|
||||
{11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"},
|
||||
{12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"},
|
||||
{13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"},
|
||||
{14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"},
|
||||
{14, nullptr, "GetFileTimeStampRaw"},
|
||||
{15, nullptr, "QueryEntry"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
@@ -501,24 +501,6 @@ public:
|
||||
rb.Push(size.get_total_size());
|
||||
}
|
||||
|
||||
void GetFileTimeStampRaw(Kernel::HLERequestContext& ctx) {
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name);
|
||||
|
||||
auto result = backend.GetFileTimeStampRaw(name);
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 10};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(*result);
|
||||
}
|
||||
|
||||
private:
|
||||
VfsDirectoryServiceWrapper backend;
|
||||
SizeGetter size;
|
||||
|
||||
@@ -15,20 +15,6 @@
|
||||
namespace Service::HID {
|
||||
class Controller_Touchscreen final : public ControllerBase {
|
||||
public:
|
||||
enum class TouchScreenModeForNx : u8 {
|
||||
UseSystemSetting,
|
||||
Finger,
|
||||
Heat2,
|
||||
};
|
||||
|
||||
struct TouchScreenConfigurationForNx {
|
||||
TouchScreenModeForNx mode;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x7);
|
||||
INSERT_PADDING_BYTES_NOINIT(0xF); // Reserved
|
||||
};
|
||||
static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17,
|
||||
"TouchScreenConfigurationForNx is an invalid size");
|
||||
|
||||
explicit Controller_Touchscreen(Core::System& system_);
|
||||
~Controller_Touchscreen() override;
|
||||
|
||||
|
||||
@@ -331,7 +331,7 @@ Hid::Hid(Core::System& system_)
|
||||
{529, nullptr, "SetDisallowedPalmaConnection"},
|
||||
{1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"},
|
||||
{1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"},
|
||||
{1002, &Hid::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"},
|
||||
{1002, nullptr, "SetTouchScreenConfiguration"},
|
||||
{1003, nullptr, "IsFirmwareUpdateNeededForNotification"},
|
||||
{2000, nullptr, "ActivateDigitizer"},
|
||||
};
|
||||
@@ -1631,18 +1631,6 @@ void Hid::GetNpadCommunicationMode(Kernel::HLERequestContext& ctx) {
|
||||
.GetNpadCommunicationMode());
|
||||
}
|
||||
|
||||
void Hid::SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto touchscreen_mode{rp.PopRaw<Controller_Touchscreen::TouchScreenConfigurationForNx>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}",
|
||||
touchscreen_mode.mode, applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
class HidDbg final : public ServiceFramework<HidDbg> {
|
||||
public:
|
||||
explicit HidDbg(Core::System& system_) : ServiceFramework{system_, "hid:dbg"} {
|
||||
|
||||
@@ -159,7 +159,6 @@ private:
|
||||
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
|
||||
void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
|
||||
void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
|
||||
void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx);
|
||||
|
||||
enum class VibrationDeviceType : u32 {
|
||||
Unknown = 0,
|
||||
|
||||
@@ -15,7 +15,7 @@ public:
|
||||
explicit IService(Core::System& system_) : ServiceFramework{system_, "ngct:u"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IService::Match, "Match"},
|
||||
{0, nullptr, "Match"},
|
||||
{1, &IService::Filter, "Filter"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -24,19 +24,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void Match(Kernel::HLERequestContext& ctx) {
|
||||
const auto buffer = ctx.ReadBuffer();
|
||||
const auto text = Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(buffer.data()), buffer.size());
|
||||
|
||||
LOG_WARNING(Service_NGCT, "(STUBBED) called, text={}", text);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
// Return false since we don't censor anything
|
||||
rb.Push(false);
|
||||
}
|
||||
|
||||
void Filter(Kernel::HLERequestContext& ctx) {
|
||||
const auto buffer = ctx.ReadBuffer();
|
||||
const auto text = Common::StringFromFixedZeroTerminatedBuffer(
|
||||
|
||||
@@ -42,14 +42,15 @@ void nvdisp_disp0::OnClose(DeviceFD fd) {}
|
||||
void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height,
|
||||
u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform,
|
||||
const Common::Rectangle<int>& crop_rect) {
|
||||
const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
|
||||
VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
|
||||
LOG_TRACE(Service,
|
||||
"Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
|
||||
addr, offset, width, height, stride, format);
|
||||
|
||||
const auto pixel_format = static_cast<Tegra::FramebufferConfig::PixelFormat>(format);
|
||||
const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
|
||||
stride, pixel_format, transform, crop_rect};
|
||||
using PixelFormat = Tegra::FramebufferConfig::PixelFormat;
|
||||
const Tegra::FramebufferConfig framebuffer{
|
||||
addr, offset, width, height, stride, static_cast<PixelFormat>(format),
|
||||
transform, crop_rect};
|
||||
|
||||
system.GetPerfStats().EndSystemFrame();
|
||||
system.GPU().SwapBuffers(&framebuffer);
|
||||
|
||||
@@ -9,20 +9,17 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_writable_event.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
|
||||
KernelHelpers::ServiceContext& service_context_)
|
||||
: id(id_), layer_id(layer_id_), service_context{service_context_} {
|
||||
buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
|
||||
BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_)
|
||||
: id(id_), layer_id(layer_id_), buffer_wait_event{kernel} {
|
||||
Kernel::KAutoObject::Create(std::addressof(buffer_wait_event));
|
||||
buffer_wait_event.Initialize("BufferQueue:WaitEvent");
|
||||
}
|
||||
|
||||
BufferQueue::~BufferQueue() {
|
||||
service_context.CloseEvent(buffer_wait_event);
|
||||
}
|
||||
BufferQueue::~BufferQueue() = default;
|
||||
|
||||
void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
|
||||
ASSERT(slot < buffer_slots);
|
||||
@@ -44,7 +41,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
|
||||
.multi_fence = {},
|
||||
};
|
||||
|
||||
buffer_wait_event->GetWritableEvent().Signal();
|
||||
buffer_wait_event.GetWritableEvent().Signal();
|
||||
}
|
||||
|
||||
std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
|
||||
@@ -122,7 +119,7 @@ void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& mult
|
||||
}
|
||||
free_buffers_condition.notify_one();
|
||||
|
||||
buffer_wait_event->GetWritableEvent().Signal();
|
||||
buffer_wait_event.GetWritableEvent().Signal();
|
||||
}
|
||||
|
||||
std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
|
||||
@@ -157,7 +154,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
|
||||
}
|
||||
free_buffers_condition.notify_one();
|
||||
|
||||
buffer_wait_event->GetWritableEvent().Signal();
|
||||
buffer_wait_event.GetWritableEvent().Signal();
|
||||
}
|
||||
|
||||
void BufferQueue::Connect() {
|
||||
@@ -172,7 +169,7 @@ void BufferQueue::Disconnect() {
|
||||
std::unique_lock lock{queue_sequence_mutex};
|
||||
queue_sequence.clear();
|
||||
}
|
||||
buffer_wait_event->GetWritableEvent().Signal();
|
||||
buffer_wait_event.GetWritableEvent().Signal();
|
||||
is_connect = false;
|
||||
free_buffers_condition.notify_one();
|
||||
}
|
||||
@@ -192,11 +189,11 @@ u32 BufferQueue::Query(QueryType type) {
|
||||
}
|
||||
|
||||
Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() {
|
||||
return buffer_wait_event->GetWritableEvent();
|
||||
return buffer_wait_event.GetWritableEvent();
|
||||
}
|
||||
|
||||
Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() {
|
||||
return buffer_wait_event->GetReadableEvent();
|
||||
return buffer_wait_event.GetReadableEvent();
|
||||
}
|
||||
|
||||
} // namespace Service::NVFlinger
|
||||
|
||||
@@ -24,10 +24,6 @@ class KReadableEvent;
|
||||
class KWritableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::KernelHelpers {
|
||||
class ServiceContext;
|
||||
} // namespace Service::KernelHelpers
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
constexpr u32 buffer_slots = 0x40;
|
||||
@@ -42,9 +38,7 @@ struct IGBPBuffer {
|
||||
u32_le index;
|
||||
INSERT_PADDING_WORDS(3);
|
||||
u32_le gpu_buffer_id;
|
||||
INSERT_PADDING_WORDS(6);
|
||||
u32_le external_format;
|
||||
INSERT_PADDING_WORDS(10);
|
||||
INSERT_PADDING_WORDS(17);
|
||||
u32_le nvmap_handle;
|
||||
u32_le offset;
|
||||
INSERT_PADDING_WORDS(60);
|
||||
@@ -60,8 +54,7 @@ public:
|
||||
NativeWindowFormat = 2,
|
||||
};
|
||||
|
||||
explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
|
||||
KernelHelpers::ServiceContext& service_context_);
|
||||
explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_);
|
||||
~BufferQueue();
|
||||
|
||||
enum class BufferTransformFlags : u32 {
|
||||
@@ -137,14 +130,12 @@ private:
|
||||
std::list<u32> free_buffers;
|
||||
std::array<Buffer, buffer_slots> buffers;
|
||||
std::list<u32> queue_sequence;
|
||||
Kernel::KEvent* buffer_wait_event{};
|
||||
Kernel::KEvent buffer_wait_event;
|
||||
|
||||
std::mutex free_buffers_mutex;
|
||||
std::condition_variable free_buffers_condition;
|
||||
|
||||
std::mutex queue_sequence_mutex;
|
||||
|
||||
KernelHelpers::ServiceContext& service_context;
|
||||
};
|
||||
|
||||
} // namespace Service::NVFlinger
|
||||
|
||||
@@ -147,7 +147,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
|
||||
void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
|
||||
const u32 buffer_queue_id = next_buffer_queue_id++;
|
||||
buffer_queues.emplace_back(
|
||||
std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id, service_context));
|
||||
std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id));
|
||||
display.CreateLayer(layer_id, *buffer_queues.back());
|
||||
}
|
||||
|
||||
@@ -298,7 +298,7 @@ void NVFlinger::Compose() {
|
||||
auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0");
|
||||
ASSERT(nvdisp);
|
||||
|
||||
nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.external_format,
|
||||
nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format,
|
||||
igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride,
|
||||
buffer->get().transform, buffer->get().crop_rect);
|
||||
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/error.h"
|
||||
#include "common/common_funcs.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
@@ -224,7 +223,7 @@ Errno GetAndLogLastError() {
|
||||
if (err == Errno::AGAIN) {
|
||||
return err;
|
||||
}
|
||||
LOG_ERROR(Network, "Socket operation error: {}", Common::NativeErrorToString(e));
|
||||
LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e));
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,18 +72,6 @@ static const char* TranslateGPUAccuracyLevel(Settings::GPUAccuracy backend) {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
static const char* TranslateNvdecEmulation(Settings::NvdecEmulation backend) {
|
||||
switch (backend) {
|
||||
case Settings::NvdecEmulation::Off:
|
||||
return "Off";
|
||||
case Settings::NvdecEmulation::CPU:
|
||||
return "CPU";
|
||||
case Settings::NvdecEmulation::GPU:
|
||||
return "GPU";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
u64 GetTelemetryId() {
|
||||
u64 telemetry_id{};
|
||||
const auto filename = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "telemetry_id";
|
||||
@@ -226,6 +214,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
|
||||
// Log user configuration information
|
||||
constexpr auto field_type = Telemetry::FieldType::UserConfig;
|
||||
AddField(field_type, "Audio_SinkId", Settings::values.sink_id.GetValue());
|
||||
AddField(field_type, "Audio_EnableAudioStretching",
|
||||
Settings::values.enable_audio_stretching.GetValue());
|
||||
AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
|
||||
AddField(field_type, "Renderer_Backend",
|
||||
TranslateRenderer(Settings::values.renderer_backend.GetValue()));
|
||||
@@ -239,8 +229,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
|
||||
TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue()));
|
||||
AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
|
||||
Settings::values.use_asynchronous_gpu_emulation.GetValue());
|
||||
AddField(field_type, "Renderer_NvdecEmulation",
|
||||
TranslateNvdecEmulation(Settings::values.nvdec_emulation.GetValue()));
|
||||
AddField(field_type, "Renderer_UseNvdecEmulation",
|
||||
Settings::values.use_nvdec_emulation.GetValue());
|
||||
AddField(field_type, "Renderer_AccelerateASTC", Settings::values.accelerate_astc.GetValue());
|
||||
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
|
||||
AddField(field_type, "Renderer_ShaderBackend",
|
||||
|
||||
@@ -21,10 +21,6 @@ add_library(input_common STATIC
|
||||
mouse/mouse_poller.h
|
||||
sdl/sdl.cpp
|
||||
sdl/sdl.h
|
||||
tas/tas_input.cpp
|
||||
tas/tas_input.h
|
||||
tas/tas_poller.cpp
|
||||
tas/tas_poller.h
|
||||
udp/client.cpp
|
||||
udp/client.h
|
||||
udp/protocol.cpp
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include "common/param_package.h"
|
||||
#include "common/settings.h"
|
||||
#include "input_common/analog_from_button.h"
|
||||
#include "input_common/gcadapter/gc_adapter.h"
|
||||
#include "input_common/gcadapter/gc_poller.h"
|
||||
@@ -14,8 +13,6 @@
|
||||
#include "input_common/motion_from_button.h"
|
||||
#include "input_common/mouse/mouse_input.h"
|
||||
#include "input_common/mouse/mouse_poller.h"
|
||||
#include "input_common/tas/tas_input.h"
|
||||
#include "input_common/tas/tas_poller.h"
|
||||
#include "input_common/touch_from_button.h"
|
||||
#include "input_common/udp/client.h"
|
||||
#include "input_common/udp/udp.h"
|
||||
@@ -63,12 +60,6 @@ struct InputSubsystem::Impl {
|
||||
Input::RegisterFactory<Input::MotionDevice>("mouse", mousemotion);
|
||||
mousetouch = std::make_shared<MouseTouchFactory>(mouse);
|
||||
Input::RegisterFactory<Input::TouchDevice>("mouse", mousetouch);
|
||||
|
||||
tas = std::make_shared<TasInput::Tas>();
|
||||
tasbuttons = std::make_shared<TasButtonFactory>(tas);
|
||||
Input::RegisterFactory<Input::ButtonDevice>("tas", tasbuttons);
|
||||
tasanalog = std::make_shared<TasAnalogFactory>(tas);
|
||||
Input::RegisterFactory<Input::AnalogDevice>("tas", tasanalog);
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
@@ -103,12 +94,6 @@ struct InputSubsystem::Impl {
|
||||
mouseanalog.reset();
|
||||
mousemotion.reset();
|
||||
mousetouch.reset();
|
||||
|
||||
Input::UnregisterFactory<Input::ButtonDevice>("tas");
|
||||
Input::UnregisterFactory<Input::AnalogDevice>("tas");
|
||||
|
||||
tasbuttons.reset();
|
||||
tasanalog.reset();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
|
||||
@@ -116,10 +101,6 @@ struct InputSubsystem::Impl {
|
||||
Common::ParamPackage{{"display", "Any"}, {"class", "any"}},
|
||||
Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}},
|
||||
};
|
||||
if (Settings::values.tas_enable) {
|
||||
devices.emplace_back(
|
||||
Common::ParamPackage{{"display", "TAS Controller"}, {"class", "tas"}});
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
auto sdl_devices = sdl->GetInputDevices();
|
||||
devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
|
||||
@@ -139,9 +120,6 @@ struct InputSubsystem::Impl {
|
||||
if (params.Get("class", "") == "gcpad") {
|
||||
return gcadapter->GetAnalogMappingForDevice(params);
|
||||
}
|
||||
if (params.Get("class", "") == "tas") {
|
||||
return tas->GetAnalogMappingForDevice(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (params.Get("class", "") == "sdl") {
|
||||
return sdl->GetAnalogMappingForDevice(params);
|
||||
@@ -158,9 +136,6 @@ struct InputSubsystem::Impl {
|
||||
if (params.Get("class", "") == "gcpad") {
|
||||
return gcadapter->GetButtonMappingForDevice(params);
|
||||
}
|
||||
if (params.Get("class", "") == "tas") {
|
||||
return tas->GetButtonMappingForDevice(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (params.Get("class", "") == "sdl") {
|
||||
return sdl->GetButtonMappingForDevice(params);
|
||||
@@ -199,12 +174,9 @@ struct InputSubsystem::Impl {
|
||||
std::shared_ptr<MouseAnalogFactory> mouseanalog;
|
||||
std::shared_ptr<MouseMotionFactory> mousemotion;
|
||||
std::shared_ptr<MouseTouchFactory> mousetouch;
|
||||
std::shared_ptr<TasButtonFactory> tasbuttons;
|
||||
std::shared_ptr<TasAnalogFactory> tasanalog;
|
||||
std::shared_ptr<CemuhookUDP::Client> udp;
|
||||
std::shared_ptr<GCAdapter::Adapter> gcadapter;
|
||||
std::shared_ptr<MouseInput::Mouse> mouse;
|
||||
std::shared_ptr<TasInput::Tas> tas;
|
||||
};
|
||||
|
||||
InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
|
||||
@@ -235,14 +207,6 @@ const MouseInput::Mouse* InputSubsystem::GetMouse() const {
|
||||
return impl->mouse.get();
|
||||
}
|
||||
|
||||
TasInput::Tas* InputSubsystem::GetTas() {
|
||||
return impl->tas.get();
|
||||
}
|
||||
|
||||
const TasInput::Tas* InputSubsystem::GetTas() const {
|
||||
return impl->tas.get();
|
||||
}
|
||||
|
||||
std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
|
||||
return impl->GetInputDevices();
|
||||
}
|
||||
@@ -323,22 +287,6 @@ const MouseTouchFactory* InputSubsystem::GetMouseTouch() const {
|
||||
return impl->mousetouch.get();
|
||||
}
|
||||
|
||||
TasButtonFactory* InputSubsystem::GetTasButtons() {
|
||||
return impl->tasbuttons.get();
|
||||
}
|
||||
|
||||
const TasButtonFactory* InputSubsystem::GetTasButtons() const {
|
||||
return impl->tasbuttons.get();
|
||||
}
|
||||
|
||||
TasAnalogFactory* InputSubsystem::GetTasAnalogs() {
|
||||
return impl->tasanalog.get();
|
||||
}
|
||||
|
||||
const TasAnalogFactory* InputSubsystem::GetTasAnalogs() const {
|
||||
return impl->tasanalog.get();
|
||||
}
|
||||
|
||||
void InputSubsystem::ReloadInputDevices() {
|
||||
if (!impl->udp) {
|
||||
return;
|
||||
|
||||
@@ -29,10 +29,6 @@ namespace MouseInput {
|
||||
class Mouse;
|
||||
}
|
||||
|
||||
namespace TasInput {
|
||||
class Tas;
|
||||
}
|
||||
|
||||
namespace InputCommon {
|
||||
namespace Polling {
|
||||
|
||||
@@ -68,8 +64,6 @@ class MouseButtonFactory;
|
||||
class MouseAnalogFactory;
|
||||
class MouseMotionFactory;
|
||||
class MouseTouchFactory;
|
||||
class TasButtonFactory;
|
||||
class TasAnalogFactory;
|
||||
class Keyboard;
|
||||
|
||||
/**
|
||||
@@ -109,11 +103,6 @@ public:
|
||||
/// Retrieves the underlying mouse device.
|
||||
[[nodiscard]] const MouseInput::Mouse* GetMouse() const;
|
||||
|
||||
/// Retrieves the underlying tas device.
|
||||
[[nodiscard]] TasInput::Tas* GetTas();
|
||||
|
||||
/// Retrieves the underlying tas device.
|
||||
[[nodiscard]] const TasInput::Tas* GetTas() const;
|
||||
/**
|
||||
* Returns all available input devices that this Factory can create a new device with.
|
||||
* Each returned ParamPackage should have a `display` field used for display, a class field for
|
||||
@@ -155,42 +144,30 @@ public:
|
||||
/// Retrieves the underlying udp touch handler.
|
||||
[[nodiscard]] const UDPTouchFactory* GetUDPTouch() const;
|
||||
|
||||
/// Retrieves the underlying mouse button handler.
|
||||
/// Retrieves the underlying GameCube button handler.
|
||||
[[nodiscard]] MouseButtonFactory* GetMouseButtons();
|
||||
|
||||
/// Retrieves the underlying mouse button handler.
|
||||
/// Retrieves the underlying GameCube button handler.
|
||||
[[nodiscard]] const MouseButtonFactory* GetMouseButtons() const;
|
||||
|
||||
/// Retrieves the underlying mouse analog handler.
|
||||
/// Retrieves the underlying udp touch handler.
|
||||
[[nodiscard]] MouseAnalogFactory* GetMouseAnalogs();
|
||||
|
||||
/// Retrieves the underlying mouse analog handler.
|
||||
/// Retrieves the underlying udp touch handler.
|
||||
[[nodiscard]] const MouseAnalogFactory* GetMouseAnalogs() const;
|
||||
|
||||
/// Retrieves the underlying mouse motion handler.
|
||||
/// Retrieves the underlying udp motion handler.
|
||||
[[nodiscard]] MouseMotionFactory* GetMouseMotions();
|
||||
|
||||
/// Retrieves the underlying mouse motion handler.
|
||||
/// Retrieves the underlying udp motion handler.
|
||||
[[nodiscard]] const MouseMotionFactory* GetMouseMotions() const;
|
||||
|
||||
/// Retrieves the underlying mouse touch handler.
|
||||
/// Retrieves the underlying udp touch handler.
|
||||
[[nodiscard]] MouseTouchFactory* GetMouseTouch();
|
||||
|
||||
/// Retrieves the underlying mouse touch handler.
|
||||
/// Retrieves the underlying udp touch handler.
|
||||
[[nodiscard]] const MouseTouchFactory* GetMouseTouch() const;
|
||||
|
||||
/// Retrieves the underlying tas button handler.
|
||||
[[nodiscard]] TasButtonFactory* GetTasButtons();
|
||||
|
||||
/// Retrieves the underlying tas button handler.
|
||||
[[nodiscard]] const TasButtonFactory* GetTasButtons() const;
|
||||
|
||||
/// Retrieves the underlying tas analogs handler.
|
||||
[[nodiscard]] TasAnalogFactory* GetTasAnalogs();
|
||||
|
||||
/// Retrieves the underlying tas analogs handler.
|
||||
[[nodiscard]] const TasAnalogFactory* GetTasAnalogs() const;
|
||||
|
||||
/// Reloads the input devices
|
||||
void ReloadInputDevices();
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/param_package.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_input.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "input_common/motion_input.h"
|
||||
@@ -254,11 +254,25 @@ public:
|
||||
}
|
||||
|
||||
bool IsJoyconLeft() const {
|
||||
return std::strstr(GetControllerName().c_str(), "Joy-Con Left") != nullptr;
|
||||
const std::string controller_name = GetControllerName();
|
||||
if (std::strstr(controller_name.c_str(), "Joy-Con Left") != nullptr) {
|
||||
return true;
|
||||
}
|
||||
if (std::strstr(controller_name.c_str(), "Joy-Con (L)") != nullptr) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsJoyconRight() const {
|
||||
return std::strstr(GetControllerName().c_str(), "Joy-Con Right") != nullptr;
|
||||
const std::string controller_name = GetControllerName();
|
||||
if (std::strstr(controller_name.c_str(), "Joy-Con Right") != nullptr) {
|
||||
return true;
|
||||
}
|
||||
if (std::strstr(controller_name.c_str(), "Joy-Con (R)") != nullptr) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string GetControllerName() const {
|
||||
@@ -889,10 +903,8 @@ SDLState::SDLState() {
|
||||
RegisterFactory<VibrationDevice>("sdl", vibration_factory);
|
||||
RegisterFactory<MotionDevice>("sdl", motion_factory);
|
||||
|
||||
if (!Settings::values.enable_raw_input) {
|
||||
// Disable raw input. When enabled this setting causes SDL to die when a web applet opens
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");
|
||||
}
|
||||
// Disable raw input. When enabled this setting causes SDL to die when a web applet opens
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");
|
||||
|
||||
// Enable HIDAPI rumble. This prevents SDL from disabling motion on PS4 and PS5 controllers
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
|
||||
@@ -900,10 +912,10 @@ SDLState::SDLState() {
|
||||
|
||||
// Tell SDL2 to use the hidapi driver. This will allow joycons to be detected as a
|
||||
// GameController and not a generic one
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
|
||||
SDL_SetHint("SDL_JOYSTICK_HIDAPI_JOY_CONS", "1");
|
||||
|
||||
// Turn off Pro controller home led
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0");
|
||||
SDL_SetHint("SDL_JOYSTICK_HIDAPI_SWITCH_HOME_LED", "0");
|
||||
|
||||
// If the frontend is going to manage the event loop, then we don't start one here
|
||||
start_thread = SDL_WasInit(SDL_INIT_JOYSTICK) == 0;
|
||||
|
||||
@@ -1,455 +0,0 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <regex>
|
||||
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/fs_types.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "input_common/tas/tas_input.h"
|
||||
|
||||
namespace TasInput {
|
||||
|
||||
// Supported keywords and buttons from a TAS file
|
||||
constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_button = {
|
||||
std::pair{"KEY_A", TasButton::BUTTON_A},
|
||||
{"KEY_B", TasButton::BUTTON_B},
|
||||
{"KEY_X", TasButton::BUTTON_X},
|
||||
{"KEY_Y", TasButton::BUTTON_Y},
|
||||
{"KEY_LSTICK", TasButton::STICK_L},
|
||||
{"KEY_RSTICK", TasButton::STICK_R},
|
||||
{"KEY_L", TasButton::TRIGGER_L},
|
||||
{"KEY_R", TasButton::TRIGGER_R},
|
||||
{"KEY_PLUS", TasButton::BUTTON_PLUS},
|
||||
{"KEY_MINUS", TasButton::BUTTON_MINUS},
|
||||
{"KEY_DLEFT", TasButton::BUTTON_LEFT},
|
||||
{"KEY_DUP", TasButton::BUTTON_UP},
|
||||
{"KEY_DRIGHT", TasButton::BUTTON_RIGHT},
|
||||
{"KEY_DDOWN", TasButton::BUTTON_DOWN},
|
||||
{"KEY_SL", TasButton::BUTTON_SL},
|
||||
{"KEY_SR", TasButton::BUTTON_SR},
|
||||
{"KEY_CAPTURE", TasButton::BUTTON_CAPTURE},
|
||||
{"KEY_HOME", TasButton::BUTTON_HOME},
|
||||
{"KEY_ZL", TasButton::TRIGGER_ZL},
|
||||
{"KEY_ZR", TasButton::TRIGGER_ZR},
|
||||
};
|
||||
|
||||
Tas::Tas() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
needs_reset = true;
|
||||
return;
|
||||
}
|
||||
LoadTasFiles();
|
||||
}
|
||||
|
||||
Tas::~Tas() {
|
||||
Stop();
|
||||
};
|
||||
|
||||
void Tas::LoadTasFiles() {
|
||||
script_length = 0;
|
||||
for (size_t i = 0; i < commands.size(); i++) {
|
||||
LoadTasFile(i);
|
||||
if (commands[i].size() > script_length) {
|
||||
script_length = commands[i].size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tas::LoadTasFile(size_t player_index) {
|
||||
if (!commands[player_index].empty()) {
|
||||
commands[player_index].clear();
|
||||
}
|
||||
std::string file =
|
||||
Common::FS::ReadStringFromFile(Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) /
|
||||
fmt::format("script0-{}.txt", player_index + 1),
|
||||
Common::FS::FileType::BinaryFile);
|
||||
std::stringstream command_line(file);
|
||||
std::string line;
|
||||
int frame_no = 0;
|
||||
while (std::getline(command_line, line, '\n')) {
|
||||
if (line.empty()) {
|
||||
continue;
|
||||
}
|
||||
LOG_DEBUG(Input, "Loading line: {}", line);
|
||||
std::smatch m;
|
||||
|
||||
std::stringstream linestream(line);
|
||||
std::string segment;
|
||||
std::vector<std::string> seglist;
|
||||
|
||||
while (std::getline(linestream, segment, ' ')) {
|
||||
seglist.push_back(segment);
|
||||
}
|
||||
|
||||
if (seglist.size() < 4) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (frame_no < std::stoi(seglist.at(0))) {
|
||||
commands[player_index].push_back({});
|
||||
frame_no++;
|
||||
}
|
||||
|
||||
TASCommand command = {
|
||||
.buttons = ReadCommandButtons(seglist.at(1)),
|
||||
.l_axis = ReadCommandAxis(seglist.at(2)),
|
||||
.r_axis = ReadCommandAxis(seglist.at(3)),
|
||||
};
|
||||
commands[player_index].push_back(command);
|
||||
frame_no++;
|
||||
}
|
||||
LOG_INFO(Input, "TAS file loaded! {} frames", frame_no);
|
||||
}
|
||||
|
||||
void Tas::WriteTasFile(std::u8string file_name) {
|
||||
std::string output_text;
|
||||
for (size_t frame = 0; frame < record_commands.size(); frame++) {
|
||||
if (!output_text.empty()) {
|
||||
output_text += "\n";
|
||||
}
|
||||
const TASCommand& line = record_commands[frame];
|
||||
output_text += std::to_string(frame) + " " + WriteCommandButtons(line.buttons) + " " +
|
||||
WriteCommandAxis(line.l_axis) + " " + WriteCommandAxis(line.r_axis);
|
||||
}
|
||||
const auto bytes_written = Common::FS::WriteStringToFile(
|
||||
Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / file_name,
|
||||
Common::FS::FileType::TextFile, output_text);
|
||||
if (bytes_written == output_text.size()) {
|
||||
LOG_INFO(Input, "TAS file written to file!");
|
||||
} else {
|
||||
LOG_ERROR(Input, "Writing the TAS-file has failed! {} / {} bytes written", bytes_written,
|
||||
output_text.size());
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<float, float> Tas::FlipAxisY(std::pair<float, float> old) {
|
||||
auto [x, y] = old;
|
||||
return {x, -y};
|
||||
}
|
||||
|
||||
void Tas::RecordInput(u32 buttons, const std::array<std::pair<float, float>, 2>& axes) {
|
||||
last_input = {buttons, FlipAxisY(axes[0]), FlipAxisY(axes[1])};
|
||||
}
|
||||
|
||||
std::tuple<TasState, size_t, size_t> Tas::GetStatus() const {
|
||||
TasState state;
|
||||
if (is_recording) {
|
||||
return {TasState::Recording, 0, record_commands.size()};
|
||||
}
|
||||
|
||||
if (is_running) {
|
||||
state = TasState::Running;
|
||||
} else {
|
||||
state = TasState::Stopped;
|
||||
}
|
||||
|
||||
return {state, current_command, script_length};
|
||||
}
|
||||
|
||||
std::string Tas::DebugButtons(u32 buttons) const {
|
||||
return fmt::format("{{ {} }}", TasInput::Tas::ButtonsToString(buttons));
|
||||
}
|
||||
|
||||
std::string Tas::DebugJoystick(float x, float y) const {
|
||||
return fmt::format("[ {} , {} ]", std::to_string(x), std::to_string(y));
|
||||
}
|
||||
|
||||
std::string Tas::DebugInput(const TasData& data) const {
|
||||
return fmt::format("{{ {} , {} , {} }}", DebugButtons(data.buttons),
|
||||
DebugJoystick(data.axis[0], data.axis[1]),
|
||||
DebugJoystick(data.axis[2], data.axis[3]));
|
||||
}
|
||||
|
||||
std::string Tas::DebugInputs(const std::array<TasData, PLAYER_NUMBER>& arr) const {
|
||||
std::string returns = "[ ";
|
||||
for (size_t i = 0; i < arr.size(); i++) {
|
||||
returns += DebugInput(arr[i]);
|
||||
if (i != arr.size() - 1) {
|
||||
returns += " , ";
|
||||
}
|
||||
}
|
||||
return returns + "]";
|
||||
}
|
||||
|
||||
std::string Tas::ButtonsToString(u32 button) const {
|
||||
std::string returns;
|
||||
for (auto [text_button, tas_button] : text_to_tas_button) {
|
||||
if ((button & static_cast<u32>(tas_button)) != 0)
|
||||
returns += fmt::format(", {}", text_button.substr(4));
|
||||
}
|
||||
return returns.empty() ? "" : returns.substr(2);
|
||||
}
|
||||
|
||||
void Tas::UpdateThread() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
if (is_running) {
|
||||
Stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_recording) {
|
||||
record_commands.push_back(last_input);
|
||||
}
|
||||
if (needs_reset) {
|
||||
current_command = 0;
|
||||
needs_reset = false;
|
||||
LoadTasFiles();
|
||||
LOG_DEBUG(Input, "tas_reset done");
|
||||
}
|
||||
|
||||
if (!is_running) {
|
||||
tas_data.fill({});
|
||||
return;
|
||||
}
|
||||
if (current_command < script_length) {
|
||||
LOG_DEBUG(Input, "Playing TAS {}/{}", current_command, script_length);
|
||||
size_t frame = current_command++;
|
||||
for (size_t i = 0; i < commands.size(); i++) {
|
||||
if (frame < commands[i].size()) {
|
||||
TASCommand command = commands[i][frame];
|
||||
tas_data[i].buttons = command.buttons;
|
||||
auto [l_axis_x, l_axis_y] = command.l_axis;
|
||||
tas_data[i].axis[0] = l_axis_x;
|
||||
tas_data[i].axis[1] = l_axis_y;
|
||||
auto [r_axis_x, r_axis_y] = command.r_axis;
|
||||
tas_data[i].axis[2] = r_axis_x;
|
||||
tas_data[i].axis[3] = r_axis_y;
|
||||
} else {
|
||||
tas_data[i] = {};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
is_running = Settings::values.tas_loop.GetValue();
|
||||
current_command = 0;
|
||||
tas_data.fill({});
|
||||
if (!is_running) {
|
||||
SwapToStoredController();
|
||||
}
|
||||
}
|
||||
LOG_DEBUG(Input, "TAS inputs: {}", DebugInputs(tas_data));
|
||||
}
|
||||
|
||||
TasAnalog Tas::ReadCommandAxis(const std::string& line) const {
|
||||
std::stringstream linestream(line);
|
||||
std::string segment;
|
||||
std::vector<std::string> seglist;
|
||||
|
||||
while (std::getline(linestream, segment, ';')) {
|
||||
seglist.push_back(segment);
|
||||
}
|
||||
|
||||
const float x = std::stof(seglist.at(0)) / 32767.0f;
|
||||
const float y = std::stof(seglist.at(1)) / 32767.0f;
|
||||
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
u32 Tas::ReadCommandButtons(const std::string& data) const {
|
||||
std::stringstream button_text(data);
|
||||
std::string line;
|
||||
u32 buttons = 0;
|
||||
while (std::getline(button_text, line, ';')) {
|
||||
for (auto [text, tas_button] : text_to_tas_button) {
|
||||
if (text == line) {
|
||||
buttons |= static_cast<u32>(tas_button);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return buttons;
|
||||
}
|
||||
|
||||
std::string Tas::WriteCommandAxis(TasAnalog data) const {
|
||||
auto [x, y] = data;
|
||||
std::string line;
|
||||
line += std::to_string(static_cast<int>(x * 32767));
|
||||
line += ";";
|
||||
line += std::to_string(static_cast<int>(y * 32767));
|
||||
return line;
|
||||
}
|
||||
|
||||
std::string Tas::WriteCommandButtons(u32 data) const {
|
||||
if (data == 0) {
|
||||
return "NONE";
|
||||
}
|
||||
|
||||
std::string line;
|
||||
u32 index = 0;
|
||||
while (data > 0) {
|
||||
if ((data & 1) == 1) {
|
||||
for (auto [text, tas_button] : text_to_tas_button) {
|
||||
if (tas_button == static_cast<TasButton>(1 << index)) {
|
||||
if (line.size() > 0) {
|
||||
line += ";";
|
||||
}
|
||||
line += text;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
index++;
|
||||
data >>= 1;
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
void Tas::StartStop() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
return;
|
||||
}
|
||||
if (is_running) {
|
||||
Stop();
|
||||
} else {
|
||||
is_running = true;
|
||||
SwapToTasController();
|
||||
}
|
||||
}
|
||||
|
||||
void Tas::Stop() {
|
||||
is_running = false;
|
||||
SwapToStoredController();
|
||||
}
|
||||
|
||||
void Tas::SwapToTasController() {
|
||||
if (!Settings::values.tas_swap_controllers) {
|
||||
return;
|
||||
}
|
||||
auto& players = Settings::values.players.GetValue();
|
||||
for (std::size_t index = 0; index < players.size(); index++) {
|
||||
auto& player = players[index];
|
||||
player_mappings[index] = player;
|
||||
|
||||
// Only swap active controllers
|
||||
if (!player.connected) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Common::ParamPackage tas_param;
|
||||
tas_param.Set("pad", static_cast<u8>(index));
|
||||
auto button_mapping = GetButtonMappingForDevice(tas_param);
|
||||
auto analog_mapping = GetAnalogMappingForDevice(tas_param);
|
||||
auto& buttons = player.buttons;
|
||||
auto& analogs = player.analogs;
|
||||
|
||||
for (std::size_t i = 0; i < buttons.size(); ++i) {
|
||||
buttons[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)].Serialize();
|
||||
}
|
||||
for (std::size_t i = 0; i < analogs.size(); ++i) {
|
||||
analogs[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)].Serialize();
|
||||
}
|
||||
}
|
||||
is_old_input_saved = true;
|
||||
Settings::values.is_device_reload_pending.store(true);
|
||||
}
|
||||
|
||||
void Tas::SwapToStoredController() {
|
||||
if (!is_old_input_saved) {
|
||||
return;
|
||||
}
|
||||
auto& players = Settings::values.players.GetValue();
|
||||
for (std::size_t index = 0; index < players.size(); index++) {
|
||||
players[index] = player_mappings[index];
|
||||
}
|
||||
is_old_input_saved = false;
|
||||
Settings::values.is_device_reload_pending.store(true);
|
||||
}
|
||||
|
||||
void Tas::Reset() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
return;
|
||||
}
|
||||
needs_reset = true;
|
||||
}
|
||||
|
||||
bool Tas::Record() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
return true;
|
||||
}
|
||||
is_recording = !is_recording;
|
||||
return is_recording;
|
||||
}
|
||||
|
||||
void Tas::SaveRecording(bool overwrite_file) {
|
||||
if (is_recording) {
|
||||
return;
|
||||
}
|
||||
if (record_commands.empty()) {
|
||||
return;
|
||||
}
|
||||
WriteTasFile(u8"record.txt");
|
||||
if (overwrite_file) {
|
||||
WriteTasFile(u8"script0-1.txt");
|
||||
}
|
||||
needs_reset = true;
|
||||
record_commands.clear();
|
||||
}
|
||||
|
||||
InputCommon::ButtonMapping Tas::GetButtonMappingForDevice(
|
||||
const Common::ParamPackage& params) const {
|
||||
// This list is missing ZL/ZR since those are not considered buttons.
|
||||
// We will add those afterwards
|
||||
// This list also excludes any button that can't be really mapped
|
||||
static constexpr std::array<std::pair<Settings::NativeButton::Values, TasButton>, 20>
|
||||
switch_to_tas_button = {
|
||||
std::pair{Settings::NativeButton::A, TasButton::BUTTON_A},
|
||||
{Settings::NativeButton::B, TasButton::BUTTON_B},
|
||||
{Settings::NativeButton::X, TasButton::BUTTON_X},
|
||||
{Settings::NativeButton::Y, TasButton::BUTTON_Y},
|
||||
{Settings::NativeButton::LStick, TasButton::STICK_L},
|
||||
{Settings::NativeButton::RStick, TasButton::STICK_R},
|
||||
{Settings::NativeButton::L, TasButton::TRIGGER_L},
|
||||
{Settings::NativeButton::R, TasButton::TRIGGER_R},
|
||||
{Settings::NativeButton::Plus, TasButton::BUTTON_PLUS},
|
||||
{Settings::NativeButton::Minus, TasButton::BUTTON_MINUS},
|
||||
{Settings::NativeButton::DLeft, TasButton::BUTTON_LEFT},
|
||||
{Settings::NativeButton::DUp, TasButton::BUTTON_UP},
|
||||
{Settings::NativeButton::DRight, TasButton::BUTTON_RIGHT},
|
||||
{Settings::NativeButton::DDown, TasButton::BUTTON_DOWN},
|
||||
{Settings::NativeButton::SL, TasButton::BUTTON_SL},
|
||||
{Settings::NativeButton::SR, TasButton::BUTTON_SR},
|
||||
{Settings::NativeButton::Screenshot, TasButton::BUTTON_CAPTURE},
|
||||
{Settings::NativeButton::Home, TasButton::BUTTON_HOME},
|
||||
{Settings::NativeButton::ZL, TasButton::TRIGGER_ZL},
|
||||
{Settings::NativeButton::ZR, TasButton::TRIGGER_ZR},
|
||||
};
|
||||
|
||||
InputCommon::ButtonMapping mapping{};
|
||||
for (const auto& [switch_button, tas_button] : switch_to_tas_button) {
|
||||
Common::ParamPackage button_params({{"engine", "tas"}});
|
||||
button_params.Set("pad", params.Get("pad", 0));
|
||||
button_params.Set("button", static_cast<int>(tas_button));
|
||||
mapping.insert_or_assign(switch_button, std::move(button_params));
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
InputCommon::AnalogMapping Tas::GetAnalogMappingForDevice(
|
||||
const Common::ParamPackage& params) const {
|
||||
|
||||
InputCommon::AnalogMapping mapping = {};
|
||||
Common::ParamPackage left_analog_params;
|
||||
left_analog_params.Set("engine", "tas");
|
||||
left_analog_params.Set("pad", params.Get("pad", 0));
|
||||
left_analog_params.Set("axis_x", static_cast<int>(TasAxes::StickX));
|
||||
left_analog_params.Set("axis_y", static_cast<int>(TasAxes::StickY));
|
||||
mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
|
||||
Common::ParamPackage right_analog_params;
|
||||
right_analog_params.Set("engine", "tas");
|
||||
right_analog_params.Set("pad", params.Get("pad", 0));
|
||||
right_analog_params.Set("axis_x", static_cast<int>(TasAxes::SubstickX));
|
||||
right_analog_params.Set("axis_y", static_cast<int>(TasAxes::SubstickY));
|
||||
mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
|
||||
return mapping;
|
||||
}
|
||||
|
||||
const TasData& Tas::GetTasState(std::size_t pad) const {
|
||||
return tas_data[pad];
|
||||
}
|
||||
} // namespace TasInput
|
||||
@@ -1,237 +0,0 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings_input.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "input_common/main.h"
|
||||
|
||||
/*
|
||||
To play back TAS scripts on Yuzu, select the folder with scripts in the configuration menu below
|
||||
Tools -> Configure TAS. The file itself has normal text format and has to be called script0-1.txt
|
||||
for controller 1, script0-2.txt for controller 2 and so forth (with max. 8 players).
|
||||
|
||||
A script file has the same format as TAS-nx uses, so final files will look like this:
|
||||
|
||||
1 KEY_B 0;0 0;0
|
||||
6 KEY_ZL 0;0 0;0
|
||||
41 KEY_ZL;KEY_Y 0;0 0;0
|
||||
43 KEY_X;KEY_A 32767;0 0;0
|
||||
44 KEY_A 32767;0 0;0
|
||||
45 KEY_A 32767;0 0;0
|
||||
46 KEY_A 32767;0 0;0
|
||||
47 KEY_A 32767;0 0;0
|
||||
|
||||
After placing the file at the correct location, it can be read into Yuzu with the (default) hotkey
|
||||
CTRL+F6 (refresh). In the bottom left corner, it will display the amount of frames the script file
|
||||
has. Playback can be started or stopped using CTRL+F5.
|
||||
|
||||
However, for playback to actually work, the correct input device has to be selected: In the Controls
|
||||
menu, select TAS from the device list for the controller that the script should be played on.
|
||||
|
||||
Recording a new script file is really simple: Just make sure that the proper device (not TAS) is
|
||||
connected on P1, and press CTRL+F7 to start recording. When done, just press the same keystroke
|
||||
again (CTRL+F7). The new script will be saved at the location previously selected, as the filename
|
||||
record.txt.
|
||||
|
||||
For debugging purposes, the common controller debugger can be used (View -> Debugging -> Controller
|
||||
P1).
|
||||
*/
|
||||
|
||||
namespace TasInput {
|
||||
|
||||
constexpr size_t PLAYER_NUMBER = 8;
|
||||
|
||||
using TasAnalog = std::pair<float, float>;
|
||||
|
||||
enum class TasState {
|
||||
Running,
|
||||
Recording,
|
||||
Stopped,
|
||||
};
|
||||
|
||||
enum class TasButton : u32 {
|
||||
BUTTON_A = 1U << 0,
|
||||
BUTTON_B = 1U << 1,
|
||||
BUTTON_X = 1U << 2,
|
||||
BUTTON_Y = 1U << 3,
|
||||
STICK_L = 1U << 4,
|
||||
STICK_R = 1U << 5,
|
||||
TRIGGER_L = 1U << 6,
|
||||
TRIGGER_R = 1U << 7,
|
||||
TRIGGER_ZL = 1U << 8,
|
||||
TRIGGER_ZR = 1U << 9,
|
||||
BUTTON_PLUS = 1U << 10,
|
||||
BUTTON_MINUS = 1U << 11,
|
||||
BUTTON_LEFT = 1U << 12,
|
||||
BUTTON_UP = 1U << 13,
|
||||
BUTTON_RIGHT = 1U << 14,
|
||||
BUTTON_DOWN = 1U << 15,
|
||||
BUTTON_SL = 1U << 16,
|
||||
BUTTON_SR = 1U << 17,
|
||||
BUTTON_HOME = 1U << 18,
|
||||
BUTTON_CAPTURE = 1U << 19,
|
||||
};
|
||||
|
||||
enum class TasAxes : u8 {
|
||||
StickX,
|
||||
StickY,
|
||||
SubstickX,
|
||||
SubstickY,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
struct TasData {
|
||||
u32 buttons{};
|
||||
std::array<float, 4> axis{};
|
||||
};
|
||||
|
||||
class Tas {
|
||||
public:
|
||||
Tas();
|
||||
~Tas();
|
||||
|
||||
// Changes the input status that will be stored in each frame
|
||||
void RecordInput(u32 buttons, const std::array<std::pair<float, float>, 2>& axes);
|
||||
|
||||
// Main loop that records or executes input
|
||||
void UpdateThread();
|
||||
|
||||
// Sets the flag to start or stop the TAS command excecution and swaps controllers profiles
|
||||
void StartStop();
|
||||
|
||||
// Stop the TAS and reverts any controller profile
|
||||
void Stop();
|
||||
|
||||
// Sets the flag to reload the file and start from the begining in the next update
|
||||
void Reset();
|
||||
|
||||
/**
|
||||
* Sets the flag to enable or disable recording of inputs
|
||||
* @return Returns true if the current recording status is enabled
|
||||
*/
|
||||
bool Record();
|
||||
|
||||
// Saves contents of record_commands on a file if overwrite is enabled player 1 will be
|
||||
// overwritten with the recorded commands
|
||||
void SaveRecording(bool overwrite_file);
|
||||
|
||||
/**
|
||||
* Returns the current status values of TAS playback/recording
|
||||
* @return Tuple of
|
||||
* TasState indicating the current state out of Running, Recording or Stopped ;
|
||||
* Current playback progress or amount of frames (so far) for Recording ;
|
||||
* Total length of script file currently loaded or amount of frames (so far) for Recording
|
||||
*/
|
||||
std::tuple<TasState, size_t, size_t> GetStatus() const;
|
||||
|
||||
// Retuns an array of the default button mappings
|
||||
InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const;
|
||||
|
||||
// Retuns an array of the default analog mappings
|
||||
InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;
|
||||
[[nodiscard]] const TasData& GetTasState(std::size_t pad) const;
|
||||
|
||||
private:
|
||||
struct TASCommand {
|
||||
u32 buttons{};
|
||||
TasAnalog l_axis{};
|
||||
TasAnalog r_axis{};
|
||||
};
|
||||
|
||||
// Loads TAS files from all players
|
||||
void LoadTasFiles();
|
||||
|
||||
// Loads TAS file from the specified player
|
||||
void LoadTasFile(size_t player_index);
|
||||
|
||||
// Writes a TAS file from the recorded commands
|
||||
void WriteTasFile(std::u8string file_name);
|
||||
|
||||
/**
|
||||
* Parses a string containing the axis values with the following format "x;y"
|
||||
* X and Y have a range from -32767 to 32767
|
||||
* @return Returns a TAS analog object with axis values with range from -1.0 to 1.0
|
||||
*/
|
||||
TasAnalog ReadCommandAxis(const std::string& line) const;
|
||||
|
||||
/**
|
||||
* Parses a string containing the button values with the following format "a;b;c;d..."
|
||||
* Each button is represented by it's text format specified in text_to_tas_button array
|
||||
* @return Returns a u32 with each bit representing the status of a button
|
||||
*/
|
||||
u32 ReadCommandButtons(const std::string& line) const;
|
||||
|
||||
/**
|
||||
* Converts an u32 containing the button status into the text equivalent
|
||||
* @return Returns a string with the name of the buttons to be written to the file
|
||||
*/
|
||||
std::string WriteCommandButtons(u32 data) const;
|
||||
|
||||
/**
|
||||
* Converts an TAS analog object containing the axis status into the text equivalent
|
||||
* @return Returns a string with the value of the axis to be written to the file
|
||||
*/
|
||||
std::string WriteCommandAxis(TasAnalog data) const;
|
||||
|
||||
// Inverts the Y axis polarity
|
||||
std::pair<float, float> FlipAxisY(std::pair<float, float> old);
|
||||
|
||||
/**
|
||||
* Converts an u32 containing the button status into the text equivalent
|
||||
* @return Returns a string with the name of the buttons to be printed on console
|
||||
*/
|
||||
std::string DebugButtons(u32 buttons) const;
|
||||
|
||||
/**
|
||||
* Converts an TAS analog object containing the axis status into the text equivalent
|
||||
* @return Returns a string with the value of the axis to be printed on console
|
||||
*/
|
||||
std::string DebugJoystick(float x, float y) const;
|
||||
|
||||
/**
|
||||
* Converts the given TAS status into the text equivalent
|
||||
* @return Returns a string with the value of the TAS status to be printed on console
|
||||
*/
|
||||
std::string DebugInput(const TasData& data) const;
|
||||
|
||||
/**
|
||||
* Converts the given TAS status of multiple players into the text equivalent
|
||||
* @return Returns a string with the value of the status of all TAS players to be printed on
|
||||
* console
|
||||
*/
|
||||
std::string DebugInputs(const std::array<TasData, PLAYER_NUMBER>& arr) const;
|
||||
|
||||
/**
|
||||
* Converts an u32 containing the button status into the text equivalent
|
||||
* @return Returns a string with the name of the buttons
|
||||
*/
|
||||
std::string ButtonsToString(u32 button) const;
|
||||
|
||||
// Stores current controller configuration and sets a TAS controller for every active controller
|
||||
// to the current config
|
||||
void SwapToTasController();
|
||||
|
||||
// Sets the stored controller configuration to the current config
|
||||
void SwapToStoredController();
|
||||
|
||||
size_t script_length{0};
|
||||
std::array<TasData, PLAYER_NUMBER> tas_data;
|
||||
bool is_old_input_saved{false};
|
||||
bool is_recording{false};
|
||||
bool is_running{false};
|
||||
bool needs_reset{false};
|
||||
std::array<std::vector<TASCommand>, PLAYER_NUMBER> commands{};
|
||||
std::vector<TASCommand> record_commands{};
|
||||
size_t current_command{0};
|
||||
TASCommand last_input{}; // only used for recording
|
||||
|
||||
// Old settings for swapping controllers
|
||||
std::array<Settings::PlayerInput, 10> player_mappings;
|
||||
};
|
||||
} // namespace TasInput
|
||||
@@ -1,101 +0,0 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "input_common/tas/tas_input.h"
|
||||
#include "input_common/tas/tas_poller.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
class TasButton final : public Input::ButtonDevice {
|
||||
public:
|
||||
explicit TasButton(u32 button_, u32 pad_, const TasInput::Tas* tas_input_)
|
||||
: button(button_), pad(pad_), tas_input(tas_input_) {}
|
||||
|
||||
bool GetStatus() const override {
|
||||
return (tas_input->GetTasState(pad).buttons & button) != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
const u32 button;
|
||||
const u32 pad;
|
||||
const TasInput::Tas* tas_input;
|
||||
};
|
||||
|
||||
TasButtonFactory::TasButtonFactory(std::shared_ptr<TasInput::Tas> tas_input_)
|
||||
: tas_input(std::move(tas_input_)) {}
|
||||
|
||||
std::unique_ptr<Input::ButtonDevice> TasButtonFactory::Create(const Common::ParamPackage& params) {
|
||||
const auto button_id = params.Get("button", 0);
|
||||
const auto pad = params.Get("pad", 0);
|
||||
|
||||
return std::make_unique<TasButton>(button_id, pad, tas_input.get());
|
||||
}
|
||||
|
||||
class TasAnalog final : public Input::AnalogDevice {
|
||||
public:
|
||||
explicit TasAnalog(u32 pad_, u32 axis_x_, u32 axis_y_, const TasInput::Tas* tas_input_)
|
||||
: pad(pad_), axis_x(axis_x_), axis_y(axis_y_), tas_input(tas_input_) {}
|
||||
|
||||
float GetAxis(u32 axis) const {
|
||||
std::lock_guard lock{mutex};
|
||||
return tas_input->GetTasState(pad).axis.at(axis);
|
||||
}
|
||||
|
||||
std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {
|
||||
float x = GetAxis(analog_axis_x);
|
||||
float y = GetAxis(analog_axis_y);
|
||||
|
||||
// Make sure the coordinates are in the unit circle,
|
||||
// otherwise normalize it.
|
||||
float r = x * x + y * y;
|
||||
if (r > 1.0f) {
|
||||
r = std::sqrt(r);
|
||||
x /= r;
|
||||
y /= r;
|
||||
}
|
||||
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
std::tuple<float, float> GetStatus() const override {
|
||||
return GetAnalog(axis_x, axis_y);
|
||||
}
|
||||
|
||||
Input::AnalogProperties GetAnalogProperties() const override {
|
||||
return {0.0f, 1.0f, 0.5f};
|
||||
}
|
||||
|
||||
private:
|
||||
const u32 pad;
|
||||
const u32 axis_x;
|
||||
const u32 axis_y;
|
||||
const TasInput::Tas* tas_input;
|
||||
mutable std::mutex mutex;
|
||||
};
|
||||
|
||||
/// An analog device factory that creates analog devices from GC Adapter
|
||||
TasAnalogFactory::TasAnalogFactory(std::shared_ptr<TasInput::Tas> tas_input_)
|
||||
: tas_input(std::move(tas_input_)) {}
|
||||
|
||||
/**
|
||||
* Creates analog device from joystick axes
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "port": the nth gcpad on the adapter
|
||||
* - "axis_x": the index of the axis to be bind as x-axis
|
||||
* - "axis_y": the index of the axis to be bind as y-axis
|
||||
*/
|
||||
std::unique_ptr<Input::AnalogDevice> TasAnalogFactory::Create(const Common::ParamPackage& params) {
|
||||
const auto pad = static_cast<u32>(params.Get("pad", 0));
|
||||
const auto axis_x = static_cast<u32>(params.Get("axis_x", 0));
|
||||
const auto axis_y = static_cast<u32>(params.Get("axis_y", 1));
|
||||
|
||||
return std::make_unique<TasAnalog>(pad, axis_x, axis_y, tas_input.get());
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "core/frontend/input.h"
|
||||
#include "input_common/tas/tas_input.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
/**
|
||||
* A button device factory representing a tas bot. It receives tas events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class TasButtonFactory final : public Input::Factory<Input::ButtonDevice> {
|
||||
public:
|
||||
explicit TasButtonFactory(std::shared_ptr<TasInput::Tas> tas_input_);
|
||||
|
||||
/**
|
||||
* Creates a button device from a button press
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "code": the code of the key to bind with the button
|
||||
*/
|
||||
std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<TasInput::Tas> tas_input;
|
||||
};
|
||||
|
||||
/// An analog device factory that creates analog devices from tas
|
||||
class TasAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
|
||||
public:
|
||||
explicit TasAnalogFactory(std::shared_ptr<TasInput::Tas> tas_input_);
|
||||
|
||||
std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<TasInput::Tas> tas_input;
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
namespace InputCommon::CemuhookUDP {
|
||||
|
||||
constexpr char DEFAULT_SRV[] = "127.0.0.1:26760";
|
||||
|
||||
class Socket;
|
||||
|
||||
namespace Response {
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr char THREAD_ID[]{"gl_SubGroupInvocationARB"};
|
||||
|
||||
void SetInBoundsFlag(EmitContext& ctx, IR::Inst& inst) {
|
||||
IR::Inst* const in_bounds{inst.GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)};
|
||||
if (!in_bounds) {
|
||||
@@ -45,100 +43,84 @@ void UseShuffleNv(EmitContext& ctx, IR::Inst& inst, std::string_view shfl_op,
|
||||
ctx.AddU32("{}={}({},{},{},shfl_in_bounds);", inst, shfl_op, value, index, width);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
}
|
||||
|
||||
std::string_view BallotIndex(EmitContext& ctx) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ".x";
|
||||
}
|
||||
return "[gl_SubGroupInvocationARB>>5]";
|
||||
}
|
||||
|
||||
std::string GetMask(EmitContext& ctx, std::string_view mask) {
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
return fmt::format("uint(uvec2({}){})", mask, ballot_index);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitLaneId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={}&31u;", inst, THREAD_ID);
|
||||
ctx.AddU32("{}=gl_SubGroupInvocationARB&31u;", inst);
|
||||
}
|
||||
|
||||
void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred);
|
||||
return;
|
||||
} else {
|
||||
const auto active_mask{fmt::format("uvec2(ballotARB(true))[gl_SubGroupInvocationARB]")};
|
||||
const auto ballot{fmt::format("uvec2(ballotARB({}))[gl_SubGroupInvocationARB]", pred)};
|
||||
ctx.AddU1("{}=({}&{})=={};", inst, ballot, active_mask, active_mask);
|
||||
}
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
|
||||
const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
|
||||
ctx.AddU1("{}=({}&{})=={};", inst, ballot, active_mask, active_mask);
|
||||
}
|
||||
|
||||
void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
ctx.AddU1("{}=anyInvocationARB({});", inst, pred);
|
||||
return;
|
||||
} else {
|
||||
const auto active_mask{fmt::format("uvec2(ballotARB(true))[gl_SubGroupInvocationARB]")};
|
||||
const auto ballot{fmt::format("uvec2(ballotARB({}))[gl_SubGroupInvocationARB]", pred)};
|
||||
ctx.AddU1("{}=({}&{})!=0u;", inst, ballot, active_mask, active_mask);
|
||||
}
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
|
||||
const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
|
||||
ctx.AddU1("{}=({}&{})!=0u;", inst, ballot, active_mask, active_mask);
|
||||
}
|
||||
|
||||
void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred);
|
||||
return;
|
||||
} else {
|
||||
const auto active_mask{fmt::format("uvec2(ballotARB(true))[gl_SubGroupInvocationARB]")};
|
||||
const auto ballot{fmt::format("uvec2(ballotARB({}))[gl_SubGroupInvocationARB]", pred)};
|
||||
const auto value{fmt::format("({}^{})", ballot, active_mask)};
|
||||
ctx.AddU1("{}=({}==0)||({}=={});", inst, value, value, active_mask);
|
||||
}
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
|
||||
const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
|
||||
const auto value{fmt::format("({}^{})", ballot, active_mask)};
|
||||
ctx.AddU1("{}=({}==0)||({}=={});", inst, value, value, active_mask);
|
||||
}
|
||||
|
||||
void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
|
||||
const auto ballot_index{BallotIndex(ctx)};
|
||||
ctx.AddU32("{}=uvec2(ballotARB({})){};", inst, pred, ballot_index);
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
ctx.AddU32("{}=uvec2(ballotARB({})).x;", inst, pred);
|
||||
} else {
|
||||
ctx.AddU32("{}=uvec2(ballotARB({}))[gl_SubGroupInvocationARB];", inst, pred);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupEqMaskARB"));
|
||||
ctx.AddU32("{}=uint(gl_SubGroupEqMaskARB.x);", inst);
|
||||
}
|
||||
|
||||
void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupLtMaskARB"));
|
||||
ctx.AddU32("{}=uint(gl_SubGroupLtMaskARB.x);", inst);
|
||||
}
|
||||
|
||||
void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupLeMaskARB"));
|
||||
ctx.AddU32("{}=uint(gl_SubGroupLeMaskARB.x);", inst);
|
||||
}
|
||||
|
||||
void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupGtMaskARB"));
|
||||
ctx.AddU32("{}=uint(gl_SubGroupGtMaskARB.x);", inst);
|
||||
}
|
||||
|
||||
void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupGeMaskARB"));
|
||||
ctx.AddU32("{}=uint(gl_SubGroupGeMaskARB.x);", inst);
|
||||
}
|
||||
|
||||
void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view index, std::string_view clamp, std::string_view seg_mask) {
|
||||
std::string_view index, std::string_view clamp,
|
||||
std::string_view segmentation_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleNV", value, index, clamp, seg_mask);
|
||||
UseShuffleNv(ctx, inst, "shuffleNV", value, index, clamp, segmentation_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_index{fmt::format("{}?{}+32:{}", is_upper_partition, index, index)};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
const auto not_seg_mask{fmt::format("(~{})", segmentation_mask)};
|
||||
const auto thread_id{"gl_SubGroupInvocationARB"};
|
||||
const auto min_thread_id{ComputeMinThreadId(thread_id, segmentation_mask)};
|
||||
const auto max_thread_id{ComputeMaxThreadId(min_thread_id, clamp, not_seg_mask)};
|
||||
|
||||
const auto not_seg_mask{fmt::format("(~{})", seg_mask)};
|
||||
const auto min_thread_id{ComputeMinThreadId(THREAD_ID, seg_mask)};
|
||||
const auto max_thread_id{
|
||||
ComputeMaxThreadId(min_thread_id, big_warp ? upper_clamp : clamp, not_seg_mask)};
|
||||
|
||||
const auto lhs{fmt::format("({}&{})", big_warp ? upper_index : index, not_seg_mask)};
|
||||
const auto lhs{fmt::format("({}&{})", index, not_seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({})|({})", lhs, min_thread_id)};
|
||||
ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
@@ -146,34 +128,29 @@ void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
}
|
||||
|
||||
void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view index,
|
||||
std::string_view clamp, std::string_view seg_mask) {
|
||||
std::string_view clamp, std::string_view segmentation_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleUpNV", value, index, clamp, seg_mask);
|
||||
UseShuffleNv(ctx, inst, "shuffleUpNV", value, index, clamp, segmentation_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
|
||||
const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({}-{})", THREAD_ID, index)};
|
||||
const auto thread_id{"gl_SubGroupInvocationARB"};
|
||||
const auto max_thread_id{GetMaxThreadId(thread_id, clamp, segmentation_mask)};
|
||||
const auto src_thread_id{fmt::format("({}-{})", thread_id, index)};
|
||||
ctx.Add("shfl_in_bounds=int({})>=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
|
||||
}
|
||||
|
||||
void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view index, std::string_view clamp, std::string_view seg_mask) {
|
||||
std::string_view index, std::string_view clamp,
|
||||
std::string_view segmentation_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleDownNV", value, index, clamp, seg_mask);
|
||||
UseShuffleNv(ctx, inst, "shuffleDownNV", value, index, clamp, segmentation_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
|
||||
const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({}+{})", THREAD_ID, index)};
|
||||
const auto thread_id{"gl_SubGroupInvocationARB"};
|
||||
const auto max_thread_id{GetMaxThreadId(thread_id, clamp, segmentation_mask)};
|
||||
const auto src_thread_id{fmt::format("({}+{})", thread_id, index)};
|
||||
ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
|
||||
@@ -181,17 +158,14 @@ void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
|
||||
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view index, std::string_view clamp,
|
||||
std::string_view seg_mask) {
|
||||
std::string_view segmentation_mask) {
|
||||
if (ctx.profile.support_gl_warp_intrinsics) {
|
||||
UseShuffleNv(ctx, inst, "shuffleXorNV", value, index, clamp, seg_mask);
|
||||
UseShuffleNv(ctx, inst, "shuffleXorNV", value, index, clamp, segmentation_mask);
|
||||
return;
|
||||
}
|
||||
const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
|
||||
const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
|
||||
const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
|
||||
|
||||
const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
|
||||
const auto src_thread_id{fmt::format("({}^{})", THREAD_ID, index)};
|
||||
const auto thread_id{"gl_SubGroupInvocationARB"};
|
||||
const auto max_thread_id{GetMaxThreadId(thread_id, clamp, segmentation_mask)};
|
||||
const auto src_thread_id{fmt::format("({}^{})", thread_id, index)};
|
||||
ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
|
||||
SetInBoundsFlag(ctx, inst);
|
||||
ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
constexpr size_t NUM_FIXEDFNCTEXTURE = 10;
|
||||
|
||||
enum class Operation {
|
||||
Increment,
|
||||
Decrement,
|
||||
@@ -429,16 +427,6 @@ Id DescType(EmitContext& ctx, Id sampled_type, Id pointer_type, u32 count) {
|
||||
return pointer_type;
|
||||
}
|
||||
}
|
||||
|
||||
size_t FindNextUnusedLocation(const std::bitset<IR::NUM_GENERICS>& used_locations,
|
||||
size_t start_offset) {
|
||||
for (size_t location = start_offset; location < used_locations.size(); ++location) {
|
||||
if (!used_locations.test(location)) {
|
||||
return location;
|
||||
}
|
||||
}
|
||||
throw RuntimeError("Unable to get an unused location for legacy attribute");
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) {
|
||||
@@ -1239,7 +1227,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
loads[IR::Attribute::TessellationEvaluationPointV]) {
|
||||
tess_coord = DefineInput(*this, F32[3], false, spv::BuiltIn::TessCoord);
|
||||
}
|
||||
std::bitset<IR::NUM_GENERICS> used_locations{};
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
const AttributeType input_type{runtime_info.generic_input_types[index]};
|
||||
if (!runtime_info.previous_stage_stores.Generic(index)) {
|
||||
@@ -1251,7 +1238,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
if (input_type == AttributeType::Disabled) {
|
||||
continue;
|
||||
}
|
||||
used_locations.set(index);
|
||||
const Id type{GetAttributeType(*this, input_type)};
|
||||
const Id id{DefineInput(*this, type, true)};
|
||||
Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
|
||||
@@ -1277,26 +1263,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
size_t previous_unused_location = 0;
|
||||
if (loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
|
||||
const size_t location = FindNextUnusedLocation(used_locations, previous_unused_location);
|
||||
previous_unused_location = location;
|
||||
used_locations.set(location);
|
||||
const Id id{DefineInput(*this, F32[4], true)};
|
||||
Decorate(id, spv::Decoration::Location, location);
|
||||
input_front_color = id;
|
||||
}
|
||||
for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) {
|
||||
if (loads.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) {
|
||||
const size_t location =
|
||||
FindNextUnusedLocation(used_locations, previous_unused_location);
|
||||
previous_unused_location = location;
|
||||
used_locations.set(location);
|
||||
const Id id{DefineInput(*this, F32[4], true)};
|
||||
Decorate(id, spv::Decoration::Location, location);
|
||||
input_fixed_fnc_textures[index] = id;
|
||||
}
|
||||
}
|
||||
if (stage == Stage::TessellationEval) {
|
||||
for (size_t index = 0; index < info.uses_patches.size(); ++index) {
|
||||
if (!info.uses_patches[index]) {
|
||||
@@ -1347,31 +1313,9 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
|
||||
viewport_mask = DefineOutput(*this, TypeArray(U32[1], Const(1u)), std::nullopt,
|
||||
spv::BuiltIn::ViewportMaskNV);
|
||||
}
|
||||
std::bitset<IR::NUM_GENERICS> used_locations{};
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (info.stores.Generic(index)) {
|
||||
DefineGenericOutput(*this, index, invocations);
|
||||
used_locations.set(index);
|
||||
}
|
||||
}
|
||||
size_t previous_unused_location = 0;
|
||||
if (info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
|
||||
const size_t location = FindNextUnusedLocation(used_locations, previous_unused_location);
|
||||
previous_unused_location = location;
|
||||
used_locations.set(location);
|
||||
const Id id{DefineOutput(*this, F32[4], invocations)};
|
||||
Decorate(id, spv::Decoration::Location, static_cast<u32>(location));
|
||||
output_front_color = id;
|
||||
}
|
||||
for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) {
|
||||
if (info.stores.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) {
|
||||
const size_t location =
|
||||
FindNextUnusedLocation(used_locations, previous_unused_location);
|
||||
previous_unused_location = location;
|
||||
used_locations.set(location);
|
||||
const Id id{DefineOutput(*this, F32[4], invocations)};
|
||||
Decorate(id, spv::Decoration::Location, location);
|
||||
output_fixed_fnc_textures[index] = id;
|
||||
}
|
||||
}
|
||||
switch (stage) {
|
||||
|
||||
@@ -268,14 +268,10 @@ public:
|
||||
Id write_global_func_u32x4{};
|
||||
|
||||
Id input_position{};
|
||||
Id input_front_color{};
|
||||
std::array<Id, 10> input_fixed_fnc_textures{};
|
||||
std::array<Id, 32> input_generics{};
|
||||
|
||||
Id output_point_size{};
|
||||
Id output_position{};
|
||||
Id output_front_color{};
|
||||
std::array<Id, 10> output_fixed_fnc_textures{};
|
||||
std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
|
||||
|
||||
Id output_tess_level_outer{};
|
||||
|
||||
@@ -43,25 +43,6 @@ Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&...
|
||||
}
|
||||
}
|
||||
|
||||
bool IsFixedFncTexture(IR::Attribute attribute) {
|
||||
return attribute >= IR::Attribute::FixedFncTexture0S &&
|
||||
attribute <= IR::Attribute::FixedFncTexture9Q;
|
||||
}
|
||||
|
||||
u32 FixedFncTextureAttributeIndex(IR::Attribute attribute) {
|
||||
if (!IsFixedFncTexture(attribute)) {
|
||||
throw InvalidArgument("Attribute {} is not a FixedFncTexture", attribute);
|
||||
}
|
||||
return (static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4u;
|
||||
}
|
||||
|
||||
u32 FixedFncTextureAttributeElement(IR::Attribute attribute) {
|
||||
if (!IsFixedFncTexture(attribute)) {
|
||||
throw InvalidArgument("Attribute {} is not a FixedFncTexture", attribute);
|
||||
}
|
||||
return static_cast<u32>(attribute) % 4u;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
Id OutputAccessChain(EmitContext& ctx, Id result_type, Id base, Args&&... args) {
|
||||
if (ctx.stage == Stage::TessellationControl) {
|
||||
@@ -93,13 +74,6 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
|
||||
return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id);
|
||||
}
|
||||
}
|
||||
if (IsFixedFncTexture(attr)) {
|
||||
const u32 index{FixedFncTextureAttributeIndex(attr)};
|
||||
const u32 element{FixedFncTextureAttributeElement(attr)};
|
||||
const Id element_id{ctx.Const(element)};
|
||||
return OutputAccessChain(ctx, ctx.output_f32, ctx.output_fixed_fnc_textures[index],
|
||||
element_id);
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PointSize:
|
||||
return ctx.output_point_size;
|
||||
@@ -111,14 +85,6 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
|
||||
const Id element_id{ctx.Const(element)};
|
||||
return OutputAccessChain(ctx, ctx.output_f32, ctx.output_position, element_id);
|
||||
}
|
||||
case IR::Attribute::ColorFrontDiffuseR:
|
||||
case IR::Attribute::ColorFrontDiffuseG:
|
||||
case IR::Attribute::ColorFrontDiffuseB:
|
||||
case IR::Attribute::ColorFrontDiffuseA: {
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const Id element_id{ctx.Const(element)};
|
||||
return OutputAccessChain(ctx, ctx.output_f32, ctx.output_front_color, element_id);
|
||||
}
|
||||
case IR::Attribute::ClipDistance0:
|
||||
case IR::Attribute::ClipDistance1:
|
||||
case IR::Attribute::ClipDistance2:
|
||||
@@ -341,12 +307,6 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
|
||||
const Id value{ctx.OpLoad(type->id, pointer)};
|
||||
return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value;
|
||||
}
|
||||
if (IsFixedFncTexture(attr)) {
|
||||
const u32 index{FixedFncTextureAttributeIndex(attr)};
|
||||
const Id attr_id{ctx.input_fixed_fnc_textures[index]};
|
||||
const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex, attr_id, ctx.Const(element))};
|
||||
return ctx.OpLoad(ctx.F32[1], attr_ptr);
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id));
|
||||
@@ -356,13 +316,6 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
|
||||
case IR::Attribute::PositionW:
|
||||
return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position,
|
||||
ctx.Const(element)));
|
||||
case IR::Attribute::ColorFrontDiffuseR:
|
||||
case IR::Attribute::ColorFrontDiffuseG:
|
||||
case IR::Attribute::ColorFrontDiffuseB:
|
||||
case IR::Attribute::ColorFrontDiffuseA: {
|
||||
return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_front_color,
|
||||
ctx.Const(element)));
|
||||
}
|
||||
case IR::Attribute::InstanceId:
|
||||
if (ctx.profile.support_vertex_instance_id) {
|
||||
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_id));
|
||||
@@ -477,13 +430,7 @@ void EmitSetSampleMask(EmitContext& ctx, Id value) {
|
||||
}
|
||||
|
||||
void EmitSetFragDepth(EmitContext& ctx, Id value) {
|
||||
if (!ctx.runtime_info.convert_depth_mode) {
|
||||
ctx.OpStore(ctx.frag_depth, value);
|
||||
return;
|
||||
}
|
||||
const Id unit{ctx.Const(0.5f)};
|
||||
const Id new_depth{ctx.OpFma(ctx.F32[1], value, unit, unit)};
|
||||
ctx.OpStore(ctx.frag_depth, new_depth);
|
||||
ctx.OpStore(ctx.frag_depth, value);
|
||||
}
|
||||
|
||||
void EmitGetZFlag(EmitContext&) {
|
||||
|
||||
@@ -7,13 +7,8 @@
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id GetThreadId(EmitContext& ctx) {
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id);
|
||||
}
|
||||
|
||||
Id WarpExtract(EmitContext& ctx, Id value) {
|
||||
const Id thread_id{GetThreadId(ctx)};
|
||||
const Id local_index{ctx.OpShiftRightArithmetic(ctx.U32[1], thread_id, ctx.Const(5U))};
|
||||
const Id local_index{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
|
||||
return ctx.OpVectorExtractDynamic(ctx.U32[1], value, local_index);
|
||||
}
|
||||
|
||||
@@ -53,17 +48,10 @@ Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) {
|
||||
return ctx.OpSelect(ctx.U32[1], in_range,
|
||||
ctx.OpSubgroupReadInvocationKHR(ctx.U32[1], value, src_thread_id), value);
|
||||
}
|
||||
|
||||
Id GetUpperClamp(EmitContext& ctx, Id invocation_id, Id clamp) {
|
||||
const Id thirty_two{ctx.Const(32u)};
|
||||
const Id is_upper_partition{ctx.OpSGreaterThanEqual(ctx.U1, invocation_id, thirty_two)};
|
||||
const Id upper_clamp{ctx.OpIAdd(ctx.U32[1], thirty_two, clamp)};
|
||||
return ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_clamp, clamp);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitLaneId(EmitContext& ctx) {
|
||||
const Id id{GetThreadId(ctx)};
|
||||
const Id id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return id;
|
||||
}
|
||||
@@ -135,15 +123,7 @@ Id EmitSubgroupGeMask(EmitContext& ctx) {
|
||||
Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)};
|
||||
const Id thread_id{GetThreadId(ctx)};
|
||||
if (ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
const Id thirty_two{ctx.Const(32u)};
|
||||
const Id is_upper_partition{ctx.OpSGreaterThanEqual(ctx.U1, thread_id, thirty_two)};
|
||||
const Id upper_index{ctx.OpIAdd(ctx.U32[1], thirty_two, index)};
|
||||
const Id upper_clamp{ctx.OpIAdd(ctx.U32[1], thirty_two, clamp)};
|
||||
index = ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_index, index);
|
||||
clamp = ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_clamp, clamp);
|
||||
}
|
||||
const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
|
||||
const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)};
|
||||
const Id max_thread_id{ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask)};
|
||||
|
||||
@@ -157,10 +137,7 @@ Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id cla
|
||||
|
||||
Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
const Id thread_id{GetThreadId(ctx)};
|
||||
if (ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
clamp = GetUpperClamp(ctx, thread_id, clamp);
|
||||
}
|
||||
const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
|
||||
const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
|
||||
const Id src_thread_id{ctx.OpISub(ctx.U32[1], thread_id, index)};
|
||||
const Id in_range{ctx.OpSGreaterThanEqual(ctx.U1, src_thread_id, max_thread_id)};
|
||||
@@ -171,10 +148,7 @@ Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
|
||||
Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
const Id thread_id{GetThreadId(ctx)};
|
||||
if (ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
clamp = GetUpperClamp(ctx, thread_id, clamp);
|
||||
}
|
||||
const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
|
||||
const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
|
||||
const Id src_thread_id{ctx.OpIAdd(ctx.U32[1], thread_id, index)};
|
||||
const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
|
||||
@@ -185,10 +159,7 @@ Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clam
|
||||
|
||||
Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
const Id thread_id{GetThreadId(ctx)};
|
||||
if (ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
clamp = GetUpperClamp(ctx, thread_id, clamp);
|
||||
}
|
||||
const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
|
||||
const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
|
||||
const Id src_thread_id{ctx.OpBitwiseXor(ctx.U32[1], thread_id, index)};
|
||||
const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)};
|
||||
|
||||
@@ -231,7 +231,6 @@ endif()
|
||||
|
||||
target_include_directories(video_core PRIVATE ${FFmpeg_INCLUDE_DIR})
|
||||
target_link_libraries(video_core PRIVATE ${FFmpeg_LIBRARIES})
|
||||
target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS})
|
||||
|
||||
add_dependencies(video_core host_shaders)
|
||||
target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include "common/assert.h"
|
||||
#include "common/settings.h"
|
||||
#include "video_core/command_classes/codecs/codec.h"
|
||||
#include "video_core/command_classes/codecs/h264.h"
|
||||
#include "video_core/command_classes/codecs/vp9.h"
|
||||
@@ -17,28 +16,44 @@ extern "C" {
|
||||
}
|
||||
|
||||
namespace Tegra {
|
||||
#if defined(LIBVA_FOUND)
|
||||
// Hardware acceleration code from FFmpeg/doc/examples/hw_decode.c originally under MIT license
|
||||
namespace {
|
||||
constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12;
|
||||
constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P;
|
||||
constexpr std::array<const char*, 2> VAAPI_DRIVERS = {
|
||||
"i915",
|
||||
"amdgpu",
|
||||
};
|
||||
|
||||
void AVPacketDeleter(AVPacket* ptr) {
|
||||
av_packet_free(&ptr);
|
||||
}
|
||||
|
||||
using AVPacketPtr = std::unique_ptr<AVPacket, decltype(&AVPacketDeleter)>;
|
||||
|
||||
AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pix_fmts) {
|
||||
AVPixelFormat GetHwFormat(AVCodecContext*, const AVPixelFormat* pix_fmts) {
|
||||
for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) {
|
||||
if (*p == av_codec_ctx->pix_fmt) {
|
||||
return av_codec_ctx->pix_fmt;
|
||||
if (*p == AV_PIX_FMT_VAAPI) {
|
||||
return AV_PIX_FMT_VAAPI;
|
||||
}
|
||||
}
|
||||
LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU");
|
||||
av_buffer_unref(&av_codec_ctx->hw_device_ctx);
|
||||
av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT;
|
||||
return PREFERRED_CPU_FMT;
|
||||
return *pix_fmts;
|
||||
}
|
||||
|
||||
bool CreateVaapiHwdevice(AVBufferRef** av_hw_device) {
|
||||
AVDictionary* hwdevice_options = nullptr;
|
||||
av_dict_set(&hwdevice_options, "connection_type", "drm", 0);
|
||||
for (const auto& driver : VAAPI_DRIVERS) {
|
||||
av_dict_set(&hwdevice_options, "kernel_driver", driver, 0);
|
||||
const int hwdevice_error = av_hwdevice_ctx_create(av_hw_device, AV_HWDEVICE_TYPE_VAAPI,
|
||||
nullptr, hwdevice_options, 0);
|
||||
if (hwdevice_error >= 0) {
|
||||
LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver);
|
||||
av_dict_free(&hwdevice_options);
|
||||
return true;
|
||||
}
|
||||
LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error);
|
||||
}
|
||||
LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers");
|
||||
av_dict_free(&hwdevice_options);
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
void AVFrameDeleter(AVFrame* ptr) {
|
||||
av_frame_free(&ptr);
|
||||
@@ -53,110 +68,56 @@ Codec::~Codec() {
|
||||
return;
|
||||
}
|
||||
// Free libav memory
|
||||
avcodec_free_context(&av_codec_ctx);
|
||||
av_buffer_unref(&av_gpu_decoder);
|
||||
avcodec_send_packet(av_codec_ctx, nullptr);
|
||||
AVFrame* av_frame = av_frame_alloc();
|
||||
avcodec_receive_frame(av_codec_ctx, av_frame);
|
||||
avcodec_flush_buffers(av_codec_ctx);
|
||||
av_frame_free(&av_frame);
|
||||
avcodec_close(av_codec_ctx);
|
||||
av_buffer_unref(&av_hw_device);
|
||||
}
|
||||
|
||||
bool Codec::CreateGpuAvDevice() {
|
||||
void Codec::InitializeHwdec() {
|
||||
// Prioritize integrated GPU to mitigate bandwidth bottlenecks
|
||||
#if defined(LIBVA_FOUND)
|
||||
static constexpr std::array<const char*, 3> VAAPI_DRIVERS = {
|
||||
"i915",
|
||||
"iHD",
|
||||
"amdgpu",
|
||||
};
|
||||
AVDictionary* hwdevice_options = nullptr;
|
||||
av_dict_set(&hwdevice_options, "connection_type", "drm", 0);
|
||||
for (const auto& driver : VAAPI_DRIVERS) {
|
||||
av_dict_set(&hwdevice_options, "kernel_driver", driver, 0);
|
||||
const int hwdevice_error = av_hwdevice_ctx_create(&av_gpu_decoder, AV_HWDEVICE_TYPE_VAAPI,
|
||||
nullptr, hwdevice_options, 0);
|
||||
if (hwdevice_error >= 0) {
|
||||
LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver);
|
||||
av_dict_free(&hwdevice_options);
|
||||
av_codec_ctx->pix_fmt = AV_PIX_FMT_VAAPI;
|
||||
return true;
|
||||
}
|
||||
LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error);
|
||||
}
|
||||
LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers");
|
||||
av_dict_free(&hwdevice_options);
|
||||
#endif
|
||||
static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
|
||||
static constexpr std::array GPU_DECODER_TYPES{
|
||||
AV_HWDEVICE_TYPE_CUDA,
|
||||
#ifdef _WIN32
|
||||
AV_HWDEVICE_TYPE_D3D11VA,
|
||||
#else
|
||||
AV_HWDEVICE_TYPE_VDPAU,
|
||||
#endif
|
||||
};
|
||||
for (const auto& type : GPU_DECODER_TYPES) {
|
||||
const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0);
|
||||
if (hwdevice_res < 0) {
|
||||
LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}",
|
||||
av_hwdevice_get_type_name(type), hwdevice_res);
|
||||
continue;
|
||||
}
|
||||
for (int i = 0;; i++) {
|
||||
const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i);
|
||||
if (!config) {
|
||||
LOG_DEBUG(Service_NVDRV, "{} decoder does not support device type {}.",
|
||||
av_codec->name, av_hwdevice_get_type_name(type));
|
||||
break;
|
||||
}
|
||||
if (config->methods & HW_CONFIG_METHOD && config->device_type == type) {
|
||||
av_codec_ctx->pix_fmt = config->pix_fmt;
|
||||
LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Codec::InitializeAvCodecContext() {
|
||||
av_codec_ctx = avcodec_alloc_context3(av_codec);
|
||||
av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
|
||||
}
|
||||
|
||||
void Codec::InitializeGpuDecoder() {
|
||||
if (!CreateGpuAvDevice()) {
|
||||
av_buffer_unref(&av_gpu_decoder);
|
||||
if (CreateVaapiHwdevice(&av_hw_device)) {
|
||||
const auto hw_device_ctx = av_buffer_ref(av_hw_device);
|
||||
ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed");
|
||||
av_codec_ctx->hw_device_ctx = hw_device_ctx;
|
||||
av_codec_ctx->get_format = GetHwFormat;
|
||||
return;
|
||||
}
|
||||
auto* hw_device_ctx = av_buffer_ref(av_gpu_decoder);
|
||||
ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed");
|
||||
av_codec_ctx->hw_device_ctx = hw_device_ctx;
|
||||
av_codec_ctx->get_format = GetGpuFormat;
|
||||
#endif
|
||||
// TODO more GPU accelerated decoders
|
||||
}
|
||||
|
||||
void Codec::Initialize() {
|
||||
const AVCodecID codec = [&] {
|
||||
switch (current_codec) {
|
||||
case NvdecCommon::VideoCodec::H264:
|
||||
return AV_CODEC_ID_H264;
|
||||
case NvdecCommon::VideoCodec::Vp9:
|
||||
return AV_CODEC_ID_VP9;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown codec {}", current_codec);
|
||||
return AV_CODEC_ID_NONE;
|
||||
}
|
||||
}();
|
||||
av_codec = avcodec_find_decoder(codec);
|
||||
|
||||
InitializeAvCodecContext();
|
||||
if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::GPU) {
|
||||
InitializeGpuDecoder();
|
||||
}
|
||||
if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) {
|
||||
LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed with result {}", res);
|
||||
avcodec_free_context(&av_codec_ctx);
|
||||
av_buffer_unref(&av_gpu_decoder);
|
||||
AVCodecID codec;
|
||||
switch (current_codec) {
|
||||
case NvdecCommon::VideoCodec::H264:
|
||||
codec = AV_CODEC_ID_H264;
|
||||
break;
|
||||
case NvdecCommon::VideoCodec::Vp9:
|
||||
codec = AV_CODEC_ID_VP9;
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown codec {}", current_codec);
|
||||
return;
|
||||
}
|
||||
av_codec = avcodec_find_decoder(codec);
|
||||
av_codec_ctx = avcodec_alloc_context3(av_codec);
|
||||
av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
|
||||
InitializeHwdec();
|
||||
if (!av_codec_ctx->hw_device_ctx) {
|
||||
LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding");
|
||||
}
|
||||
const auto av_error = avcodec_open2(av_codec_ctx, av_codec, nullptr);
|
||||
if (av_error < 0) {
|
||||
LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed.");
|
||||
avcodec_close(av_codec_ctx);
|
||||
av_buffer_unref(&av_hw_device);
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
@@ -172,9 +133,6 @@ void Codec::Decode() {
|
||||
if (is_first_frame) {
|
||||
Initialize();
|
||||
}
|
||||
if (!initialized) {
|
||||
return;
|
||||
}
|
||||
bool vp9_hidden_frame = false;
|
||||
std::vector<u8> frame_data;
|
||||
if (current_codec == NvdecCommon::VideoCodec::H264) {
|
||||
@@ -183,48 +141,50 @@ void Codec::Decode() {
|
||||
frame_data = vp9_decoder->ComposeFrameHeader(state);
|
||||
vp9_hidden_frame = vp9_decoder->WasFrameHidden();
|
||||
}
|
||||
AVPacketPtr packet{av_packet_alloc(), AVPacketDeleter};
|
||||
if (!packet) {
|
||||
LOG_ERROR(Service_NVDRV, "av_packet_alloc failed");
|
||||
return;
|
||||
}
|
||||
packet->data = frame_data.data();
|
||||
packet->size = static_cast<s32>(frame_data.size());
|
||||
if (const int res = avcodec_send_packet(av_codec_ctx, packet.get()); res != 0) {
|
||||
LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", res);
|
||||
AVPacket packet{};
|
||||
av_init_packet(&packet);
|
||||
packet.data = frame_data.data();
|
||||
packet.size = static_cast<s32>(frame_data.size());
|
||||
if (const int ret = avcodec_send_packet(av_codec_ctx, &packet); ret) {
|
||||
LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", ret);
|
||||
return;
|
||||
}
|
||||
// Only receive/store visible frames
|
||||
if (vp9_hidden_frame) {
|
||||
return;
|
||||
}
|
||||
AVFramePtr initial_frame{av_frame_alloc(), AVFrameDeleter};
|
||||
AVFramePtr final_frame{nullptr, AVFrameDeleter};
|
||||
ASSERT_MSG(initial_frame, "av_frame_alloc initial_frame failed");
|
||||
if (const int ret = avcodec_receive_frame(av_codec_ctx, initial_frame.get()); ret) {
|
||||
AVFrame* hw_frame = av_frame_alloc();
|
||||
AVFrame* sw_frame = hw_frame;
|
||||
ASSERT_MSG(hw_frame, "av_frame_alloc hw_frame failed");
|
||||
if (const int ret = avcodec_receive_frame(av_codec_ctx, hw_frame); ret) {
|
||||
LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret);
|
||||
av_frame_free(&hw_frame);
|
||||
return;
|
||||
}
|
||||
if (initial_frame->width == 0 || initial_frame->height == 0) {
|
||||
if (!hw_frame->width || !hw_frame->height) {
|
||||
LOG_WARNING(Service_NVDRV, "Zero width or height in frame");
|
||||
av_frame_free(&hw_frame);
|
||||
return;
|
||||
}
|
||||
if (av_codec_ctx->hw_device_ctx) {
|
||||
final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
|
||||
ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed");
|
||||
#if defined(LIBVA_FOUND)
|
||||
// Hardware acceleration code from FFmpeg/doc/examples/hw_decode.c under MIT license
|
||||
if (hw_frame->format == AV_PIX_FMT_VAAPI) {
|
||||
sw_frame = av_frame_alloc();
|
||||
ASSERT_MSG(sw_frame, "av_frame_alloc sw_frame failed");
|
||||
// Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp
|
||||
// because Intel drivers crash unless using AV_PIX_FMT_NV12
|
||||
final_frame->format = PREFERRED_GPU_FMT;
|
||||
const int ret = av_hwframe_transfer_data(final_frame.get(), initial_frame.get(), 0);
|
||||
ASSERT_MSG(!ret, "av_hwframe_transfer_data error {}", ret);
|
||||
} else {
|
||||
final_frame = std::move(initial_frame);
|
||||
sw_frame->format = AV_PIX_FMT_NV12;
|
||||
const int transfer_data_ret = av_hwframe_transfer_data(sw_frame, hw_frame, 0);
|
||||
ASSERT_MSG(!transfer_data_ret, "av_hwframe_transfer_data error {}", transfer_data_ret);
|
||||
av_frame_free(&hw_frame);
|
||||
}
|
||||
if (final_frame->format != PREFERRED_CPU_FMT && final_frame->format != PREFERRED_GPU_FMT) {
|
||||
UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format);
|
||||
#endif
|
||||
if (sw_frame->format != AV_PIX_FMT_YUV420P && sw_frame->format != AV_PIX_FMT_NV12) {
|
||||
UNIMPLEMENTED_MSG("Unexpected video format from host graphics: {}", sw_frame->format);
|
||||
av_frame_free(&sw_frame);
|
||||
return;
|
||||
}
|
||||
av_frames.push(std::move(final_frame));
|
||||
av_frames.push(AVFramePtr{sw_frame, AVFrameDeleter});
|
||||
if (av_frames.size() > 10) {
|
||||
LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame");
|
||||
av_frames.pop();
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <queue>
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/command_classes/nvdec_common.h"
|
||||
@@ -51,23 +50,18 @@ public:
|
||||
|
||||
/// Returns the value of current_codec
|
||||
[[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const;
|
||||
|
||||
/// Return name of the current codec
|
||||
[[nodiscard]] std::string_view GetCurrentCodecName() const;
|
||||
|
||||
private:
|
||||
void InitializeAvCodecContext();
|
||||
|
||||
void InitializeGpuDecoder();
|
||||
|
||||
bool CreateGpuAvDevice();
|
||||
void InitializeHwdec();
|
||||
|
||||
bool initialized{};
|
||||
NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None};
|
||||
|
||||
AVCodec* av_codec{nullptr};
|
||||
AVBufferRef* av_hw_device{nullptr};
|
||||
AVCodecContext* av_codec_ctx{nullptr};
|
||||
AVBufferRef* av_gpu_decoder{nullptr};
|
||||
|
||||
GPU& gpu;
|
||||
const NvdecCommon::NvdecRegisters& state;
|
||||
|
||||
@@ -95,8 +95,7 @@ const std::vector<u8>& H264::ComposeFrameHeader(const NvdecCommon::NvdecRegister
|
||||
const s32 pic_height = context.h264_parameter_set.frame_height_in_map_units /
|
||||
(context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2);
|
||||
|
||||
// TODO (ameerj): Where do we get this number, it seems to be particular for each stream
|
||||
writer.WriteUe(6); // Max number of reference frames
|
||||
writer.WriteUe(16);
|
||||
writer.WriteBit(false);
|
||||
writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1);
|
||||
writer.WriteUe(pic_height - 1);
|
||||
|
||||
@@ -475,10 +475,10 @@ public:
|
||||
|
||||
// These values are used by Nouveau and some games.
|
||||
AddGL = 0x8006,
|
||||
MinGL = 0x8007,
|
||||
MaxGL = 0x8008,
|
||||
SubtractGL = 0x800a,
|
||||
ReverseSubtractGL = 0x800b
|
||||
SubtractGL = 0x8007,
|
||||
ReverseSubtractGL = 0x8008,
|
||||
MinGL = 0x800a,
|
||||
MaxGL = 0x800b
|
||||
};
|
||||
|
||||
enum class Factor : u32 {
|
||||
|
||||
@@ -531,6 +531,14 @@ void GPU::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const {
|
||||
interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value);
|
||||
}
|
||||
|
||||
void GPU::ShutDown() {
|
||||
// Signal that threads should no longer block on syncpoint fences
|
||||
shutting_down.store(true, std::memory_order_relaxed);
|
||||
sync_cv.notify_all();
|
||||
|
||||
gpu_thread.ShutDown();
|
||||
}
|
||||
|
||||
void GPU::OnCommandListEnd() {
|
||||
if (is_async) {
|
||||
// This command only applies to asynchronous GPU mode
|
||||
|
||||
@@ -219,6 +219,9 @@ public:
|
||||
return *shader_notify;
|
||||
}
|
||||
|
||||
// Stops the GPU execution and waits for the GPU to finish working
|
||||
void ShutDown();
|
||||
|
||||
/// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame.
|
||||
void WaitFence(u32 syncpoint_id, u32 value);
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
namespace VideoCommon::GPUThread {
|
||||
|
||||
/// Runs the GPU thread
|
||||
static void RunThread(std::stop_token stop_token, Core::System& system,
|
||||
VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
|
||||
Tegra::DmaPusher& dma_pusher, SynchState& state) {
|
||||
static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
|
||||
Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher,
|
||||
SynchState& state) {
|
||||
std::string name = "yuzu:GPU";
|
||||
MicroProfileOnThreadCreate(name.c_str());
|
||||
SCOPE_EXIT({ MicroProfileOnThreadExit(); });
|
||||
@@ -28,14 +28,20 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
|
||||
system.RegisterHostThread();
|
||||
|
||||
// Wait for first GPU command before acquiring the window context
|
||||
state.queue.Wait();
|
||||
|
||||
// If emulation was stopped during disk shader loading, abort before trying to acquire context
|
||||
if (!state.is_running) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto current_context = context.Acquire();
|
||||
VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer();
|
||||
|
||||
while (!stop_token.stop_requested()) {
|
||||
CommandDataContainer next = state.queue.PopWait(stop_token);
|
||||
if (stop_token.stop_requested()) {
|
||||
break;
|
||||
}
|
||||
CommandDataContainer next;
|
||||
while (state.is_running) {
|
||||
next = state.queue.PopWait();
|
||||
if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) {
|
||||
dma_pusher.Push(std::move(submit_list->entries));
|
||||
dma_pusher.DispatchCalls();
|
||||
@@ -49,6 +55,8 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
|
||||
rasterizer->FlushRegion(flush->addr, flush->size);
|
||||
} else if (const auto* invalidate = std::get_if<InvalidateRegionCommand>(&next.data)) {
|
||||
rasterizer->OnCPUWrite(invalidate->addr, invalidate->size);
|
||||
} else if (std::holds_alternative<EndProcessingCommand>(next.data)) {
|
||||
ASSERT(state.is_running == false);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -65,14 +73,16 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
|
||||
ThreadManager::ThreadManager(Core::System& system_, bool is_async_)
|
||||
: system{system_}, is_async{is_async_} {}
|
||||
|
||||
ThreadManager::~ThreadManager() = default;
|
||||
ThreadManager::~ThreadManager() {
|
||||
ShutDown();
|
||||
}
|
||||
|
||||
void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
|
||||
Core::Frontend::GraphicsContext& context,
|
||||
Tegra::DmaPusher& dma_pusher) {
|
||||
rasterizer = renderer.ReadRasterizer();
|
||||
thread = std::jthread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
|
||||
std::ref(dma_pusher), std::ref(state));
|
||||
thread = std::thread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
|
||||
std::ref(dma_pusher), std::ref(state));
|
||||
}
|
||||
|
||||
void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
|
||||
@@ -107,6 +117,26 @@ void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) {
|
||||
rasterizer->OnCPUWrite(addr, size);
|
||||
}
|
||||
|
||||
void ThreadManager::ShutDown() {
|
||||
if (!state.is_running) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard lk(state.write_lock);
|
||||
state.is_running = false;
|
||||
state.cv.notify_all();
|
||||
}
|
||||
|
||||
if (!thread.joinable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify GPU thread that a shutdown is pending
|
||||
PushCommand(EndProcessingCommand());
|
||||
thread.join();
|
||||
}
|
||||
|
||||
void ThreadManager::OnCommandListEnd() {
|
||||
PushCommand(OnCommandListEndCommand());
|
||||
}
|
||||
@@ -122,8 +152,9 @@ u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) {
|
||||
state.queue.Push(CommandDataContainer(std::move(command_data), fence, block));
|
||||
|
||||
if (block) {
|
||||
state.cv.wait(lk, thread.get_stop_token(), [this, fence] {
|
||||
return fence <= state.signaled_fence.load(std::memory_order_relaxed);
|
||||
state.cv.wait(lk, [this, fence] {
|
||||
return fence <= state.signaled_fence.load(std::memory_order_relaxed) ||
|
||||
!state.is_running;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,9 @@ class RendererBase;
|
||||
|
||||
namespace VideoCommon::GPUThread {
|
||||
|
||||
/// Command to signal to the GPU thread that processing has ended
|
||||
struct EndProcessingCommand final {};
|
||||
|
||||
/// Command to signal to the GPU thread that a command list is ready for processing
|
||||
struct SubmitListCommand final {
|
||||
explicit SubmitListCommand(Tegra::CommandList&& entries_) : entries{std::move(entries_)} {}
|
||||
@@ -80,7 +83,7 @@ struct OnCommandListEndCommand final {};
|
||||
struct GPUTickCommand final {};
|
||||
|
||||
using CommandData =
|
||||
std::variant<std::monostate, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
|
||||
std::variant<EndProcessingCommand, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
|
||||
InvalidateRegionCommand, FlushAndInvalidateRegionCommand, OnCommandListEndCommand,
|
||||
GPUTickCommand>;
|
||||
|
||||
@@ -97,12 +100,14 @@ struct CommandDataContainer {
|
||||
|
||||
/// Struct used to synchronize the GPU thread
|
||||
struct SynchState final {
|
||||
using CommandQueue = Common::SPSCQueue<CommandDataContainer, true>;
|
||||
std::atomic_bool is_running{true};
|
||||
|
||||
using CommandQueue = Common::SPSCQueue<CommandDataContainer>;
|
||||
std::mutex write_lock;
|
||||
CommandQueue queue;
|
||||
u64 last_fence{};
|
||||
std::atomic<u64> signaled_fence{};
|
||||
std::condition_variable_any cv;
|
||||
std::condition_variable cv;
|
||||
};
|
||||
|
||||
/// Class used to manage the GPU thread
|
||||
@@ -144,7 +149,7 @@ private:
|
||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||
|
||||
SynchState state;
|
||||
std::jthread thread;
|
||||
std::thread thread;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon::GPUThread
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
@@ -293,8 +293,6 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
|
||||
}};
|
||||
LoadPipelines(stop_loading, shader_cache_filename, CACHE_VERSION, load_compute, load_graphics);
|
||||
|
||||
LOG_INFO(Render_OpenGL, "Total Pipeline Count: {}", state.total);
|
||||
|
||||
std::unique_lock lock{state.mutex};
|
||||
callback(VideoCore::LoadCallbackStage::Build, 0, state.total);
|
||||
state.has_loaded = true;
|
||||
|
||||
@@ -149,7 +149,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
|
||||
swapchain.Create(layout.width, layout.height, is_srgb);
|
||||
};
|
||||
if (swapchain.NeedsRecreation(is_srgb)) {
|
||||
if (swapchain.IsSubOptimal() || swapchain.HasColorSpaceChanged(is_srgb)) {
|
||||
recreate_swapchain();
|
||||
}
|
||||
bool is_outdated;
|
||||
@@ -164,8 +164,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
blit_screen.Recreate();
|
||||
}
|
||||
const VkSemaphore render_semaphore = blit_screen.DrawToSwapchain(*framebuffer, use_accelerated);
|
||||
const VkSemaphore present_semaphore = swapchain.CurrentPresentSemaphore();
|
||||
scheduler.Flush(render_semaphore, present_semaphore);
|
||||
scheduler.Flush(render_semaphore);
|
||||
scheduler.WaitWorker();
|
||||
swapchain.Present(render_semaphore);
|
||||
|
||||
|
||||
@@ -358,7 +358,7 @@ void VKBlitScreen::CreateDescriptorPool() {
|
||||
void VKBlitScreen::CreateRenderPass() {
|
||||
const VkAttachmentDescription color_attachment{
|
||||
.flags = 0,
|
||||
.format = swapchain.GetImageViewFormat(),
|
||||
.format = swapchain.GetImageFormat(),
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
@@ -19,6 +18,7 @@ namespace Vulkan {
|
||||
// Prefer small grow rates to avoid saturating the descriptor pool with barely used pipelines
|
||||
constexpr size_t SETS_GROW_RATE = 16;
|
||||
constexpr s32 SCORE_THRESHOLD = 3;
|
||||
constexpr u32 SETS_PER_POOL = 64;
|
||||
|
||||
struct DescriptorBank {
|
||||
DescriptorBankInfo info;
|
||||
@@ -58,12 +58,11 @@ static DescriptorBankInfo MakeBankInfo(std::span<const Shader::Info> infos) {
|
||||
static void AllocatePool(const Device& device, DescriptorBank& bank) {
|
||||
std::array<VkDescriptorPoolSize, 6> pool_sizes;
|
||||
size_t pool_cursor{};
|
||||
const u32 sets_per_pool = device.GetSetsPerPool();
|
||||
const auto add = [&](VkDescriptorType type, u32 count) {
|
||||
if (count > 0) {
|
||||
pool_sizes[pool_cursor++] = {
|
||||
.type = type,
|
||||
.descriptorCount = count * sets_per_pool,
|
||||
.descriptorCount = count * SETS_PER_POOL,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -78,7 +77,7 @@ static void AllocatePool(const Device& device, DescriptorBank& bank) {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
|
||||
.maxSets = sets_per_pool,
|
||||
.maxSets = SETS_PER_POOL,
|
||||
.poolSizeCount = static_cast<u32>(pool_cursor),
|
||||
.pPoolSizes = std::data(pool_sizes),
|
||||
}));
|
||||
|
||||
@@ -447,8 +447,6 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
||||
VideoCommon::LoadPipelines(stop_loading, pipeline_cache_filename, CACHE_VERSION, load_compute,
|
||||
load_graphics);
|
||||
|
||||
LOG_INFO(Render_Vulkan, "Total Pipeline Count: {}", state.total);
|
||||
|
||||
std::unique_lock lock{state.mutex};
|
||||
callback(VideoCore::LoadCallbackStage::Build, 0, state.total);
|
||||
state.has_loaded = true;
|
||||
|
||||
@@ -228,7 +228,9 @@ void RasterizerVulkan::Clear() {
|
||||
};
|
||||
|
||||
const u32 color_attachment = regs.clear_buffers.RT;
|
||||
if (use_color && framebuffer->HasAspectColorBit(color_attachment)) {
|
||||
const auto attachment_aspect_mask = framebuffer->ImageRanges()[color_attachment].aspectMask;
|
||||
const bool is_color_rt = (attachment_aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0;
|
||||
if (use_color && is_color_rt) {
|
||||
VkClearValue clear_value;
|
||||
std::memcpy(clear_value.color.float32, regs.clear_color, sizeof(regs.clear_color));
|
||||
|
||||
@@ -246,15 +248,12 @@ void RasterizerVulkan::Clear() {
|
||||
return;
|
||||
}
|
||||
VkImageAspectFlags aspect_flags = 0;
|
||||
if (use_depth && framebuffer->HasAspectDepthBit()) {
|
||||
if (use_depth) {
|
||||
aspect_flags |= VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
}
|
||||
if (use_stencil && framebuffer->HasAspectStencilBit()) {
|
||||
if (use_stencil) {
|
||||
aspect_flags |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
}
|
||||
if (aspect_flags == 0) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([clear_depth = regs.clear_depth, clear_stencil = regs.clear_stencil,
|
||||
clear_rect, aspect_flags](vk::CommandBuffer cmdbuf) {
|
||||
VkClearAttachment attachment;
|
||||
@@ -765,7 +764,12 @@ void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
const Maxwell::StencilOp zpass = regs.stencil_front_op_zpass;
|
||||
const Maxwell::ComparisonOp compare = regs.stencil_front_func_func;
|
||||
if (regs.stencil_two_side_enable) {
|
||||
// Separate stencil op per face
|
||||
scheduler.Record([fail, zfail, zpass, compare](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_AND_BACK, MaxwellToVK::StencilOp(fail),
|
||||
MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
|
||||
MaxwellToVK::ComparisonOp(compare));
|
||||
});
|
||||
} else {
|
||||
const Maxwell::StencilOp back_fail = regs.stencil_back_op_fail;
|
||||
const Maxwell::StencilOp back_zfail = regs.stencil_back_op_zfail;
|
||||
const Maxwell::StencilOp back_zpass = regs.stencil_back_op_zpass;
|
||||
@@ -780,13 +784,6 @@ void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
MaxwellToVK::StencilOp(back_zfail),
|
||||
MaxwellToVK::ComparisonOp(back_compare));
|
||||
});
|
||||
} else {
|
||||
// Front face defines the stencil op of both faces
|
||||
scheduler.Record([fail, zfail, zpass, compare](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_AND_BACK, MaxwellToVK::StencilOp(fail),
|
||||
MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
|
||||
MaxwellToVK::ComparisonOp(compare));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,19 +43,26 @@ VKScheduler::VKScheduler(const Device& device_, StateTracker& state_tracker_)
|
||||
command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} {
|
||||
AcquireNewChunk();
|
||||
AllocateWorkerCommandBuffer();
|
||||
worker_thread = std::jthread([this](std::stop_token token) { WorkerThread(token); });
|
||||
worker_thread = std::thread(&VKScheduler::WorkerThread, this);
|
||||
}
|
||||
|
||||
VKScheduler::~VKScheduler() = default;
|
||||
VKScheduler::~VKScheduler() {
|
||||
{
|
||||
std::lock_guard lock{work_mutex};
|
||||
quit = true;
|
||||
}
|
||||
work_cv.notify_all();
|
||||
worker_thread.join();
|
||||
}
|
||||
|
||||
void VKScheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
|
||||
SubmitExecution(signal_semaphore, wait_semaphore);
|
||||
void VKScheduler::Flush(VkSemaphore semaphore) {
|
||||
SubmitExecution(semaphore);
|
||||
AllocateNewContext();
|
||||
}
|
||||
|
||||
void VKScheduler::Finish(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
|
||||
void VKScheduler::Finish(VkSemaphore semaphore) {
|
||||
const u64 presubmit_tick = CurrentTick();
|
||||
SubmitExecution(signal_semaphore, wait_semaphore);
|
||||
SubmitExecution(semaphore);
|
||||
WaitWorker();
|
||||
Wait(presubmit_tick);
|
||||
AllocateNewContext();
|
||||
@@ -128,7 +135,7 @@ bool VKScheduler::UpdateGraphicsPipeline(GraphicsPipeline* pipeline) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void VKScheduler::WorkerThread(std::stop_token stop_token) {
|
||||
void VKScheduler::WorkerThread() {
|
||||
Common::SetCurrentThreadName("yuzu:VulkanWorker");
|
||||
do {
|
||||
if (work_queue.empty()) {
|
||||
@@ -137,8 +144,8 @@ void VKScheduler::WorkerThread(std::stop_token stop_token) {
|
||||
std::unique_ptr<CommandChunk> work;
|
||||
{
|
||||
std::unique_lock lock{work_mutex};
|
||||
work_cv.wait(lock, stop_token, [this] { return !work_queue.empty(); });
|
||||
if (stop_token.stop_requested()) {
|
||||
work_cv.wait(lock, [this] { return !work_queue.empty() || quit; });
|
||||
if (quit) {
|
||||
continue;
|
||||
}
|
||||
work = std::move(work_queue.front());
|
||||
@@ -151,7 +158,7 @@ void VKScheduler::WorkerThread(std::stop_token stop_token) {
|
||||
}
|
||||
std::lock_guard reserve_lock{reserve_mutex};
|
||||
chunk_reserve.push_back(std::move(work));
|
||||
} while (!stop_token.stop_requested());
|
||||
} while (!quit);
|
||||
}
|
||||
|
||||
void VKScheduler::AllocateWorkerCommandBuffer() {
|
||||
@@ -164,41 +171,37 @@ void VKScheduler::AllocateWorkerCommandBuffer() {
|
||||
});
|
||||
}
|
||||
|
||||
void VKScheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
|
||||
void VKScheduler::SubmitExecution(VkSemaphore semaphore) {
|
||||
EndPendingOperations();
|
||||
InvalidateState();
|
||||
|
||||
const u64 signal_value = master_semaphore->NextTick();
|
||||
Record([signal_semaphore, wait_semaphore, signal_value, this](vk::CommandBuffer cmdbuf) {
|
||||
Record([semaphore, signal_value, this](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.End();
|
||||
|
||||
const u32 num_signal_semaphores = semaphore ? 2U : 1U;
|
||||
|
||||
const u64 wait_value = signal_value - 1;
|
||||
const VkPipelineStageFlags wait_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
|
||||
const VkSemaphore timeline_semaphore = master_semaphore->Handle();
|
||||
|
||||
const u32 num_signal_semaphores = signal_semaphore ? 2U : 1U;
|
||||
const std::array signal_values{signal_value, u64(0)};
|
||||
const std::array signal_semaphores{timeline_semaphore, signal_semaphore};
|
||||
|
||||
const u32 num_wait_semaphores = wait_semaphore ? 2U : 1U;
|
||||
const std::array wait_values{signal_value - 1, u64(1)};
|
||||
const std::array wait_semaphores{timeline_semaphore, wait_semaphore};
|
||||
static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
};
|
||||
const std::array signal_semaphores{timeline_semaphore, semaphore};
|
||||
|
||||
const VkTimelineSemaphoreSubmitInfoKHR timeline_si{
|
||||
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR,
|
||||
.pNext = nullptr,
|
||||
.waitSemaphoreValueCount = num_wait_semaphores,
|
||||
.pWaitSemaphoreValues = wait_values.data(),
|
||||
.waitSemaphoreValueCount = 1,
|
||||
.pWaitSemaphoreValues = &wait_value,
|
||||
.signalSemaphoreValueCount = num_signal_semaphores,
|
||||
.pSignalSemaphoreValues = signal_values.data(),
|
||||
};
|
||||
const VkSubmitInfo submit_info{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.pNext = &timeline_si,
|
||||
.waitSemaphoreCount = num_wait_semaphores,
|
||||
.pWaitSemaphores = wait_semaphores.data(),
|
||||
.pWaitDstStageMask = wait_stage_masks.data(),
|
||||
.waitSemaphoreCount = 1,
|
||||
.pWaitSemaphores = &timeline_semaphore,
|
||||
.pWaitDstStageMask = &wait_stage_mask,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = cmdbuf.address(),
|
||||
.signalSemaphoreCount = num_signal_semaphores,
|
||||
|
||||
@@ -34,10 +34,10 @@ public:
|
||||
~VKScheduler();
|
||||
|
||||
/// Sends the current execution context to the GPU.
|
||||
void Flush(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr);
|
||||
void Flush(VkSemaphore semaphore = nullptr);
|
||||
|
||||
/// Sends the current execution context to the GPU and waits for it to complete.
|
||||
void Finish(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr);
|
||||
void Finish(VkSemaphore semaphore = nullptr);
|
||||
|
||||
/// Waits for the worker thread to finish executing everything. After this function returns it's
|
||||
/// safe to touch worker resources.
|
||||
@@ -187,11 +187,11 @@ private:
|
||||
GraphicsPipeline* graphics_pipeline = nullptr;
|
||||
};
|
||||
|
||||
void WorkerThread(std::stop_token stop_token);
|
||||
void WorkerThread();
|
||||
|
||||
void AllocateWorkerCommandBuffer();
|
||||
|
||||
void SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore);
|
||||
void SubmitExecution(VkSemaphore semaphore);
|
||||
|
||||
void AllocateNewContext();
|
||||
|
||||
@@ -212,7 +212,7 @@ private:
|
||||
vk::CommandBuffer current_cmdbuf;
|
||||
|
||||
std::unique_ptr<CommandChunk> chunk;
|
||||
std::jthread worker_thread;
|
||||
std::thread worker_thread;
|
||||
|
||||
State state;
|
||||
|
||||
@@ -224,8 +224,9 @@ private:
|
||||
std::vector<std::unique_ptr<CommandChunk>> chunk_reserve;
|
||||
std::mutex reserve_mutex;
|
||||
std::mutex work_mutex;
|
||||
std::condition_variable_any work_cv;
|
||||
std::condition_variable work_cv;
|
||||
std::condition_variable wait_cv;
|
||||
std::atomic_bool quit{};
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -110,6 +110,10 @@ public:
|
||||
return Exchange(Dirty::DepthTestEnable, false);
|
||||
}
|
||||
|
||||
bool TouchDepthBoundsEnable() {
|
||||
return Exchange(Dirty::DepthBoundsEnable, false);
|
||||
}
|
||||
|
||||
bool TouchDepthWriteEnable() {
|
||||
return Exchange(Dirty::DepthWriteEnable, false);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
@@ -21,15 +20,16 @@ namespace Vulkan {
|
||||
|
||||
namespace {
|
||||
|
||||
VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats) {
|
||||
VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats, bool srgb) {
|
||||
if (formats.size() == 1 && formats[0].format == VK_FORMAT_UNDEFINED) {
|
||||
VkSurfaceFormatKHR format;
|
||||
format.format = VK_FORMAT_B8G8R8A8_UNORM;
|
||||
format.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||
return format;
|
||||
}
|
||||
const auto& found = std::find_if(formats.begin(), formats.end(), [](const auto& format) {
|
||||
return format.format == VK_FORMAT_B8G8R8A8_UNORM &&
|
||||
const auto& found = std::find_if(formats.begin(), formats.end(), [srgb](const auto& format) {
|
||||
const auto request_format = srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
|
||||
return format.format == request_format &&
|
||||
format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||
});
|
||||
return found != formats.end() ? *found : formats[0];
|
||||
@@ -37,19 +37,8 @@ VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats)
|
||||
|
||||
VkPresentModeKHR ChooseSwapPresentMode(vk::Span<VkPresentModeKHR> modes) {
|
||||
// Mailbox doesn't lock the application like fifo (vsync), prefer it
|
||||
const auto found_mailbox = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR);
|
||||
if (found_mailbox != modes.end()) {
|
||||
return VK_PRESENT_MODE_MAILBOX_KHR;
|
||||
}
|
||||
if (Settings::values.disable_fps_limit.GetValue()) {
|
||||
// FIFO present mode locks the framerate to the monitor's refresh rate,
|
||||
// Find an alternative to surpass this limitation if FPS is unlocked.
|
||||
const auto found_imm = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_IMMEDIATE_KHR);
|
||||
if (found_imm != modes.end()) {
|
||||
return VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||
}
|
||||
}
|
||||
return VK_PRESENT_MODE_FIFO_KHR;
|
||||
const auto found = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR);
|
||||
return found != modes.end() ? *found : VK_PRESENT_MODE_FIFO_KHR;
|
||||
}
|
||||
|
||||
VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height) {
|
||||
@@ -118,12 +107,14 @@ void VKSwapchain::AcquireNextImage() {
|
||||
}
|
||||
|
||||
void VKSwapchain::Present(VkSemaphore render_semaphore) {
|
||||
const VkSemaphore present_semaphore{*present_semaphores[frame_index]};
|
||||
const std::array<VkSemaphore, 2> semaphores{present_semaphore, render_semaphore};
|
||||
const auto present_queue{device.GetPresentQueue()};
|
||||
const VkPresentInfoKHR present_info{
|
||||
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
||||
.pNext = nullptr,
|
||||
.waitSemaphoreCount = render_semaphore ? 1U : 0U,
|
||||
.pWaitSemaphores = &render_semaphore,
|
||||
.waitSemaphoreCount = render_semaphore ? 2U : 1U,
|
||||
.pWaitSemaphores = semaphores.data(),
|
||||
.swapchainCount = 1,
|
||||
.pSwapchains = swapchain.address(),
|
||||
.pImageIndices = &image_index,
|
||||
@@ -154,8 +145,8 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
|
||||
const auto formats{physical_device.GetSurfaceFormatsKHR(surface)};
|
||||
const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)};
|
||||
|
||||
const VkSurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats)};
|
||||
present_mode = ChooseSwapPresentMode(present_modes);
|
||||
const VkSurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats, srgb)};
|
||||
const VkPresentModeKHR present_mode{ChooseSwapPresentMode(present_modes)};
|
||||
|
||||
u32 requested_image_count{capabilities.minImageCount + 1};
|
||||
if (capabilities.maxImageCount > 0 && requested_image_count > capabilities.maxImageCount) {
|
||||
@@ -189,17 +180,6 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
|
||||
swapchain_ci.queueFamilyIndexCount = static_cast<u32>(queue_indices.size());
|
||||
swapchain_ci.pQueueFamilyIndices = queue_indices.data();
|
||||
}
|
||||
static constexpr std::array view_formats{VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SRGB};
|
||||
VkImageFormatListCreateInfo format_list{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR,
|
||||
.pNext = nullptr,
|
||||
.viewFormatCount = static_cast<u32>(view_formats.size()),
|
||||
.pViewFormats = view_formats.data(),
|
||||
};
|
||||
if (device.IsKhrSwapchainMutableFormatEnabled()) {
|
||||
format_list.pNext = std::exchange(swapchain_ci.pNext, &format_list);
|
||||
swapchain_ci.flags |= VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR;
|
||||
}
|
||||
// Request the size again to reduce the possibility of a TOCTOU race condition.
|
||||
const auto updated_capabilities = physical_device.GetSurfaceCapabilitiesKHR(surface);
|
||||
swapchain_ci.imageExtent = ChooseSwapExtent(updated_capabilities, width, height);
|
||||
@@ -208,11 +188,10 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
|
||||
|
||||
extent = swapchain_ci.imageExtent;
|
||||
current_srgb = srgb;
|
||||
current_fps_unlocked = Settings::values.disable_fps_limit.GetValue();
|
||||
|
||||
images = swapchain.GetImages();
|
||||
image_count = static_cast<u32>(images.size());
|
||||
image_view_format = srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
|
||||
image_format = surface_format.format;
|
||||
}
|
||||
|
||||
void VKSwapchain::CreateSemaphores() {
|
||||
@@ -228,7 +207,7 @@ void VKSwapchain::CreateImageViews() {
|
||||
.flags = 0,
|
||||
.image = {},
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = image_view_format,
|
||||
.format = image_format,
|
||||
.components =
|
||||
{
|
||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
@@ -261,14 +240,4 @@ void VKSwapchain::Destroy() {
|
||||
swapchain.reset();
|
||||
}
|
||||
|
||||
bool VKSwapchain::HasFpsUnlockChanged() const {
|
||||
return current_fps_unlocked != Settings::values.disable_fps_limit.GetValue();
|
||||
}
|
||||
|
||||
bool VKSwapchain::NeedsPresentModeUpdate() const {
|
||||
// Mailbox present mode is the ideal for all scenarios. If it is not available,
|
||||
// A different present mode is needed to support unlocked FPS above the monitor's refresh rate.
|
||||
return present_mode != VK_PRESENT_MODE_MAILBOX_KHR && HasFpsUnlockChanged();
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -33,11 +33,6 @@ public:
|
||||
/// Presents the rendered image to the swapchain.
|
||||
void Present(VkSemaphore render_semaphore);
|
||||
|
||||
/// Returns true when the swapchain needs to be recreated.
|
||||
bool NeedsRecreation(bool is_srgb) const {
|
||||
return HasColorSpaceChanged(is_srgb) || IsSubOptimal() || NeedsPresentModeUpdate();
|
||||
}
|
||||
|
||||
/// Returns true when the color space has changed.
|
||||
bool HasColorSpaceChanged(bool is_srgb) const {
|
||||
return current_srgb != is_srgb;
|
||||
@@ -73,12 +68,8 @@ public:
|
||||
return *image_views[index];
|
||||
}
|
||||
|
||||
VkFormat GetImageViewFormat() const {
|
||||
return image_view_format;
|
||||
}
|
||||
|
||||
VkSemaphore CurrentPresentSemaphore() const {
|
||||
return *present_semaphores[frame_index];
|
||||
VkFormat GetImageFormat() const {
|
||||
return image_format;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -89,10 +80,6 @@ private:
|
||||
|
||||
void Destroy();
|
||||
|
||||
bool HasFpsUnlockChanged() const;
|
||||
|
||||
bool NeedsPresentModeUpdate() const;
|
||||
|
||||
const VkSurfaceKHR surface;
|
||||
const Device& device;
|
||||
VKScheduler& scheduler;
|
||||
@@ -109,12 +96,10 @@ private:
|
||||
u32 image_index{};
|
||||
u32 frame_index{};
|
||||
|
||||
VkFormat image_view_format{};
|
||||
VkFormat image_format{};
|
||||
VkExtent2D extent{};
|
||||
VkPresentModeKHR present_mode{};
|
||||
|
||||
bool current_srgb{};
|
||||
bool current_fps_unlocked{};
|
||||
bool is_outdated{};
|
||||
bool is_suboptimal{};
|
||||
};
|
||||
|
||||
@@ -127,7 +127,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||
const auto format_info = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, format);
|
||||
VkImageCreateFlags flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
||||
if (info.type == ImageType::e2D && info.resources.layers >= 6 &&
|
||||
info.size.width == info.size.height && !device.HasBrokenCubeImageCompability()) {
|
||||
info.size.width == info.size.height) {
|
||||
flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
|
||||
}
|
||||
if (info.type == ImageType::e3D) {
|
||||
@@ -1186,12 +1186,9 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM
|
||||
renderpass_key.depth_format = depth_buffer->format;
|
||||
num_layers = std::max(num_layers, depth_buffer->range.extent.layers);
|
||||
images[num_images] = depth_buffer->ImageHandle();
|
||||
const VkImageSubresourceRange subresource_range = MakeSubresourceRange(depth_buffer);
|
||||
image_ranges[num_images] = subresource_range;
|
||||
image_ranges[num_images] = MakeSubresourceRange(depth_buffer);
|
||||
samples = depth_buffer->Samples();
|
||||
++num_images;
|
||||
has_depth = (subresource_range.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0;
|
||||
has_stencil = (subresource_range.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0;
|
||||
} else {
|
||||
renderpass_key.depth_format = PixelFormat::Invalid;
|
||||
}
|
||||
|
||||
@@ -232,18 +232,6 @@ public:
|
||||
return image_ranges;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasAspectColorBit(size_t index) const noexcept {
|
||||
return (image_ranges.at(index).aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasAspectDepthBit() const noexcept {
|
||||
return has_depth;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasAspectStencilBit() const noexcept {
|
||||
return has_stencil;
|
||||
}
|
||||
|
||||
private:
|
||||
vk::Framebuffer framebuffer;
|
||||
VkRenderPass renderpass{};
|
||||
@@ -253,8 +241,6 @@ private:
|
||||
u32 num_images = 0;
|
||||
std::array<VkImage, 9> images{};
|
||||
std::array<VkImageSubresourceRange, 9> image_ranges{};
|
||||
bool has_depth{};
|
||||
bool has_stencil{};
|
||||
};
|
||||
|
||||
struct TextureCacheParams {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <concepts>
|
||||
|
||||
@@ -37,8 +37,7 @@ std::unique_ptr<VideoCore::RendererBase> CreateRenderer(
|
||||
namespace VideoCore {
|
||||
|
||||
std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) {
|
||||
const auto nvdec_value = Settings::values.nvdec_emulation.GetValue();
|
||||
const bool use_nvdec = nvdec_value != Settings::NvdecEmulation::Off;
|
||||
const bool use_nvdec = Settings::values.use_nvdec_emulation.GetValue();
|
||||
const bool use_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
||||
auto gpu = std::make_unique<Tegra::GPU>(system, use_async, use_nvdec);
|
||||
auto context = emu_window.CreateSharedContext();
|
||||
|
||||
@@ -16,7 +16,6 @@ VkBool32 Callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
||||
switch (static_cast<u32>(data->messageIdNumber)) {
|
||||
case 0x682a878au: // VUID-vkCmdBindVertexBuffers2EXT-pBuffers-parameter
|
||||
case 0x99fb7dfdu: // UNASSIGNED-RequiredParameter (vkCmdBindVertexBuffers2EXT pBuffers[0])
|
||||
case 0xe8616bf2u: // Bound VkDescriptorSet 0x0[] was destroyed. Likely push_descriptor related
|
||||
return VK_FALSE;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -368,9 +368,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
};
|
||||
SetNext(next, demote);
|
||||
|
||||
VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8;
|
||||
if (is_int8_supported || is_float16_supported) {
|
||||
float16_int8 = {
|
||||
VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR,
|
||||
.pNext = nullptr,
|
||||
.shaderFloat16 = is_float16_supported,
|
||||
@@ -588,31 +587,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
ext_extended_dynamic_state = false;
|
||||
}
|
||||
}
|
||||
sets_per_pool = 64;
|
||||
|
||||
const bool is_amd =
|
||||
driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE;
|
||||
if (is_amd) {
|
||||
// AMD drivers need a higher amount of Sets per Pool in certain circunstances like in XC2.
|
||||
sets_per_pool = 96;
|
||||
// Disable VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT on AMD GCN4 and lower as it is broken.
|
||||
if (!is_float16_supported) {
|
||||
LOG_WARNING(
|
||||
Render_Vulkan,
|
||||
"AMD GCN4 and earlier do not properly support VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT");
|
||||
has_broken_cube_compatibility = true;
|
||||
}
|
||||
}
|
||||
const bool is_amd_or_radv = is_amd || driver_id == VK_DRIVER_ID_MESA_RADV;
|
||||
if (ext_sampler_filter_minmax && is_amd_or_radv) {
|
||||
// Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
|
||||
if (!is_float16_supported) {
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Blacklisting AMD GCN4 and earlier for VK_EXT_sampler_filter_minmax");
|
||||
ext_sampler_filter_minmax = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ext_vertex_input_dynamic_state && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
|
||||
LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state");
|
||||
ext_vertex_input_dynamic_state = false;
|
||||
@@ -865,8 +839,6 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
|
||||
bool has_khr_shader_float16_int8{};
|
||||
bool has_khr_workgroup_memory_explicit_layout{};
|
||||
bool has_khr_pipeline_executable_properties{};
|
||||
bool has_khr_image_format_list{};
|
||||
bool has_khr_swapchain_mutable_format{};
|
||||
bool has_ext_subgroup_size_control{};
|
||||
bool has_ext_transform_feedback{};
|
||||
bool has_ext_custom_border_color{};
|
||||
@@ -916,9 +888,6 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
|
||||
test(has_ext_shader_atomic_int64, VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME, false);
|
||||
test(has_khr_workgroup_memory_explicit_layout,
|
||||
VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME, false);
|
||||
test(has_khr_image_format_list, VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, false);
|
||||
test(has_khr_swapchain_mutable_format, VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME,
|
||||
false);
|
||||
test(has_ext_line_rasterization, VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false);
|
||||
if (Settings::values.enable_nsight_aftermath) {
|
||||
test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
|
||||
@@ -1097,11 +1066,6 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
|
||||
khr_pipeline_executable_properties = true;
|
||||
}
|
||||
}
|
||||
if (has_khr_image_format_list && has_khr_swapchain_mutable_format) {
|
||||
extensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME);
|
||||
extensions.push_back(VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME);
|
||||
khr_swapchain_mutable_format = true;
|
||||
}
|
||||
if (khr_push_descriptor) {
|
||||
VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor;
|
||||
push_descriptor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR;
|
||||
|
||||
@@ -224,11 +224,6 @@ public:
|
||||
return khr_pipeline_executable_properties;
|
||||
}
|
||||
|
||||
/// Returns true if VK_KHR_swapchain_mutable_format is enabled.
|
||||
bool IsKhrSwapchainMutableFormatEnabled() const {
|
||||
return khr_swapchain_mutable_format;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout.
|
||||
bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const {
|
||||
return khr_workgroup_memory_explicit_layout;
|
||||
@@ -309,11 +304,6 @@ public:
|
||||
return has_renderdoc || has_nsight_graphics;
|
||||
}
|
||||
|
||||
/// Returns true when the device does not properly support cube compatibility.
|
||||
bool HasBrokenCubeImageCompability() const {
|
||||
return has_broken_cube_compatibility;
|
||||
}
|
||||
|
||||
/// Returns the vendor name reported from Vulkan.
|
||||
std::string_view GetVendorName() const {
|
||||
return vendor_name;
|
||||
@@ -328,10 +318,6 @@ public:
|
||||
return device_access_memory;
|
||||
}
|
||||
|
||||
u32 GetSetsPerPool() const {
|
||||
return sets_per_pool;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Checks if the physical device is suitable.
|
||||
void CheckSuitability(bool requires_swapchain) const;
|
||||
@@ -385,7 +371,6 @@ private:
|
||||
VkShaderStageFlags guest_warp_stages{}; ///< Stages where the guest warp size can be forced.
|
||||
u64 device_access_memory{}; ///< Total size of device local memory in bytes.
|
||||
u32 max_push_descriptors{}; ///< Maximum number of push descriptors
|
||||
u32 sets_per_pool{}; ///< Sets per Description Pool
|
||||
bool is_optimal_astc_supported{}; ///< Support for native ASTC.
|
||||
bool is_float16_supported{}; ///< Support for float16 arithmetic.
|
||||
bool is_int8_supported{}; ///< Support for int8 arithmetic.
|
||||
@@ -405,7 +390,6 @@ private:
|
||||
bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts.
|
||||
bool khr_push_descriptor{}; ///< Support for VK_KHR_push_descritor.
|
||||
bool khr_pipeline_executable_properties{}; ///< Support for executable properties.
|
||||
bool khr_swapchain_mutable_format{}; ///< Support for VK_KHR_swapchain_mutable_format.
|
||||
bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
|
||||
bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
|
||||
bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
|
||||
@@ -422,7 +406,6 @@ private:
|
||||
bool ext_conservative_rasterization{}; ///< Support for VK_EXT_conservative_rasterization.
|
||||
bool ext_provoking_vertex{}; ///< Support for VK_EXT_provoking_vertex.
|
||||
bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
|
||||
bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit
|
||||
bool has_renderdoc{}; ///< Has RenderDoc attached
|
||||
bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
|
||||
|
||||
|
||||
@@ -108,9 +108,6 @@ add_executable(yuzu
|
||||
configuration/configure_system.cpp
|
||||
configuration/configure_system.h
|
||||
configuration/configure_system.ui
|
||||
configuration/configure_tas.cpp
|
||||
configuration/configure_tas.h
|
||||
configuration/configure_tas.ui
|
||||
configuration/configure_touch_from_button.cpp
|
||||
configuration/configure_touch_from_button.h
|
||||
configuration/configure_touch_from_button.ui
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
#include "input_common/keyboard.h"
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/mouse/mouse_input.h"
|
||||
#include "input_common/tas/tas_input.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
#include "yuzu/bootmanager.h"
|
||||
@@ -313,7 +312,6 @@ GRenderWindow::~GRenderWindow() {
|
||||
}
|
||||
|
||||
void GRenderWindow::OnFrameDisplayed() {
|
||||
input_subsystem->GetTas()->UpdateThread();
|
||||
if (!first_frame) {
|
||||
first_frame = true;
|
||||
emit FirstFrameDisplayed();
|
||||
|
||||
@@ -221,7 +221,7 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default
|
||||
// This must be in alphabetical order according to action name as it must have the same order as
|
||||
// UISetting::values.shortcuts, which is alphabetically ordered.
|
||||
// clang-format off
|
||||
const std::array<UISettings::Shortcut, 21> Config::default_hotkeys{{
|
||||
const std::array<UISettings::Shortcut, 18> Config::default_hotkeys{{
|
||||
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
|
||||
@@ -235,9 +235,6 @@ const std::array<UISettings::Shortcut, 21> Config::default_hotkeys{{
|
||||
{QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), Qt::ApplicationShortcut}},
|
||||
@@ -545,6 +542,7 @@ void Config::ReadAudioValues() {
|
||||
ReadBasicSetting(Settings::values.audio_device_id);
|
||||
ReadBasicSetting(Settings::values.sink_id);
|
||||
}
|
||||
ReadGlobalSetting(Settings::values.enable_audio_stretching);
|
||||
ReadGlobalSetting(Settings::values.volume);
|
||||
|
||||
qt_config->endGroup();
|
||||
@@ -562,16 +560,10 @@ void Config::ReadControlValues() {
|
||||
ReadTouchscreenValues();
|
||||
ReadMotionTouchValues();
|
||||
|
||||
ReadBasicSetting(Settings::values.enable_raw_input);
|
||||
ReadBasicSetting(Settings::values.emulate_analog_keyboard);
|
||||
Settings::values.mouse_panning = false;
|
||||
ReadBasicSetting(Settings::values.mouse_panning_sensitivity);
|
||||
|
||||
ReadBasicSetting(Settings::values.tas_enable);
|
||||
ReadBasicSetting(Settings::values.tas_loop);
|
||||
ReadBasicSetting(Settings::values.tas_swap_controllers);
|
||||
ReadBasicSetting(Settings::values.pause_tas_on_load);
|
||||
|
||||
ReadGlobalSetting(Settings::values.use_docked_mode);
|
||||
|
||||
// Disable docked mode if handheld is selected
|
||||
@@ -669,13 +661,6 @@ void Config::ReadDataStorageValues() {
|
||||
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)))
|
||||
.toString()
|
||||
.toStdString());
|
||||
FS::SetYuzuPath(FS::YuzuPath::TASDir,
|
||||
qt_config
|
||||
->value(QStringLiteral("tas_directory"),
|
||||
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)))
|
||||
.toString()
|
||||
.toStdString());
|
||||
|
||||
ReadBasicSetting(Settings::values.gamecard_inserted);
|
||||
ReadBasicSetting(Settings::values.gamecard_current_game);
|
||||
ReadBasicSetting(Settings::values.gamecard_path);
|
||||
@@ -827,7 +812,7 @@ void Config::ReadRendererValues() {
|
||||
ReadGlobalSetting(Settings::values.use_disk_shader_cache);
|
||||
ReadGlobalSetting(Settings::values.gpu_accuracy);
|
||||
ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation);
|
||||
ReadGlobalSetting(Settings::values.nvdec_emulation);
|
||||
ReadGlobalSetting(Settings::values.use_nvdec_emulation);
|
||||
ReadGlobalSetting(Settings::values.accelerate_astc);
|
||||
ReadGlobalSetting(Settings::values.use_vsync);
|
||||
ReadGlobalSetting(Settings::values.shader_backend);
|
||||
@@ -1178,6 +1163,7 @@ void Config::SaveAudioValues() {
|
||||
WriteBasicSetting(Settings::values.sink_id);
|
||||
WriteBasicSetting(Settings::values.audio_device_id);
|
||||
}
|
||||
WriteGlobalSetting(Settings::values.enable_audio_stretching);
|
||||
WriteGlobalSetting(Settings::values.volume);
|
||||
|
||||
qt_config->endGroup();
|
||||
@@ -1198,16 +1184,10 @@ void Config::SaveControlValues() {
|
||||
WriteGlobalSetting(Settings::values.vibration_enabled);
|
||||
WriteGlobalSetting(Settings::values.enable_accurate_vibrations);
|
||||
WriteGlobalSetting(Settings::values.motion_enabled);
|
||||
WriteBasicSetting(Settings::values.enable_raw_input);
|
||||
WriteBasicSetting(Settings::values.keyboard_enabled);
|
||||
WriteBasicSetting(Settings::values.emulate_analog_keyboard);
|
||||
WriteBasicSetting(Settings::values.mouse_panning_sensitivity);
|
||||
|
||||
WriteBasicSetting(Settings::values.tas_enable);
|
||||
WriteBasicSetting(Settings::values.tas_loop);
|
||||
WriteBasicSetting(Settings::values.tas_swap_controllers);
|
||||
WriteBasicSetting(Settings::values.pause_tas_on_load);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
@@ -1235,10 +1215,6 @@ void Config::SaveDataStorageValues() {
|
||||
WriteSetting(QStringLiteral("dump_directory"),
|
||||
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)),
|
||||
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
|
||||
WriteSetting(QStringLiteral("tas_directory"),
|
||||
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)),
|
||||
QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)));
|
||||
|
||||
WriteBasicSetting(Settings::values.gamecard_inserted);
|
||||
WriteBasicSetting(Settings::values.gamecard_current_game);
|
||||
WriteBasicSetting(Settings::values.gamecard_path);
|
||||
@@ -1373,10 +1349,7 @@ void Config::SaveRendererValues() {
|
||||
static_cast<u32>(Settings::values.gpu_accuracy.GetDefault()),
|
||||
Settings::values.gpu_accuracy.UsingGlobal());
|
||||
WriteGlobalSetting(Settings::values.use_asynchronous_gpu_emulation);
|
||||
WriteSetting(QString::fromStdString(Settings::values.nvdec_emulation.GetLabel()),
|
||||
static_cast<u32>(Settings::values.nvdec_emulation.GetValue(global)),
|
||||
static_cast<u32>(Settings::values.nvdec_emulation.GetDefault()),
|
||||
Settings::values.nvdec_emulation.UsingGlobal());
|
||||
WriteGlobalSetting(Settings::values.use_nvdec_emulation);
|
||||
WriteGlobalSetting(Settings::values.accelerate_astc);
|
||||
WriteGlobalSetting(Settings::values.use_vsync);
|
||||
WriteSetting(QString::fromStdString(Settings::values.shader_backend.GetLabel()),
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
default_mouse_buttons;
|
||||
static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
|
||||
static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods;
|
||||
static const std::array<UISettings::Shortcut, 21> default_hotkeys;
|
||||
static const std::array<UISettings::Shortcut, 18> default_hotkeys;
|
||||
|
||||
private:
|
||||
void Initialize(const std::string& config_name);
|
||||
@@ -182,6 +182,5 @@ private:
|
||||
Q_DECLARE_METATYPE(Settings::CPUAccuracy);
|
||||
Q_DECLARE_METATYPE(Settings::GPUAccuracy);
|
||||
Q_DECLARE_METATYPE(Settings::FullscreenMode);
|
||||
Q_DECLARE_METATYPE(Settings::NvdecEmulation);
|
||||
Q_DECLARE_METATYPE(Settings::RendererBackend);
|
||||
Q_DECLARE_METATYPE(Settings::ShaderBackend);
|
||||
|
||||
@@ -50,6 +50,8 @@ void ConfigureAudio::SetConfiguration() {
|
||||
const auto volume_value = static_cast<int>(Settings::values.volume.GetValue());
|
||||
ui->volume_slider->setValue(volume_value);
|
||||
|
||||
ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching.GetValue());
|
||||
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
if (Settings::values.volume.UsingGlobal()) {
|
||||
ui->volume_combo_box->setCurrentIndex(0);
|
||||
@@ -98,6 +100,8 @@ void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
|
||||
}
|
||||
|
||||
void ConfigureAudio::ApplyConfiguration() {
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_audio_stretching,
|
||||
ui->toggle_audio_stretching, enable_audio_stretching);
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
Settings::values.sink_id =
|
||||
@@ -158,10 +162,15 @@ void ConfigureAudio::RetranslateUI() {
|
||||
void ConfigureAudio::SetupPerGameUI() {
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal());
|
||||
ui->toggle_audio_stretching->setEnabled(
|
||||
Settings::values.enable_audio_stretching.UsingGlobal());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_audio_stretching,
|
||||
Settings::values.enable_audio_stretching,
|
||||
enable_audio_stretching);
|
||||
connect(ui->volume_combo_box, qOverload<int>(&QComboBox::activated), this, [this](int index) {
|
||||
ui->volume_slider->setEnabled(index == 1);
|
||||
ConfigurationShared::SetHighlight(ui->volume_layout, index == 1);
|
||||
|
||||
@@ -41,4 +41,6 @@ private:
|
||||
void SetupPerGameUI();
|
||||
|
||||
std::unique_ptr<Ui::ConfigureAudio> ui;
|
||||
|
||||
ConfigurationShared::CheckState enable_audio_stretching;
|
||||
};
|
||||
|
||||
@@ -31,6 +31,16 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_audio_stretching">
|
||||
<property name="toolTip">
|
||||
<string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable audio stretching</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="_2">
|
||||
<item>
|
||||
|
||||
@@ -88,30 +88,24 @@ void ConfigureGraphics::SetConfiguration() {
|
||||
ui->api_widget->setEnabled(runtime_lock);
|
||||
ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock);
|
||||
ui->use_disk_shader_cache->setEnabled(runtime_lock);
|
||||
ui->nvdec_emulation_widget->setEnabled(runtime_lock);
|
||||
ui->use_nvdec_emulation->setEnabled(runtime_lock);
|
||||
ui->accelerate_astc->setEnabled(runtime_lock);
|
||||
ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
|
||||
ui->use_asynchronous_gpu_emulation->setChecked(
|
||||
Settings::values.use_asynchronous_gpu_emulation.GetValue());
|
||||
ui->use_nvdec_emulation->setChecked(Settings::values.use_nvdec_emulation.GetValue());
|
||||
ui->accelerate_astc->setChecked(Settings::values.accelerate_astc.GetValue());
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue()));
|
||||
ui->fullscreen_mode_combobox->setCurrentIndex(
|
||||
static_cast<int>(Settings::values.fullscreen_mode.GetValue()));
|
||||
ui->nvdec_emulation->setCurrentIndex(
|
||||
static_cast<int>(Settings::values.nvdec_emulation.GetValue()));
|
||||
ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
|
||||
} else {
|
||||
ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
|
||||
ConfigurationShared::SetHighlight(ui->api_widget,
|
||||
!Settings::values.renderer_backend.UsingGlobal());
|
||||
|
||||
ConfigurationShared::SetPerGameSetting(ui->nvdec_emulation,
|
||||
&Settings::values.nvdec_emulation);
|
||||
ConfigurationShared::SetHighlight(ui->nvdec_emulation_widget,
|
||||
!Settings::values.nvdec_emulation.UsingGlobal());
|
||||
|
||||
ConfigurationShared::SetPerGameSetting(ui->fullscreen_mode_combobox,
|
||||
&Settings::values.fullscreen_mode);
|
||||
ConfigurationShared::SetHighlight(ui->fullscreen_mode_label,
|
||||
@@ -143,6 +137,8 @@ void ConfigureGraphics::ApplyConfiguration() {
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation,
|
||||
ui->use_asynchronous_gpu_emulation,
|
||||
use_asynchronous_gpu_emulation);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_nvdec_emulation,
|
||||
ui->use_nvdec_emulation, use_nvdec_emulation);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.accelerate_astc, ui->accelerate_astc,
|
||||
accelerate_astc);
|
||||
|
||||
@@ -151,9 +147,6 @@ void ConfigureGraphics::ApplyConfiguration() {
|
||||
if (Settings::values.renderer_backend.UsingGlobal()) {
|
||||
Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
|
||||
}
|
||||
if (Settings::values.nvdec_emulation.UsingGlobal()) {
|
||||
Settings::values.nvdec_emulation.SetValue(GetCurrentNvdecEmulation());
|
||||
}
|
||||
if (Settings::values.shader_backend.UsingGlobal()) {
|
||||
Settings::values.shader_backend.SetValue(shader_backend);
|
||||
}
|
||||
@@ -187,13 +180,6 @@ void ConfigureGraphics::ApplyConfiguration() {
|
||||
}
|
||||
}
|
||||
|
||||
if (ui->nvdec_emulation->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
Settings::values.nvdec_emulation.SetGlobal(true);
|
||||
} else {
|
||||
Settings::values.nvdec_emulation.SetGlobal(false);
|
||||
Settings::values.nvdec_emulation.SetValue(GetCurrentNvdecEmulation());
|
||||
}
|
||||
|
||||
if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
Settings::values.bg_red.SetGlobal(true);
|
||||
Settings::values.bg_green.SetGlobal(true);
|
||||
@@ -292,20 +278,6 @@ Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
|
||||
ConfigurationShared::USE_GLOBAL_OFFSET);
|
||||
}
|
||||
|
||||
Settings::NvdecEmulation ConfigureGraphics::GetCurrentNvdecEmulation() const {
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
return static_cast<Settings::NvdecEmulation>(ui->nvdec_emulation->currentIndex());
|
||||
}
|
||||
|
||||
if (ui->nvdec_emulation->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
Settings::values.nvdec_emulation.SetGlobal(true);
|
||||
return Settings::values.nvdec_emulation.GetValue();
|
||||
}
|
||||
Settings::values.nvdec_emulation.SetGlobal(false);
|
||||
return static_cast<Settings::NvdecEmulation>(ui->nvdec_emulation->currentIndex() -
|
||||
ConfigurationShared::USE_GLOBAL_OFFSET);
|
||||
}
|
||||
|
||||
void ConfigureGraphics::SetupPerGameUI() {
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal());
|
||||
@@ -314,7 +286,7 @@ void ConfigureGraphics::SetupPerGameUI() {
|
||||
ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal());
|
||||
ui->use_asynchronous_gpu_emulation->setEnabled(
|
||||
Settings::values.use_asynchronous_gpu_emulation.UsingGlobal());
|
||||
ui->nvdec_emulation->setEnabled(Settings::values.nvdec_emulation.UsingGlobal());
|
||||
ui->use_nvdec_emulation->setEnabled(Settings::values.use_nvdec_emulation.UsingGlobal());
|
||||
ui->accelerate_astc->setEnabled(Settings::values.accelerate_astc.UsingGlobal());
|
||||
ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal());
|
||||
ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal());
|
||||
@@ -329,6 +301,8 @@ void ConfigureGraphics::SetupPerGameUI() {
|
||||
|
||||
ConfigurationShared::SetColoredTristate(
|
||||
ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache);
|
||||
ConfigurationShared::SetColoredTristate(
|
||||
ui->use_nvdec_emulation, Settings::values.use_nvdec_emulation, use_nvdec_emulation);
|
||||
ConfigurationShared::SetColoredTristate(ui->accelerate_astc, Settings::values.accelerate_astc,
|
||||
accelerate_astc);
|
||||
ConfigurationShared::SetColoredTristate(ui->use_asynchronous_gpu_emulation,
|
||||
@@ -342,6 +316,4 @@ void ConfigureGraphics::SetupPerGameUI() {
|
||||
static_cast<int>(Settings::values.fullscreen_mode.GetValue(true)));
|
||||
ConfigurationShared::InsertGlobalItem(
|
||||
ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
|
||||
ConfigurationShared::InsertGlobalItem(
|
||||
ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true)));
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ private:
|
||||
void SetupPerGameUI();
|
||||
|
||||
Settings::RendererBackend GetCurrentGraphicsBackend() const;
|
||||
Settings::NvdecEmulation GetCurrentNvdecEmulation() const;
|
||||
|
||||
std::unique_ptr<Ui::ConfigureGraphics> ui;
|
||||
QColor bg_color;
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="use_disk_shader_cache">
|
||||
<property name="text">
|
||||
<string>Use disk pipeline cache</string>
|
||||
<string>Use disk shader cache</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -167,6 +167,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="use_nvdec_emulation">
|
||||
<property name="text">
|
||||
<string>Use NVDEC emulation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="accelerate_astc">
|
||||
<property name="text">
|
||||
@@ -174,50 +181,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="nvdec_emulation_widget" native="true">
|
||||
<layout class="QHBoxLayout" name="nvdec_emulation_layout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="nvdec_emulation_label">
|
||||
<property name="text">
|
||||
<string>NVDEC emulation:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="nvdec_emulation">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Disabled</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>CPU Decoding</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>GPU Decoding</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="fullscreen_mode_layout" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_1">
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
<string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use asynchronous shader building (Hack)</string>
|
||||
<string>Use asynchronous shader building (hack)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -92,7 +92,7 @@
|
||||
<string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use Fast GPU Time (Hack)</string>
|
||||
<string>Use Fast GPU Time (hack)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -126,7 +126,6 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
|
||||
Settings::values.mouse_panning_sensitivity =
|
||||
static_cast<float>(ui->mouse_panning_sensitivity->value());
|
||||
Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
|
||||
Settings::values.enable_raw_input = ui->enable_raw_input->isChecked();
|
||||
}
|
||||
|
||||
void ConfigureInputAdvanced::LoadConfiguration() {
|
||||
@@ -156,7 +155,6 @@ void ConfigureInputAdvanced::LoadConfiguration() {
|
||||
ui->mouse_panning->setChecked(Settings::values.mouse_panning.GetValue());
|
||||
ui->mouse_panning_sensitivity->setValue(Settings::values.mouse_panning_sensitivity.GetValue());
|
||||
ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
|
||||
ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue());
|
||||
|
||||
UpdateUIEnabled();
|
||||
}
|
||||
|
||||
@@ -2672,22 +2672,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QCheckBox" name="enable_raw_input">
|
||||
<property name="toolTip">
|
||||
<string>Requires restarting yuzu</string>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>23</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable XInput 8 player support (disables web applet)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -124,19 +124,6 @@ QString ButtonToText(const Common::ParamPackage& param) {
|
||||
return GetKeyName(param.Get("code", 0));
|
||||
}
|
||||
|
||||
if (param.Get("engine", "") == "tas") {
|
||||
if (param.Has("axis")) {
|
||||
const QString axis_str = QString::fromStdString(param.Get("axis", ""));
|
||||
|
||||
return QObject::tr("TAS Axis %1").arg(axis_str);
|
||||
}
|
||||
if (param.Has("button")) {
|
||||
const QString button_str = QString::number(int(std::log2(param.Get("button", 0))));
|
||||
return QObject::tr("TAS Btn %1").arg(button_str);
|
||||
}
|
||||
return GetKeyName(param.Get("code", 0));
|
||||
}
|
||||
|
||||
if (param.Get("engine", "") == "cemuhookudp") {
|
||||
if (param.Has("pad_index")) {
|
||||
const QString motion_str = QString::fromStdString(param.Get("pad_index", ""));
|
||||
@@ -200,8 +187,7 @@ QString AnalogToText(const Common::ParamPackage& param, const std::string& dir)
|
||||
const QString axis_y_str = QString::fromStdString(param.Get("axis_y", ""));
|
||||
const bool invert_x = param.Get("invert_x", "+") == "-";
|
||||
const bool invert_y = param.Get("invert_y", "+") == "-";
|
||||
if (engine_str == "sdl" || engine_str == "gcpad" || engine_str == "mouse" ||
|
||||
engine_str == "tas") {
|
||||
if (engine_str == "sdl" || engine_str == "gcpad" || engine_str == "mouse") {
|
||||
if (dir == "modifier") {
|
||||
return QObject::tr("[unused]");
|
||||
}
|
||||
@@ -940,9 +926,9 @@ void ConfigureInputPlayer::UpdateUI() {
|
||||
|
||||
int slider_value;
|
||||
auto& param = analogs_param[analog_id];
|
||||
const bool is_controller =
|
||||
param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad" ||
|
||||
param.Get("engine", "") == "mouse" || param.Get("engine", "") == "tas";
|
||||
const bool is_controller = param.Get("engine", "") == "sdl" ||
|
||||
param.Get("engine", "") == "gcpad" ||
|
||||
param.Get("engine", "") == "mouse";
|
||||
|
||||
if (is_controller) {
|
||||
if (!param.Has("deadzone")) {
|
||||
@@ -1059,12 +1045,8 @@ int ConfigureInputPlayer::GetIndexFromControllerType(Settings::ControllerType ty
|
||||
void ConfigureInputPlayer::UpdateInputDevices() {
|
||||
input_devices = input_subsystem->GetInputDevices();
|
||||
ui->comboDevices->clear();
|
||||
for (auto& device : input_devices) {
|
||||
const std::string display = device.Get("display", "Unknown");
|
||||
ui->comboDevices->addItem(QString::fromStdString(display), {});
|
||||
if (display == "TAS") {
|
||||
device.Set("pad", static_cast<u8>(player_index));
|
||||
}
|
||||
for (auto device : input_devices) {
|
||||
ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ void PlayerControlPreview::ResetInputs() {
|
||||
}
|
||||
|
||||
void PlayerControlPreview::UpdateInput() {
|
||||
if (!is_enabled && !mapping_active && !Settings::values.tas_enable) {
|
||||
if (!is_enabled && !mapping_active) {
|
||||
return;
|
||||
}
|
||||
bool input_changed = false;
|
||||
@@ -222,19 +222,6 @@ void PlayerControlPreview::UpdateInput() {
|
||||
|
||||
if (input_changed) {
|
||||
update();
|
||||
if (controller_callback.input != nullptr) {
|
||||
ControllerInput input{
|
||||
.axis_values = {std::pair<float, float>{
|
||||
axis_values[Settings::NativeAnalog::LStick].value.x(),
|
||||
axis_values[Settings::NativeAnalog::LStick].value.y()},
|
||||
std::pair<float, float>{
|
||||
axis_values[Settings::NativeAnalog::RStick].value.x(),
|
||||
axis_values[Settings::NativeAnalog::RStick].value.y()}},
|
||||
.button_values = button_values,
|
||||
.changed = true,
|
||||
};
|
||||
controller_callback.input(std::move(input));
|
||||
}
|
||||
}
|
||||
|
||||
if (mapping_active) {
|
||||
@@ -242,10 +229,6 @@ void PlayerControlPreview::UpdateInput() {
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerControlPreview::SetCallBack(ControllerCallback callback_) {
|
||||
controller_callback = std::move(callback_);
|
||||
}
|
||||
|
||||
void PlayerControlPreview::paintEvent(QPaintEvent* event) {
|
||||
QFrame::paintEvent(event);
|
||||
QPainter p(this);
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <QPointer>
|
||||
#include "common/settings.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "yuzu/debugger/controller.h"
|
||||
|
||||
class QLabel;
|
||||
|
||||
@@ -34,7 +33,6 @@ public:
|
||||
void BeginMappingAnalog(std::size_t button_id);
|
||||
void EndMapping();
|
||||
void UpdateInput();
|
||||
void SetCallBack(ControllerCallback callback_);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
@@ -183,7 +181,6 @@ private:
|
||||
using StickArray =
|
||||
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>;
|
||||
|
||||
ControllerCallback controller_callback;
|
||||
bool is_enabled{};
|
||||
bool mapping_active{};
|
||||
int blink_counter{};
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/settings.h"
|
||||
#include "ui_configure_tas.h"
|
||||
#include "yuzu/configuration/configure_tas.h"
|
||||
#include "yuzu/uisettings.h"
|
||||
|
||||
ConfigureTasDialog::ConfigureTasDialog(QWidget* parent)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::ConfigureTas>()) {
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
setWindowTitle(tr("TAS Configuration"));
|
||||
|
||||
connect(ui->tas_path_button, &QToolButton::pressed, this,
|
||||
[this] { SetDirectory(DirectoryTarget::TAS, ui->tas_path_edit); });
|
||||
|
||||
LoadConfiguration();
|
||||
}
|
||||
|
||||
ConfigureTasDialog::~ConfigureTasDialog() = default;
|
||||
|
||||
void ConfigureTasDialog::LoadConfiguration() {
|
||||
ui->tas_path_edit->setText(
|
||||
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASDir)));
|
||||
ui->tas_enable->setChecked(Settings::values.tas_enable.GetValue());
|
||||
ui->tas_control_swap->setChecked(Settings::values.tas_swap_controllers.GetValue());
|
||||
ui->tas_loop_script->setChecked(Settings::values.tas_loop.GetValue());
|
||||
ui->tas_pause_on_load->setChecked(Settings::values.pause_tas_on_load.GetValue());
|
||||
}
|
||||
|
||||
void ConfigureTasDialog::ApplyConfiguration() {
|
||||
Common::FS::SetYuzuPath(Common::FS::YuzuPath::TASDir, ui->tas_path_edit->text().toStdString());
|
||||
Settings::values.tas_enable.SetValue(ui->tas_enable->isChecked());
|
||||
Settings::values.tas_swap_controllers.SetValue(ui->tas_control_swap->isChecked());
|
||||
Settings::values.tas_loop.SetValue(ui->tas_loop_script->isChecked());
|
||||
Settings::values.pause_tas_on_load.SetValue(ui->tas_pause_on_load->isChecked());
|
||||
}
|
||||
|
||||
void ConfigureTasDialog::SetDirectory(DirectoryTarget target, QLineEdit* edit) {
|
||||
QString caption;
|
||||
|
||||
switch (target) {
|
||||
case DirectoryTarget::TAS:
|
||||
caption = tr("Select TAS Load Directory...");
|
||||
break;
|
||||
}
|
||||
|
||||
QString str = QFileDialog::getExistingDirectory(this, caption, edit->text());
|
||||
|
||||
if (str.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (str.back() != QChar::fromLatin1('/')) {
|
||||
str.append(QChar::fromLatin1('/'));
|
||||
}
|
||||
|
||||
edit->setText(str);
|
||||
}
|
||||
|
||||
void ConfigureTasDialog::changeEvent(QEvent* event) {
|
||||
if (event->type() == QEvent::LanguageChange) {
|
||||
RetranslateUI();
|
||||
}
|
||||
|
||||
QDialog::changeEvent(event);
|
||||
}
|
||||
|
||||
void ConfigureTasDialog::RetranslateUI() {
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
|
||||
void ConfigureTasDialog::HandleApplyButtonClicked() {
|
||||
UISettings::values.configuration_applied = true;
|
||||
ApplyConfiguration();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user