Compare commits
87 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dec1cbaf7f | ||
|
|
769a50661a | ||
|
|
e1b3be7ced | ||
|
|
04ac7a637a | ||
|
|
adb8a9152b | ||
|
|
7322c8bd7c | ||
|
|
03d9bbaa90 | ||
|
|
bf4dfb3ad4 | ||
|
|
a20ba09bfd | ||
|
|
55c5029171 | ||
|
|
e4fea833d4 | ||
|
|
8d7a012297 | ||
|
|
0bcc305797 | ||
|
|
8304aaf282 | ||
|
|
d7d5bffa18 | ||
|
|
799302bc9d | ||
|
|
81e09bb121 | ||
|
|
6aff1005ef | ||
|
|
5b93290183 | ||
|
|
b5e1e87922 | ||
|
|
e1d755bdda | ||
|
|
2beaaa35c5 | ||
|
|
1eb979221f | ||
|
|
dd4fe0dab1 | ||
|
|
433ca686a8 | ||
|
|
6ce5f3e1bf | ||
|
|
dfc40a3f9e | ||
|
|
4f7a1f6c8c | ||
|
|
19dcb50692 | ||
|
|
2ba4aa8a3b | ||
|
|
2548661c08 | ||
|
|
42f5fd0ab3 | ||
|
|
79189c7e3e | ||
|
|
55f8111543 | ||
|
|
b35953e4fd | ||
|
|
a32c52b1d8 | ||
|
|
5ccf2a7b82 | ||
|
|
cfac942e63 | ||
|
|
b9b23c98ff | ||
|
|
50048d9f5a | ||
|
|
ca7ca2919c | ||
|
|
b73ea457cc | ||
|
|
2c679cda51 | ||
|
|
b46e615551 | ||
|
|
7ea07c6063 | ||
|
|
cf0d01a5d7 | ||
|
|
00f0827a26 | ||
|
|
77ce85f51d | ||
|
|
e32bf646cf | ||
|
|
536c9cf006 | ||
|
|
0a650ec99e | ||
|
|
2575403acf | ||
|
|
cfb59aad3f | ||
|
|
49e3a6e924 | ||
|
|
0935c2d97b | ||
|
|
e70f16fff7 | ||
|
|
1edf018319 | ||
|
|
e72b9044a0 | ||
|
|
ad321564ed | ||
|
|
838b6d2ff8 | ||
|
|
e9772a2539 | ||
|
|
3f11d1c821 | ||
|
|
d9c1b94f03 | ||
|
|
cfc9d92b38 | ||
|
|
802dd3cc95 | ||
|
|
139301c5a1 | ||
|
|
8bbe930fac | ||
|
|
c6f05b586f | ||
|
|
84a8fb9264 | ||
|
|
b1a4ab2ccc | ||
|
|
215fd82738 | ||
|
|
05af9d915c | ||
|
|
2fb3b9b951 | ||
|
|
52b80d231c | ||
|
|
e0920ef4ba | ||
|
|
33ac193bf6 | ||
|
|
f424b46036 | ||
|
|
a4c5e3e339 | ||
|
|
dec3c981d0 | ||
|
|
d623e38d18 | ||
|
|
d8827b07b5 | ||
|
|
46177901b8 | ||
|
|
016f2eab73 | ||
|
|
2ed896075e | ||
|
|
d81b58f320 | ||
|
|
7fba9c7224 | ||
|
|
7dbf4c1ae5 |
@@ -82,6 +82,9 @@ set(HASH_FILES
|
||||
"${VIDEO_CORE}/shader/decode/video.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/xmad.cpp"
|
||||
"${VIDEO_CORE}/shader/decode.cpp"
|
||||
"${VIDEO_CORE}/shader/node.h"
|
||||
"${VIDEO_CORE}/shader/node_helper.cpp"
|
||||
"${VIDEO_CORE}/shader/node_helper.h"
|
||||
"${VIDEO_CORE}/shader/shader_ir.cpp"
|
||||
"${VIDEO_CORE}/shader/shader_ir.h"
|
||||
"${VIDEO_CORE}/shader/track.cpp"
|
||||
|
||||
@@ -7,7 +7,7 @@ yuzu is an experimental open-source emulator for the Nintendo Switch from the cr
|
||||
|
||||
It is written in C++ with portability in mind, with builds actively maintained for Windows, Linux and macOS. The emulator is currently only useful for homebrew development and research purposes.
|
||||
|
||||
yuzu only emulates a subset of Switch hardware and therefore is generally only useful for running/debugging homebrew applications. At this time, yuzu cannot play any commercial games without major problems. yuzu can boot some games, to varying degrees of success.
|
||||
yuzu only emulates a subset of Switch hardware and therefore is generally only useful for running/debugging homebrew applications. yuzu can boot some games, to varying degrees of success.
|
||||
|
||||
yuzu is licensed under the GPLv2 (or any later version). Refer to the license.txt file included.
|
||||
|
||||
|
||||
2
externals/Vulkan-Headers
vendored
2
externals/Vulkan-Headers
vendored
Submodule externals/Vulkan-Headers updated: 15e5c4db75...d05c8df88d
@@ -57,7 +57,9 @@ Stream::State Stream::GetState() const {
|
||||
|
||||
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
|
||||
const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
|
||||
return Core::Timing::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
|
||||
const auto us =
|
||||
std::chrono::microseconds((static_cast<u64>(num_samples) * 1000000) / sample_rate);
|
||||
return Core::Timing::usToCycles(us);
|
||||
}
|
||||
|
||||
static void VolumeAdjustSamples(std::vector<s16>& samples) {
|
||||
|
||||
@@ -56,6 +56,9 @@ add_custom_command(OUTPUT scm_rev.cpp
|
||||
"${VIDEO_CORE}/shader/decode/video.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/xmad.cpp"
|
||||
"${VIDEO_CORE}/shader/decode.cpp"
|
||||
"${VIDEO_CORE}/shader/node.h"
|
||||
"${VIDEO_CORE}/shader/node_helper.cpp"
|
||||
"${VIDEO_CORE}/shader/node_helper.h"
|
||||
"${VIDEO_CORE}/shader/shader_ir.cpp"
|
||||
"${VIDEO_CORE}/shader/shader_ir.h"
|
||||
"${VIDEO_CORE}/shader/track.cpp"
|
||||
|
||||
@@ -41,4 +41,7 @@ struct Rectangle {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
Rectangle(T, T, T, T)->Rectangle<T>;
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -328,6 +328,9 @@ add_library(core STATIC
|
||||
hle/service/nim/nim.h
|
||||
hle/service/npns/npns.cpp
|
||||
hle/service/npns/npns.h
|
||||
hle/service/ns/errors.h
|
||||
hle/service/ns/language.cpp
|
||||
hle/service/ns/language.h
|
||||
hle/service/ns/ns.cpp
|
||||
hle/service/ns/ns.h
|
||||
hle/service/ns/pl_u.cpp
|
||||
|
||||
@@ -18,11 +18,6 @@
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/vfs_concat.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
#include "core/frontend/applets/error.h"
|
||||
#include "core/frontend/applets/general_frontend.h"
|
||||
#include "core/frontend/applets/profile_select.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
#include "core/frontend/applets/web_browser.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
@@ -37,9 +32,6 @@
|
||||
#include "core/settings.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "file_sys/cheat_engine.h"
|
||||
#include "frontend/applets/profile_select.h"
|
||||
#include "frontend/applets/software_keyboard.h"
|
||||
#include "frontend/applets/web_browser.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
@@ -144,20 +136,10 @@ struct System::Impl {
|
||||
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
|
||||
const std::string& filepath) {
|
||||
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
|
||||
|
||||
if (!app_loader) {
|
||||
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
|
||||
return ResultStatus::ErrorGetLoader;
|
||||
}
|
||||
std::pair<std::optional<u32>, Loader::ResultStatus> system_mode =
|
||||
app_loader->LoadKernelSystemMode();
|
||||
|
||||
if (system_mode.second != Loader::ResultStatus::Success) {
|
||||
LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!",
|
||||
static_cast<int>(system_mode.second));
|
||||
|
||||
return ResultStatus::ErrorSystemMode;
|
||||
}
|
||||
|
||||
ResultStatus init_result{Init(system, emu_window)};
|
||||
if (init_result != ResultStatus::Success) {
|
||||
@@ -167,6 +149,7 @@ struct System::Impl {
|
||||
return init_result;
|
||||
}
|
||||
|
||||
telemetry_session->AddInitialInfo(*app_loader);
|
||||
auto main_process = Kernel::Process::Create(system, "main");
|
||||
const auto [load_result, load_parameters] = app_loader->Load(*main_process);
|
||||
if (load_result != Loader::ResultStatus::Success) {
|
||||
|
||||
@@ -98,7 +98,6 @@ public:
|
||||
Success, ///< Succeeded
|
||||
ErrorNotInitialized, ///< Error trying to use core prior to initialization
|
||||
ErrorGetLoader, ///< Error finding the correct application loader
|
||||
ErrorSystemMode, ///< Error determining the system mode
|
||||
ErrorSystemFiles, ///< Error in finding system files
|
||||
ErrorSharedFont, ///< Error in finding shared font
|
||||
ErrorVideoCore, ///< Error in the video core
|
||||
|
||||
@@ -13,52 +13,40 @@ namespace Core::Timing {
|
||||
|
||||
constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / BASE_CLOCK_RATE;
|
||||
|
||||
s64 usToCycles(s64 us) {
|
||||
if (static_cast<u64>(us / 1000000) > MAX_VALUE_TO_MULTIPLY) {
|
||||
s64 msToCycles(std::chrono::milliseconds ms) {
|
||||
if (static_cast<u64>(ms.count() / 1000) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
|
||||
return std::numeric_limits<s64>::max();
|
||||
}
|
||||
if (static_cast<u64>(us) > MAX_VALUE_TO_MULTIPLY) {
|
||||
if (static_cast<u64>(ms.count()) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
|
||||
return BASE_CLOCK_RATE * (us / 1000000);
|
||||
return BASE_CLOCK_RATE * (ms.count() / 1000);
|
||||
}
|
||||
return (BASE_CLOCK_RATE * us) / 1000000;
|
||||
return (BASE_CLOCK_RATE * ms.count()) / 1000;
|
||||
}
|
||||
|
||||
s64 usToCycles(u64 us) {
|
||||
if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) {
|
||||
s64 usToCycles(std::chrono::microseconds us) {
|
||||
if (static_cast<u64>(us.count() / 1000000) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
|
||||
return std::numeric_limits<s64>::max();
|
||||
}
|
||||
if (us > MAX_VALUE_TO_MULTIPLY) {
|
||||
if (static_cast<u64>(us.count()) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
|
||||
return BASE_CLOCK_RATE * static_cast<s64>(us / 1000000);
|
||||
return BASE_CLOCK_RATE * (us.count() / 1000000);
|
||||
}
|
||||
return (BASE_CLOCK_RATE * static_cast<s64>(us)) / 1000000;
|
||||
return (BASE_CLOCK_RATE * us.count()) / 1000000;
|
||||
}
|
||||
|
||||
s64 nsToCycles(s64 ns) {
|
||||
if (static_cast<u64>(ns / 1000000000) > MAX_VALUE_TO_MULTIPLY) {
|
||||
s64 nsToCycles(std::chrono::nanoseconds ns) {
|
||||
if (static_cast<u64>(ns.count() / 1000000000) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
|
||||
return std::numeric_limits<s64>::max();
|
||||
}
|
||||
if (static_cast<u64>(ns) > MAX_VALUE_TO_MULTIPLY) {
|
||||
if (static_cast<u64>(ns.count()) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
|
||||
return BASE_CLOCK_RATE * (ns / 1000000000);
|
||||
return BASE_CLOCK_RATE * (ns.count() / 1000000000);
|
||||
}
|
||||
return (BASE_CLOCK_RATE * ns) / 1000000000;
|
||||
}
|
||||
|
||||
s64 nsToCycles(u64 ns) {
|
||||
if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
|
||||
return std::numeric_limits<s64>::max();
|
||||
}
|
||||
if (ns > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
|
||||
return BASE_CLOCK_RATE * (static_cast<s64>(ns) / 1000000000);
|
||||
}
|
||||
return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000;
|
||||
return (BASE_CLOCK_RATE * ns.count()) / 1000000000;
|
||||
}
|
||||
|
||||
u64 CpuCyclesToClockCycles(u64 ticks) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
@@ -13,53 +14,20 @@ namespace Core::Timing {
|
||||
constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch clock speed is 1020MHz un/docked
|
||||
constexpr u64 CNTFREQ = 19200000; // Value from fusee.
|
||||
|
||||
inline s64 msToCycles(int ms) {
|
||||
// since ms is int there is no way to overflow
|
||||
return BASE_CLOCK_RATE * static_cast<s64>(ms) / 1000;
|
||||
s64 msToCycles(std::chrono::milliseconds ms);
|
||||
s64 usToCycles(std::chrono::microseconds us);
|
||||
s64 nsToCycles(std::chrono::nanoseconds ns);
|
||||
|
||||
inline std::chrono::milliseconds CyclesToMs(s64 cycles) {
|
||||
return std::chrono::milliseconds(cycles * 1000 / BASE_CLOCK_RATE);
|
||||
}
|
||||
|
||||
inline s64 msToCycles(float ms) {
|
||||
return static_cast<s64>(BASE_CLOCK_RATE * (0.001f) * ms);
|
||||
inline std::chrono::nanoseconds CyclesToNs(s64 cycles) {
|
||||
return std::chrono::nanoseconds(cycles * 1000000000 / BASE_CLOCK_RATE);
|
||||
}
|
||||
|
||||
inline s64 msToCycles(double ms) {
|
||||
return static_cast<s64>(BASE_CLOCK_RATE * (0.001) * ms);
|
||||
}
|
||||
|
||||
inline s64 usToCycles(float us) {
|
||||
return static_cast<s64>(BASE_CLOCK_RATE * (0.000001f) * us);
|
||||
}
|
||||
|
||||
inline s64 usToCycles(int us) {
|
||||
return (BASE_CLOCK_RATE * static_cast<s64>(us) / 1000000);
|
||||
}
|
||||
|
||||
s64 usToCycles(s64 us);
|
||||
|
||||
s64 usToCycles(u64 us);
|
||||
|
||||
inline s64 nsToCycles(float ns) {
|
||||
return static_cast<s64>(BASE_CLOCK_RATE * (0.000000001f) * ns);
|
||||
}
|
||||
|
||||
inline s64 nsToCycles(int ns) {
|
||||
return BASE_CLOCK_RATE * static_cast<s64>(ns) / 1000000000;
|
||||
}
|
||||
|
||||
s64 nsToCycles(s64 ns);
|
||||
|
||||
s64 nsToCycles(u64 ns);
|
||||
|
||||
inline u64 cyclesToNs(s64 cycles) {
|
||||
return cycles * 1000000000 / BASE_CLOCK_RATE;
|
||||
}
|
||||
|
||||
inline s64 cyclesToUs(s64 cycles) {
|
||||
return cycles * 1000000 / BASE_CLOCK_RATE;
|
||||
}
|
||||
|
||||
inline u64 cyclesToMs(s64 cycles) {
|
||||
return cycles * 1000 / BASE_CLOCK_RATE;
|
||||
inline std::chrono::microseconds CyclesToUs(s64 cycles) {
|
||||
return std::chrono::microseconds(cycles * 1000000 / BASE_CLOCK_RATE);
|
||||
}
|
||||
|
||||
u64 CpuCyclesToClockCycles(u64 ticks);
|
||||
|
||||
@@ -87,6 +87,10 @@ u64 NACP::GetDefaultJournalSaveSize() const {
|
||||
return raw.user_account_save_data_journal_size;
|
||||
}
|
||||
|
||||
u32 NACP::GetSupportedLanguages() const {
|
||||
return raw.supported_languages;
|
||||
}
|
||||
|
||||
std::vector<u8> NACP::GetRawBytes() const {
|
||||
std::vector<u8> out(sizeof(RawNACP));
|
||||
std::memcpy(out.data(), &raw, sizeof(RawNACP));
|
||||
|
||||
@@ -109,6 +109,7 @@ public:
|
||||
std::string GetVersionString() const;
|
||||
u64 GetDefaultNormalSaveSize() const;
|
||||
u64 GetDefaultJournalSaveSize() const;
|
||||
u32 GetSupportedLanguages() const;
|
||||
std::vector<u8> GetRawBytes() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -20,7 +20,7 @@ static Common::Rectangle<T> MaxRectangle(Common::Rectangle<T> window_area,
|
||||
static_cast<T>(std::round(scale * screen_aspect_ratio))};
|
||||
}
|
||||
|
||||
FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) {
|
||||
FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
|
||||
ASSERT(width > 0);
|
||||
ASSERT(height > 0);
|
||||
// The drawing code needs at least somewhat valid values for both screens
|
||||
@@ -29,22 +29,23 @@ FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) {
|
||||
|
||||
const float emulation_aspect_ratio{static_cast<float>(ScreenUndocked::Height) /
|
||||
ScreenUndocked::Width};
|
||||
Common::Rectangle<unsigned> screen_window_area{0, 0, width, height};
|
||||
Common::Rectangle<unsigned> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);
|
||||
const auto window_aspect_ratio = static_cast<float>(height) / width;
|
||||
|
||||
float window_aspect_ratio = static_cast<float>(height) / width;
|
||||
const Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
||||
Common::Rectangle<u32> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);
|
||||
|
||||
if (window_aspect_ratio < emulation_aspect_ratio) {
|
||||
screen = screen.TranslateX((screen_window_area.GetWidth() - screen.GetWidth()) / 2);
|
||||
} else {
|
||||
screen = screen.TranslateY((height - screen.GetHeight()) / 2);
|
||||
}
|
||||
|
||||
res.screen = screen;
|
||||
return res;
|
||||
}
|
||||
|
||||
FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale) {
|
||||
int width, height;
|
||||
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
|
||||
u32 width, height;
|
||||
|
||||
if (Settings::values.use_docked_mode) {
|
||||
width = ScreenDocked::WidthDocked * res_scale;
|
||||
|
||||
@@ -8,15 +8,22 @@
|
||||
|
||||
namespace Layout {
|
||||
|
||||
enum ScreenUndocked : unsigned { Width = 1280, Height = 720 };
|
||||
enum ScreenDocked : unsigned { WidthDocked = 1920, HeightDocked = 1080 };
|
||||
enum ScreenUndocked : u32 {
|
||||
Width = 1280,
|
||||
Height = 720,
|
||||
};
|
||||
|
||||
enum ScreenDocked : u32 {
|
||||
WidthDocked = 1920,
|
||||
HeightDocked = 1080,
|
||||
};
|
||||
|
||||
/// Describes the layout of the window framebuffer
|
||||
struct FramebufferLayout {
|
||||
unsigned width{ScreenUndocked::Width};
|
||||
unsigned height{ScreenUndocked::Height};
|
||||
u32 width{ScreenUndocked::Width};
|
||||
u32 height{ScreenUndocked::Height};
|
||||
|
||||
Common::Rectangle<unsigned> screen;
|
||||
Common::Rectangle<u32> screen;
|
||||
|
||||
/**
|
||||
* Returns the ration of pixel size of the screen, compared to the native size of the undocked
|
||||
@@ -33,12 +40,12 @@ struct FramebufferLayout {
|
||||
* @param height Window framebuffer height in pixels
|
||||
* @return Newly created FramebufferLayout object with default screen regions initialized
|
||||
*/
|
||||
FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height);
|
||||
FramebufferLayout DefaultFrameLayout(u32 width, u32 height);
|
||||
|
||||
/**
|
||||
* Convenience method to get frame layout by resolution scale
|
||||
* @param res_scale resolution scale factor
|
||||
*/
|
||||
FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale);
|
||||
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale);
|
||||
|
||||
} // namespace Layout
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/address_arbiter.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
|
||||
@@ -75,9 +75,9 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
|
||||
|
||||
// This function might be called from any thread so we have to be cautious and use the
|
||||
// thread-safe version of ScheduleEvent.
|
||||
const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds});
|
||||
Core::System::GetInstance().CoreTiming().ScheduleEventThreadsafe(
|
||||
Core::Timing::nsToCycles(nanoseconds), kernel.ThreadWakeupCallbackEventType(),
|
||||
callback_handle);
|
||||
cycles, kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
||||
}
|
||||
|
||||
void Thread::CancelWakeupTimer() {
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <cstring>
|
||||
#include "audio_core/audio_renderer.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
@@ -29,9 +31,11 @@
|
||||
#include "core/hle/service/am/tcap.h"
|
||||
#include "core/hle/service/apm/apm.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/ns/ns.h"
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
#include "core/hle/service/pm/pm.h"
|
||||
#include "core/hle/service/set/set.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
@@ -1100,10 +1104,42 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
|
||||
// TODO(bunnei): This should be configurable
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
// Get supported languages from NACP, if possible
|
||||
// Default to 0 (all languages supported)
|
||||
u32 supported_languages = 0;
|
||||
FileSys::PatchManager pm{Core::System::GetInstance().CurrentProcess()->GetTitleID()};
|
||||
|
||||
const auto res = pm.GetControlMetadata();
|
||||
if (res.first != nullptr) {
|
||||
supported_languages = res.first->GetSupportedLanguages();
|
||||
}
|
||||
|
||||
// Call IApplicationManagerInterface implementation.
|
||||
auto& service_manager = Core::System::GetInstance().ServiceManager();
|
||||
auto ns_am2 = service_manager.GetService<Service::NS::NS>("ns:am2");
|
||||
auto app_man = ns_am2->GetApplicationManagerInterface();
|
||||
|
||||
// Get desired application language
|
||||
const auto res_lang = app_man->GetApplicationDesiredLanguage(supported_languages);
|
||||
if (res_lang.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res_lang.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert to settings language code.
|
||||
const auto res_code = app_man->ConvertApplicationLanguageToLanguageCode(*res_lang);
|
||||
if (res_code.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res_code.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_AM, "got desired_language={:016X}", *res_code);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(
|
||||
static_cast<u64>(Service::Set::GetLanguageCodeFromIndex(Settings::values.language_index)));
|
||||
rb.Push(*res_code);
|
||||
}
|
||||
|
||||
void IApplicationFunctions::InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -121,6 +121,21 @@ void Applet::Initialize() {
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
AppletFrontendSet::AppletFrontendSet() = default;
|
||||
|
||||
AppletFrontendSet::AppletFrontendSet(ErrorApplet error, PhotoViewer photo_viewer,
|
||||
ProfileSelect profile_select,
|
||||
SoftwareKeyboard software_keyboard, WebBrowser web_browser)
|
||||
: error{std::move(error)}, photo_viewer{std::move(photo_viewer)}, profile_select{std::move(
|
||||
profile_select)},
|
||||
software_keyboard{std::move(software_keyboard)}, web_browser{std::move(web_browser)} {}
|
||||
|
||||
AppletFrontendSet::~AppletFrontendSet() = default;
|
||||
|
||||
AppletFrontendSet::AppletFrontendSet(AppletFrontendSet&&) noexcept = default;
|
||||
|
||||
AppletFrontendSet& AppletFrontendSet::operator=(AppletFrontendSet&&) noexcept = default;
|
||||
|
||||
AppletManager::AppletManager() = default;
|
||||
|
||||
AppletManager::~AppletManager() = default;
|
||||
|
||||
@@ -137,11 +137,28 @@ protected:
|
||||
};
|
||||
|
||||
struct AppletFrontendSet {
|
||||
std::unique_ptr<Core::Frontend::ErrorApplet> error;
|
||||
std::unique_ptr<Core::Frontend::PhotoViewerApplet> photo_viewer;
|
||||
std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_select;
|
||||
std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
|
||||
std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser;
|
||||
using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
|
||||
using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
|
||||
using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
|
||||
using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>;
|
||||
using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
|
||||
|
||||
AppletFrontendSet();
|
||||
AppletFrontendSet(ErrorApplet error, PhotoViewer photo_viewer, ProfileSelect profile_select,
|
||||
SoftwareKeyboard software_keyboard, WebBrowser web_browser);
|
||||
~AppletFrontendSet();
|
||||
|
||||
AppletFrontendSet(const AppletFrontendSet&) = delete;
|
||||
AppletFrontendSet& operator=(const AppletFrontendSet&) = delete;
|
||||
|
||||
AppletFrontendSet(AppletFrontendSet&&) noexcept;
|
||||
AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
|
||||
|
||||
ErrorApplet error;
|
||||
PhotoViewer photo_viewer;
|
||||
ProfileSelect profile_select;
|
||||
SoftwareKeyboard software_keyboard;
|
||||
WebBrowser web_browser;
|
||||
};
|
||||
|
||||
class AppletManager {
|
||||
|
||||
@@ -4,15 +4,89 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/ncm/ncm.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::NCM {
|
||||
|
||||
class LocationResolver final : public ServiceFramework<LocationResolver> {
|
||||
class ILocationResolver final : public ServiceFramework<ILocationResolver> {
|
||||
public:
|
||||
explicit LocationResolver() : ServiceFramework{"lr"} {
|
||||
explicit ILocationResolver(FileSys::StorageId id)
|
||||
: ServiceFramework{"ILocationResolver"}, storage(id) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "ResolveProgramPath"},
|
||||
{1, nullptr, "RedirectProgramPath"},
|
||||
{2, nullptr, "ResolveApplicationControlPath"},
|
||||
{3, nullptr, "ResolveApplicationHtmlDocumentPath"},
|
||||
{4, nullptr, "ResolveDataPath"},
|
||||
{5, nullptr, "RedirectApplicationControlPath"},
|
||||
{6, nullptr, "RedirectApplicationHtmlDocumentPath"},
|
||||
{7, nullptr, "ResolveApplicationLegalInformationPath"},
|
||||
{8, nullptr, "RedirectApplicationLegalInformationPath"},
|
||||
{9, nullptr, "Refresh"},
|
||||
{10, nullptr, "RedirectProgramPath2"},
|
||||
{11, nullptr, "Refresh2"},
|
||||
{12, nullptr, "DeleteProgramPath"},
|
||||
{13, nullptr, "DeleteApplicationControlPath"},
|
||||
{14, nullptr, "DeleteApplicationHtmlDocumentPath"},
|
||||
{15, nullptr, "DeleteApplicationLegalInformationPath"},
|
||||
{16, nullptr, ""},
|
||||
{17, nullptr, ""},
|
||||
{18, nullptr, ""},
|
||||
{19, nullptr, ""},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
FileSys::StorageId storage;
|
||||
};
|
||||
|
||||
class IRegisteredLocationResolver final : public ServiceFramework<IRegisteredLocationResolver> {
|
||||
public:
|
||||
explicit IRegisteredLocationResolver() : ServiceFramework{"IRegisteredLocationResolver"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "ResolveProgramPath"},
|
||||
{1, nullptr, "RegisterProgramPath"},
|
||||
{2, nullptr, "UnregisterProgramPath"},
|
||||
{3, nullptr, "RedirectProgramPath"},
|
||||
{4, nullptr, "ResolveHtmlDocumentPath"},
|
||||
{5, nullptr, "RegisterHtmlDocumentPath"},
|
||||
{6, nullptr, "UnregisterHtmlDocumentPath"},
|
||||
{7, nullptr, "RedirectHtmlDocumentPath"},
|
||||
{8, nullptr, ""},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class IAddOnContentLocationResolver final : public ServiceFramework<IAddOnContentLocationResolver> {
|
||||
public:
|
||||
explicit IAddOnContentLocationResolver() : ServiceFramework{"IAddOnContentLocationResolver"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "ResolveAddOnContentPath"},
|
||||
{1, nullptr, "RegisterAddOnContentStorage"},
|
||||
{2, nullptr, "UnregisterAllAddOnContentPath"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class LR final : public ServiceFramework<LR> {
|
||||
public:
|
||||
explicit LR() : ServiceFramework{"lr"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "OpenLocationResolver"},
|
||||
@@ -52,7 +126,7 @@ public:
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
std::make_shared<LocationResolver>()->InstallAsService(sm);
|
||||
std::make_shared<LR>()->InstallAsService(sm);
|
||||
std::make_shared<NCM>()->InstallAsService(sm);
|
||||
}
|
||||
|
||||
|
||||
12
src/core/hle/service/ns/errors.h
Normal file
12
src/core/hle/service/ns/errors.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Service::NS {
|
||||
|
||||
constexpr ResultCode ERR_APPLICATION_LANGUAGE_NOT_FOUND{ErrorModule::NS, 300};
|
||||
}
|
||||
392
src/core/hle/service/ns/language.cpp
Normal file
392
src/core/hle/service/ns/language.cpp
Normal file
@@ -0,0 +1,392 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/ns/language.h"
|
||||
#include "core/hle/service/set/set.h"
|
||||
|
||||
namespace Service::NS {
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_american_english = {{
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_british_english = {{
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_japanese = {{
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_french = {{
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_german = {{
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_latin_american_spanish = {{
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_spanish = {{
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_italian = {{
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_dutch = {{
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_canadian_french = {{
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_portuguese = {{
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_russian = {{
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_korean = {{
|
||||
ApplicationLanguage::Korean,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_traditional_chinese = {{
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
constexpr ApplicationLanguagePriorityList priority_list_simplified_chinese = {{
|
||||
ApplicationLanguage::SimplifiedChinese,
|
||||
ApplicationLanguage::TraditionalChinese,
|
||||
ApplicationLanguage::AmericanEnglish,
|
||||
ApplicationLanguage::BritishEnglish,
|
||||
ApplicationLanguage::Japanese,
|
||||
ApplicationLanguage::LatinAmericanSpanish,
|
||||
ApplicationLanguage::CanadianFrench,
|
||||
ApplicationLanguage::French,
|
||||
ApplicationLanguage::German,
|
||||
ApplicationLanguage::Spanish,
|
||||
ApplicationLanguage::Italian,
|
||||
ApplicationLanguage::Dutch,
|
||||
ApplicationLanguage::Portuguese,
|
||||
ApplicationLanguage::Russian,
|
||||
ApplicationLanguage::Korean,
|
||||
}};
|
||||
|
||||
const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(
|
||||
const ApplicationLanguage lang) {
|
||||
switch (lang) {
|
||||
case ApplicationLanguage::AmericanEnglish:
|
||||
return &priority_list_american_english;
|
||||
case ApplicationLanguage::BritishEnglish:
|
||||
return &priority_list_british_english;
|
||||
case ApplicationLanguage::Japanese:
|
||||
return &priority_list_japanese;
|
||||
case ApplicationLanguage::French:
|
||||
return &priority_list_french;
|
||||
case ApplicationLanguage::German:
|
||||
return &priority_list_german;
|
||||
case ApplicationLanguage::LatinAmericanSpanish:
|
||||
return &priority_list_latin_american_spanish;
|
||||
case ApplicationLanguage::Spanish:
|
||||
return &priority_list_spanish;
|
||||
case ApplicationLanguage::Italian:
|
||||
return &priority_list_italian;
|
||||
case ApplicationLanguage::Dutch:
|
||||
return &priority_list_dutch;
|
||||
case ApplicationLanguage::CanadianFrench:
|
||||
return &priority_list_canadian_french;
|
||||
case ApplicationLanguage::Portuguese:
|
||||
return &priority_list_portuguese;
|
||||
case ApplicationLanguage::Russian:
|
||||
return &priority_list_russian;
|
||||
case ApplicationLanguage::Korean:
|
||||
return &priority_list_korean;
|
||||
case ApplicationLanguage::TraditionalChinese:
|
||||
return &priority_list_traditional_chinese;
|
||||
case ApplicationLanguage::SimplifiedChinese:
|
||||
return &priority_list_simplified_chinese;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
|
||||
const Set::LanguageCode language_code) {
|
||||
switch (language_code) {
|
||||
case Set::LanguageCode::EN_US:
|
||||
return ApplicationLanguage::AmericanEnglish;
|
||||
case Set::LanguageCode::EN_GB:
|
||||
return ApplicationLanguage::BritishEnglish;
|
||||
case Set::LanguageCode::JA:
|
||||
return ApplicationLanguage::Japanese;
|
||||
case Set::LanguageCode::FR:
|
||||
return ApplicationLanguage::French;
|
||||
case Set::LanguageCode::DE:
|
||||
return ApplicationLanguage::German;
|
||||
case Set::LanguageCode::ES_419:
|
||||
return ApplicationLanguage::LatinAmericanSpanish;
|
||||
case Set::LanguageCode::ES:
|
||||
return ApplicationLanguage::Spanish;
|
||||
case Set::LanguageCode::IT:
|
||||
return ApplicationLanguage::Italian;
|
||||
case Set::LanguageCode::NL:
|
||||
return ApplicationLanguage::Dutch;
|
||||
case Set::LanguageCode::FR_CA:
|
||||
return ApplicationLanguage::CanadianFrench;
|
||||
case Set::LanguageCode::PT:
|
||||
return ApplicationLanguage::Portuguese;
|
||||
case Set::LanguageCode::RU:
|
||||
return ApplicationLanguage::Russian;
|
||||
case Set::LanguageCode::KO:
|
||||
return ApplicationLanguage::Korean;
|
||||
case Set::LanguageCode::ZH_HANT:
|
||||
return ApplicationLanguage::TraditionalChinese;
|
||||
case Set::LanguageCode::ZH_HANS:
|
||||
return ApplicationLanguage::SimplifiedChinese;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Set::LanguageCode> ConvertToLanguageCode(const ApplicationLanguage lang) {
|
||||
switch (lang) {
|
||||
case ApplicationLanguage::AmericanEnglish:
|
||||
return Set::LanguageCode::EN_US;
|
||||
case ApplicationLanguage::BritishEnglish:
|
||||
return Set::LanguageCode::EN_GB;
|
||||
case ApplicationLanguage::Japanese:
|
||||
return Set::LanguageCode::JA;
|
||||
case ApplicationLanguage::French:
|
||||
return Set::LanguageCode::FR;
|
||||
case ApplicationLanguage::German:
|
||||
return Set::LanguageCode::DE;
|
||||
case ApplicationLanguage::LatinAmericanSpanish:
|
||||
return Set::LanguageCode::ES_419;
|
||||
case ApplicationLanguage::Spanish:
|
||||
return Set::LanguageCode::ES;
|
||||
case ApplicationLanguage::Italian:
|
||||
return Set::LanguageCode::IT;
|
||||
case ApplicationLanguage::Dutch:
|
||||
return Set::LanguageCode::NL;
|
||||
case ApplicationLanguage::CanadianFrench:
|
||||
return Set::LanguageCode::FR_CA;
|
||||
case ApplicationLanguage::Portuguese:
|
||||
return Set::LanguageCode::PT;
|
||||
case ApplicationLanguage::Russian:
|
||||
return Set::LanguageCode::RU;
|
||||
case ApplicationLanguage::Korean:
|
||||
return Set::LanguageCode::KO;
|
||||
case ApplicationLanguage::TraditionalChinese:
|
||||
return Set::LanguageCode::ZH_HANT;
|
||||
case ApplicationLanguage::SimplifiedChinese:
|
||||
return Set::LanguageCode::ZH_HANS;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
} // namespace Service::NS
|
||||
45
src/core/hle/service/ns/language.h
Normal file
45
src/core/hle/service/ns/language.h
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::Set {
|
||||
enum class LanguageCode : u64;
|
||||
}
|
||||
|
||||
namespace Service::NS {
|
||||
/// This is nn::ns::detail::ApplicationLanguage
|
||||
enum class ApplicationLanguage : u8 {
|
||||
AmericanEnglish = 0,
|
||||
BritishEnglish,
|
||||
Japanese,
|
||||
French,
|
||||
German,
|
||||
LatinAmericanSpanish,
|
||||
Spanish,
|
||||
Italian,
|
||||
Dutch,
|
||||
CanadianFrench,
|
||||
Portuguese,
|
||||
Russian,
|
||||
Korean,
|
||||
TraditionalChinese,
|
||||
SimplifiedChinese,
|
||||
Count
|
||||
};
|
||||
using ApplicationLanguagePriorityList =
|
||||
const std::array<ApplicationLanguage, static_cast<std::size_t>(ApplicationLanguage::Count)>;
|
||||
|
||||
constexpr u32 GetSupportedLanguageFlag(const ApplicationLanguage lang) {
|
||||
return 1U << static_cast<u32>(lang);
|
||||
}
|
||||
|
||||
const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(ApplicationLanguage lang);
|
||||
std::optional<ApplicationLanguage> ConvertToApplicationLanguage(Set::LanguageCode language_code);
|
||||
std::optional<Set::LanguageCode> ConvertToLanguageCode(ApplicationLanguage lang);
|
||||
} // namespace Service::NS
|
||||
@@ -7,445 +7,507 @@
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/ns/errors.h"
|
||||
#include "core/hle/service/ns/language.h"
|
||||
#include "core/hle/service/ns/ns.h"
|
||||
#include "core/hle/service/ns/pl_u.h"
|
||||
#include "core/hle/service/set/set.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::NS {
|
||||
|
||||
class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
|
||||
public:
|
||||
explicit IAccountProxyInterface() : ServiceFramework{"IAccountProxyInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CreateUserAccount"},
|
||||
};
|
||||
// clang-format on
|
||||
IAccountProxyInterface::IAccountProxyInterface() : ServiceFramework{"IAccountProxyInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CreateUserAccount"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
|
||||
public:
|
||||
explicit IApplicationManagerInterface() : ServiceFramework{"IApplicationManagerInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "ListApplicationRecord"},
|
||||
{1, nullptr, "GenerateApplicationRecordCount"},
|
||||
{2, nullptr, "GetApplicationRecordUpdateSystemEvent"},
|
||||
{3, nullptr, "GetApplicationViewDeprecated"},
|
||||
{4, nullptr, "DeleteApplicationEntity"},
|
||||
{5, nullptr, "DeleteApplicationCompletely"},
|
||||
{6, nullptr, "IsAnyApplicationEntityRedundant"},
|
||||
{7, nullptr, "DeleteRedundantApplicationEntity"},
|
||||
{8, nullptr, "IsApplicationEntityMovable"},
|
||||
{9, nullptr, "MoveApplicationEntity"},
|
||||
{11, nullptr, "CalculateApplicationOccupiedSize"},
|
||||
{16, nullptr, "PushApplicationRecord"},
|
||||
{17, nullptr, "ListApplicationRecordContentMeta"},
|
||||
{19, nullptr, "LaunchApplicationOld"},
|
||||
{21, nullptr, "GetApplicationContentPath"},
|
||||
{22, nullptr, "TerminateApplication"},
|
||||
{23, nullptr, "ResolveApplicationContentPath"},
|
||||
{26, nullptr, "BeginInstallApplication"},
|
||||
{27, nullptr, "DeleteApplicationRecord"},
|
||||
{30, nullptr, "RequestApplicationUpdateInfo"},
|
||||
{32, nullptr, "CancelApplicationDownload"},
|
||||
{33, nullptr, "ResumeApplicationDownload"},
|
||||
{35, nullptr, "UpdateVersionList"},
|
||||
{36, nullptr, "PushLaunchVersion"},
|
||||
{37, nullptr, "ListRequiredVersion"},
|
||||
{38, nullptr, "CheckApplicationLaunchVersion"},
|
||||
{39, nullptr, "CheckApplicationLaunchRights"},
|
||||
{40, nullptr, "GetApplicationLogoData"},
|
||||
{41, nullptr, "CalculateApplicationDownloadRequiredSize"},
|
||||
{42, nullptr, "CleanupSdCard"},
|
||||
{43, nullptr, "CheckSdCardMountStatus"},
|
||||
{44, nullptr, "GetSdCardMountStatusChangedEvent"},
|
||||
{45, nullptr, "GetGameCardAttachmentEvent"},
|
||||
{46, nullptr, "GetGameCardAttachmentInfo"},
|
||||
{47, nullptr, "GetTotalSpaceSize"},
|
||||
{48, nullptr, "GetFreeSpaceSize"},
|
||||
{49, nullptr, "GetSdCardRemovedEvent"},
|
||||
{52, nullptr, "GetGameCardUpdateDetectionEvent"},
|
||||
{53, nullptr, "DisableApplicationAutoDelete"},
|
||||
{54, nullptr, "EnableApplicationAutoDelete"},
|
||||
{55, nullptr, "GetApplicationDesiredLanguage"},
|
||||
{56, nullptr, "SetApplicationTerminateResult"},
|
||||
{57, nullptr, "ClearApplicationTerminateResult"},
|
||||
{58, nullptr, "GetLastSdCardMountUnexpectedResult"},
|
||||
{59, nullptr, "ConvertApplicationLanguageToLanguageCode"},
|
||||
{60, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
|
||||
{61, nullptr, "GetBackgroundDownloadStressTaskInfo"},
|
||||
{62, nullptr, "GetGameCardStopper"},
|
||||
{63, nullptr, "IsSystemProgramInstalled"},
|
||||
{64, nullptr, "StartApplyDeltaTask"},
|
||||
{65, nullptr, "GetRequestServerStopper"},
|
||||
{66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"},
|
||||
{67, nullptr, "CancelApplicationApplyDelta"},
|
||||
{68, nullptr, "ResumeApplicationApplyDelta"},
|
||||
{69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"},
|
||||
{70, nullptr, "ResumeAll"},
|
||||
{71, nullptr, "GetStorageSize"},
|
||||
{80, nullptr, "RequestDownloadApplication"},
|
||||
{81, nullptr, "RequestDownloadAddOnContent"},
|
||||
{82, nullptr, "DownloadApplication"},
|
||||
{83, nullptr, "CheckApplicationResumeRights"},
|
||||
{84, nullptr, "GetDynamicCommitEvent"},
|
||||
{85, nullptr, "RequestUpdateApplication2"},
|
||||
{86, nullptr, "EnableApplicationCrashReport"},
|
||||
{87, nullptr, "IsApplicationCrashReportEnabled"},
|
||||
{90, nullptr, "BoostSystemMemoryResourceLimit"},
|
||||
{91, nullptr, "DeprecatedLaunchApplication"},
|
||||
{92, nullptr, "GetRunningApplicationProgramId"},
|
||||
{93, nullptr, "GetMainApplicationProgramIndex"},
|
||||
{94, nullptr, "LaunchApplication"},
|
||||
{95, nullptr, "GetApplicationLaunchInfo"},
|
||||
{96, nullptr, "AcquireApplicationLaunchInfo"},
|
||||
{97, nullptr, "GetMainApplicationProgramIndex2"},
|
||||
{98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
|
||||
{100, nullptr, "ResetToFactorySettings"},
|
||||
{101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
|
||||
{102, nullptr, "ResetToFactorySettingsForRefurbishment"},
|
||||
{200, nullptr, "CalculateUserSaveDataStatistics"},
|
||||
{201, nullptr, "DeleteUserSaveDataAll"},
|
||||
{210, nullptr, "DeleteUserSystemSaveData"},
|
||||
{211, nullptr, "DeleteSaveData"},
|
||||
{220, nullptr, "UnregisterNetworkServiceAccount"},
|
||||
{221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"},
|
||||
{300, nullptr, "GetApplicationShellEvent"},
|
||||
{301, nullptr, "PopApplicationShellEventInfo"},
|
||||
{302, nullptr, "LaunchLibraryApplet"},
|
||||
{303, nullptr, "TerminateLibraryApplet"},
|
||||
{304, nullptr, "LaunchSystemApplet"},
|
||||
{305, nullptr, "TerminateSystemApplet"},
|
||||
{306, nullptr, "LaunchOverlayApplet"},
|
||||
{307, nullptr, "TerminateOverlayApplet"},
|
||||
{400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"},
|
||||
{401, nullptr, "InvalidateAllApplicationControlCache"},
|
||||
{402, nullptr, "RequestDownloadApplicationControlData"},
|
||||
{403, nullptr, "GetMaxApplicationControlCacheCount"},
|
||||
{404, nullptr, "InvalidateApplicationControlCache"},
|
||||
{405, nullptr, "ListApplicationControlCacheEntryInfo"},
|
||||
{406, nullptr, "GetApplicationControlProperty"},
|
||||
{502, nullptr, "RequestCheckGameCardRegistration"},
|
||||
{503, nullptr, "RequestGameCardRegistrationGoldPoint"},
|
||||
{504, nullptr, "RequestRegisterGameCard"},
|
||||
{505, nullptr, "GetGameCardMountFailureEvent"},
|
||||
{506, nullptr, "IsGameCardInserted"},
|
||||
{507, nullptr, "EnsureGameCardAccess"},
|
||||
{508, nullptr, "GetLastGameCardMountFailureResult"},
|
||||
{509, nullptr, "ListApplicationIdOnGameCard"},
|
||||
{600, nullptr, "CountApplicationContentMeta"},
|
||||
{601, nullptr, "ListApplicationContentMetaStatus"},
|
||||
{602, nullptr, "ListAvailableAddOnContent"},
|
||||
{603, nullptr, "GetOwnedApplicationContentMetaStatus"},
|
||||
{604, nullptr, "RegisterContentsExternalKey"},
|
||||
{605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
|
||||
{606, nullptr, "GetContentMetaStorage"},
|
||||
{607, nullptr, "ListAvailableAddOnContent"},
|
||||
{700, nullptr, "PushDownloadTaskList"},
|
||||
{701, nullptr, "ClearTaskStatusList"},
|
||||
{702, nullptr, "RequestDownloadTaskList"},
|
||||
{703, nullptr, "RequestEnsureDownloadTask"},
|
||||
{704, nullptr, "ListDownloadTaskStatus"},
|
||||
{705, nullptr, "RequestDownloadTaskListData"},
|
||||
{800, nullptr, "RequestVersionList"},
|
||||
{801, nullptr, "ListVersionList"},
|
||||
{802, nullptr, "RequestVersionListData"},
|
||||
{900, nullptr, "GetApplicationRecord"},
|
||||
{901, nullptr, "GetApplicationRecordProperty"},
|
||||
{902, nullptr, "EnableApplicationAutoUpdate"},
|
||||
{903, nullptr, "DisableApplicationAutoUpdate"},
|
||||
{904, nullptr, "TouchApplication"},
|
||||
{905, nullptr, "RequestApplicationUpdate"},
|
||||
{906, nullptr, "IsApplicationUpdateRequested"},
|
||||
{907, nullptr, "WithdrawApplicationUpdateRequest"},
|
||||
{908, nullptr, "ListApplicationRecordInstalledContentMeta"},
|
||||
{909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
|
||||
{910, nullptr, "HasApplicationRecord"},
|
||||
{911, nullptr, "SetPreInstalledApplication"},
|
||||
{912, nullptr, "ClearPreInstalledApplicationFlag"},
|
||||
{1000, nullptr, "RequestVerifyApplicationDeprecated"},
|
||||
{1001, nullptr, "CorruptApplicationForDebug"},
|
||||
{1002, nullptr, "RequestVerifyAddOnContentsRights"},
|
||||
{1003, nullptr, "RequestVerifyApplication"},
|
||||
{1004, nullptr, "CorruptContentForDebug"},
|
||||
{1200, nullptr, "NeedsUpdateVulnerability"},
|
||||
{1300, nullptr, "IsAnyApplicationEntityInstalled"},
|
||||
{1301, nullptr, "DeleteApplicationContentEntities"},
|
||||
{1302, nullptr, "CleanupUnrecordedApplicationEntity"},
|
||||
{1303, nullptr, "CleanupAddOnContentsWithNoRights"},
|
||||
{1304, nullptr, "DeleteApplicationContentEntity"},
|
||||
{1305, nullptr, "TryDeleteRunningApplicationEntity"},
|
||||
{1306, nullptr, "TryDeleteRunningApplicationCompletely"},
|
||||
{1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
|
||||
{1308, nullptr, "DeleteApplicationCompletelyForDebug"},
|
||||
{1309, nullptr, "CleanupUnavailableAddOnContents"},
|
||||
{1400, nullptr, "PrepareShutdown"},
|
||||
{1500, nullptr, "FormatSdCard"},
|
||||
{1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
|
||||
{1502, nullptr, "GetLastSdCardFormatUnexpectedResult"},
|
||||
{1504, nullptr, "InsertSdCard"},
|
||||
{1505, nullptr, "RemoveSdCard"},
|
||||
{1600, nullptr, "GetSystemSeedForPseudoDeviceId"},
|
||||
{1601, nullptr, "ResetSystemSeedForPseudoDeviceId"},
|
||||
{1700, nullptr, "ListApplicationDownloadingContentMeta"},
|
||||
{1701, nullptr, "GetApplicationView"},
|
||||
{1702, nullptr, "GetApplicationDownloadTaskStatus"},
|
||||
{1703, nullptr, "GetApplicationViewDownloadErrorContext"},
|
||||
{1800, nullptr, "IsNotificationSetupCompleted"},
|
||||
{1801, nullptr, "GetLastNotificationInfoCount"},
|
||||
{1802, nullptr, "ListLastNotificationInfo"},
|
||||
{1803, nullptr, "ListNotificationTask"},
|
||||
{1900, nullptr, "IsActiveAccount"},
|
||||
{1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"},
|
||||
{1902, nullptr, "GetApplicationTicketInfo"},
|
||||
{2000, nullptr, "GetSystemDeliveryInfo"},
|
||||
{2001, nullptr, "SelectLatestSystemDeliveryInfo"},
|
||||
{2002, nullptr, "VerifyDeliveryProtocolVersion"},
|
||||
{2003, nullptr, "GetApplicationDeliveryInfo"},
|
||||
{2004, nullptr, "HasAllContentsToDeliver"},
|
||||
{2005, nullptr, "CompareApplicationDeliveryInfo"},
|
||||
{2006, nullptr, "CanDeliverApplication"},
|
||||
{2007, nullptr, "ListContentMetaKeyToDeliverApplication"},
|
||||
{2008, nullptr, "NeedsSystemUpdateToDeliverApplication"},
|
||||
{2009, nullptr, "EstimateRequiredSize"},
|
||||
{2010, nullptr, "RequestReceiveApplication"},
|
||||
{2011, nullptr, "CommitReceiveApplication"},
|
||||
{2012, nullptr, "GetReceiveApplicationProgress"},
|
||||
{2013, nullptr, "RequestSendApplication"},
|
||||
{2014, nullptr, "GetSendApplicationProgress"},
|
||||
{2015, nullptr, "CompareSystemDeliveryInfo"},
|
||||
{2016, nullptr, "ListNotCommittedContentMeta"},
|
||||
{2017, nullptr, "CreateDownloadTask"},
|
||||
{2018, nullptr, "GetApplicationDeliveryInfoHash"},
|
||||
{2050, nullptr, "GetApplicationRightsOnClient"},
|
||||
{2100, nullptr, "GetApplicationTerminateResult"},
|
||||
{2101, nullptr, "GetRawApplicationTerminateResult"},
|
||||
{2150, nullptr, "CreateRightsEnvironment"},
|
||||
{2151, nullptr, "DestroyRightsEnvironment"},
|
||||
{2152, nullptr, "ActivateRightsEnvironment"},
|
||||
{2153, nullptr, "DeactivateRightsEnvironment"},
|
||||
{2154, nullptr, "ForceActivateRightsContextForExit"},
|
||||
{2160, nullptr, "AddTargetApplicationToRightsEnvironment"},
|
||||
{2161, nullptr, "SetUsersToRightsEnvironment"},
|
||||
{2170, nullptr, "GetRightsEnvironmentStatus"},
|
||||
{2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
|
||||
{2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
|
||||
{2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"},
|
||||
{2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
|
||||
{2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
|
||||
{2199, nullptr, "GetRightsEnvironmentCountForDebug"},
|
||||
{2200, nullptr, "GetGameCardApplicationCopyIdentifier"},
|
||||
{2201, nullptr, "GetInstalledApplicationCopyIdentifier"},
|
||||
{2250, nullptr, "RequestReportActiveELicence"},
|
||||
{2300, nullptr, "ListEventLog"},
|
||||
};
|
||||
// clang-format on
|
||||
IAccountProxyInterface::~IAccountProxyInterface() = default;
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
IApplicationManagerInterface::IApplicationManagerInterface()
|
||||
: ServiceFramework{"IApplicationManagerInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "ListApplicationRecord"},
|
||||
{1, nullptr, "GenerateApplicationRecordCount"},
|
||||
{2, nullptr, "GetApplicationRecordUpdateSystemEvent"},
|
||||
{3, nullptr, "GetApplicationViewDeprecated"},
|
||||
{4, nullptr, "DeleteApplicationEntity"},
|
||||
{5, nullptr, "DeleteApplicationCompletely"},
|
||||
{6, nullptr, "IsAnyApplicationEntityRedundant"},
|
||||
{7, nullptr, "DeleteRedundantApplicationEntity"},
|
||||
{8, nullptr, "IsApplicationEntityMovable"},
|
||||
{9, nullptr, "MoveApplicationEntity"},
|
||||
{11, nullptr, "CalculateApplicationOccupiedSize"},
|
||||
{16, nullptr, "PushApplicationRecord"},
|
||||
{17, nullptr, "ListApplicationRecordContentMeta"},
|
||||
{19, nullptr, "LaunchApplicationOld"},
|
||||
{21, nullptr, "GetApplicationContentPath"},
|
||||
{22, nullptr, "TerminateApplication"},
|
||||
{23, nullptr, "ResolveApplicationContentPath"},
|
||||
{26, nullptr, "BeginInstallApplication"},
|
||||
{27, nullptr, "DeleteApplicationRecord"},
|
||||
{30, nullptr, "RequestApplicationUpdateInfo"},
|
||||
{32, nullptr, "CancelApplicationDownload"},
|
||||
{33, nullptr, "ResumeApplicationDownload"},
|
||||
{35, nullptr, "UpdateVersionList"},
|
||||
{36, nullptr, "PushLaunchVersion"},
|
||||
{37, nullptr, "ListRequiredVersion"},
|
||||
{38, nullptr, "CheckApplicationLaunchVersion"},
|
||||
{39, nullptr, "CheckApplicationLaunchRights"},
|
||||
{40, nullptr, "GetApplicationLogoData"},
|
||||
{41, nullptr, "CalculateApplicationDownloadRequiredSize"},
|
||||
{42, nullptr, "CleanupSdCard"},
|
||||
{43, nullptr, "CheckSdCardMountStatus"},
|
||||
{44, nullptr, "GetSdCardMountStatusChangedEvent"},
|
||||
{45, nullptr, "GetGameCardAttachmentEvent"},
|
||||
{46, nullptr, "GetGameCardAttachmentInfo"},
|
||||
{47, nullptr, "GetTotalSpaceSize"},
|
||||
{48, nullptr, "GetFreeSpaceSize"},
|
||||
{49, nullptr, "GetSdCardRemovedEvent"},
|
||||
{52, nullptr, "GetGameCardUpdateDetectionEvent"},
|
||||
{53, nullptr, "DisableApplicationAutoDelete"},
|
||||
{54, nullptr, "EnableApplicationAutoDelete"},
|
||||
{55, &IApplicationManagerInterface::GetApplicationDesiredLanguage, "GetApplicationDesiredLanguage"},
|
||||
{56, nullptr, "SetApplicationTerminateResult"},
|
||||
{57, nullptr, "ClearApplicationTerminateResult"},
|
||||
{58, nullptr, "GetLastSdCardMountUnexpectedResult"},
|
||||
{59, &IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode, "ConvertApplicationLanguageToLanguageCode"},
|
||||
{60, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
|
||||
{61, nullptr, "GetBackgroundDownloadStressTaskInfo"},
|
||||
{62, nullptr, "GetGameCardStopper"},
|
||||
{63, nullptr, "IsSystemProgramInstalled"},
|
||||
{64, nullptr, "StartApplyDeltaTask"},
|
||||
{65, nullptr, "GetRequestServerStopper"},
|
||||
{66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"},
|
||||
{67, nullptr, "CancelApplicationApplyDelta"},
|
||||
{68, nullptr, "ResumeApplicationApplyDelta"},
|
||||
{69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"},
|
||||
{70, nullptr, "ResumeAll"},
|
||||
{71, nullptr, "GetStorageSize"},
|
||||
{80, nullptr, "RequestDownloadApplication"},
|
||||
{81, nullptr, "RequestDownloadAddOnContent"},
|
||||
{82, nullptr, "DownloadApplication"},
|
||||
{83, nullptr, "CheckApplicationResumeRights"},
|
||||
{84, nullptr, "GetDynamicCommitEvent"},
|
||||
{85, nullptr, "RequestUpdateApplication2"},
|
||||
{86, nullptr, "EnableApplicationCrashReport"},
|
||||
{87, nullptr, "IsApplicationCrashReportEnabled"},
|
||||
{90, nullptr, "BoostSystemMemoryResourceLimit"},
|
||||
{91, nullptr, "DeprecatedLaunchApplication"},
|
||||
{92, nullptr, "GetRunningApplicationProgramId"},
|
||||
{93, nullptr, "GetMainApplicationProgramIndex"},
|
||||
{94, nullptr, "LaunchApplication"},
|
||||
{95, nullptr, "GetApplicationLaunchInfo"},
|
||||
{96, nullptr, "AcquireApplicationLaunchInfo"},
|
||||
{97, nullptr, "GetMainApplicationProgramIndex2"},
|
||||
{98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
|
||||
{100, nullptr, "ResetToFactorySettings"},
|
||||
{101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
|
||||
{102, nullptr, "ResetToFactorySettingsForRefurbishment"},
|
||||
{200, nullptr, "CalculateUserSaveDataStatistics"},
|
||||
{201, nullptr, "DeleteUserSaveDataAll"},
|
||||
{210, nullptr, "DeleteUserSystemSaveData"},
|
||||
{211, nullptr, "DeleteSaveData"},
|
||||
{220, nullptr, "UnregisterNetworkServiceAccount"},
|
||||
{221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"},
|
||||
{300, nullptr, "GetApplicationShellEvent"},
|
||||
{301, nullptr, "PopApplicationShellEventInfo"},
|
||||
{302, nullptr, "LaunchLibraryApplet"},
|
||||
{303, nullptr, "TerminateLibraryApplet"},
|
||||
{304, nullptr, "LaunchSystemApplet"},
|
||||
{305, nullptr, "TerminateSystemApplet"},
|
||||
{306, nullptr, "LaunchOverlayApplet"},
|
||||
{307, nullptr, "TerminateOverlayApplet"},
|
||||
{400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"},
|
||||
{401, nullptr, "InvalidateAllApplicationControlCache"},
|
||||
{402, nullptr, "RequestDownloadApplicationControlData"},
|
||||
{403, nullptr, "GetMaxApplicationControlCacheCount"},
|
||||
{404, nullptr, "InvalidateApplicationControlCache"},
|
||||
{405, nullptr, "ListApplicationControlCacheEntryInfo"},
|
||||
{406, nullptr, "GetApplicationControlProperty"},
|
||||
{502, nullptr, "RequestCheckGameCardRegistration"},
|
||||
{503, nullptr, "RequestGameCardRegistrationGoldPoint"},
|
||||
{504, nullptr, "RequestRegisterGameCard"},
|
||||
{505, nullptr, "GetGameCardMountFailureEvent"},
|
||||
{506, nullptr, "IsGameCardInserted"},
|
||||
{507, nullptr, "EnsureGameCardAccess"},
|
||||
{508, nullptr, "GetLastGameCardMountFailureResult"},
|
||||
{509, nullptr, "ListApplicationIdOnGameCard"},
|
||||
{600, nullptr, "CountApplicationContentMeta"},
|
||||
{601, nullptr, "ListApplicationContentMetaStatus"},
|
||||
{602, nullptr, "ListAvailableAddOnContent"},
|
||||
{603, nullptr, "GetOwnedApplicationContentMetaStatus"},
|
||||
{604, nullptr, "RegisterContentsExternalKey"},
|
||||
{605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
|
||||
{606, nullptr, "GetContentMetaStorage"},
|
||||
{607, nullptr, "ListAvailableAddOnContent"},
|
||||
{700, nullptr, "PushDownloadTaskList"},
|
||||
{701, nullptr, "ClearTaskStatusList"},
|
||||
{702, nullptr, "RequestDownloadTaskList"},
|
||||
{703, nullptr, "RequestEnsureDownloadTask"},
|
||||
{704, nullptr, "ListDownloadTaskStatus"},
|
||||
{705, nullptr, "RequestDownloadTaskListData"},
|
||||
{800, nullptr, "RequestVersionList"},
|
||||
{801, nullptr, "ListVersionList"},
|
||||
{802, nullptr, "RequestVersionListData"},
|
||||
{900, nullptr, "GetApplicationRecord"},
|
||||
{901, nullptr, "GetApplicationRecordProperty"},
|
||||
{902, nullptr, "EnableApplicationAutoUpdate"},
|
||||
{903, nullptr, "DisableApplicationAutoUpdate"},
|
||||
{904, nullptr, "TouchApplication"},
|
||||
{905, nullptr, "RequestApplicationUpdate"},
|
||||
{906, nullptr, "IsApplicationUpdateRequested"},
|
||||
{907, nullptr, "WithdrawApplicationUpdateRequest"},
|
||||
{908, nullptr, "ListApplicationRecordInstalledContentMeta"},
|
||||
{909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
|
||||
{910, nullptr, "HasApplicationRecord"},
|
||||
{911, nullptr, "SetPreInstalledApplication"},
|
||||
{912, nullptr, "ClearPreInstalledApplicationFlag"},
|
||||
{1000, nullptr, "RequestVerifyApplicationDeprecated"},
|
||||
{1001, nullptr, "CorruptApplicationForDebug"},
|
||||
{1002, nullptr, "RequestVerifyAddOnContentsRights"},
|
||||
{1003, nullptr, "RequestVerifyApplication"},
|
||||
{1004, nullptr, "CorruptContentForDebug"},
|
||||
{1200, nullptr, "NeedsUpdateVulnerability"},
|
||||
{1300, nullptr, "IsAnyApplicationEntityInstalled"},
|
||||
{1301, nullptr, "DeleteApplicationContentEntities"},
|
||||
{1302, nullptr, "CleanupUnrecordedApplicationEntity"},
|
||||
{1303, nullptr, "CleanupAddOnContentsWithNoRights"},
|
||||
{1304, nullptr, "DeleteApplicationContentEntity"},
|
||||
{1305, nullptr, "TryDeleteRunningApplicationEntity"},
|
||||
{1306, nullptr, "TryDeleteRunningApplicationCompletely"},
|
||||
{1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
|
||||
{1308, nullptr, "DeleteApplicationCompletelyForDebug"},
|
||||
{1309, nullptr, "CleanupUnavailableAddOnContents"},
|
||||
{1400, nullptr, "PrepareShutdown"},
|
||||
{1500, nullptr, "FormatSdCard"},
|
||||
{1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
|
||||
{1502, nullptr, "GetLastSdCardFormatUnexpectedResult"},
|
||||
{1504, nullptr, "InsertSdCard"},
|
||||
{1505, nullptr, "RemoveSdCard"},
|
||||
{1600, nullptr, "GetSystemSeedForPseudoDeviceId"},
|
||||
{1601, nullptr, "ResetSystemSeedForPseudoDeviceId"},
|
||||
{1700, nullptr, "ListApplicationDownloadingContentMeta"},
|
||||
{1701, nullptr, "GetApplicationView"},
|
||||
{1702, nullptr, "GetApplicationDownloadTaskStatus"},
|
||||
{1703, nullptr, "GetApplicationViewDownloadErrorContext"},
|
||||
{1800, nullptr, "IsNotificationSetupCompleted"},
|
||||
{1801, nullptr, "GetLastNotificationInfoCount"},
|
||||
{1802, nullptr, "ListLastNotificationInfo"},
|
||||
{1803, nullptr, "ListNotificationTask"},
|
||||
{1900, nullptr, "IsActiveAccount"},
|
||||
{1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"},
|
||||
{1902, nullptr, "GetApplicationTicketInfo"},
|
||||
{2000, nullptr, "GetSystemDeliveryInfo"},
|
||||
{2001, nullptr, "SelectLatestSystemDeliveryInfo"},
|
||||
{2002, nullptr, "VerifyDeliveryProtocolVersion"},
|
||||
{2003, nullptr, "GetApplicationDeliveryInfo"},
|
||||
{2004, nullptr, "HasAllContentsToDeliver"},
|
||||
{2005, nullptr, "CompareApplicationDeliveryInfo"},
|
||||
{2006, nullptr, "CanDeliverApplication"},
|
||||
{2007, nullptr, "ListContentMetaKeyToDeliverApplication"},
|
||||
{2008, nullptr, "NeedsSystemUpdateToDeliverApplication"},
|
||||
{2009, nullptr, "EstimateRequiredSize"},
|
||||
{2010, nullptr, "RequestReceiveApplication"},
|
||||
{2011, nullptr, "CommitReceiveApplication"},
|
||||
{2012, nullptr, "GetReceiveApplicationProgress"},
|
||||
{2013, nullptr, "RequestSendApplication"},
|
||||
{2014, nullptr, "GetSendApplicationProgress"},
|
||||
{2015, nullptr, "CompareSystemDeliveryInfo"},
|
||||
{2016, nullptr, "ListNotCommittedContentMeta"},
|
||||
{2017, nullptr, "CreateDownloadTask"},
|
||||
{2018, nullptr, "GetApplicationDeliveryInfoHash"},
|
||||
{2050, nullptr, "GetApplicationRightsOnClient"},
|
||||
{2100, nullptr, "GetApplicationTerminateResult"},
|
||||
{2101, nullptr, "GetRawApplicationTerminateResult"},
|
||||
{2150, nullptr, "CreateRightsEnvironment"},
|
||||
{2151, nullptr, "DestroyRightsEnvironment"},
|
||||
{2152, nullptr, "ActivateRightsEnvironment"},
|
||||
{2153, nullptr, "DeactivateRightsEnvironment"},
|
||||
{2154, nullptr, "ForceActivateRightsContextForExit"},
|
||||
{2160, nullptr, "AddTargetApplicationToRightsEnvironment"},
|
||||
{2161, nullptr, "SetUsersToRightsEnvironment"},
|
||||
{2170, nullptr, "GetRightsEnvironmentStatus"},
|
||||
{2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
|
||||
{2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
|
||||
{2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"},
|
||||
{2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
|
||||
{2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
|
||||
{2199, nullptr, "GetRightsEnvironmentCountForDebug"},
|
||||
{2200, nullptr, "GetGameCardApplicationCopyIdentifier"},
|
||||
{2201, nullptr, "GetInstalledApplicationCopyIdentifier"},
|
||||
{2250, nullptr, "RequestReportActiveELicence"},
|
||||
{2300, nullptr, "ListEventLog"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
void GetApplicationControlData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto flag = rp.PopRaw<u64>();
|
||||
LOG_DEBUG(Service_NS, "called with flag={:016X}", flag);
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
IApplicationManagerInterface::~IApplicationManagerInterface() = default;
|
||||
|
||||
const auto size = ctx.GetWriteBufferSize();
|
||||
void IApplicationManagerInterface::GetApplicationControlData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto flag = rp.PopRaw<u64>();
|
||||
LOG_DEBUG(Service_NS, "called with flag={:016X}", flag);
|
||||
|
||||
const FileSys::PatchManager pm{title_id};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
std::vector<u8> out;
|
||||
const auto size = ctx.GetWriteBufferSize();
|
||||
|
||||
if (control.first != nullptr) {
|
||||
if (size < 0x4000) {
|
||||
LOG_ERROR(Service_NS,
|
||||
"output buffer is too small! (actual={:016X}, expected_min=0x4000)",
|
||||
size);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find a better error code for this.
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
const FileSys::PatchManager pm{title_id};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
|
||||
out.resize(0x4000);
|
||||
const auto bytes = control.first->GetRawBytes();
|
||||
std::memcpy(out.data(), bytes.data(), bytes.size());
|
||||
} else {
|
||||
LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.",
|
||||
title_id);
|
||||
out.resize(std::min<u64>(0x4000, size));
|
||||
std::vector<u8> out;
|
||||
|
||||
if (control.first != nullptr) {
|
||||
if (size < 0x4000) {
|
||||
LOG_ERROR(Service_NS,
|
||||
"output buffer is too small! (actual={:016X}, expected_min=0x4000)", size);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find a better error code for this.
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
if (control.second != nullptr) {
|
||||
if (size < 0x4000 + control.second->GetSize()) {
|
||||
LOG_ERROR(Service_NS,
|
||||
"output buffer is too small! (actual={:016X}, expected_min={:016X})",
|
||||
size, 0x4000 + control.second->GetSize());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find a better error code for this.
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
out.resize(0x4000);
|
||||
const auto bytes = control.first->GetRawBytes();
|
||||
std::memcpy(out.data(), bytes.data(), bytes.size());
|
||||
} else {
|
||||
LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.",
|
||||
title_id);
|
||||
out.resize(std::min<u64>(0x4000, size));
|
||||
}
|
||||
|
||||
out.resize(0x4000 + control.second->GetSize());
|
||||
control.second->Read(out.data() + 0x4000, control.second->GetSize());
|
||||
} else {
|
||||
LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.",
|
||||
title_id);
|
||||
if (control.second != nullptr) {
|
||||
if (size < 0x4000 + control.second->GetSize()) {
|
||||
LOG_ERROR(Service_NS,
|
||||
"output buffer is too small! (actual={:016X}, expected_min={:016X})", size,
|
||||
0x4000 + control.second->GetSize());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find a better error code for this.
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(out);
|
||||
out.resize(0x4000 + control.second->GetSize());
|
||||
control.second->Read(out.data() + 0x4000, control.second->GetSize());
|
||||
} else {
|
||||
LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.",
|
||||
title_id);
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(out);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(static_cast<u32>(out.size()));
|
||||
}
|
||||
|
||||
void IApplicationManagerInterface::GetApplicationDesiredLanguage(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto supported_languages = rp.Pop<u32>();
|
||||
|
||||
const auto res = GetApplicationDesiredLanguage(supported_languages);
|
||||
if (res.Succeeded()) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(static_cast<u32>(out.size()));
|
||||
rb.Push<u32>(*res);
|
||||
} else {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res.Code());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
|
||||
public:
|
||||
explicit IApplicationVersionInterface() : ServiceFramework{"IApplicationVersionInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetLaunchRequiredVersion"},
|
||||
{1, nullptr, "UpgradeLaunchRequiredVersion"},
|
||||
{35, nullptr, "UpdateVersionList"},
|
||||
{36, nullptr, "PushLaunchVersion"},
|
||||
{37, nullptr, "ListRequiredVersion"},
|
||||
{800, nullptr, "RequestVersionList"},
|
||||
{801, nullptr, "ListVersionList"},
|
||||
{802, nullptr, "RequestVersionListData"},
|
||||
{1000, nullptr, "PerformAutoUpdate"},
|
||||
};
|
||||
// clang-format on
|
||||
ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage(
|
||||
const u32 supported_languages) {
|
||||
LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages);
|
||||
|
||||
RegisterHandlers(functions);
|
||||
// Get language code from settings
|
||||
const auto language_code = Set::GetLanguageCodeFromIndex(Settings::values.language_index);
|
||||
|
||||
// Convert to application language, get priority list
|
||||
const auto application_language = ConvertToApplicationLanguage(language_code);
|
||||
if (application_language == std::nullopt) {
|
||||
return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
|
||||
}
|
||||
};
|
||||
|
||||
class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> {
|
||||
public:
|
||||
explicit IContentManagerInterface() : ServiceFramework{"IContentManagerInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{11, nullptr, "CalculateApplicationOccupiedSize"},
|
||||
{43, nullptr, "CheckSdCardMountStatus"},
|
||||
{47, nullptr, "GetTotalSpaceSize"},
|
||||
{48, nullptr, "GetFreeSpaceSize"},
|
||||
{600, nullptr, "CountApplicationContentMeta"},
|
||||
{601, nullptr, "ListApplicationContentMetaStatus"},
|
||||
{605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
|
||||
{607, nullptr, "IsAnyApplicationRunning"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
const auto priority_list = GetApplicationLanguagePriorityList(*application_language);
|
||||
if (!priority_list) {
|
||||
return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
|
||||
}
|
||||
};
|
||||
|
||||
class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
|
||||
public:
|
||||
explicit IDocumentInterface() : ServiceFramework{"IDocumentInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{21, nullptr, "GetApplicationContentPath"},
|
||||
{23, nullptr, "ResolveApplicationContentPath"},
|
||||
{93, nullptr, "GetRunningApplicationProgramId"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
// Try to find a valid language.
|
||||
for (const auto lang : *priority_list) {
|
||||
const auto supported_flag = GetSupportedLanguageFlag(lang);
|
||||
if (supported_languages == 0 || (supported_languages & supported_flag) == supported_flag) {
|
||||
return MakeResult(static_cast<u8>(lang));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> {
|
||||
public:
|
||||
explicit IDownloadTaskInterface() : ServiceFramework{"IDownloadTaskInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{701, nullptr, "ClearTaskStatusList"},
|
||||
{702, nullptr, "RequestDownloadTaskList"},
|
||||
{703, nullptr, "RequestEnsureDownloadTask"},
|
||||
{704, nullptr, "ListDownloadTaskStatus"},
|
||||
{705, nullptr, "RequestDownloadTaskListData"},
|
||||
{706, nullptr, "TryCommitCurrentApplicationDownloadTask"},
|
||||
{707, nullptr, "EnableAutoCommit"},
|
||||
{708, nullptr, "DisableAutoCommit"},
|
||||
{709, nullptr, "TriggerDynamicCommitEvent"},
|
||||
};
|
||||
// clang-format on
|
||||
return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
|
||||
}
|
||||
|
||||
RegisterHandlers(functions);
|
||||
void IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
|
||||
Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto application_language = rp.Pop<u8>();
|
||||
|
||||
const auto res = ConvertApplicationLanguageToLanguageCode(application_language);
|
||||
if (res.Succeeded()) {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(*res);
|
||||
} else {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res.Code());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class IECommerceInterface final : public ServiceFramework<IECommerceInterface> {
|
||||
public:
|
||||
explicit IECommerceInterface() : ServiceFramework{"IECommerceInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "RequestLinkDevice"},
|
||||
{1, nullptr, "RequestCleanupAllPreInstalledApplications"},
|
||||
{2, nullptr, "RequestCleanupPreInstalledApplication"},
|
||||
{3, nullptr, "RequestSyncRights"},
|
||||
{4, nullptr, "RequestUnlinkDevice"},
|
||||
{5, nullptr, "RequestRevokeAllELicense"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
ResultVal<u64> IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
|
||||
u8 application_language) {
|
||||
const auto language_code =
|
||||
ConvertToLanguageCode(static_cast<ApplicationLanguage>(application_language));
|
||||
if (language_code == std::nullopt) {
|
||||
return ERR_APPLICATION_LANGUAGE_NOT_FOUND;
|
||||
}
|
||||
};
|
||||
|
||||
class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> {
|
||||
public:
|
||||
explicit IFactoryResetInterface() : ServiceFramework{"IFactoryResetInterface"} {
|
||||
// clang-format off
|
||||
return MakeResult(static_cast<u64>(*language_code));
|
||||
}
|
||||
|
||||
IApplicationVersionInterface::IApplicationVersionInterface()
|
||||
: ServiceFramework{"IApplicationVersionInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetLaunchRequiredVersion"},
|
||||
{1, nullptr, "UpgradeLaunchRequiredVersion"},
|
||||
{35, nullptr, "UpdateVersionList"},
|
||||
{36, nullptr, "PushLaunchVersion"},
|
||||
{37, nullptr, "ListRequiredVersion"},
|
||||
{800, nullptr, "RequestVersionList"},
|
||||
{801, nullptr, "ListVersionList"},
|
||||
{802, nullptr, "RequestVersionListData"},
|
||||
{1000, nullptr, "PerformAutoUpdate"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IApplicationVersionInterface::~IApplicationVersionInterface() = default;
|
||||
|
||||
IContentManagerInterface::IContentManagerInterface()
|
||||
: ServiceFramework{"IContentManagerInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{11, nullptr, "CalculateApplicationOccupiedSize"},
|
||||
{43, nullptr, "CheckSdCardMountStatus"},
|
||||
{47, nullptr, "GetTotalSpaceSize"},
|
||||
{48, nullptr, "GetFreeSpaceSize"},
|
||||
{600, nullptr, "CountApplicationContentMeta"},
|
||||
{601, nullptr, "ListApplicationContentMetaStatus"},
|
||||
{605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
|
||||
{607, nullptr, "IsAnyApplicationRunning"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IContentManagerInterface::~IContentManagerInterface() = default;
|
||||
|
||||
IDocumentInterface::IDocumentInterface() : ServiceFramework{"IDocumentInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{21, nullptr, "GetApplicationContentPath"},
|
||||
{23, nullptr, "ResolveApplicationContentPath"},
|
||||
{93, nullptr, "GetRunningApplicationProgramId"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IDocumentInterface::~IDocumentInterface() = default;
|
||||
|
||||
IDownloadTaskInterface::IDownloadTaskInterface() : ServiceFramework{"IDownloadTaskInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{701, nullptr, "ClearTaskStatusList"},
|
||||
{702, nullptr, "RequestDownloadTaskList"},
|
||||
{703, nullptr, "RequestEnsureDownloadTask"},
|
||||
{704, nullptr, "ListDownloadTaskStatus"},
|
||||
{705, nullptr, "RequestDownloadTaskListData"},
|
||||
{706, nullptr, "TryCommitCurrentApplicationDownloadTask"},
|
||||
{707, nullptr, "EnableAutoCommit"},
|
||||
{708, nullptr, "DisableAutoCommit"},
|
||||
{709, nullptr, "TriggerDynamicCommitEvent"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IDownloadTaskInterface::~IDownloadTaskInterface() = default;
|
||||
|
||||
IECommerceInterface::IECommerceInterface() : ServiceFramework{"IECommerceInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "RequestLinkDevice"},
|
||||
{1, nullptr, "RequestCleanupAllPreInstalledApplications"},
|
||||
{2, nullptr, "RequestCleanupPreInstalledApplication"},
|
||||
{3, nullptr, "RequestSyncRights"},
|
||||
{4, nullptr, "RequestUnlinkDevice"},
|
||||
{5, nullptr, "RequestRevokeAllELicense"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IECommerceInterface::~IECommerceInterface() = default;
|
||||
|
||||
IFactoryResetInterface::IFactoryResetInterface::IFactoryResetInterface()
|
||||
: ServiceFramework{"IFactoryResetInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{100, nullptr, "ResetToFactorySettings"},
|
||||
{101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
|
||||
{102, nullptr, "ResetToFactorySettingsForRefurbishment"},
|
||||
};
|
||||
// clang-format on
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
class NS final : public ServiceFramework<NS> {
|
||||
public:
|
||||
explicit NS(const char* name) : ServiceFramework{name} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
|
||||
{7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
|
||||
{7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"},
|
||||
{7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
|
||||
{7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"},
|
||||
{7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
|
||||
{7998, &NS::PushInterface<IContentManagerInterface>, "GetContentManagementInterface"},
|
||||
{7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
|
||||
};
|
||||
// clang-format on
|
||||
IFactoryResetInterface::~IFactoryResetInterface() = default;
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
NS::NS(const char* name) : ServiceFramework{name} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
|
||||
{7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
|
||||
{7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"},
|
||||
{7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
|
||||
{7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"},
|
||||
{7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
|
||||
{7998, &NS::PushInterface<IContentManagerInterface>, "GetContentManagementInterface"},
|
||||
{7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void PushInterface(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<T>();
|
||||
}
|
||||
};
|
||||
NS::~NS() = default;
|
||||
|
||||
std::shared_ptr<IApplicationManagerInterface> NS::GetApplicationManagerInterface() const {
|
||||
return GetInterface<IApplicationManagerInterface>();
|
||||
}
|
||||
|
||||
class NS_DEV final : public ServiceFramework<NS_DEV> {
|
||||
public:
|
||||
|
||||
@@ -8,6 +8,88 @@
|
||||
|
||||
namespace Service::NS {
|
||||
|
||||
class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
|
||||
public:
|
||||
explicit IAccountProxyInterface();
|
||||
~IAccountProxyInterface() override;
|
||||
};
|
||||
|
||||
class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
|
||||
public:
|
||||
explicit IApplicationManagerInterface();
|
||||
~IApplicationManagerInterface() override;
|
||||
|
||||
ResultVal<u8> GetApplicationDesiredLanguage(u32 supported_languages);
|
||||
ResultVal<u64> ConvertApplicationLanguageToLanguageCode(u8 application_language);
|
||||
|
||||
private:
|
||||
void GetApplicationControlData(Kernel::HLERequestContext& ctx);
|
||||
void GetApplicationDesiredLanguage(Kernel::HLERequestContext& ctx);
|
||||
void ConvertApplicationLanguageToLanguageCode(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
|
||||
public:
|
||||
explicit IApplicationVersionInterface();
|
||||
~IApplicationVersionInterface() override;
|
||||
};
|
||||
|
||||
class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> {
|
||||
public:
|
||||
explicit IContentManagerInterface();
|
||||
~IContentManagerInterface() override;
|
||||
};
|
||||
|
||||
class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
|
||||
public:
|
||||
explicit IDocumentInterface();
|
||||
~IDocumentInterface() override;
|
||||
};
|
||||
|
||||
class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> {
|
||||
public:
|
||||
explicit IDownloadTaskInterface();
|
||||
~IDownloadTaskInterface() override;
|
||||
};
|
||||
|
||||
class IECommerceInterface final : public ServiceFramework<IECommerceInterface> {
|
||||
public:
|
||||
explicit IECommerceInterface();
|
||||
~IECommerceInterface() override;
|
||||
};
|
||||
|
||||
class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> {
|
||||
public:
|
||||
explicit IFactoryResetInterface();
|
||||
~IFactoryResetInterface() override;
|
||||
};
|
||||
|
||||
class NS final : public ServiceFramework<NS> {
|
||||
public:
|
||||
explicit NS(const char* name);
|
||||
~NS() override;
|
||||
|
||||
std::shared_ptr<IApplicationManagerInterface> GetApplicationManagerInterface() const;
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void PushInterface(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<T> GetInterface() const {
|
||||
static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>,
|
||||
"Not a base of ServiceFrameworkBase");
|
||||
|
||||
return std::make_shared<T>();
|
||||
}
|
||||
};
|
||||
|
||||
/// Registers all NS services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
|
||||
|
||||
42
src/core/hle/service/ns/ns_language.h
Normal file
42
src/core/hle/service/ns/ns_language.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/set/set.h"
|
||||
|
||||
namespace Service::NS {
|
||||
/// This is nn::ns::detail::ApplicationLanguage
|
||||
enum class ApplicationLanguage : u8 {
|
||||
AmericanEnglish = 0,
|
||||
BritishEnglish,
|
||||
Japanese,
|
||||
French,
|
||||
German,
|
||||
LatinAmericanSpanish,
|
||||
Spanish,
|
||||
Italian,
|
||||
Dutch,
|
||||
CanadianFrench,
|
||||
Portuguese,
|
||||
Russian,
|
||||
Korean,
|
||||
TraditionalChinese,
|
||||
SimplifiedChinese,
|
||||
Count
|
||||
};
|
||||
using ApplicationLanguagePriorityList =
|
||||
const std::array<ApplicationLanguage, static_cast<std::size_t>(ApplicationLanguage::Count)>;
|
||||
|
||||
constexpr u32 GetSupportedLanguageFlag(const ApplicationLanguage lang) {
|
||||
return 1U << static_cast<u32>(lang);
|
||||
}
|
||||
|
||||
const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(ApplicationLanguage lang);
|
||||
std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
|
||||
Service::Set::LanguageCode language_code);
|
||||
std::optional<Service::Set::LanguageCode> ConvertToLanguageCode(ApplicationLanguage lang);
|
||||
} // namespace Service::NS
|
||||
@@ -185,7 +185,8 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o
|
||||
|
||||
IoctlGetGpuTime params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
params.gpu_time = Core::Timing::cyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks());
|
||||
const auto ns = Core::Timing::CyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks());
|
||||
params.gpu_time = static_cast<u64_le>(ns.count());
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -108,8 +108,9 @@ private:
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
const auto& core_timing = Core::System::GetInstance().CoreTiming();
|
||||
const SteadyClockTimePoint steady_clock_time_point{
|
||||
Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000};
|
||||
const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
|
||||
const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000),
|
||||
{}};
|
||||
IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(steady_clock_time_point);
|
||||
@@ -284,8 +285,8 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
const auto& core_timing = Core::System::GetInstance().CoreTiming();
|
||||
const SteadyClockTimePoint steady_clock_time_point{
|
||||
Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000, {}};
|
||||
const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
|
||||
const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), {}};
|
||||
|
||||
CalendarTime calendar_time{};
|
||||
calendar_time.year = tm->tm_year + 1900;
|
||||
|
||||
@@ -153,17 +153,6 @@ public:
|
||||
*/
|
||||
virtual LoadResult Load(Kernel::Process& process) = 0;
|
||||
|
||||
/**
|
||||
* Loads the system mode that this application needs.
|
||||
* This function defaults to 2 (96MB allocated to the application) if it can't read the
|
||||
* information.
|
||||
* @returns A pair with the optional system mode, and and the status.
|
||||
*/
|
||||
virtual std::pair<std::optional<u32>, ResultStatus> LoadKernelSystemMode() {
|
||||
// 96MB allocated to the application.
|
||||
return std::make_pair(2, ResultStatus::Success);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the code (typically .code section) of the application
|
||||
* @param buffer Reference to buffer to store data
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/loader/loader.h"
|
||||
@@ -101,7 +100,30 @@ bool VerifyLogin(const std::string& username, const std::string& token) {
|
||||
#endif
|
||||
}
|
||||
|
||||
TelemetrySession::TelemetrySession() {
|
||||
TelemetrySession::TelemetrySession() = default;
|
||||
|
||||
TelemetrySession::~TelemetrySession() {
|
||||
// Log one-time session end information
|
||||
const s64 shutdown_time{std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count()};
|
||||
AddField(Telemetry::FieldType::Session, "Shutdown_Time", shutdown_time);
|
||||
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
auto backend = std::make_unique<WebService::TelemetryJson>(
|
||||
Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token);
|
||||
#else
|
||||
auto backend = std::make_unique<Telemetry::NullVisitor>();
|
||||
#endif
|
||||
|
||||
// Complete the session, submitting to the web service backend if necessary
|
||||
field_collection.Accept(*backend);
|
||||
if (Settings::values.enable_telemetry) {
|
||||
backend->Complete();
|
||||
}
|
||||
}
|
||||
|
||||
void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
|
||||
// Log one-time top-level information
|
||||
AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId());
|
||||
|
||||
@@ -112,26 +134,28 @@ TelemetrySession::TelemetrySession() {
|
||||
AddField(Telemetry::FieldType::Session, "Init_Time", init_time);
|
||||
|
||||
u64 program_id{};
|
||||
const Loader::ResultStatus res{System::GetInstance().GetAppLoader().ReadProgramId(program_id)};
|
||||
const Loader::ResultStatus res{app_loader.ReadProgramId(program_id)};
|
||||
if (res == Loader::ResultStatus::Success) {
|
||||
const std::string formatted_program_id{fmt::format("{:016X}", program_id)};
|
||||
AddField(Telemetry::FieldType::Session, "ProgramId", formatted_program_id);
|
||||
|
||||
std::string name;
|
||||
System::GetInstance().GetAppLoader().ReadTitle(name);
|
||||
app_loader.ReadTitle(name);
|
||||
|
||||
if (name.empty()) {
|
||||
auto [nacp, icon_file] = FileSys::PatchManager(program_id).GetControlMetadata();
|
||||
if (nacp != nullptr)
|
||||
if (nacp != nullptr) {
|
||||
name = nacp->GetApplicationName();
|
||||
}
|
||||
}
|
||||
|
||||
if (!name.empty())
|
||||
if (!name.empty()) {
|
||||
AddField(Telemetry::FieldType::Session, "ProgramName", name);
|
||||
}
|
||||
}
|
||||
|
||||
AddField(Telemetry::FieldType::Session, "ProgramFormat",
|
||||
static_cast<u8>(System::GetInstance().GetAppLoader().GetFileType()));
|
||||
static_cast<u8>(app_loader.GetFileType()));
|
||||
|
||||
// Log application information
|
||||
Telemetry::AppendBuildInfo(field_collection);
|
||||
@@ -162,27 +186,6 @@ TelemetrySession::TelemetrySession() {
|
||||
Settings::values.use_docked_mode);
|
||||
}
|
||||
|
||||
TelemetrySession::~TelemetrySession() {
|
||||
// Log one-time session end information
|
||||
const s64 shutdown_time{std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count()};
|
||||
AddField(Telemetry::FieldType::Session, "Shutdown_Time", shutdown_time);
|
||||
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
auto backend = std::make_unique<WebService::TelemetryJson>(
|
||||
Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token);
|
||||
#else
|
||||
auto backend = std::make_unique<Telemetry::NullVisitor>();
|
||||
#endif
|
||||
|
||||
// Complete the session, submitting to web service if necessary
|
||||
field_collection.Accept(*backend);
|
||||
if (Settings::values.enable_telemetry)
|
||||
backend->Complete();
|
||||
backend = nullptr;
|
||||
}
|
||||
|
||||
bool TelemetrySession::SubmitTestcase() {
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
auto backend = std::make_unique<WebService::TelemetryJson>(
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/telemetry.h"
|
||||
|
||||
namespace Loader {
|
||||
class AppLoader;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
/**
|
||||
@@ -15,11 +18,33 @@ namespace Core {
|
||||
* session, logging any one-time fields. Interfaces with the telemetry backend used for submitting
|
||||
* data to the web service. Submits session data on close.
|
||||
*/
|
||||
class TelemetrySession : NonCopyable {
|
||||
class TelemetrySession {
|
||||
public:
|
||||
TelemetrySession();
|
||||
explicit TelemetrySession();
|
||||
~TelemetrySession();
|
||||
|
||||
TelemetrySession(const TelemetrySession&) = delete;
|
||||
TelemetrySession& operator=(const TelemetrySession&) = delete;
|
||||
|
||||
TelemetrySession(TelemetrySession&&) = delete;
|
||||
TelemetrySession& operator=(TelemetrySession&&) = delete;
|
||||
|
||||
/**
|
||||
* Adds the initial telemetry info necessary when starting up a title.
|
||||
*
|
||||
* This includes information such as:
|
||||
* - Telemetry ID
|
||||
* - Initialization time
|
||||
* - Title ID
|
||||
* - Title name
|
||||
* - Title file format
|
||||
* - Miscellaneous settings values.
|
||||
*
|
||||
* @param app_loader The application loader to use to retrieve
|
||||
* title-specific information.
|
||||
*/
|
||||
void AddInitialInfo(Loader::AppLoader& app_loader);
|
||||
|
||||
/**
|
||||
* Wrapper around the Telemetry::FieldCollection::AddField method.
|
||||
* @param type Type of the field to add.
|
||||
|
||||
@@ -6,15 +6,8 @@
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "core/frontend/input.h"
|
||||
#include "input_common/main.h"
|
||||
|
||||
union SDL_Event;
|
||||
|
||||
namespace Common {
|
||||
class ParamPackage;
|
||||
} // namespace Common
|
||||
|
||||
namespace InputCommon::Polling {
|
||||
class DevicePoller;
|
||||
enum class DeviceType;
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@@ -15,7 +14,6 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <SDL.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/param_package.h"
|
||||
@@ -23,12 +21,10 @@
|
||||
#include "core/frontend/input.h"
|
||||
#include "input_common/sdl/sdl_impl.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
namespace SDL {
|
||||
namespace InputCommon::SDL {
|
||||
|
||||
static std::string GetGUID(SDL_Joystick* joystick) {
|
||||
SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
|
||||
const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
|
||||
char guid_str[33];
|
||||
SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
|
||||
return guid_str;
|
||||
@@ -37,26 +33,27 @@ static std::string GetGUID(SDL_Joystick* joystick) {
|
||||
/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
|
||||
static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
|
||||
|
||||
static int SDLEventWatcher(void* userdata, SDL_Event* event) {
|
||||
SDLState* sdl_state = reinterpret_cast<SDLState*>(userdata);
|
||||
static int SDLEventWatcher(void* user_data, SDL_Event* event) {
|
||||
auto* const sdl_state = static_cast<SDLState*>(user_data);
|
||||
|
||||
// Don't handle the event if we are configuring
|
||||
if (sdl_state->polling) {
|
||||
sdl_state->event_queue.Push(*event);
|
||||
} else {
|
||||
sdl_state->HandleGameControllerEvent(*event);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
class SDLJoystick {
|
||||
public:
|
||||
SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
|
||||
decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose)
|
||||
: guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, deleter} {}
|
||||
SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick)
|
||||
: guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose} {}
|
||||
|
||||
void SetButton(int button, bool value) {
|
||||
std::lock_guard lock{mutex};
|
||||
state.buttons[button] = value;
|
||||
state.buttons.insert_or_assign(button, value);
|
||||
}
|
||||
|
||||
bool GetButton(int button) const {
|
||||
@@ -66,7 +63,7 @@ public:
|
||||
|
||||
void SetAxis(int axis, Sint16 value) {
|
||||
std::lock_guard lock{mutex};
|
||||
state.axes[axis] = value;
|
||||
state.axes.insert_or_assign(axis, value);
|
||||
}
|
||||
|
||||
float GetAxis(int axis) const {
|
||||
@@ -93,7 +90,7 @@ public:
|
||||
|
||||
void SetHat(int hat, Uint8 direction) {
|
||||
std::lock_guard lock{mutex};
|
||||
state.hats[hat] = direction;
|
||||
state.hats.insert_or_assign(hat, direction);
|
||||
}
|
||||
|
||||
bool GetHatDirection(int hat, Uint8 direction) const {
|
||||
@@ -118,10 +115,8 @@ public:
|
||||
return sdl_joystick.get();
|
||||
}
|
||||
|
||||
void SetSDLJoystick(SDL_Joystick* joystick,
|
||||
decltype(&SDL_JoystickClose) deleter = &SDL_JoystickClose) {
|
||||
sdl_joystick =
|
||||
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)>(joystick, deleter);
|
||||
void SetSDLJoystick(SDL_Joystick* joystick) {
|
||||
sdl_joystick.reset(joystick);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -136,59 +131,57 @@ private:
|
||||
mutable std::mutex mutex;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the nth joystick with the corresponding GUID
|
||||
*/
|
||||
std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) {
|
||||
std::lock_guard lock{joystick_map_mutex};
|
||||
const auto it = joystick_map.find(guid);
|
||||
if (it != joystick_map.end()) {
|
||||
while (it->second.size() <= port) {
|
||||
auto joystick = std::make_shared<SDLJoystick>(guid, it->second.size(), nullptr,
|
||||
[](SDL_Joystick*) {});
|
||||
while (it->second.size() <= static_cast<std::size_t>(port)) {
|
||||
auto joystick =
|
||||
std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), nullptr);
|
||||
it->second.emplace_back(std::move(joystick));
|
||||
}
|
||||
return it->second[port];
|
||||
}
|
||||
auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, [](SDL_Joystick*) {});
|
||||
auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr);
|
||||
return joystick_map[guid].emplace_back(std::move(joystick));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check how many identical joysticks (by guid) were connected before the one with sdl_id and so tie
|
||||
* it to a SDLJoystick with the same guid and that port
|
||||
*/
|
||||
std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
|
||||
auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
|
||||
const std::string guid = GetGUID(sdl_joystick);
|
||||
|
||||
std::lock_guard lock{joystick_map_mutex};
|
||||
auto map_it = joystick_map.find(guid);
|
||||
const auto map_it = joystick_map.find(guid);
|
||||
if (map_it != joystick_map.end()) {
|
||||
auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
|
||||
[&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
|
||||
return sdl_joystick == joystick->GetSDLJoystick();
|
||||
});
|
||||
const auto vec_it =
|
||||
std::find_if(map_it->second.begin(), map_it->second.end(),
|
||||
[&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
|
||||
return sdl_joystick == joystick->GetSDLJoystick();
|
||||
});
|
||||
if (vec_it != map_it->second.end()) {
|
||||
// This is the common case: There is already an existing SDL_Joystick maped to a
|
||||
// SDLJoystick. return the SDLJoystick
|
||||
return *vec_it;
|
||||
}
|
||||
|
||||
// Search for a SDLJoystick without a mapped SDL_Joystick...
|
||||
auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(),
|
||||
[](const std::shared_ptr<SDLJoystick>& joystick) {
|
||||
return !joystick->GetSDLJoystick();
|
||||
});
|
||||
const auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(),
|
||||
[](const std::shared_ptr<SDLJoystick>& joystick) {
|
||||
return !joystick->GetSDLJoystick();
|
||||
});
|
||||
if (nullptr_it != map_it->second.end()) {
|
||||
// ... and map it
|
||||
(*nullptr_it)->SetSDLJoystick(sdl_joystick);
|
||||
return *nullptr_it;
|
||||
}
|
||||
|
||||
// There is no SDLJoystick without a mapped SDL_Joystick
|
||||
// Create a new SDLJoystick
|
||||
auto joystick = std::make_shared<SDLJoystick>(guid, map_it->second.size(), sdl_joystick);
|
||||
const int port = static_cast<int>(map_it->second.size());
|
||||
auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick);
|
||||
return map_it->second.emplace_back(std::move(joystick));
|
||||
}
|
||||
|
||||
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
|
||||
return joystick_map[guid].emplace_back(std::move(joystick));
|
||||
}
|
||||
@@ -215,17 +208,19 @@ void SDLState::InitJoystick(int joystick_index) {
|
||||
(*it)->SetSDLJoystick(sdl_joystick);
|
||||
return;
|
||||
}
|
||||
auto joystick = std::make_shared<SDLJoystick>(guid, joystick_guid_list.size(), sdl_joystick);
|
||||
const int port = static_cast<int>(joystick_guid_list.size());
|
||||
auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick);
|
||||
joystick_guid_list.emplace_back(std::move(joystick));
|
||||
}
|
||||
|
||||
void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
|
||||
std::string guid = GetGUID(sdl_joystick);
|
||||
const std::string guid = GetGUID(sdl_joystick);
|
||||
|
||||
std::shared_ptr<SDLJoystick> joystick;
|
||||
{
|
||||
std::lock_guard lock{joystick_map_mutex};
|
||||
// This call to guid is safe since the joystick is guaranteed to be in the map
|
||||
auto& joystick_guid_list = joystick_map[guid];
|
||||
const auto& joystick_guid_list = joystick_map[guid];
|
||||
const auto joystick_it =
|
||||
std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
|
||||
[&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
|
||||
@@ -233,9 +228,10 @@ void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
|
||||
});
|
||||
joystick = *joystick_it;
|
||||
}
|
||||
// Destruct SDL_Joystick outside the lock guard because SDL can internally call event calback
|
||||
// which locks the mutex again
|
||||
joystick->SetSDLJoystick(nullptr, [](SDL_Joystick*) {});
|
||||
|
||||
// Destruct SDL_Joystick outside the lock guard because SDL can internally call the
|
||||
// event callback which locks the mutex again.
|
||||
joystick->SetSDLJoystick(nullptr);
|
||||
}
|
||||
|
||||
void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
|
||||
@@ -317,9 +313,10 @@ public:
|
||||
trigger_if_greater(trigger_if_greater_) {}
|
||||
|
||||
bool GetStatus() const override {
|
||||
float axis_value = joystick->GetAxis(axis);
|
||||
if (trigger_if_greater)
|
||||
const float axis_value = joystick->GetAxis(axis);
|
||||
if (trigger_if_greater) {
|
||||
return axis_value > threshold;
|
||||
}
|
||||
return axis_value < threshold;
|
||||
}
|
||||
|
||||
@@ -444,7 +441,7 @@ public:
|
||||
const int port = params.Get("port", 0);
|
||||
const int axis_x = params.Get("axis_x", 0);
|
||||
const int axis_y = params.Get("axis_y", 1);
|
||||
float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
|
||||
const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
|
||||
|
||||
auto joystick = state.GetSDLJoystickByGUID(guid, port);
|
||||
|
||||
@@ -470,7 +467,7 @@ SDLState::SDLState() {
|
||||
return;
|
||||
}
|
||||
if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
|
||||
LOG_ERROR(Input, "Failed to set Hint for background events", SDL_GetError());
|
||||
LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
SDL_AddEventWatch(&SDLEventWatcher, this);
|
||||
@@ -507,12 +504,12 @@ SDLState::~SDLState() {
|
||||
}
|
||||
}
|
||||
|
||||
Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
|
||||
static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
|
||||
Common::ParamPackage params({{"engine", "sdl"}});
|
||||
|
||||
switch (event.type) {
|
||||
case SDL_JOYAXISMOTION: {
|
||||
auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
|
||||
const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
|
||||
params.Set("port", joystick->GetPort());
|
||||
params.Set("guid", joystick->GetGUID());
|
||||
params.Set("axis", event.jaxis.axis);
|
||||
@@ -526,14 +523,14 @@ Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Eve
|
||||
break;
|
||||
}
|
||||
case SDL_JOYBUTTONUP: {
|
||||
auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
|
||||
const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
|
||||
params.Set("port", joystick->GetPort());
|
||||
params.Set("guid", joystick->GetGUID());
|
||||
params.Set("button", event.jbutton.button);
|
||||
break;
|
||||
}
|
||||
case SDL_JOYHATMOTION: {
|
||||
auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
|
||||
const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
|
||||
params.Set("port", joystick->GetPort());
|
||||
params.Set("guid", joystick->GetGUID());
|
||||
params.Set("hat", event.jhat.hat);
|
||||
@@ -607,8 +604,8 @@ public:
|
||||
SDLPoller::Start();
|
||||
|
||||
// Reset stored axes
|
||||
analog_xaxis = -1;
|
||||
analog_yaxis = -1;
|
||||
analog_x_axis = -1;
|
||||
analog_y_axis = -1;
|
||||
analog_axes_joystick = -1;
|
||||
}
|
||||
|
||||
@@ -620,25 +617,25 @@ public:
|
||||
}
|
||||
// An analog device needs two axes, so we need to store the axis for later and wait for
|
||||
// a second SDL event. The axes also must be from the same joystick.
|
||||
int axis = event.jaxis.axis;
|
||||
if (analog_xaxis == -1) {
|
||||
analog_xaxis = axis;
|
||||
const int axis = event.jaxis.axis;
|
||||
if (analog_x_axis == -1) {
|
||||
analog_x_axis = axis;
|
||||
analog_axes_joystick = event.jaxis.which;
|
||||
} else if (analog_yaxis == -1 && analog_xaxis != axis &&
|
||||
} else if (analog_y_axis == -1 && analog_x_axis != axis &&
|
||||
analog_axes_joystick == event.jaxis.which) {
|
||||
analog_yaxis = axis;
|
||||
analog_y_axis = axis;
|
||||
}
|
||||
}
|
||||
Common::ParamPackage params;
|
||||
if (analog_xaxis != -1 && analog_yaxis != -1) {
|
||||
auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
|
||||
if (analog_x_axis != -1 && analog_y_axis != -1) {
|
||||
const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
|
||||
params.Set("engine", "sdl");
|
||||
params.Set("port", joystick->GetPort());
|
||||
params.Set("guid", joystick->GetGUID());
|
||||
params.Set("axis_x", analog_xaxis);
|
||||
params.Set("axis_y", analog_yaxis);
|
||||
analog_xaxis = -1;
|
||||
analog_yaxis = -1;
|
||||
params.Set("axis_x", analog_x_axis);
|
||||
params.Set("axis_y", analog_y_axis);
|
||||
analog_x_axis = -1;
|
||||
analog_y_axis = -1;
|
||||
analog_axes_joystick = -1;
|
||||
return params;
|
||||
}
|
||||
@@ -646,8 +643,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
int analog_xaxis = -1;
|
||||
int analog_yaxis = -1;
|
||||
int analog_x_axis = -1;
|
||||
int analog_y_axis = -1;
|
||||
SDL_JoystickID analog_axes_joystick = -1;
|
||||
};
|
||||
} // namespace Polling
|
||||
@@ -667,5 +664,4 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
|
||||
return pollers;
|
||||
}
|
||||
|
||||
} // namespace SDL
|
||||
} // namespace InputCommon
|
||||
} // namespace InputCommon::SDL
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include "common/common_types.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "input_common/sdl/sdl.h"
|
||||
|
||||
@@ -16,9 +19,9 @@ using SDL_JoystickID = s32;
|
||||
|
||||
namespace InputCommon::SDL {
|
||||
|
||||
class SDLJoystick;
|
||||
class SDLButtonFactory;
|
||||
class SDLAnalogFactory;
|
||||
class SDLButtonFactory;
|
||||
class SDLJoystick;
|
||||
|
||||
class SDLState : public State {
|
||||
public:
|
||||
@@ -31,7 +34,13 @@ public:
|
||||
/// Handle SDL_Events for joysticks from SDL_PollEvent
|
||||
void HandleGameControllerEvent(const SDL_Event& event);
|
||||
|
||||
/// Get the nth joystick with the corresponding GUID
|
||||
std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id);
|
||||
|
||||
/**
|
||||
* Check how many identical joysticks (by guid) were connected before the one with sdl_id and so
|
||||
* tie it to a SDLJoystick with the same guid and that port
|
||||
*/
|
||||
std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
|
||||
|
||||
/// Get all DevicePoller that use the SDL backend for a specific device type
|
||||
|
||||
@@ -102,6 +102,9 @@ add_library(video_core STATIC
|
||||
shader/decode/xmad.cpp
|
||||
shader/decode/other.cpp
|
||||
shader/decode.cpp
|
||||
shader/node_helper.cpp
|
||||
shader/node_helper.h
|
||||
shader/node.h
|
||||
shader/shader_ir.cpp
|
||||
shader/shader_ir.h
|
||||
shader/track.cpp
|
||||
|
||||
@@ -1663,6 +1663,7 @@ private:
|
||||
INST("111000100100----", Id::BRA, Type::Flow, "BRA"),
|
||||
INST("1111000011111---", Id::SYNC, Type::Flow, "SYNC"),
|
||||
INST("111000110100---", Id::BRK, Type::Flow, "BRK"),
|
||||
INST("111000110000----", Id::EXIT, Type::Flow, "EXIT"),
|
||||
INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"),
|
||||
INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
|
||||
INST("1110111101001---", Id::LD_S, Type::Memory, "LD_S"),
|
||||
@@ -1686,7 +1687,6 @@ private:
|
||||
INST("1101111100------", Id::TLD4S, Type::Texture, "TLD4S"),
|
||||
INST("110111110110----", Id::TMML_B, Type::Texture, "TMML_B"),
|
||||
INST("1101111101011---", Id::TMML, Type::Texture, "TMML"),
|
||||
INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"),
|
||||
INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
|
||||
INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
|
||||
INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"),
|
||||
|
||||
@@ -75,7 +75,7 @@ void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPus
|
||||
|
||||
void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
|
||||
const u64 fence{PushCommand(SubmitListCommand(std::move(entries)))};
|
||||
const s64 synchronization_ticks{Core::Timing::usToCycles(9000)};
|
||||
const s64 synchronization_ticks{Core::Timing::usToCycles(std::chrono::microseconds{9000})};
|
||||
system.CoreTiming().ScheduleEvent(synchronization_ticks, synchronization_event, fence);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "video_core/renderer_opengl/gl_device.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
@@ -24,6 +27,7 @@ Device::Device() {
|
||||
max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS);
|
||||
max_varyings = GetInteger<u32>(GL_MAX_VARYING_VECTORS);
|
||||
has_variable_aoffi = TestVariableAoffi();
|
||||
has_component_indexing_bug = TestComponentIndexingBug();
|
||||
}
|
||||
|
||||
Device::Device(std::nullptr_t) {
|
||||
@@ -31,6 +35,7 @@ Device::Device(std::nullptr_t) {
|
||||
max_vertex_attributes = 16;
|
||||
max_varyings = 15;
|
||||
has_variable_aoffi = true;
|
||||
has_component_indexing_bug = false;
|
||||
}
|
||||
|
||||
bool Device::TestVariableAoffi() {
|
||||
@@ -52,4 +57,53 @@ void main() {
|
||||
return supported;
|
||||
}
|
||||
|
||||
bool Device::TestComponentIndexingBug() {
|
||||
constexpr char log_message[] = "Renderer_ComponentIndexingBug: {}";
|
||||
const GLchar* COMPONENT_TEST = R"(#version 430 core
|
||||
layout (std430, binding = 0) buffer OutputBuffer {
|
||||
uint output_value;
|
||||
};
|
||||
layout (std140, binding = 0) uniform InputBuffer {
|
||||
uvec4 input_value[4096];
|
||||
};
|
||||
layout (location = 0) uniform uint idx;
|
||||
void main() {
|
||||
output_value = input_value[idx >> 2][idx & 3];
|
||||
})";
|
||||
const GLuint shader{glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &COMPONENT_TEST)};
|
||||
SCOPE_EXIT({ glDeleteProgram(shader); });
|
||||
glUseProgram(shader);
|
||||
|
||||
OGLVertexArray vao;
|
||||
vao.Create();
|
||||
glBindVertexArray(vao.handle);
|
||||
|
||||
constexpr std::array<GLuint, 8> values{0, 0, 0, 0, 0x1236327, 0x985482, 0x872753, 0x2378432};
|
||||
OGLBuffer ubo;
|
||||
ubo.Create();
|
||||
glNamedBufferData(ubo.handle, sizeof(values), values.data(), GL_STATIC_DRAW);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo.handle);
|
||||
|
||||
OGLBuffer ssbo;
|
||||
ssbo.Create();
|
||||
glNamedBufferStorage(ssbo.handle, sizeof(GLuint), nullptr, GL_CLIENT_STORAGE_BIT);
|
||||
|
||||
for (GLuint index = 4; index < 8; ++index) {
|
||||
glInvalidateBufferData(ssbo.handle);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo.handle);
|
||||
|
||||
glProgramUniform1ui(shader, 0, index);
|
||||
glDrawArrays(GL_POINTS, 0, 1);
|
||||
|
||||
GLuint result;
|
||||
glGetNamedBufferSubData(ssbo.handle, 0, sizeof(result), &result);
|
||||
if (result != values.at(index)) {
|
||||
LOG_INFO(Render_OpenGL, log_message, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
LOG_INFO(Render_OpenGL, log_message, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -30,13 +30,19 @@ public:
|
||||
return has_variable_aoffi;
|
||||
}
|
||||
|
||||
bool HasComponentIndexingBug() const {
|
||||
return has_component_indexing_bug;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool TestVariableAoffi();
|
||||
static bool TestComponentIndexingBug();
|
||||
|
||||
std::size_t uniform_buffer_alignment{};
|
||||
u32 max_vertex_attributes{};
|
||||
u32 max_varyings{};
|
||||
bool has_variable_aoffi{};
|
||||
bool has_component_indexing_bug{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -35,8 +35,8 @@ struct UnspecializedShader {
|
||||
namespace {
|
||||
|
||||
/// Gets the address for the specified shader stage program
|
||||
GPUVAddr GetShaderAddress(Maxwell::ShaderProgram program) {
|
||||
const auto& gpu{Core::System::GetInstance().GPU().Maxwell3D()};
|
||||
GPUVAddr GetShaderAddress(Core::System& system, Maxwell::ShaderProgram program) {
|
||||
const auto& gpu{system.GPU().Maxwell3D()};
|
||||
const auto& shader_config{gpu.regs.shader_config[static_cast<std::size_t>(program)]};
|
||||
return gpu.regs.code_address.CodeAddress() + shader_config.offset;
|
||||
}
|
||||
@@ -350,7 +350,8 @@ ShaderDiskCacheUsage CachedShader::GetUsage(GLenum primitive_mode,
|
||||
|
||||
ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system,
|
||||
Core::Frontend::EmuWindow& emu_window, const Device& device)
|
||||
: RasterizerCache{rasterizer}, emu_window{emu_window}, device{device}, disk_cache{system} {}
|
||||
: RasterizerCache{rasterizer}, system{system}, emu_window{emu_window}, device{device},
|
||||
disk_cache{system} {}
|
||||
|
||||
void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||
const VideoCore::DiskResourceLoadCallback& callback) {
|
||||
@@ -546,42 +547,45 @@ std::unordered_map<u64, UnspecializedShader> ShaderCacheOpenGL::GenerateUnspecia
|
||||
}
|
||||
|
||||
Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
|
||||
if (!Core::System::GetInstance().GPU().Maxwell3D().dirty_flags.shaders) {
|
||||
return last_shaders[static_cast<u32>(program)];
|
||||
if (!system.GPU().Maxwell3D().dirty_flags.shaders) {
|
||||
return last_shaders[static_cast<std::size_t>(program)];
|
||||
}
|
||||
|
||||
auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
|
||||
const GPUVAddr program_addr{GetShaderAddress(program)};
|
||||
auto& memory_manager{system.GPU().MemoryManager()};
|
||||
const GPUVAddr program_addr{GetShaderAddress(system, program)};
|
||||
|
||||
// Look up shader in the cache based on address
|
||||
const auto& host_ptr{memory_manager.GetPointer(program_addr)};
|
||||
const auto host_ptr{memory_manager.GetPointer(program_addr)};
|
||||
Shader shader{TryGet(host_ptr)};
|
||||
|
||||
if (!shader) {
|
||||
// No shader found - create a new one
|
||||
ProgramCode program_code{GetShaderCode(memory_manager, program_addr, host_ptr)};
|
||||
ProgramCode program_code_b;
|
||||
if (program == Maxwell::ShaderProgram::VertexA) {
|
||||
const GPUVAddr program_addr_b{GetShaderAddress(Maxwell::ShaderProgram::VertexB)};
|
||||
program_code_b = GetShaderCode(memory_manager, program_addr_b,
|
||||
memory_manager.GetPointer(program_addr_b));
|
||||
}
|
||||
const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b);
|
||||
const VAddr cpu_addr{*memory_manager.GpuToCpuAddress(program_addr)};
|
||||
const auto found = precompiled_shaders.find(unique_identifier);
|
||||
if (found != precompiled_shaders.end()) {
|
||||
shader =
|
||||
std::make_shared<CachedShader>(cpu_addr, unique_identifier, program, disk_cache,
|
||||
precompiled_programs, found->second, host_ptr);
|
||||
} else {
|
||||
shader = std::make_shared<CachedShader>(
|
||||
device, cpu_addr, unique_identifier, program, disk_cache, precompiled_programs,
|
||||
std::move(program_code), std::move(program_code_b), host_ptr);
|
||||
}
|
||||
Register(shader);
|
||||
if (shader) {
|
||||
return last_shaders[static_cast<std::size_t>(program)] = shader;
|
||||
}
|
||||
|
||||
return last_shaders[static_cast<u32>(program)] = shader;
|
||||
// No shader found - create a new one
|
||||
ProgramCode program_code{GetShaderCode(memory_manager, program_addr, host_ptr)};
|
||||
ProgramCode program_code_b;
|
||||
if (program == Maxwell::ShaderProgram::VertexA) {
|
||||
const GPUVAddr program_addr_b{GetShaderAddress(system, Maxwell::ShaderProgram::VertexB)};
|
||||
program_code_b = GetShaderCode(memory_manager, program_addr_b,
|
||||
memory_manager.GetPointer(program_addr_b));
|
||||
}
|
||||
|
||||
const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b);
|
||||
const VAddr cpu_addr{*memory_manager.GpuToCpuAddress(program_addr)};
|
||||
const auto found = precompiled_shaders.find(unique_identifier);
|
||||
if (found != precompiled_shaders.end()) {
|
||||
// Create a shader from the cache
|
||||
shader = std::make_shared<CachedShader>(cpu_addr, unique_identifier, program, disk_cache,
|
||||
precompiled_programs, found->second, host_ptr);
|
||||
} else {
|
||||
// Create a shader from guest memory
|
||||
shader = std::make_shared<CachedShader>(
|
||||
device, cpu_addr, unique_identifier, program, disk_cache, precompiled_programs,
|
||||
std::move(program_code), std::move(program_code_b), host_ptr);
|
||||
}
|
||||
Register(shader);
|
||||
|
||||
return last_shaders[static_cast<std::size_t>(program)] = shader;
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -137,6 +137,7 @@ private:
|
||||
CachedProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump,
|
||||
const std::set<GLenum>& supported_formats);
|
||||
|
||||
Core::System& system;
|
||||
Core::Frontend::EmuWindow& emu_window;
|
||||
const Device& device;
|
||||
ShaderDiskCacheOpenGL disk_cache;
|
||||
|
||||
@@ -45,7 +45,6 @@ struct TextureAoffi {};
|
||||
using TextureArgument = std::pair<Type, Node>;
|
||||
using TextureIR = std::variant<TextureAoffi, TextureArgument>;
|
||||
|
||||
enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 };
|
||||
constexpr u32 MAX_CONSTBUFFER_ELEMENTS =
|
||||
static_cast<u32>(RasterizerOpenGL::MaxConstbufferSize) / (4 * sizeof(float));
|
||||
|
||||
@@ -124,8 +123,8 @@ bool IsPrecise(Operation operand) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsPrecise(Node node) {
|
||||
if (const auto operation = std::get_if<OperationNode>(node)) {
|
||||
bool IsPrecise(const Node& node) {
|
||||
if (const auto operation = std::get_if<OperationNode>(&*node)) {
|
||||
return IsPrecise(*operation);
|
||||
}
|
||||
return false;
|
||||
@@ -247,6 +246,12 @@ private:
|
||||
code.AddLine("layout ({}, max_vertices = {}) out;", topology, max_vertices);
|
||||
code.AddNewLine();
|
||||
|
||||
code.AddLine("in gl_PerVertex {{");
|
||||
++code.scope;
|
||||
code.AddLine("vec4 gl_Position;");
|
||||
--code.scope;
|
||||
code.AddLine("}} gl_in[];");
|
||||
|
||||
DeclareVertexRedeclarations();
|
||||
}
|
||||
|
||||
@@ -349,7 +354,7 @@ private:
|
||||
}
|
||||
|
||||
void DeclareInputAttribute(Attribute::Index index, bool skip_unused) {
|
||||
const u32 generic_index{GetGenericAttributeIndex(index)};
|
||||
const u32 location{GetGenericAttributeIndex(index)};
|
||||
|
||||
std::string name{GetInputAttribute(index)};
|
||||
if (stage == ShaderStage::Geometry) {
|
||||
@@ -358,19 +363,13 @@ private:
|
||||
|
||||
std::string suffix;
|
||||
if (stage == ShaderStage::Fragment) {
|
||||
const auto input_mode{header.ps.GetAttributeUse(generic_index)};
|
||||
const auto input_mode{header.ps.GetAttributeUse(location)};
|
||||
if (skip_unused && input_mode == AttributeUse::Unused) {
|
||||
return;
|
||||
}
|
||||
suffix = GetInputFlags(input_mode);
|
||||
}
|
||||
|
||||
u32 location = generic_index;
|
||||
if (stage != ShaderStage::Vertex) {
|
||||
// If inputs are varyings, add an offset
|
||||
location += GENERIC_VARYING_START_LOCATION;
|
||||
}
|
||||
|
||||
code.AddLine("layout (location = {}) {} in vec4 {};", location, suffix, name);
|
||||
}
|
||||
|
||||
@@ -395,7 +394,7 @@ private:
|
||||
}
|
||||
|
||||
void DeclareOutputAttribute(Attribute::Index index) {
|
||||
const u32 location{GetGenericAttributeIndex(index) + GENERIC_VARYING_START_LOCATION};
|
||||
const u32 location{GetGenericAttributeIndex(index)};
|
||||
code.AddLine("layout (location = {}) out vec4 {};", location, GetOutputAttribute(index));
|
||||
}
|
||||
|
||||
@@ -498,15 +497,15 @@ private:
|
||||
}
|
||||
|
||||
void VisitBlock(const NodeBlock& bb) {
|
||||
for (const Node node : bb) {
|
||||
for (const auto& node : bb) {
|
||||
if (const std::string expr = Visit(node); !expr.empty()) {
|
||||
code.AddLine(expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string Visit(Node node) {
|
||||
if (const auto operation = std::get_if<OperationNode>(node)) {
|
||||
std::string Visit(const Node& node) {
|
||||
if (const auto operation = std::get_if<OperationNode>(&*node)) {
|
||||
const auto operation_index = static_cast<std::size_t>(operation->GetCode());
|
||||
if (operation_index >= operation_decompilers.size()) {
|
||||
UNREACHABLE_MSG("Out of bounds operation: {}", operation_index);
|
||||
@@ -520,7 +519,7 @@ private:
|
||||
return (this->*decompiler)(*operation);
|
||||
}
|
||||
|
||||
if (const auto gpr = std::get_if<GprNode>(node)) {
|
||||
if (const auto gpr = std::get_if<GprNode>(&*node)) {
|
||||
const u32 index = gpr->GetIndex();
|
||||
if (index == Register::ZeroIndex) {
|
||||
return "0";
|
||||
@@ -528,7 +527,7 @@ private:
|
||||
return GetRegister(index);
|
||||
}
|
||||
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(node)) {
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(&*node)) {
|
||||
const u32 value = immediate->GetValue();
|
||||
if (value < 10) {
|
||||
// For eyecandy avoid using hex numbers on single digits
|
||||
@@ -537,7 +536,7 @@ private:
|
||||
return fmt::format("utof(0x{:x}u)", immediate->GetValue());
|
||||
}
|
||||
|
||||
if (const auto predicate = std::get_if<PredicateNode>(node)) {
|
||||
if (const auto predicate = std::get_if<PredicateNode>(&*node)) {
|
||||
const auto value = [&]() -> std::string {
|
||||
switch (const auto index = predicate->GetIndex(); index) {
|
||||
case Tegra::Shader::Pred::UnusedIndex:
|
||||
@@ -554,7 +553,7 @@ private:
|
||||
return value;
|
||||
}
|
||||
|
||||
if (const auto abuf = std::get_if<AbufNode>(node)) {
|
||||
if (const auto abuf = std::get_if<AbufNode>(&*node)) {
|
||||
UNIMPLEMENTED_IF_MSG(abuf->IsPhysicalBuffer() && stage == ShaderStage::Geometry,
|
||||
"Physical attributes in geometry shaders are not implemented");
|
||||
if (abuf->IsPhysicalBuffer()) {
|
||||
@@ -564,9 +563,9 @@ private:
|
||||
return ReadAttribute(abuf->GetIndex(), abuf->GetElement(), abuf->GetBuffer());
|
||||
}
|
||||
|
||||
if (const auto cbuf = std::get_if<CbufNode>(node)) {
|
||||
if (const auto cbuf = std::get_if<CbufNode>(&*node)) {
|
||||
const Node offset = cbuf->GetOffset();
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(offset)) {
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) {
|
||||
// Direct access
|
||||
const u32 offset_imm = immediate->GetValue();
|
||||
ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access");
|
||||
@@ -577,30 +576,47 @@ private:
|
||||
if (std::holds_alternative<OperationNode>(*offset)) {
|
||||
// Indirect access
|
||||
const std::string final_offset = code.GenerateTemporary();
|
||||
code.AddLine("uint {} = (ftou({}) / 4);", final_offset, Visit(offset));
|
||||
return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()),
|
||||
final_offset, final_offset);
|
||||
code.AddLine("uint {} = ftou({}) >> 2;", final_offset, Visit(offset));
|
||||
|
||||
if (!device.HasComponentIndexingBug()) {
|
||||
return fmt::format("{}[{} >> 2][{} & 3]", GetConstBuffer(cbuf->GetIndex()),
|
||||
final_offset, final_offset);
|
||||
}
|
||||
|
||||
// AMD's proprietary GLSL compiler emits ill code for variable component access.
|
||||
// To bypass this driver bug generate 4 ifs, one per each component.
|
||||
const std::string pack = code.GenerateTemporary();
|
||||
code.AddLine("vec4 {} = {}[{} >> 2];", pack, GetConstBuffer(cbuf->GetIndex()),
|
||||
final_offset);
|
||||
|
||||
const std::string result = code.GenerateTemporary();
|
||||
code.AddLine("float {};", result);
|
||||
for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
|
||||
code.AddLine("if (({} & 3) == {}) {} = {}{};", final_offset, swizzle, result,
|
||||
pack, GetSwizzle(swizzle));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
UNREACHABLE_MSG("Unmanaged offset node type");
|
||||
}
|
||||
|
||||
if (const auto gmem = std::get_if<GmemNode>(node)) {
|
||||
if (const auto gmem = std::get_if<GmemNode>(&*node)) {
|
||||
const std::string real = Visit(gmem->GetRealAddress());
|
||||
const std::string base = Visit(gmem->GetBaseAddress());
|
||||
const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base);
|
||||
return fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset);
|
||||
}
|
||||
|
||||
if (const auto lmem = std::get_if<LmemNode>(node)) {
|
||||
if (const auto lmem = std::get_if<LmemNode>(&*node)) {
|
||||
return fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress()));
|
||||
}
|
||||
|
||||
if (const auto internal_flag = std::get_if<InternalFlagNode>(node)) {
|
||||
if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) {
|
||||
return GetInternalFlag(internal_flag->GetFlag());
|
||||
}
|
||||
|
||||
if (const auto conditional = std::get_if<ConditionalNode>(node)) {
|
||||
if (const auto conditional = std::get_if<ConditionalNode>(&*node)) {
|
||||
// It's invalid to call conditional on nested nodes, use an operation instead
|
||||
code.AddLine("if ({}) {{", Visit(conditional->GetCondition()));
|
||||
++code.scope;
|
||||
@@ -612,7 +628,7 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
if (const auto comment = std::get_if<CommentNode>(node)) {
|
||||
if (const auto comment = std::get_if<CommentNode>(&*node)) {
|
||||
return "// " + comment->GetText();
|
||||
}
|
||||
|
||||
@@ -620,7 +636,7 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string ReadAttribute(Attribute::Index attribute, u32 element, Node buffer = {}) {
|
||||
std::string ReadAttribute(Attribute::Index attribute, u32 element, const Node& buffer = {}) {
|
||||
const auto GeometryPass = [&](std::string_view name) {
|
||||
if (stage == ShaderStage::Geometry && buffer) {
|
||||
// TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games
|
||||
@@ -633,10 +649,14 @@ private:
|
||||
|
||||
switch (attribute) {
|
||||
case Attribute::Index::Position:
|
||||
if (stage != ShaderStage::Fragment) {
|
||||
return GeometryPass("position") + GetSwizzle(element);
|
||||
} else {
|
||||
switch (stage) {
|
||||
case ShaderStage::Geometry:
|
||||
return fmt::format("gl_in[ftou({})].gl_Position{}", Visit(buffer),
|
||||
GetSwizzle(element));
|
||||
case ShaderStage::Fragment:
|
||||
return element == 3 ? "1.0f" : ("gl_FragCoord"s + GetSwizzle(element));
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
case Attribute::Index::PointCoord:
|
||||
switch (element) {
|
||||
@@ -852,7 +872,7 @@ private:
|
||||
std::string expr = ", ";
|
||||
switch (type) {
|
||||
case Type::Int:
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(operand)) {
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(&*operand)) {
|
||||
// Inline the string as an immediate integer in GLSL (some extra arguments are
|
||||
// required to be constant)
|
||||
expr += std::to_string(static_cast<s32>(immediate->GetValue()));
|
||||
@@ -884,7 +904,7 @@ private:
|
||||
|
||||
for (std::size_t index = 0; index < aoffi.size(); ++index) {
|
||||
const auto operand{aoffi.at(index)};
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(operand)) {
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(&*operand)) {
|
||||
// Inline the string as an immediate integer in GLSL (AOFFI arguments are required
|
||||
// to be constant by the standard).
|
||||
expr += std::to_string(static_cast<s32>(immediate->GetValue()));
|
||||
@@ -905,23 +925,23 @@ private:
|
||||
}
|
||||
|
||||
std::string Assign(Operation operation) {
|
||||
const Node dest = operation[0];
|
||||
const Node src = operation[1];
|
||||
const Node& dest = operation[0];
|
||||
const Node& src = operation[1];
|
||||
|
||||
std::string target;
|
||||
if (const auto gpr = std::get_if<GprNode>(dest)) {
|
||||
if (const auto gpr = std::get_if<GprNode>(&*dest)) {
|
||||
if (gpr->GetIndex() == Register::ZeroIndex) {
|
||||
// Writing to Register::ZeroIndex is a no op
|
||||
return {};
|
||||
}
|
||||
target = GetRegister(gpr->GetIndex());
|
||||
} else if (const auto abuf = std::get_if<AbufNode>(dest)) {
|
||||
} else if (const auto abuf = std::get_if<AbufNode>(&*dest)) {
|
||||
UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer());
|
||||
|
||||
target = [&]() -> std::string {
|
||||
switch (const auto attribute = abuf->GetIndex(); abuf->GetIndex()) {
|
||||
case Attribute::Index::Position:
|
||||
return "position"s + GetSwizzle(abuf->GetElement());
|
||||
return "gl_Position"s + GetSwizzle(abuf->GetElement());
|
||||
case Attribute::Index::PointSize:
|
||||
return "gl_PointSize";
|
||||
case Attribute::Index::ClipDistances0123:
|
||||
@@ -937,9 +957,9 @@ private:
|
||||
return "0";
|
||||
}
|
||||
}();
|
||||
} else if (const auto lmem = std::get_if<LmemNode>(dest)) {
|
||||
} else if (const auto lmem = std::get_if<LmemNode>(&*dest)) {
|
||||
target = fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress()));
|
||||
} else if (const auto gmem = std::get_if<GmemNode>(dest)) {
|
||||
} else if (const auto gmem = std::get_if<GmemNode>(&*dest)) {
|
||||
const std::string real = Visit(gmem->GetRealAddress());
|
||||
const std::string base = Visit(gmem->GetBaseAddress());
|
||||
const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base);
|
||||
@@ -1216,12 +1236,12 @@ private:
|
||||
}
|
||||
|
||||
std::string LogicalAssign(Operation operation) {
|
||||
const Node dest = operation[0];
|
||||
const Node src = operation[1];
|
||||
const Node& dest = operation[0];
|
||||
const Node& src = operation[1];
|
||||
|
||||
std::string target;
|
||||
|
||||
if (const auto pred = std::get_if<PredicateNode>(dest)) {
|
||||
if (const auto pred = std::get_if<PredicateNode>(&*dest)) {
|
||||
ASSERT_MSG(!pred->IsNegated(), "Negating logical assignment");
|
||||
|
||||
const auto index = pred->GetIndex();
|
||||
@@ -1232,7 +1252,7 @@ private:
|
||||
return {};
|
||||
}
|
||||
target = GetPredicate(index);
|
||||
} else if (const auto flag = std::get_if<InternalFlagNode>(dest)) {
|
||||
} else if (const auto flag = std::get_if<InternalFlagNode>(&*dest)) {
|
||||
target = GetInternalFlag(flag->GetFlag());
|
||||
}
|
||||
|
||||
@@ -1409,7 +1429,7 @@ private:
|
||||
}
|
||||
|
||||
std::string Branch(Operation operation) {
|
||||
const auto target = std::get_if<ImmediateNode>(operation[0]);
|
||||
const auto target = std::get_if<ImmediateNode>(&*operation[0]);
|
||||
UNIMPLEMENTED_IF(!target);
|
||||
|
||||
code.AddLine("jmp_to = 0x{:x}u;", target->GetValue());
|
||||
@@ -1418,7 +1438,7 @@ private:
|
||||
}
|
||||
|
||||
std::string PushFlowStack(Operation operation) {
|
||||
const auto target = std::get_if<ImmediateNode>(operation[0]);
|
||||
const auto target = std::get_if<ImmediateNode>(&*operation[0]);
|
||||
UNIMPLEMENTED_IF(!target);
|
||||
|
||||
code.AddLine("flow_stack[flow_stack_top++] = 0x{:x}u;", target->GetValue());
|
||||
@@ -1506,9 +1526,7 @@ private:
|
||||
|
||||
// If a geometry shader is attached, it will always flip (it's the last stage before
|
||||
// fragment). For more info about flipping, refer to gl_shader_gen.cpp.
|
||||
code.AddLine("position.xy *= viewport_flip.xy;");
|
||||
code.AddLine("gl_Position = position;");
|
||||
code.AddLine("position.w = 1.0;");
|
||||
code.AddLine("gl_Position.xy *= viewport_flip.xy;");
|
||||
code.AddLine("EmitVertex();");
|
||||
return {};
|
||||
}
|
||||
@@ -1746,8 +1764,7 @@ private:
|
||||
}
|
||||
|
||||
u32 GetNumPhysicalVaryings() const {
|
||||
return std::min<u32>(device.GetMaxVaryings() - GENERIC_VARYING_START_LOCATION,
|
||||
Maxwell::NumVaryings);
|
||||
return std::min<u32>(device.GetMaxVaryings(), Maxwell::NumVaryings);
|
||||
}
|
||||
|
||||
const Device& device;
|
||||
|
||||
@@ -23,8 +23,6 @@ ProgramResult GenerateVertexShader(const Device& device, const ShaderSetup& setu
|
||||
out += GetCommonDeclarations();
|
||||
|
||||
out += R"(
|
||||
layout (location = 0) out vec4 position;
|
||||
|
||||
layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
|
||||
@@ -48,7 +46,6 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config {
|
||||
|
||||
out += R"(
|
||||
void main() {
|
||||
position = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
execute_vertex();
|
||||
)";
|
||||
|
||||
@@ -59,19 +56,12 @@ void main() {
|
||||
out += R"(
|
||||
|
||||
// Set Position Y direction
|
||||
position.y *= utof(config_pack[2]);
|
||||
gl_Position.y *= utof(config_pack[2]);
|
||||
// Check if the flip stage is VertexB
|
||||
// Config pack's second value is flip_stage
|
||||
if (config_pack[1] == 1) {
|
||||
// Viewport can be flipped, which is unsupported by glViewport
|
||||
position.xy *= viewport_flip.xy;
|
||||
}
|
||||
gl_Position = position;
|
||||
|
||||
// TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0
|
||||
// For now, this is here to bring order in lieu of proper emulation
|
||||
if (config_pack[1] == 1) {
|
||||
position.w = 1.0;
|
||||
gl_Position.xy *= viewport_flip.xy;
|
||||
}
|
||||
})";
|
||||
|
||||
@@ -85,9 +75,6 @@ ProgramResult GenerateGeometryShader(const Device& device, const ShaderSetup& se
|
||||
out += GetCommonDeclarations();
|
||||
|
||||
out += R"(
|
||||
layout (location = 0) in vec4 gs_position[];
|
||||
layout (location = 0) out vec4 position;
|
||||
|
||||
layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
|
||||
@@ -124,8 +111,6 @@ layout (location = 5) out vec4 FragColor5;
|
||||
layout (location = 6) out vec4 FragColor6;
|
||||
layout (location = 7) out vec4 FragColor7;
|
||||
|
||||
layout (location = 0) in noperspective vec4 position;
|
||||
|
||||
layout (std140, binding = EMULATION_UBO_BINDING) uniform fs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
|
||||
|
||||
@@ -18,6 +18,7 @@ constexpr std::array<vk::Format, 3> Depth24UnormS8Uint = {
|
||||
vk::Format::eD32SfloatS8Uint, vk::Format::eD16UnormS8Uint, {}};
|
||||
constexpr std::array<vk::Format, 3> Depth16UnormS8Uint = {
|
||||
vk::Format::eD24UnormS8Uint, vk::Format::eD32SfloatS8Uint, {}};
|
||||
constexpr std::array<vk::Format, 2> Astc = {vk::Format::eA8B8G8R8UnormPack32, {}};
|
||||
|
||||
} // namespace Alternatives
|
||||
|
||||
@@ -51,15 +52,19 @@ VKDevice::VKDevice(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice phy
|
||||
: physical{physical}, format_properties{GetFormatProperties(dldi, physical)} {
|
||||
SetupFamilies(dldi, surface);
|
||||
SetupProperties(dldi);
|
||||
SetupFeatures(dldi);
|
||||
}
|
||||
|
||||
VKDevice::~VKDevice() = default;
|
||||
|
||||
bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instance) {
|
||||
const auto queue_cis = GetDeviceQueueCreateInfos();
|
||||
vk::PhysicalDeviceFeatures device_features{};
|
||||
vk::PhysicalDeviceFeatures device_features;
|
||||
device_features.vertexPipelineStoresAndAtomics = true;
|
||||
device_features.independentBlend = true;
|
||||
device_features.textureCompressionASTC_LDR = is_optimal_astc_supported;
|
||||
|
||||
const std::vector<const char*> extensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
|
||||
const auto queue_cis = GetDeviceQueueCreateInfos();
|
||||
const std::vector<const char*> extensions = LoadExtensions(dldi);
|
||||
const vk::DeviceCreateInfo device_ci({}, static_cast<u32>(queue_cis.size()), queue_cis.data(),
|
||||
0, nullptr, static_cast<u32>(extensions.size()),
|
||||
extensions.data(), &device_features);
|
||||
@@ -90,7 +95,7 @@ vk::Format VKDevice::GetSupportedFormat(vk::Format wanted_format,
|
||||
LOG_CRITICAL(Render_Vulkan,
|
||||
"Format={} with usage={} and type={} has no defined alternatives and host "
|
||||
"hardware does not support it",
|
||||
static_cast<u32>(wanted_format), static_cast<u32>(wanted_usage),
|
||||
vk::to_string(wanted_format), vk::to_string(wanted_usage),
|
||||
static_cast<u32>(format_type));
|
||||
UNREACHABLE();
|
||||
return wanted_format;
|
||||
@@ -118,6 +123,30 @@ vk::Format VKDevice::GetSupportedFormat(vk::Format wanted_format,
|
||||
return wanted_format;
|
||||
}
|
||||
|
||||
bool VKDevice::IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features,
|
||||
const vk::DispatchLoaderDynamic& dldi) const {
|
||||
if (!features.textureCompressionASTC_LDR) {
|
||||
return false;
|
||||
}
|
||||
const auto format_feature_usage{
|
||||
vk::FormatFeatureFlagBits::eSampledImage | vk::FormatFeatureFlagBits::eBlitSrc |
|
||||
vk::FormatFeatureFlagBits::eBlitDst | vk::FormatFeatureFlagBits::eTransferSrc |
|
||||
vk::FormatFeatureFlagBits::eTransferDst};
|
||||
constexpr std::array<vk::Format, 9> astc_formats = {
|
||||
vk::Format::eAstc4x4UnormBlock, vk::Format::eAstc4x4SrgbBlock,
|
||||
vk::Format::eAstc8x8SrgbBlock, vk::Format::eAstc8x6SrgbBlock,
|
||||
vk::Format::eAstc5x4SrgbBlock, vk::Format::eAstc5x5UnormBlock,
|
||||
vk::Format::eAstc5x5SrgbBlock, vk::Format::eAstc10x8UnormBlock,
|
||||
vk::Format::eAstc10x8SrgbBlock};
|
||||
for (const auto format : astc_formats) {
|
||||
const auto format_properties{physical.getFormatProperties(format, dldi)};
|
||||
if (!(format_properties.optimalTilingFeatures & format_feature_usage)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VKDevice::IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlags wanted_usage,
|
||||
FormatType format_type) const {
|
||||
const auto it = format_properties.find(wanted_format);
|
||||
@@ -132,11 +161,9 @@ bool VKDevice::IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlag
|
||||
|
||||
bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical,
|
||||
vk::SurfaceKHR surface) {
|
||||
const std::string swapchain_extension = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
|
||||
|
||||
bool has_swapchain{};
|
||||
for (const auto& prop : physical.enumerateDeviceExtensionProperties(nullptr, dldi)) {
|
||||
has_swapchain |= prop.extensionName == swapchain_extension;
|
||||
has_swapchain |= prop.extensionName == std::string(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
}
|
||||
if (!has_swapchain) {
|
||||
// The device doesn't support creating swapchains.
|
||||
@@ -160,8 +187,14 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev
|
||||
}
|
||||
|
||||
// TODO(Rodrigo): Check if the device matches all requeriments.
|
||||
const vk::PhysicalDeviceProperties props = physical.getProperties(dldi);
|
||||
if (props.limits.maxUniformBufferRange < 65536) {
|
||||
const auto properties{physical.getProperties(dldi)};
|
||||
const auto limits{properties.limits};
|
||||
if (limits.maxUniformBufferRange < 65536) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const vk::PhysicalDeviceFeatures features{physical.getFeatures(dldi)};
|
||||
if (!features.vertexPipelineStoresAndAtomics || !features.independentBlend) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -169,6 +202,30 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynamic& dldi) {
|
||||
std::vector<const char*> extensions;
|
||||
extensions.reserve(2);
|
||||
extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
|
||||
const auto Test = [&](const vk::ExtensionProperties& extension,
|
||||
std::optional<std::reference_wrapper<bool>> status, const char* name,
|
||||
u32 revision) {
|
||||
if (extension.extensionName != std::string(name)) {
|
||||
return;
|
||||
}
|
||||
extensions.push_back(name);
|
||||
if (status) {
|
||||
status->get() = true;
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto& extension : physical.enumerateDeviceExtensionProperties(nullptr, dldi)) {
|
||||
Test(extension, ext_scalar_block_layout, VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME, 1);
|
||||
}
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceKHR surface) {
|
||||
std::optional<u32> graphics_family_, present_family_;
|
||||
|
||||
@@ -196,10 +253,16 @@ void VKDevice::SetupProperties(const vk::DispatchLoaderDynamic& dldi) {
|
||||
const vk::PhysicalDeviceProperties props = physical.getProperties(dldi);
|
||||
device_type = props.deviceType;
|
||||
uniform_buffer_alignment = static_cast<u64>(props.limits.minUniformBufferOffsetAlignment);
|
||||
max_storage_buffer_range = static_cast<u64>(props.limits.maxStorageBufferRange);
|
||||
}
|
||||
|
||||
void VKDevice::SetupFeatures(const vk::DispatchLoaderDynamic& dldi) {
|
||||
const auto supported_features{physical.getFeatures(dldi)};
|
||||
is_optimal_astc_supported = IsOptimalAstcSupported(supported_features, dldi);
|
||||
}
|
||||
|
||||
std::vector<vk::DeviceQueueCreateInfo> VKDevice::GetDeviceQueueCreateInfos() const {
|
||||
static const float QUEUE_PRIORITY = 1.f;
|
||||
static const float QUEUE_PRIORITY = 1.0f;
|
||||
|
||||
std::set<u32> unique_queue_families = {graphics_family, present_family};
|
||||
std::vector<vk::DeviceQueueCreateInfo> queue_cis;
|
||||
@@ -212,26 +275,43 @@ std::vector<vk::DeviceQueueCreateInfo> VKDevice::GetDeviceQueueCreateInfos() con
|
||||
|
||||
std::map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperties(
|
||||
const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical) {
|
||||
static constexpr std::array formats{vk::Format::eA8B8G8R8UnormPack32,
|
||||
vk::Format::eB5G6R5UnormPack16,
|
||||
vk::Format::eA2B10G10R10UnormPack32,
|
||||
vk::Format::eR32G32B32A32Sfloat,
|
||||
vk::Format::eR16G16Unorm,
|
||||
vk::Format::eR16G16Snorm,
|
||||
vk::Format::eR8G8B8A8Srgb,
|
||||
vk::Format::eR8Unorm,
|
||||
vk::Format::eB10G11R11UfloatPack32,
|
||||
vk::Format::eR32Sfloat,
|
||||
vk::Format::eR16Sfloat,
|
||||
vk::Format::eR16G16B16A16Sfloat,
|
||||
vk::Format::eD32Sfloat,
|
||||
vk::Format::eD16Unorm,
|
||||
vk::Format::eD16UnormS8Uint,
|
||||
vk::Format::eD24UnormS8Uint,
|
||||
vk::Format::eD32SfloatS8Uint,
|
||||
vk::Format::eBc1RgbaUnormBlock,
|
||||
vk::Format::eBc2UnormBlock,
|
||||
vk::Format::eBc3UnormBlock,
|
||||
vk::Format::eBc4UnormBlock,
|
||||
vk::Format::eBc5UnormBlock,
|
||||
vk::Format::eBc5SnormBlock,
|
||||
vk::Format::eBc7UnormBlock,
|
||||
vk::Format::eAstc4x4UnormBlock,
|
||||
vk::Format::eAstc4x4SrgbBlock,
|
||||
vk::Format::eAstc8x8SrgbBlock,
|
||||
vk::Format::eAstc8x6SrgbBlock,
|
||||
vk::Format::eAstc5x4SrgbBlock,
|
||||
vk::Format::eAstc5x5UnormBlock,
|
||||
vk::Format::eAstc5x5SrgbBlock,
|
||||
vk::Format::eAstc10x8UnormBlock,
|
||||
vk::Format::eAstc10x8SrgbBlock};
|
||||
std::map<vk::Format, vk::FormatProperties> format_properties;
|
||||
|
||||
const auto AddFormatQuery = [&format_properties, &dldi, physical](vk::Format format) {
|
||||
for (const auto format : formats) {
|
||||
format_properties.emplace(format, physical.getFormatProperties(format, dldi));
|
||||
};
|
||||
AddFormatQuery(vk::Format::eA8B8G8R8UnormPack32);
|
||||
AddFormatQuery(vk::Format::eB5G6R5UnormPack16);
|
||||
AddFormatQuery(vk::Format::eA2B10G10R10UnormPack32);
|
||||
AddFormatQuery(vk::Format::eR8G8B8A8Srgb);
|
||||
AddFormatQuery(vk::Format::eR8Unorm);
|
||||
AddFormatQuery(vk::Format::eD32Sfloat);
|
||||
AddFormatQuery(vk::Format::eD16Unorm);
|
||||
AddFormatQuery(vk::Format::eD16UnormS8Uint);
|
||||
AddFormatQuery(vk::Format::eD24UnormS8Uint);
|
||||
AddFormatQuery(vk::Format::eD32SfloatS8Uint);
|
||||
AddFormatQuery(vk::Format::eBc1RgbaUnormBlock);
|
||||
AddFormatQuery(vk::Format::eBc2UnormBlock);
|
||||
AddFormatQuery(vk::Format::eBc3UnormBlock);
|
||||
AddFormatQuery(vk::Format::eBc4UnormBlock);
|
||||
|
||||
}
|
||||
return format_properties;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
/// Format usage descriptor
|
||||
/// Format usage descriptor.
|
||||
enum class FormatType { Linear, Optimal, Buffer };
|
||||
|
||||
/// Handles data specific to a physical device.
|
||||
@@ -34,12 +34,12 @@ public:
|
||||
vk::Format GetSupportedFormat(vk::Format wanted_format, vk::FormatFeatureFlags wanted_usage,
|
||||
FormatType format_type) const;
|
||||
|
||||
/// Returns the dispatch loader with direct function pointers of the device
|
||||
/// Returns the dispatch loader with direct function pointers of the device.
|
||||
const vk::DispatchLoaderDynamic& GetDispatchLoader() const {
|
||||
return dld;
|
||||
}
|
||||
|
||||
/// Returns the logical device
|
||||
/// Returns the logical device.
|
||||
vk::Device GetLogical() const {
|
||||
return logical.get();
|
||||
}
|
||||
@@ -69,30 +69,55 @@ public:
|
||||
return present_family;
|
||||
}
|
||||
|
||||
/// Returns if the device is integrated with the host CPU
|
||||
/// Returns if the device is integrated with the host CPU.
|
||||
bool IsIntegrated() const {
|
||||
return device_type == vk::PhysicalDeviceType::eIntegratedGpu;
|
||||
}
|
||||
|
||||
/// Returns uniform buffer alignment requeriment
|
||||
/// Returns uniform buffer alignment requeriment.
|
||||
u64 GetUniformBufferAlignment() const {
|
||||
return uniform_buffer_alignment;
|
||||
}
|
||||
|
||||
/// Returns the maximum range for storage buffers.
|
||||
u64 GetMaxStorageBufferRange() const {
|
||||
return max_storage_buffer_range;
|
||||
}
|
||||
|
||||
/// Returns true if ASTC is natively supported.
|
||||
bool IsOptimalAstcSupported() const {
|
||||
return is_optimal_astc_supported;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_scalar_block_layout.
|
||||
bool IsExtScalarBlockLayoutSupported() const {
|
||||
return ext_scalar_block_layout;
|
||||
}
|
||||
|
||||
/// Checks if the physical device is suitable.
|
||||
static bool IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical,
|
||||
vk::SurfaceKHR surface);
|
||||
|
||||
private:
|
||||
/// Loads extensions into a vector and stores available ones in this object.
|
||||
std::vector<const char*> LoadExtensions(const vk::DispatchLoaderDynamic& dldi);
|
||||
|
||||
/// Sets up queue families.
|
||||
void SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceKHR surface);
|
||||
|
||||
/// Sets up device properties.
|
||||
void SetupProperties(const vk::DispatchLoaderDynamic& dldi);
|
||||
|
||||
/// Sets up device features.
|
||||
void SetupFeatures(const vk::DispatchLoaderDynamic& dldi);
|
||||
|
||||
/// Returns a list of queue initialization descriptors.
|
||||
std::vector<vk::DeviceQueueCreateInfo> GetDeviceQueueCreateInfos() const;
|
||||
|
||||
/// Returns true if ASTC textures are natively supported.
|
||||
bool IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features,
|
||||
const vk::DispatchLoaderDynamic& dldi) const;
|
||||
|
||||
/// Returns true if a format is supported.
|
||||
bool IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlags wanted_usage,
|
||||
FormatType format_type) const;
|
||||
@@ -101,16 +126,19 @@ private:
|
||||
static std::map<vk::Format, vk::FormatProperties> GetFormatProperties(
|
||||
const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical);
|
||||
|
||||
const vk::PhysicalDevice physical; ///< Physical device
|
||||
vk::DispatchLoaderDynamic dld; ///< Device function pointers
|
||||
UniqueDevice logical; ///< Logical device
|
||||
vk::Queue graphics_queue; ///< Main graphics queue
|
||||
vk::Queue present_queue; ///< Main present queue
|
||||
u32 graphics_family{}; ///< Main graphics queue family index
|
||||
u32 present_family{}; ///< Main present queue family index
|
||||
vk::PhysicalDeviceType device_type; ///< Physical device type
|
||||
u64 uniform_buffer_alignment{}; ///< Uniform buffer alignment requeriment
|
||||
std::map<vk::Format, vk::FormatProperties> format_properties; ///< Format properties dictionary
|
||||
const vk::PhysicalDevice physical; ///< Physical device.
|
||||
vk::DispatchLoaderDynamic dld; ///< Device function pointers.
|
||||
UniqueDevice logical; ///< Logical device.
|
||||
vk::Queue graphics_queue; ///< Main graphics queue.
|
||||
vk::Queue present_queue; ///< Main present queue.
|
||||
u32 graphics_family{}; ///< Main graphics queue family index.
|
||||
u32 present_family{}; ///< Main present queue family index.
|
||||
vk::PhysicalDeviceType device_type; ///< Physical device type.
|
||||
u64 uniform_buffer_alignment{}; ///< Uniform buffer alignment requeriment.
|
||||
u64 max_storage_buffer_range{}; ///< Max storage buffer size.
|
||||
bool is_optimal_astc_supported{}; ///< Support for native ASTC.
|
||||
bool ext_scalar_block_layout{}; ///< Support for VK_EXT_scalar_block_layout.
|
||||
std::map<vk::Format, vk::FormatProperties> format_properties; ///< Format properties dictionary.
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/engines/shader_header.h"
|
||||
#include "video_core/renderer_vulkan/vk_device.h"
|
||||
#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
@@ -33,7 +34,8 @@ using ShaderStage = Tegra::Engines::Maxwell3D::Regs::ShaderStage;
|
||||
using Operation = const OperationNode&;
|
||||
|
||||
// TODO(Rodrigo): Use rasterizer's value
|
||||
constexpr u32 MAX_CONSTBUFFER_ELEMENTS = 0x1000;
|
||||
constexpr u32 MAX_CONSTBUFFER_FLOATS = 0x4000;
|
||||
constexpr u32 MAX_CONSTBUFFER_ELEMENTS = MAX_CONSTBUFFER_FLOATS / 4;
|
||||
constexpr u32 STAGE_BINDING_STRIDE = 0x100;
|
||||
|
||||
enum class Type { Bool, Bool2, Float, Int, Uint, HalfFloat };
|
||||
@@ -87,8 +89,8 @@ bool IsPrecise(Operation operand) {
|
||||
|
||||
class SPIRVDecompiler : public Sirit::Module {
|
||||
public:
|
||||
explicit SPIRVDecompiler(const ShaderIR& ir, ShaderStage stage)
|
||||
: Module(0x00010300), ir{ir}, stage{stage}, header{ir.GetHeader()} {
|
||||
explicit SPIRVDecompiler(const VKDevice& device, const ShaderIR& ir, ShaderStage stage)
|
||||
: Module(0x00010300), device{device}, ir{ir}, stage{stage}, header{ir.GetHeader()} {
|
||||
AddCapability(spv::Capability::Shader);
|
||||
AddExtension("SPV_KHR_storage_buffer_storage_class");
|
||||
AddExtension("SPV_KHR_variable_pointers");
|
||||
@@ -195,7 +197,9 @@ public:
|
||||
entries.samplers.emplace_back(sampler);
|
||||
}
|
||||
for (const auto& attribute : ir.GetInputAttributes()) {
|
||||
entries.attributes.insert(GetGenericAttributeLocation(attribute));
|
||||
if (IsGenericAttribute(attribute)) {
|
||||
entries.attributes.insert(GetGenericAttributeLocation(attribute));
|
||||
}
|
||||
}
|
||||
entries.clip_distances = ir.GetClipDistances();
|
||||
entries.shader_length = ir.GetLength();
|
||||
@@ -210,7 +214,6 @@ private:
|
||||
std::array<OperationDecompilerFn, static_cast<std::size_t>(OperationCode::Amount)>;
|
||||
|
||||
static constexpr auto INTERNAL_FLAGS_COUNT = static_cast<std::size_t>(InternalFlag::Amount);
|
||||
static constexpr u32 CBUF_STRIDE = 16;
|
||||
|
||||
void AllocateBindings() {
|
||||
const u32 binding_base = static_cast<u32>(stage) * STAGE_BINDING_STRIDE;
|
||||
@@ -315,6 +318,7 @@ private:
|
||||
constexpr std::array<const char*, INTERNAL_FLAGS_COUNT> names = {"zero", "sign", "carry",
|
||||
"overflow"};
|
||||
for (std::size_t flag = 0; flag < INTERNAL_FLAGS_COUNT; ++flag) {
|
||||
const auto flag_code = static_cast<InternalFlag>(flag);
|
||||
const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
|
||||
internal_flags[flag] = AddGlobalVariable(Name(id, names[flag]));
|
||||
}
|
||||
@@ -374,7 +378,9 @@ private:
|
||||
u32 binding = const_buffers_base_binding;
|
||||
for (const auto& entry : ir.GetConstantBuffers()) {
|
||||
const auto [index, size] = entry;
|
||||
const Id id = OpVariable(t_cbuf_ubo, spv::StorageClass::Uniform);
|
||||
const Id type =
|
||||
device.IsExtScalarBlockLayoutSupported() ? t_cbuf_scalar_ubo : t_cbuf_std140_ubo;
|
||||
const Id id = OpVariable(type, spv::StorageClass::Uniform);
|
||||
AddGlobalVariable(Name(id, fmt::format("cbuf_{}", index)));
|
||||
|
||||
Decorate(id, spv::Decoration::Binding, binding++);
|
||||
@@ -475,13 +481,13 @@ private:
|
||||
}
|
||||
|
||||
void VisitBasicBlock(const NodeBlock& bb) {
|
||||
for (const Node node : bb) {
|
||||
for (const auto& node : bb) {
|
||||
static_cast<void>(Visit(node));
|
||||
}
|
||||
}
|
||||
|
||||
Id Visit(Node node) {
|
||||
if (const auto operation = std::get_if<OperationNode>(node)) {
|
||||
Id Visit(const Node& node) {
|
||||
if (const auto operation = std::get_if<OperationNode>(&*node)) {
|
||||
const auto operation_index = static_cast<std::size_t>(operation->GetCode());
|
||||
const auto decompiler = operation_decompilers[operation_index];
|
||||
if (decompiler == nullptr) {
|
||||
@@ -489,17 +495,17 @@ private:
|
||||
}
|
||||
return (this->*decompiler)(*operation);
|
||||
|
||||
} else if (const auto gpr = std::get_if<GprNode>(node)) {
|
||||
} else if (const auto gpr = std::get_if<GprNode>(&*node)) {
|
||||
const u32 index = gpr->GetIndex();
|
||||
if (index == Register::ZeroIndex) {
|
||||
return Constant(t_float, 0.0f);
|
||||
}
|
||||
return Emit(OpLoad(t_float, registers.at(index)));
|
||||
|
||||
} else if (const auto immediate = std::get_if<ImmediateNode>(node)) {
|
||||
} else if (const auto immediate = std::get_if<ImmediateNode>(&*node)) {
|
||||
return BitcastTo<Type::Float>(Constant(t_uint, immediate->GetValue()));
|
||||
|
||||
} else if (const auto predicate = std::get_if<PredicateNode>(node)) {
|
||||
} else if (const auto predicate = std::get_if<PredicateNode>(&*node)) {
|
||||
const auto value = [&]() -> Id {
|
||||
switch (const auto index = predicate->GetIndex(); index) {
|
||||
case Tegra::Shader::Pred::UnusedIndex:
|
||||
@@ -515,7 +521,7 @@ private:
|
||||
}
|
||||
return value;
|
||||
|
||||
} else if (const auto abuf = std::get_if<AbufNode>(node)) {
|
||||
} else if (const auto abuf = std::get_if<AbufNode>(&*node)) {
|
||||
const auto attribute = abuf->GetIndex();
|
||||
const auto element = abuf->GetElement();
|
||||
|
||||
@@ -565,40 +571,42 @@ private:
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute));
|
||||
|
||||
} else if (const auto cbuf = std::get_if<CbufNode>(node)) {
|
||||
const Node offset = cbuf->GetOffset();
|
||||
} else if (const auto cbuf = std::get_if<CbufNode>(&*node)) {
|
||||
const Node& offset = cbuf->GetOffset();
|
||||
const Id buffer_id = constant_buffers.at(cbuf->GetIndex());
|
||||
|
||||
Id buffer_index{};
|
||||
Id buffer_element{};
|
||||
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(offset)) {
|
||||
// Direct access
|
||||
const u32 offset_imm = immediate->GetValue();
|
||||
ASSERT(offset_imm % 4 == 0);
|
||||
buffer_index = Constant(t_uint, offset_imm / 16);
|
||||
buffer_element = Constant(t_uint, (offset_imm / 4) % 4);
|
||||
|
||||
} else if (std::holds_alternative<OperationNode>(*offset)) {
|
||||
// Indirect access
|
||||
// TODO(Rodrigo): Use a uniform buffer stride of 4 and drop this slow math (which
|
||||
// emits sub-optimal code on GLSL from my testing).
|
||||
const Id offset_id = BitcastTo<Type::Uint>(Visit(offset));
|
||||
const Id unsafe_offset = Emit(OpUDiv(t_uint, offset_id, Constant(t_uint, 4)));
|
||||
const Id final_offset = Emit(
|
||||
OpUMod(t_uint, unsafe_offset, Constant(t_uint, MAX_CONSTBUFFER_ELEMENTS - 1)));
|
||||
buffer_index = Emit(OpUDiv(t_uint, final_offset, Constant(t_uint, 4)));
|
||||
buffer_element = Emit(OpUMod(t_uint, final_offset, Constant(t_uint, 4)));
|
||||
|
||||
Id pointer{};
|
||||
if (device.IsExtScalarBlockLayoutSupported()) {
|
||||
const Id buffer_offset = Emit(OpShiftRightLogical(
|
||||
t_uint, BitcastTo<Type::Uint>(Visit(offset)), Constant(t_uint, 2u)));
|
||||
pointer = Emit(
|
||||
OpAccessChain(t_cbuf_float, buffer_id, Constant(t_uint, 0u), buffer_offset));
|
||||
} else {
|
||||
UNREACHABLE_MSG("Unmanaged offset node type");
|
||||
Id buffer_index{};
|
||||
Id buffer_element{};
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) {
|
||||
// Direct access
|
||||
const u32 offset_imm = immediate->GetValue();
|
||||
ASSERT(offset_imm % 4 == 0);
|
||||
buffer_index = Constant(t_uint, offset_imm / 16);
|
||||
buffer_element = Constant(t_uint, (offset_imm / 4) % 4);
|
||||
} else if (std::holds_alternative<OperationNode>(*offset)) {
|
||||
// Indirect access
|
||||
const Id offset_id = BitcastTo<Type::Uint>(Visit(offset));
|
||||
const Id unsafe_offset = Emit(OpUDiv(t_uint, offset_id, Constant(t_uint, 4)));
|
||||
const Id final_offset = Emit(OpUMod(
|
||||
t_uint, unsafe_offset, Constant(t_uint, MAX_CONSTBUFFER_ELEMENTS - 1)));
|
||||
buffer_index = Emit(OpUDiv(t_uint, final_offset, Constant(t_uint, 4)));
|
||||
buffer_element = Emit(OpUMod(t_uint, final_offset, Constant(t_uint, 4)));
|
||||
} else {
|
||||
UNREACHABLE_MSG("Unmanaged offset node type");
|
||||
}
|
||||
pointer = Emit(OpAccessChain(t_cbuf_float, buffer_id, Constant(t_uint, 0),
|
||||
buffer_index, buffer_element));
|
||||
}
|
||||
|
||||
const Id pointer = Emit(OpAccessChain(t_cbuf_float, buffer_id, Constant(t_uint, 0),
|
||||
buffer_index, buffer_element));
|
||||
return Emit(OpLoad(t_float, pointer));
|
||||
|
||||
} else if (const auto gmem = std::get_if<GmemNode>(node)) {
|
||||
} else if (const auto gmem = std::get_if<GmemNode>(&*node)) {
|
||||
const Id gmem_buffer = global_buffers.at(gmem->GetDescriptor());
|
||||
const Id real = BitcastTo<Type::Uint>(Visit(gmem->GetRealAddress()));
|
||||
const Id base = BitcastTo<Type::Uint>(Visit(gmem->GetBaseAddress()));
|
||||
@@ -608,11 +616,13 @@ private:
|
||||
return Emit(OpLoad(t_float, Emit(OpAccessChain(t_gmem_float, gmem_buffer,
|
||||
Constant(t_uint, 0u), offset))));
|
||||
|
||||
} else if (const auto conditional = std::get_if<ConditionalNode>(node)) {
|
||||
} else if (const auto conditional = std::get_if<ConditionalNode>(&*node)) {
|
||||
// It's invalid to call conditional on nested nodes, use an operation instead
|
||||
const Id true_label = OpLabel();
|
||||
const Id skip_label = OpLabel();
|
||||
Emit(OpBranchConditional(Visit(conditional->GetCondition()), true_label, skip_label));
|
||||
const Id condition = Visit(conditional->GetCondition());
|
||||
Emit(OpSelectionMerge(skip_label, spv::SelectionControlMask::MaskNone));
|
||||
Emit(OpBranchConditional(condition, true_label, skip_label));
|
||||
Emit(true_label);
|
||||
|
||||
VisitBasicBlock(conditional->GetCode());
|
||||
@@ -621,7 +631,7 @@ private:
|
||||
Emit(skip_label);
|
||||
return {};
|
||||
|
||||
} else if (const auto comment = std::get_if<CommentNode>(node)) {
|
||||
} else if (const auto comment = std::get_if<CommentNode>(&*node)) {
|
||||
Name(Emit(OpUndef(t_void)), comment->GetText());
|
||||
return {};
|
||||
}
|
||||
@@ -689,18 +699,18 @@ private:
|
||||
}
|
||||
|
||||
Id Assign(Operation operation) {
|
||||
const Node dest = operation[0];
|
||||
const Node src = operation[1];
|
||||
const Node& dest = operation[0];
|
||||
const Node& src = operation[1];
|
||||
|
||||
Id target{};
|
||||
if (const auto gpr = std::get_if<GprNode>(dest)) {
|
||||
if (const auto gpr = std::get_if<GprNode>(&*dest)) {
|
||||
if (gpr->GetIndex() == Register::ZeroIndex) {
|
||||
// Writing to Register::ZeroIndex is a no op
|
||||
return {};
|
||||
}
|
||||
target = registers.at(gpr->GetIndex());
|
||||
|
||||
} else if (const auto abuf = std::get_if<AbufNode>(dest)) {
|
||||
} else if (const auto abuf = std::get_if<AbufNode>(&*dest)) {
|
||||
target = [&]() -> Id {
|
||||
switch (const auto attribute = abuf->GetIndex(); attribute) {
|
||||
case Attribute::Index::Position:
|
||||
@@ -725,7 +735,7 @@ private:
|
||||
}
|
||||
}();
|
||||
|
||||
} else if (const auto lmem = std::get_if<LmemNode>(dest)) {
|
||||
} else if (const auto lmem = std::get_if<LmemNode>(&*dest)) {
|
||||
Id address = BitcastTo<Type::Uint>(Visit(lmem->GetAddress()));
|
||||
address = Emit(OpUDiv(t_uint, address, Constant(t_uint, 4)));
|
||||
target = Emit(OpAccessChain(t_prv_float, local_memory, {address}));
|
||||
@@ -771,11 +781,11 @@ private:
|
||||
}
|
||||
|
||||
Id LogicalAssign(Operation operation) {
|
||||
const Node dest = operation[0];
|
||||
const Node src = operation[1];
|
||||
const Node& dest = operation[0];
|
||||
const Node& src = operation[1];
|
||||
|
||||
Id target{};
|
||||
if (const auto pred = std::get_if<PredicateNode>(dest)) {
|
||||
if (const auto pred = std::get_if<PredicateNode>(&*dest)) {
|
||||
ASSERT_MSG(!pred->IsNegated(), "Negating logical assignment");
|
||||
|
||||
const auto index = pred->GetIndex();
|
||||
@@ -787,7 +797,7 @@ private:
|
||||
}
|
||||
target = predicates.at(index);
|
||||
|
||||
} else if (const auto flag = std::get_if<InternalFlagNode>(dest)) {
|
||||
} else if (const auto flag = std::get_if<InternalFlagNode>(&*dest)) {
|
||||
target = internal_flags.at(static_cast<u32>(flag->GetFlag()));
|
||||
}
|
||||
|
||||
@@ -873,7 +883,7 @@ private:
|
||||
} else {
|
||||
u32 component_value = 0;
|
||||
if (meta->component) {
|
||||
const auto component = std::get_if<ImmediateNode>(meta->component);
|
||||
const auto component = std::get_if<ImmediateNode>(&*meta->component);
|
||||
ASSERT_MSG(component, "Component is not an immediate value");
|
||||
component_value = component->GetValue();
|
||||
}
|
||||
@@ -930,7 +940,7 @@ private:
|
||||
}
|
||||
|
||||
Id Branch(Operation operation) {
|
||||
const auto target = std::get_if<ImmediateNode>(operation[0]);
|
||||
const auto target = std::get_if<ImmediateNode>(&*operation[0]);
|
||||
UNIMPLEMENTED_IF(!target);
|
||||
|
||||
Emit(OpStore(jmp_to, Constant(t_uint, target->GetValue())));
|
||||
@@ -939,7 +949,7 @@ private:
|
||||
}
|
||||
|
||||
Id PushFlowStack(Operation operation) {
|
||||
const auto target = std::get_if<ImmediateNode>(operation[0]);
|
||||
const auto target = std::get_if<ImmediateNode>(&*operation[0]);
|
||||
ASSERT(target);
|
||||
|
||||
const Id current = Emit(OpLoad(t_uint, flow_stack_top));
|
||||
@@ -968,11 +978,11 @@ private:
|
||||
case ShaderStage::Vertex: {
|
||||
// TODO(Rodrigo): We should use VK_EXT_depth_range_unrestricted instead, but it doesn't
|
||||
// seem to be working on Nvidia's drivers and Intel (mesa and blob) doesn't support it.
|
||||
const Id position = AccessElement(t_float4, per_vertex, position_index);
|
||||
Id depth = Emit(OpLoad(t_float, AccessElement(t_out_float, position, 2)));
|
||||
const Id z_pointer = AccessElement(t_out_float, per_vertex, position_index, 2u);
|
||||
Id depth = Emit(OpLoad(t_float, z_pointer));
|
||||
depth = Emit(OpFAdd(t_float, depth, Constant(t_float, 1.0f)));
|
||||
depth = Emit(OpFMul(t_float, depth, Constant(t_float, 0.5f)));
|
||||
Emit(OpStore(AccessElement(t_out_float, position, 2), depth));
|
||||
Emit(OpStore(z_pointer, depth));
|
||||
break;
|
||||
}
|
||||
case ShaderStage::Fragment: {
|
||||
@@ -1311,6 +1321,7 @@ private:
|
||||
&SPIRVDecompiler::WorkGroupId<2>,
|
||||
};
|
||||
|
||||
const VKDevice& device;
|
||||
const ShaderIR& ir;
|
||||
const ShaderStage stage;
|
||||
const Tegra::Shader::Header header;
|
||||
@@ -1349,12 +1360,18 @@ private:
|
||||
const Id t_out_float4 = Name(TypePointer(spv::StorageClass::Output, t_float4), "out_float4");
|
||||
|
||||
const Id t_cbuf_float = TypePointer(spv::StorageClass::Uniform, t_float);
|
||||
const Id t_cbuf_array =
|
||||
Decorate(Name(TypeArray(t_float4, Constant(t_uint, MAX_CONSTBUFFER_ELEMENTS)), "CbufArray"),
|
||||
spv::Decoration::ArrayStride, CBUF_STRIDE);
|
||||
const Id t_cbuf_struct = MemberDecorate(
|
||||
Decorate(TypeStruct(t_cbuf_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0);
|
||||
const Id t_cbuf_ubo = TypePointer(spv::StorageClass::Uniform, t_cbuf_struct);
|
||||
const Id t_cbuf_std140 = Decorate(
|
||||
Name(TypeArray(t_float4, Constant(t_uint, MAX_CONSTBUFFER_ELEMENTS)), "CbufStd140Array"),
|
||||
spv::Decoration::ArrayStride, 16u);
|
||||
const Id t_cbuf_scalar = Decorate(
|
||||
Name(TypeArray(t_float, Constant(t_uint, MAX_CONSTBUFFER_FLOATS)), "CbufScalarArray"),
|
||||
spv::Decoration::ArrayStride, 4u);
|
||||
const Id t_cbuf_std140_struct = MemberDecorate(
|
||||
Decorate(TypeStruct(t_cbuf_std140), spv::Decoration::Block), 0, spv::Decoration::Offset, 0);
|
||||
const Id t_cbuf_scalar_struct = MemberDecorate(
|
||||
Decorate(TypeStruct(t_cbuf_scalar), spv::Decoration::Block), 0, spv::Decoration::Offset, 0);
|
||||
const Id t_cbuf_std140_ubo = TypePointer(spv::StorageClass::Uniform, t_cbuf_std140_struct);
|
||||
const Id t_cbuf_scalar_ubo = TypePointer(spv::StorageClass::Uniform, t_cbuf_scalar_struct);
|
||||
|
||||
const Id t_gmem_float = TypePointer(spv::StorageClass::StorageBuffer, t_float);
|
||||
const Id t_gmem_array =
|
||||
@@ -1403,8 +1420,9 @@ private:
|
||||
std::map<u32, Id> labels;
|
||||
};
|
||||
|
||||
DecompilerResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage) {
|
||||
auto decompiler = std::make_unique<SPIRVDecompiler>(ir, stage);
|
||||
DecompilerResult Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir,
|
||||
Maxwell::ShaderStage stage) {
|
||||
auto decompiler = std::make_unique<SPIRVDecompiler>(device, ir, stage);
|
||||
decompiler->Decompile();
|
||||
return {std::move(decompiler), decompiler->GetShaderEntries()};
|
||||
}
|
||||
|
||||
@@ -20,10 +20,13 @@ namespace VideoCommon::Shader {
|
||||
class ShaderIR;
|
||||
}
|
||||
|
||||
namespace Vulkan {
|
||||
class VKDevice;
|
||||
}
|
||||
|
||||
namespace Vulkan::VKShader {
|
||||
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
using SamplerEntry = VideoCommon::Shader::Sampler;
|
||||
|
||||
constexpr u32 DESCRIPTOR_SET = 0;
|
||||
@@ -75,6 +78,7 @@ struct ShaderEntries {
|
||||
|
||||
using DecompilerResult = std::pair<std::unique_ptr<Sirit::Module>, ShaderEntries>;
|
||||
|
||||
DecompilerResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage);
|
||||
DecompilerResult Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir,
|
||||
Maxwell::ShaderStage stage);
|
||||
|
||||
} // namespace Vulkan::VKShader
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/engines/shader_header.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
@@ -169,7 +170,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
const Node it_offset = Immediate(i * 4);
|
||||
const Node real_address =
|
||||
Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset);
|
||||
const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor));
|
||||
const Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor);
|
||||
|
||||
SetTemporal(bb, i, gmem);
|
||||
}
|
||||
@@ -262,7 +263,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
const Node it_offset = Immediate(i * 4);
|
||||
const Node real_address =
|
||||
Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset);
|
||||
const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor));
|
||||
const Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor);
|
||||
|
||||
bb.push_back(Operation(OperationCode::Assign, gmem, GetTemporal(i + 1)));
|
||||
}
|
||||
@@ -298,9 +299,9 @@ std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackAndGetGlobalMemory(NodeB
|
||||
|
||||
const Node base_address{
|
||||
TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size()))};
|
||||
const auto cbuf = std::get_if<CbufNode>(base_address);
|
||||
const auto cbuf = std::get_if<CbufNode>(&*base_address);
|
||||
ASSERT(cbuf != nullptr);
|
||||
const auto cbuf_offset_imm = std::get_if<ImmediateNode>(cbuf->GetOffset());
|
||||
const auto cbuf_offset_imm = std::get_if<ImmediateNode>(&*cbuf->GetOffset());
|
||||
ASSERT(cbuf_offset_imm != nullptr);
|
||||
const auto cbuf_offset = cbuf_offset_imm->GetValue();
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
@@ -291,8 +292,8 @@ const Sampler& ShaderIR::GetBindlessSampler(const Tegra::Shader::Register& reg,
|
||||
const Node sampler_register = GetRegister(reg);
|
||||
const Node base_sampler =
|
||||
TrackCbuf(sampler_register, global_code, static_cast<s64>(global_code.size()));
|
||||
const auto cbuf = std::get_if<CbufNode>(base_sampler);
|
||||
const auto cbuf_offset_imm = std::get_if<ImmediateNode>(cbuf->GetOffset());
|
||||
const auto cbuf = std::get_if<CbufNode>(&*base_sampler);
|
||||
const auto cbuf_offset_imm = std::get_if<ImmediateNode>(&*cbuf->GetOffset());
|
||||
ASSERT(cbuf_offset_imm != nullptr);
|
||||
const auto cbuf_offset = cbuf_offset_imm->GetValue();
|
||||
const auto cbuf_index = cbuf->GetIndex();
|
||||
@@ -388,8 +389,8 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
|
||||
Node array, Node depth_compare, u32 bias_offset,
|
||||
std::vector<Node> aoffi,
|
||||
std::optional<Tegra::Shader::Register> bindless_reg) {
|
||||
const bool is_array = array;
|
||||
const bool is_shadow = depth_compare;
|
||||
const auto is_array = static_cast<bool>(array);
|
||||
const auto is_shadow = static_cast<bool>(depth_compare);
|
||||
const bool is_bindless = bindless_reg.has_value();
|
||||
|
||||
UNIMPLEMENTED_IF_MSG((texture_type == TextureType::Texture3D && (is_array || is_shadow)) ||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
514
src/video_core/shader/node.h
Normal file
514
src/video_core/shader/node.h
Normal file
@@ -0,0 +1,514 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
enum class OperationCode {
|
||||
Assign, /// (float& dest, float src) -> void
|
||||
|
||||
Select, /// (MetaArithmetic, bool pred, float a, float b) -> float
|
||||
|
||||
FAdd, /// (MetaArithmetic, float a, float b) -> float
|
||||
FMul, /// (MetaArithmetic, float a, float b) -> float
|
||||
FDiv, /// (MetaArithmetic, float a, float b) -> float
|
||||
FFma, /// (MetaArithmetic, float a, float b, float c) -> float
|
||||
FNegate, /// (MetaArithmetic, float a) -> float
|
||||
FAbsolute, /// (MetaArithmetic, float a) -> float
|
||||
FClamp, /// (MetaArithmetic, float value, float min, float max) -> float
|
||||
FMin, /// (MetaArithmetic, float a, float b) -> float
|
||||
FMax, /// (MetaArithmetic, float a, float b) -> float
|
||||
FCos, /// (MetaArithmetic, float a) -> float
|
||||
FSin, /// (MetaArithmetic, float a) -> float
|
||||
FExp2, /// (MetaArithmetic, float a) -> float
|
||||
FLog2, /// (MetaArithmetic, float a) -> float
|
||||
FInverseSqrt, /// (MetaArithmetic, float a) -> float
|
||||
FSqrt, /// (MetaArithmetic, float a) -> float
|
||||
FRoundEven, /// (MetaArithmetic, float a) -> float
|
||||
FFloor, /// (MetaArithmetic, float a) -> float
|
||||
FCeil, /// (MetaArithmetic, float a) -> float
|
||||
FTrunc, /// (MetaArithmetic, float a) -> float
|
||||
FCastInteger, /// (MetaArithmetic, int a) -> float
|
||||
FCastUInteger, /// (MetaArithmetic, uint a) -> float
|
||||
|
||||
IAdd, /// (MetaArithmetic, int a, int b) -> int
|
||||
IMul, /// (MetaArithmetic, int a, int b) -> int
|
||||
IDiv, /// (MetaArithmetic, int a, int b) -> int
|
||||
INegate, /// (MetaArithmetic, int a) -> int
|
||||
IAbsolute, /// (MetaArithmetic, int a) -> int
|
||||
IMin, /// (MetaArithmetic, int a, int b) -> int
|
||||
IMax, /// (MetaArithmetic, int a, int b) -> int
|
||||
ICastFloat, /// (MetaArithmetic, float a) -> int
|
||||
ICastUnsigned, /// (MetaArithmetic, uint a) -> int
|
||||
ILogicalShiftLeft, /// (MetaArithmetic, int a, uint b) -> int
|
||||
ILogicalShiftRight, /// (MetaArithmetic, int a, uint b) -> int
|
||||
IArithmeticShiftRight, /// (MetaArithmetic, int a, uint b) -> int
|
||||
IBitwiseAnd, /// (MetaArithmetic, int a, int b) -> int
|
||||
IBitwiseOr, /// (MetaArithmetic, int a, int b) -> int
|
||||
IBitwiseXor, /// (MetaArithmetic, int a, int b) -> int
|
||||
IBitwiseNot, /// (MetaArithmetic, int a) -> int
|
||||
IBitfieldInsert, /// (MetaArithmetic, int base, int insert, int offset, int bits) -> int
|
||||
IBitfieldExtract, /// (MetaArithmetic, int value, int offset, int offset) -> int
|
||||
IBitCount, /// (MetaArithmetic, int) -> int
|
||||
|
||||
UAdd, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UMul, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UDiv, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UMin, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UMax, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UCastFloat, /// (MetaArithmetic, float a) -> uint
|
||||
UCastSigned, /// (MetaArithmetic, int a) -> uint
|
||||
ULogicalShiftLeft, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
ULogicalShiftRight, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UArithmeticShiftRight, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UBitwiseAnd, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UBitwiseOr, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UBitwiseXor, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UBitwiseNot, /// (MetaArithmetic, uint a) -> uint
|
||||
UBitfieldInsert, /// (MetaArithmetic, uint base, uint insert, int offset, int bits) -> uint
|
||||
UBitfieldExtract, /// (MetaArithmetic, uint value, int offset, int offset) -> uint
|
||||
UBitCount, /// (MetaArithmetic, uint) -> uint
|
||||
|
||||
HAdd, /// (MetaArithmetic, f16vec2 a, f16vec2 b) -> f16vec2
|
||||
HMul, /// (MetaArithmetic, f16vec2 a, f16vec2 b) -> f16vec2
|
||||
HFma, /// (MetaArithmetic, f16vec2 a, f16vec2 b, f16vec2 c) -> f16vec2
|
||||
HAbsolute, /// (f16vec2 a) -> f16vec2
|
||||
HNegate, /// (f16vec2 a, bool first, bool second) -> f16vec2
|
||||
HClamp, /// (f16vec2 src, float min, float max) -> f16vec2
|
||||
HUnpack, /// (Tegra::Shader::HalfType, T value) -> f16vec2
|
||||
HMergeF32, /// (f16vec2 src) -> float
|
||||
HMergeH0, /// (f16vec2 dest, f16vec2 src) -> f16vec2
|
||||
HMergeH1, /// (f16vec2 dest, f16vec2 src) -> f16vec2
|
||||
HPack2, /// (float a, float b) -> f16vec2
|
||||
|
||||
LogicalAssign, /// (bool& dst, bool src) -> void
|
||||
LogicalAnd, /// (bool a, bool b) -> bool
|
||||
LogicalOr, /// (bool a, bool b) -> bool
|
||||
LogicalXor, /// (bool a, bool b) -> bool
|
||||
LogicalNegate, /// (bool a) -> bool
|
||||
LogicalPick2, /// (bool2 pair, uint index) -> bool
|
||||
LogicalAll2, /// (bool2 a) -> bool
|
||||
LogicalAny2, /// (bool2 a) -> bool
|
||||
|
||||
LogicalFLessThan, /// (float a, float b) -> bool
|
||||
LogicalFEqual, /// (float a, float b) -> bool
|
||||
LogicalFLessEqual, /// (float a, float b) -> bool
|
||||
LogicalFGreaterThan, /// (float a, float b) -> bool
|
||||
LogicalFNotEqual, /// (float a, float b) -> bool
|
||||
LogicalFGreaterEqual, /// (float a, float b) -> bool
|
||||
LogicalFIsNan, /// (float a) -> bool
|
||||
|
||||
LogicalILessThan, /// (int a, int b) -> bool
|
||||
LogicalIEqual, /// (int a, int b) -> bool
|
||||
LogicalILessEqual, /// (int a, int b) -> bool
|
||||
LogicalIGreaterThan, /// (int a, int b) -> bool
|
||||
LogicalINotEqual, /// (int a, int b) -> bool
|
||||
LogicalIGreaterEqual, /// (int a, int b) -> bool
|
||||
|
||||
LogicalULessThan, /// (uint a, uint b) -> bool
|
||||
LogicalUEqual, /// (uint a, uint b) -> bool
|
||||
LogicalULessEqual, /// (uint a, uint b) -> bool
|
||||
LogicalUGreaterThan, /// (uint a, uint b) -> bool
|
||||
LogicalUNotEqual, /// (uint a, uint b) -> bool
|
||||
LogicalUGreaterEqual, /// (uint a, uint b) -> bool
|
||||
|
||||
Logical2HLessThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HLessEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HGreaterThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HNotEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HGreaterEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HLessThanWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HLessEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HGreaterThanWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HNotEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HGreaterEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
|
||||
Texture, /// (MetaTexture, float[N] coords) -> float4
|
||||
TextureLod, /// (MetaTexture, float[N] coords) -> float4
|
||||
TextureGather, /// (MetaTexture, float[N] coords) -> float4
|
||||
TextureQueryDimensions, /// (MetaTexture, float a) -> float4
|
||||
TextureQueryLod, /// (MetaTexture, float[N] coords) -> float4
|
||||
TexelFetch, /// (MetaTexture, int[N], int) -> float4
|
||||
|
||||
Branch, /// (uint branch_target) -> void
|
||||
PushFlowStack, /// (uint branch_target) -> void
|
||||
PopFlowStack, /// () -> void
|
||||
Exit, /// () -> void
|
||||
Discard, /// () -> void
|
||||
|
||||
EmitVertex, /// () -> void
|
||||
EndPrimitive, /// () -> void
|
||||
|
||||
YNegate, /// () -> float
|
||||
LocalInvocationIdX, /// () -> uint
|
||||
LocalInvocationIdY, /// () -> uint
|
||||
LocalInvocationIdZ, /// () -> uint
|
||||
WorkGroupIdX, /// () -> uint
|
||||
WorkGroupIdY, /// () -> uint
|
||||
WorkGroupIdZ, /// () -> uint
|
||||
|
||||
Amount,
|
||||
};
|
||||
|
||||
enum class InternalFlag {
|
||||
Zero = 0,
|
||||
Sign = 1,
|
||||
Carry = 2,
|
||||
Overflow = 3,
|
||||
Amount = 4,
|
||||
};
|
||||
|
||||
class OperationNode;
|
||||
class ConditionalNode;
|
||||
class GprNode;
|
||||
class ImmediateNode;
|
||||
class InternalFlagNode;
|
||||
class PredicateNode;
|
||||
class AbufNode;
|
||||
class CbufNode;
|
||||
class LmemNode;
|
||||
class GmemNode;
|
||||
class CommentNode;
|
||||
|
||||
using NodeData =
|
||||
std::variant<OperationNode, ConditionalNode, GprNode, ImmediateNode, InternalFlagNode,
|
||||
PredicateNode, AbufNode, CbufNode, LmemNode, GmemNode, CommentNode>;
|
||||
using Node = std::shared_ptr<NodeData>;
|
||||
using Node4 = std::array<Node, 4>;
|
||||
using NodeBlock = std::vector<Node>;
|
||||
|
||||
class Sampler {
|
||||
public:
|
||||
/// This constructor is for bound samplers
|
||||
explicit Sampler(std::size_t offset, std::size_t index, Tegra::Shader::TextureType type,
|
||||
bool is_array, bool is_shadow)
|
||||
: offset{offset}, index{index}, type{type}, is_array{is_array}, is_shadow{is_shadow},
|
||||
is_bindless{false} {}
|
||||
|
||||
/// This constructor is for bindless samplers
|
||||
explicit Sampler(u32 cbuf_index, u32 cbuf_offset, std::size_t index,
|
||||
Tegra::Shader::TextureType type, bool is_array, bool is_shadow)
|
||||
: offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type},
|
||||
is_array{is_array}, is_shadow{is_shadow}, is_bindless{true} {}
|
||||
|
||||
/// This constructor is for serialization/deserialization
|
||||
explicit Sampler(std::size_t offset, std::size_t index, Tegra::Shader::TextureType type,
|
||||
bool is_array, bool is_shadow, bool is_bindless)
|
||||
: offset{offset}, index{index}, type{type}, is_array{is_array}, is_shadow{is_shadow},
|
||||
is_bindless{is_bindless} {}
|
||||
|
||||
std::size_t GetOffset() const {
|
||||
return offset;
|
||||
}
|
||||
|
||||
std::size_t GetIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
Tegra::Shader::TextureType GetType() const {
|
||||
return type;
|
||||
}
|
||||
|
||||
bool IsArray() const {
|
||||
return is_array;
|
||||
}
|
||||
|
||||
bool IsShadow() const {
|
||||
return is_shadow;
|
||||
}
|
||||
|
||||
bool IsBindless() const {
|
||||
return is_bindless;
|
||||
}
|
||||
|
||||
std::pair<u32, u32> GetBindlessCBuf() const {
|
||||
return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)};
|
||||
}
|
||||
|
||||
bool operator<(const Sampler& rhs) const {
|
||||
return std::tie(index, offset, type, is_array, is_shadow, is_bindless) <
|
||||
std::tie(rhs.index, rhs.offset, rhs.type, rhs.is_array, rhs.is_shadow,
|
||||
rhs.is_bindless);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Offset in TSC memory from which to read the sampler object, as specified by the sampling
|
||||
/// instruction.
|
||||
std::size_t offset{};
|
||||
std::size_t index{}; ///< Value used to index into the generated GLSL sampler array.
|
||||
Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc)
|
||||
bool is_array{}; ///< Whether the texture is being sampled as an array texture or not.
|
||||
bool is_shadow{}; ///< Whether the texture is being sampled as a depth texture or not.
|
||||
bool is_bindless{}; ///< Whether this sampler belongs to a bindless texture or not.
|
||||
};
|
||||
|
||||
struct GlobalMemoryBase {
|
||||
u32 cbuf_index{};
|
||||
u32 cbuf_offset{};
|
||||
|
||||
bool operator<(const GlobalMemoryBase& rhs) const {
|
||||
return std::tie(cbuf_index, cbuf_offset) < std::tie(rhs.cbuf_index, rhs.cbuf_offset);
|
||||
}
|
||||
};
|
||||
|
||||
/// Parameters describing an arithmetic operation
|
||||
struct MetaArithmetic {
|
||||
bool precise{}; ///< Whether the operation can be constraint or not
|
||||
};
|
||||
|
||||
/// Parameters describing a texture sampler
|
||||
struct MetaTexture {
|
||||
const Sampler& sampler;
|
||||
Node array;
|
||||
Node depth_compare;
|
||||
std::vector<Node> aoffi;
|
||||
Node bias;
|
||||
Node lod;
|
||||
Node component{};
|
||||
u32 element{};
|
||||
};
|
||||
|
||||
/// Parameters that modify an operation but are not part of any particular operand
|
||||
using Meta = std::variant<MetaArithmetic, MetaTexture, Tegra::Shader::HalfType>;
|
||||
|
||||
/// Holds any kind of operation that can be done in the IR
|
||||
class OperationNode final {
|
||||
public:
|
||||
explicit OperationNode(OperationCode code) : OperationNode(code, Meta{}) {}
|
||||
|
||||
explicit OperationNode(OperationCode code, Meta meta)
|
||||
: OperationNode(code, meta, std::vector<Node>{}) {}
|
||||
|
||||
explicit OperationNode(OperationCode code, std::vector<Node> operands)
|
||||
: OperationNode(code, Meta{}, std::move(operands)) {}
|
||||
|
||||
explicit OperationNode(OperationCode code, Meta meta, std::vector<Node> operands)
|
||||
: code{code}, meta{std::move(meta)}, operands{std::move(operands)} {}
|
||||
|
||||
template <typename... Args>
|
||||
explicit OperationNode(OperationCode code, Meta meta, Args&&... operands)
|
||||
: code{code}, meta{std::move(meta)}, operands{operands...} {}
|
||||
|
||||
OperationCode GetCode() const {
|
||||
return code;
|
||||
}
|
||||
|
||||
const Meta& GetMeta() const {
|
||||
return meta;
|
||||
}
|
||||
|
||||
std::size_t GetOperandsCount() const {
|
||||
return operands.size();
|
||||
}
|
||||
|
||||
const Node& operator[](std::size_t operand_index) const {
|
||||
return operands.at(operand_index);
|
||||
}
|
||||
|
||||
private:
|
||||
OperationCode code{};
|
||||
Meta meta{};
|
||||
std::vector<Node> operands;
|
||||
};
|
||||
|
||||
/// Encloses inside any kind of node that returns a boolean conditionally-executed code
|
||||
class ConditionalNode final {
|
||||
public:
|
||||
explicit ConditionalNode(Node condition, std::vector<Node>&& code)
|
||||
: condition{std::move(condition)}, code{std::move(code)} {}
|
||||
|
||||
const Node& GetCondition() const {
|
||||
return condition;
|
||||
}
|
||||
|
||||
const std::vector<Node>& GetCode() const {
|
||||
return code;
|
||||
}
|
||||
|
||||
private:
|
||||
Node condition; ///< Condition to be satisfied
|
||||
std::vector<Node> code; ///< Code to execute
|
||||
};
|
||||
|
||||
/// A general purpose register
|
||||
class GprNode final {
|
||||
public:
|
||||
explicit constexpr GprNode(Tegra::Shader::Register index) : index{index} {}
|
||||
|
||||
u32 GetIndex() const {
|
||||
return static_cast<u32>(index);
|
||||
}
|
||||
|
||||
private:
|
||||
Tegra::Shader::Register index{};
|
||||
};
|
||||
|
||||
/// A 32-bits value that represents an immediate value
|
||||
class ImmediateNode final {
|
||||
public:
|
||||
explicit constexpr ImmediateNode(u32 value) : value{value} {}
|
||||
|
||||
u32 GetValue() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 value{};
|
||||
};
|
||||
|
||||
/// One of Maxwell's internal flags
|
||||
class InternalFlagNode final {
|
||||
public:
|
||||
explicit constexpr InternalFlagNode(InternalFlag flag) : flag{flag} {}
|
||||
|
||||
InternalFlag GetFlag() const {
|
||||
return flag;
|
||||
}
|
||||
|
||||
private:
|
||||
InternalFlag flag{};
|
||||
};
|
||||
|
||||
/// A predicate register, it can be negated without additional nodes
|
||||
class PredicateNode final {
|
||||
public:
|
||||
explicit constexpr PredicateNode(Tegra::Shader::Pred index, bool negated)
|
||||
: index{index}, negated{negated} {}
|
||||
|
||||
Tegra::Shader::Pred GetIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
bool IsNegated() const {
|
||||
return negated;
|
||||
}
|
||||
|
||||
private:
|
||||
Tegra::Shader::Pred index{};
|
||||
bool negated{};
|
||||
};
|
||||
|
||||
/// Attribute buffer memory (known as attributes or varyings in GLSL terms)
|
||||
class AbufNode final {
|
||||
public:
|
||||
// Initialize for standard attributes (index is explicit).
|
||||
explicit AbufNode(Tegra::Shader::Attribute::Index index, u32 element, Node buffer = {})
|
||||
: buffer{std::move(buffer)}, index{index}, element{element} {}
|
||||
|
||||
// Initialize for physical attributes (index is a variable value).
|
||||
explicit AbufNode(Node physical_address, Node buffer = {})
|
||||
: physical_address{std::move(physical_address)}, buffer{std::move(buffer)} {}
|
||||
|
||||
Tegra::Shader::Attribute::Index GetIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
u32 GetElement() const {
|
||||
return element;
|
||||
}
|
||||
|
||||
const Node& GetBuffer() const {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool IsPhysicalBuffer() const {
|
||||
return static_cast<bool>(physical_address);
|
||||
}
|
||||
|
||||
const Node& GetPhysicalAddress() const {
|
||||
return physical_address;
|
||||
}
|
||||
|
||||
private:
|
||||
Node physical_address;
|
||||
Node buffer;
|
||||
Tegra::Shader::Attribute::Index index{};
|
||||
u32 element{};
|
||||
};
|
||||
|
||||
/// Constant buffer node, usually mapped to uniform buffers in GLSL
|
||||
class CbufNode final {
|
||||
public:
|
||||
explicit CbufNode(u32 index, Node offset) : index{index}, offset{std::move(offset)} {}
|
||||
|
||||
u32 GetIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
const Node& GetOffset() const {
|
||||
return offset;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 index{};
|
||||
Node offset;
|
||||
};
|
||||
|
||||
/// Local memory node
|
||||
class LmemNode final {
|
||||
public:
|
||||
explicit LmemNode(Node address) : address{std::move(address)} {}
|
||||
|
||||
const Node& GetAddress() const {
|
||||
return address;
|
||||
}
|
||||
|
||||
private:
|
||||
Node address;
|
||||
};
|
||||
|
||||
/// Global memory node
|
||||
class GmemNode final {
|
||||
public:
|
||||
explicit GmemNode(Node real_address, Node base_address, const GlobalMemoryBase& descriptor)
|
||||
: real_address{std::move(real_address)}, base_address{std::move(base_address)},
|
||||
descriptor{descriptor} {}
|
||||
|
||||
const Node& GetRealAddress() const {
|
||||
return real_address;
|
||||
}
|
||||
|
||||
const Node& GetBaseAddress() const {
|
||||
return base_address;
|
||||
}
|
||||
|
||||
const GlobalMemoryBase& GetDescriptor() const {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
private:
|
||||
Node real_address;
|
||||
Node base_address;
|
||||
GlobalMemoryBase descriptor;
|
||||
};
|
||||
|
||||
/// Commentary, can be dropped
|
||||
class CommentNode final {
|
||||
public:
|
||||
explicit CommentNode(std::string text) : text{std::move(text)} {}
|
||||
|
||||
const std::string& GetText() const {
|
||||
return text;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string text;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
99
src/video_core/shader/node_helper.cpp
Normal file
99
src/video_core/shader/node_helper.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
Node Conditional(Node condition, std::vector<Node> code) {
|
||||
return MakeNode<ConditionalNode>(condition, std::move(code));
|
||||
}
|
||||
|
||||
Node Comment(std::string text) {
|
||||
return MakeNode<CommentNode>(std::move(text));
|
||||
}
|
||||
|
||||
Node Immediate(u32 value) {
|
||||
return MakeNode<ImmediateNode>(value);
|
||||
}
|
||||
|
||||
Node Immediate(s32 value) {
|
||||
return Immediate(static_cast<u32>(value));
|
||||
}
|
||||
|
||||
Node Immediate(f32 value) {
|
||||
u32 integral;
|
||||
std::memcpy(&integral, &value, sizeof(u32));
|
||||
return Immediate(integral);
|
||||
}
|
||||
|
||||
OperationCode SignedToUnsignedCode(OperationCode operation_code, bool is_signed) {
|
||||
if (is_signed) {
|
||||
return operation_code;
|
||||
}
|
||||
switch (operation_code) {
|
||||
case OperationCode::FCastInteger:
|
||||
return OperationCode::FCastUInteger;
|
||||
case OperationCode::IAdd:
|
||||
return OperationCode::UAdd;
|
||||
case OperationCode::IMul:
|
||||
return OperationCode::UMul;
|
||||
case OperationCode::IDiv:
|
||||
return OperationCode::UDiv;
|
||||
case OperationCode::IMin:
|
||||
return OperationCode::UMin;
|
||||
case OperationCode::IMax:
|
||||
return OperationCode::UMax;
|
||||
case OperationCode::ICastFloat:
|
||||
return OperationCode::UCastFloat;
|
||||
case OperationCode::ICastUnsigned:
|
||||
return OperationCode::UCastSigned;
|
||||
case OperationCode::ILogicalShiftLeft:
|
||||
return OperationCode::ULogicalShiftLeft;
|
||||
case OperationCode::ILogicalShiftRight:
|
||||
return OperationCode::ULogicalShiftRight;
|
||||
case OperationCode::IArithmeticShiftRight:
|
||||
return OperationCode::UArithmeticShiftRight;
|
||||
case OperationCode::IBitwiseAnd:
|
||||
return OperationCode::UBitwiseAnd;
|
||||
case OperationCode::IBitwiseOr:
|
||||
return OperationCode::UBitwiseOr;
|
||||
case OperationCode::IBitwiseXor:
|
||||
return OperationCode::UBitwiseXor;
|
||||
case OperationCode::IBitwiseNot:
|
||||
return OperationCode::UBitwiseNot;
|
||||
case OperationCode::IBitfieldInsert:
|
||||
return OperationCode::UBitfieldInsert;
|
||||
case OperationCode::IBitCount:
|
||||
return OperationCode::UBitCount;
|
||||
case OperationCode::LogicalILessThan:
|
||||
return OperationCode::LogicalULessThan;
|
||||
case OperationCode::LogicalIEqual:
|
||||
return OperationCode::LogicalUEqual;
|
||||
case OperationCode::LogicalILessEqual:
|
||||
return OperationCode::LogicalULessEqual;
|
||||
case OperationCode::LogicalIGreaterThan:
|
||||
return OperationCode::LogicalUGreaterThan;
|
||||
case OperationCode::LogicalINotEqual:
|
||||
return OperationCode::LogicalUNotEqual;
|
||||
case OperationCode::LogicalIGreaterEqual:
|
||||
return OperationCode::LogicalUGreaterEqual;
|
||||
case OperationCode::INegate:
|
||||
UNREACHABLE_MSG("Can't negate an unsigned integer");
|
||||
return {};
|
||||
case OperationCode::IAbsolute:
|
||||
UNREACHABLE_MSG("Can't apply absolute to an unsigned integer");
|
||||
return {};
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown signed operation with code={}", static_cast<u32>(operation_code));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
65
src/video_core/shader/node_helper.h
Normal file
65
src/video_core/shader/node_helper.h
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/shader/node.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
/// This arithmetic operation cannot be constraint
|
||||
inline constexpr MetaArithmetic PRECISE = {true};
|
||||
/// This arithmetic operation can be optimized away
|
||||
inline constexpr MetaArithmetic NO_PRECISE = {false};
|
||||
|
||||
/// Creates a conditional node
|
||||
Node Conditional(Node condition, std::vector<Node> code);
|
||||
|
||||
/// Creates a commentary node
|
||||
Node Comment(std::string text);
|
||||
|
||||
/// Creates an u32 immediate
|
||||
Node Immediate(u32 value);
|
||||
|
||||
/// Creates a s32 immediate
|
||||
Node Immediate(s32 value);
|
||||
|
||||
/// Creates a f32 immediate
|
||||
Node Immediate(f32 value);
|
||||
|
||||
/// Converts an signed operation code to an unsigned operation code
|
||||
OperationCode SignedToUnsignedCode(OperationCode operation_code, bool is_signed);
|
||||
|
||||
template <typename T, typename... Args>
|
||||
Node MakeNode(Args&&... args) {
|
||||
static_assert(std::is_convertible_v<T, NodeData>);
|
||||
return std::make_shared<NodeData>(T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
Node Operation(OperationCode code, Args&&... args) {
|
||||
if constexpr (sizeof...(args) == 0) {
|
||||
return MakeNode<OperationNode>(code);
|
||||
} else if constexpr (std::is_convertible_v<std::tuple_element_t<0, std::tuple<Args...>>,
|
||||
Meta>) {
|
||||
return MakeNode<OperationNode>(code, std::forward<Args>(args)...);
|
||||
} else {
|
||||
return MakeNode<OperationNode>(code, Meta{}, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
Node SignedOperation(OperationCode code, bool is_signed, Args&&... args) {
|
||||
return Operation(SignedToUnsignedCode(code, is_signed), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
@@ -28,30 +29,11 @@ ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset)
|
||||
|
||||
ShaderIR::~ShaderIR() = default;
|
||||
|
||||
Node ShaderIR::StoreNode(NodeData&& node_data) {
|
||||
auto store = std::make_unique<NodeData>(node_data);
|
||||
const Node node = store.get();
|
||||
stored_nodes.push_back(std::move(store));
|
||||
return node;
|
||||
}
|
||||
|
||||
Node ShaderIR::Conditional(Node condition, std::vector<Node>&& code) {
|
||||
return StoreNode(ConditionalNode(condition, std::move(code)));
|
||||
}
|
||||
|
||||
Node ShaderIR::Comment(std::string text) {
|
||||
return StoreNode(CommentNode(std::move(text)));
|
||||
}
|
||||
|
||||
Node ShaderIR::Immediate(u32 value) {
|
||||
return StoreNode(ImmediateNode(value));
|
||||
}
|
||||
|
||||
Node ShaderIR::GetRegister(Register reg) {
|
||||
if (reg != Register::ZeroIndex) {
|
||||
used_registers.insert(static_cast<u32>(reg));
|
||||
}
|
||||
return StoreNode(GprNode(reg));
|
||||
return MakeNode<GprNode>(reg);
|
||||
}
|
||||
|
||||
Node ShaderIR::GetImmediate19(Instruction instr) {
|
||||
@@ -69,7 +51,7 @@ Node ShaderIR::GetConstBuffer(u64 index_, u64 offset_) {
|
||||
const auto [entry, is_new] = used_cbufs.try_emplace(index);
|
||||
entry->second.MarkAsUsed(offset);
|
||||
|
||||
return StoreNode(CbufNode(index, Immediate(offset)));
|
||||
return MakeNode<CbufNode>(index, Immediate(offset));
|
||||
}
|
||||
|
||||
Node ShaderIR::GetConstBufferIndirect(u64 index_, u64 offset_, Node node) {
|
||||
@@ -80,7 +62,7 @@ Node ShaderIR::GetConstBufferIndirect(u64 index_, u64 offset_, Node node) {
|
||||
entry->second.MarkAsUsedIndirect();
|
||||
|
||||
const Node final_offset = Operation(OperationCode::UAdd, NO_PRECISE, node, Immediate(offset));
|
||||
return StoreNode(CbufNode(index, final_offset));
|
||||
return MakeNode<CbufNode>(index, final_offset);
|
||||
}
|
||||
|
||||
Node ShaderIR::GetPredicate(u64 pred_, bool negated) {
|
||||
@@ -89,7 +71,7 @@ Node ShaderIR::GetPredicate(u64 pred_, bool negated) {
|
||||
used_predicates.insert(pred);
|
||||
}
|
||||
|
||||
return StoreNode(PredicateNode(pred, negated));
|
||||
return MakeNode<PredicateNode>(pred, negated);
|
||||
}
|
||||
|
||||
Node ShaderIR::GetPredicate(bool immediate) {
|
||||
@@ -98,12 +80,12 @@ Node ShaderIR::GetPredicate(bool immediate) {
|
||||
|
||||
Node ShaderIR::GetInputAttribute(Attribute::Index index, u64 element, Node buffer) {
|
||||
used_input_attributes.emplace(index);
|
||||
return StoreNode(AbufNode(index, static_cast<u32>(element), buffer));
|
||||
return MakeNode<AbufNode>(index, static_cast<u32>(element), buffer);
|
||||
}
|
||||
|
||||
Node ShaderIR::GetPhysicalInputAttribute(Tegra::Shader::Register physical_address, Node buffer) {
|
||||
uses_physical_attributes = true;
|
||||
return StoreNode(AbufNode(GetRegister(physical_address), buffer));
|
||||
return MakeNode<AbufNode>(GetRegister(physical_address), buffer);
|
||||
}
|
||||
|
||||
Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buffer) {
|
||||
@@ -115,11 +97,11 @@ Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buff
|
||||
}
|
||||
used_output_attributes.insert(index);
|
||||
|
||||
return StoreNode(AbufNode(index, static_cast<u32>(element), buffer));
|
||||
return MakeNode<AbufNode>(index, static_cast<u32>(element), buffer);
|
||||
}
|
||||
|
||||
Node ShaderIR::GetInternalFlag(InternalFlag flag, bool negated) {
|
||||
const Node node = StoreNode(InternalFlagNode(flag));
|
||||
const Node node = MakeNode<InternalFlagNode>(flag);
|
||||
if (negated) {
|
||||
return Operation(OperationCode::LogicalNegate, node);
|
||||
}
|
||||
@@ -127,7 +109,7 @@ Node ShaderIR::GetInternalFlag(InternalFlag flag, bool negated) {
|
||||
}
|
||||
|
||||
Node ShaderIR::GetLocalMemory(Node address) {
|
||||
return StoreNode(LmemNode(address));
|
||||
return MakeNode<LmemNode>(address);
|
||||
}
|
||||
|
||||
Node ShaderIR::GetTemporal(u32 id) {
|
||||
@@ -393,68 +375,4 @@ Node ShaderIR::BitfieldExtract(Node value, u32 offset, u32 bits) {
|
||||
Immediate(bits));
|
||||
}
|
||||
|
||||
/*static*/ OperationCode ShaderIR::SignedToUnsignedCode(OperationCode operation_code,
|
||||
bool is_signed) {
|
||||
if (is_signed) {
|
||||
return operation_code;
|
||||
}
|
||||
switch (operation_code) {
|
||||
case OperationCode::FCastInteger:
|
||||
return OperationCode::FCastUInteger;
|
||||
case OperationCode::IAdd:
|
||||
return OperationCode::UAdd;
|
||||
case OperationCode::IMul:
|
||||
return OperationCode::UMul;
|
||||
case OperationCode::IDiv:
|
||||
return OperationCode::UDiv;
|
||||
case OperationCode::IMin:
|
||||
return OperationCode::UMin;
|
||||
case OperationCode::IMax:
|
||||
return OperationCode::UMax;
|
||||
case OperationCode::ICastFloat:
|
||||
return OperationCode::UCastFloat;
|
||||
case OperationCode::ICastUnsigned:
|
||||
return OperationCode::UCastSigned;
|
||||
case OperationCode::ILogicalShiftLeft:
|
||||
return OperationCode::ULogicalShiftLeft;
|
||||
case OperationCode::ILogicalShiftRight:
|
||||
return OperationCode::ULogicalShiftRight;
|
||||
case OperationCode::IArithmeticShiftRight:
|
||||
return OperationCode::UArithmeticShiftRight;
|
||||
case OperationCode::IBitwiseAnd:
|
||||
return OperationCode::UBitwiseAnd;
|
||||
case OperationCode::IBitwiseOr:
|
||||
return OperationCode::UBitwiseOr;
|
||||
case OperationCode::IBitwiseXor:
|
||||
return OperationCode::UBitwiseXor;
|
||||
case OperationCode::IBitwiseNot:
|
||||
return OperationCode::UBitwiseNot;
|
||||
case OperationCode::IBitfieldInsert:
|
||||
return OperationCode::UBitfieldInsert;
|
||||
case OperationCode::IBitCount:
|
||||
return OperationCode::UBitCount;
|
||||
case OperationCode::LogicalILessThan:
|
||||
return OperationCode::LogicalULessThan;
|
||||
case OperationCode::LogicalIEqual:
|
||||
return OperationCode::LogicalUEqual;
|
||||
case OperationCode::LogicalILessEqual:
|
||||
return OperationCode::LogicalULessEqual;
|
||||
case OperationCode::LogicalIGreaterThan:
|
||||
return OperationCode::LogicalUGreaterThan;
|
||||
case OperationCode::LogicalINotEqual:
|
||||
return OperationCode::LogicalUNotEqual;
|
||||
case OperationCode::LogicalIGreaterEqual:
|
||||
return OperationCode::LogicalUGreaterEqual;
|
||||
case OperationCode::INegate:
|
||||
UNREACHABLE_MSG("Can't negate an unsigned integer");
|
||||
return {};
|
||||
case OperationCode::IAbsolute:
|
||||
UNREACHABLE_MSG("Can't apply absolute to an unsigned integer");
|
||||
return {};
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown signed operation with code={}", static_cast<u32>(operation_code));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -18,188 +18,14 @@
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/engines/shader_header.h"
|
||||
#include "video_core/shader/node.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
class OperationNode;
|
||||
class ConditionalNode;
|
||||
class GprNode;
|
||||
class ImmediateNode;
|
||||
class InternalFlagNode;
|
||||
class PredicateNode;
|
||||
class AbufNode; ///< Attribute buffer
|
||||
class CbufNode; ///< Constant buffer
|
||||
class LmemNode; ///< Local memory
|
||||
class GmemNode; ///< Global memory
|
||||
class CommentNode;
|
||||
|
||||
using ProgramCode = std::vector<u64>;
|
||||
|
||||
using NodeData =
|
||||
std::variant<OperationNode, ConditionalNode, GprNode, ImmediateNode, InternalFlagNode,
|
||||
PredicateNode, AbufNode, CbufNode, LmemNode, GmemNode, CommentNode>;
|
||||
using Node = const NodeData*;
|
||||
using Node4 = std::array<Node, 4>;
|
||||
using NodeBlock = std::vector<Node>;
|
||||
|
||||
constexpr u32 MAX_PROGRAM_LENGTH = 0x1000;
|
||||
|
||||
enum class OperationCode {
|
||||
Assign, /// (float& dest, float src) -> void
|
||||
|
||||
Select, /// (MetaArithmetic, bool pred, float a, float b) -> float
|
||||
|
||||
FAdd, /// (MetaArithmetic, float a, float b) -> float
|
||||
FMul, /// (MetaArithmetic, float a, float b) -> float
|
||||
FDiv, /// (MetaArithmetic, float a, float b) -> float
|
||||
FFma, /// (MetaArithmetic, float a, float b, float c) -> float
|
||||
FNegate, /// (MetaArithmetic, float a) -> float
|
||||
FAbsolute, /// (MetaArithmetic, float a) -> float
|
||||
FClamp, /// (MetaArithmetic, float value, float min, float max) -> float
|
||||
FMin, /// (MetaArithmetic, float a, float b) -> float
|
||||
FMax, /// (MetaArithmetic, float a, float b) -> float
|
||||
FCos, /// (MetaArithmetic, float a) -> float
|
||||
FSin, /// (MetaArithmetic, float a) -> float
|
||||
FExp2, /// (MetaArithmetic, float a) -> float
|
||||
FLog2, /// (MetaArithmetic, float a) -> float
|
||||
FInverseSqrt, /// (MetaArithmetic, float a) -> float
|
||||
FSqrt, /// (MetaArithmetic, float a) -> float
|
||||
FRoundEven, /// (MetaArithmetic, float a) -> float
|
||||
FFloor, /// (MetaArithmetic, float a) -> float
|
||||
FCeil, /// (MetaArithmetic, float a) -> float
|
||||
FTrunc, /// (MetaArithmetic, float a) -> float
|
||||
FCastInteger, /// (MetaArithmetic, int a) -> float
|
||||
FCastUInteger, /// (MetaArithmetic, uint a) -> float
|
||||
|
||||
IAdd, /// (MetaArithmetic, int a, int b) -> int
|
||||
IMul, /// (MetaArithmetic, int a, int b) -> int
|
||||
IDiv, /// (MetaArithmetic, int a, int b) -> int
|
||||
INegate, /// (MetaArithmetic, int a) -> int
|
||||
IAbsolute, /// (MetaArithmetic, int a) -> int
|
||||
IMin, /// (MetaArithmetic, int a, int b) -> int
|
||||
IMax, /// (MetaArithmetic, int a, int b) -> int
|
||||
ICastFloat, /// (MetaArithmetic, float a) -> int
|
||||
ICastUnsigned, /// (MetaArithmetic, uint a) -> int
|
||||
ILogicalShiftLeft, /// (MetaArithmetic, int a, uint b) -> int
|
||||
ILogicalShiftRight, /// (MetaArithmetic, int a, uint b) -> int
|
||||
IArithmeticShiftRight, /// (MetaArithmetic, int a, uint b) -> int
|
||||
IBitwiseAnd, /// (MetaArithmetic, int a, int b) -> int
|
||||
IBitwiseOr, /// (MetaArithmetic, int a, int b) -> int
|
||||
IBitwiseXor, /// (MetaArithmetic, int a, int b) -> int
|
||||
IBitwiseNot, /// (MetaArithmetic, int a) -> int
|
||||
IBitfieldInsert, /// (MetaArithmetic, int base, int insert, int offset, int bits) -> int
|
||||
IBitfieldExtract, /// (MetaArithmetic, int value, int offset, int offset) -> int
|
||||
IBitCount, /// (MetaArithmetic, int) -> int
|
||||
|
||||
UAdd, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UMul, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UDiv, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UMin, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UMax, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UCastFloat, /// (MetaArithmetic, float a) -> uint
|
||||
UCastSigned, /// (MetaArithmetic, int a) -> uint
|
||||
ULogicalShiftLeft, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
ULogicalShiftRight, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UArithmeticShiftRight, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UBitwiseAnd, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UBitwiseOr, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UBitwiseXor, /// (MetaArithmetic, uint a, uint b) -> uint
|
||||
UBitwiseNot, /// (MetaArithmetic, uint a) -> uint
|
||||
UBitfieldInsert, /// (MetaArithmetic, uint base, uint insert, int offset, int bits) -> uint
|
||||
UBitfieldExtract, /// (MetaArithmetic, uint value, int offset, int offset) -> uint
|
||||
UBitCount, /// (MetaArithmetic, uint) -> uint
|
||||
|
||||
HAdd, /// (MetaArithmetic, f16vec2 a, f16vec2 b) -> f16vec2
|
||||
HMul, /// (MetaArithmetic, f16vec2 a, f16vec2 b) -> f16vec2
|
||||
HFma, /// (MetaArithmetic, f16vec2 a, f16vec2 b, f16vec2 c) -> f16vec2
|
||||
HAbsolute, /// (f16vec2 a) -> f16vec2
|
||||
HNegate, /// (f16vec2 a, bool first, bool second) -> f16vec2
|
||||
HClamp, /// (f16vec2 src, float min, float max) -> f16vec2
|
||||
HUnpack, /// (Tegra::Shader::HalfType, T value) -> f16vec2
|
||||
HMergeF32, /// (f16vec2 src) -> float
|
||||
HMergeH0, /// (f16vec2 dest, f16vec2 src) -> f16vec2
|
||||
HMergeH1, /// (f16vec2 dest, f16vec2 src) -> f16vec2
|
||||
HPack2, /// (float a, float b) -> f16vec2
|
||||
|
||||
LogicalAssign, /// (bool& dst, bool src) -> void
|
||||
LogicalAnd, /// (bool a, bool b) -> bool
|
||||
LogicalOr, /// (bool a, bool b) -> bool
|
||||
LogicalXor, /// (bool a, bool b) -> bool
|
||||
LogicalNegate, /// (bool a) -> bool
|
||||
LogicalPick2, /// (bool2 pair, uint index) -> bool
|
||||
LogicalAll2, /// (bool2 a) -> bool
|
||||
LogicalAny2, /// (bool2 a) -> bool
|
||||
|
||||
LogicalFLessThan, /// (float a, float b) -> bool
|
||||
LogicalFEqual, /// (float a, float b) -> bool
|
||||
LogicalFLessEqual, /// (float a, float b) -> bool
|
||||
LogicalFGreaterThan, /// (float a, float b) -> bool
|
||||
LogicalFNotEqual, /// (float a, float b) -> bool
|
||||
LogicalFGreaterEqual, /// (float a, float b) -> bool
|
||||
LogicalFIsNan, /// (float a) -> bool
|
||||
|
||||
LogicalILessThan, /// (int a, int b) -> bool
|
||||
LogicalIEqual, /// (int a, int b) -> bool
|
||||
LogicalILessEqual, /// (int a, int b) -> bool
|
||||
LogicalIGreaterThan, /// (int a, int b) -> bool
|
||||
LogicalINotEqual, /// (int a, int b) -> bool
|
||||
LogicalIGreaterEqual, /// (int a, int b) -> bool
|
||||
|
||||
LogicalULessThan, /// (uint a, uint b) -> bool
|
||||
LogicalUEqual, /// (uint a, uint b) -> bool
|
||||
LogicalULessEqual, /// (uint a, uint b) -> bool
|
||||
LogicalUGreaterThan, /// (uint a, uint b) -> bool
|
||||
LogicalUNotEqual, /// (uint a, uint b) -> bool
|
||||
LogicalUGreaterEqual, /// (uint a, uint b) -> bool
|
||||
|
||||
Logical2HLessThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HLessEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HGreaterThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HNotEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HGreaterEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HLessThanWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HLessEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HGreaterThanWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HNotEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
Logical2HGreaterEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
|
||||
|
||||
Texture, /// (MetaTexture, float[N] coords) -> float4
|
||||
TextureLod, /// (MetaTexture, float[N] coords) -> float4
|
||||
TextureGather, /// (MetaTexture, float[N] coords) -> float4
|
||||
TextureQueryDimensions, /// (MetaTexture, float a) -> float4
|
||||
TextureQueryLod, /// (MetaTexture, float[N] coords) -> float4
|
||||
TexelFetch, /// (MetaTexture, int[N], int) -> float4
|
||||
|
||||
Branch, /// (uint branch_target) -> void
|
||||
PushFlowStack, /// (uint branch_target) -> void
|
||||
PopFlowStack, /// () -> void
|
||||
Exit, /// () -> void
|
||||
Discard, /// () -> void
|
||||
|
||||
EmitVertex, /// () -> void
|
||||
EndPrimitive, /// () -> void
|
||||
|
||||
YNegate, /// () -> float
|
||||
LocalInvocationIdX, /// () -> uint
|
||||
LocalInvocationIdY, /// () -> uint
|
||||
LocalInvocationIdZ, /// () -> uint
|
||||
WorkGroupIdX, /// () -> uint
|
||||
WorkGroupIdY, /// () -> uint
|
||||
WorkGroupIdZ, /// () -> uint
|
||||
|
||||
Amount,
|
||||
};
|
||||
|
||||
enum class InternalFlag {
|
||||
Zero = 0,
|
||||
Sign = 1,
|
||||
Carry = 2,
|
||||
Overflow = 3,
|
||||
Amount = 4,
|
||||
};
|
||||
|
||||
/// Describes the behaviour of code path of a given entry point and a return point.
|
||||
enum class ExitMethod {
|
||||
Undetermined, ///< Internal value. Only occur when analyzing JMP loop.
|
||||
@@ -208,71 +34,6 @@ enum class ExitMethod {
|
||||
AlwaysEnd, ///< All code paths reach a END instruction.
|
||||
};
|
||||
|
||||
class Sampler {
|
||||
public:
|
||||
// Use this constructor for bounded Samplers
|
||||
explicit Sampler(std::size_t offset, std::size_t index, Tegra::Shader::TextureType type,
|
||||
bool is_array, bool is_shadow)
|
||||
: offset{offset}, index{index}, type{type}, is_array{is_array}, is_shadow{is_shadow},
|
||||
is_bindless{false} {}
|
||||
|
||||
// Use this constructor for bindless Samplers
|
||||
explicit Sampler(u32 cbuf_index, u32 cbuf_offset, std::size_t index,
|
||||
Tegra::Shader::TextureType type, bool is_array, bool is_shadow)
|
||||
: offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type},
|
||||
is_array{is_array}, is_shadow{is_shadow}, is_bindless{true} {}
|
||||
|
||||
// Use this only for serialization/deserialization
|
||||
explicit Sampler(std::size_t offset, std::size_t index, Tegra::Shader::TextureType type,
|
||||
bool is_array, bool is_shadow, bool is_bindless)
|
||||
: offset{offset}, index{index}, type{type}, is_array{is_array}, is_shadow{is_shadow},
|
||||
is_bindless{is_bindless} {}
|
||||
|
||||
std::size_t GetOffset() const {
|
||||
return offset;
|
||||
}
|
||||
|
||||
std::size_t GetIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
Tegra::Shader::TextureType GetType() const {
|
||||
return type;
|
||||
}
|
||||
|
||||
bool IsArray() const {
|
||||
return is_array;
|
||||
}
|
||||
|
||||
bool IsShadow() const {
|
||||
return is_shadow;
|
||||
}
|
||||
|
||||
bool IsBindless() const {
|
||||
return is_bindless;
|
||||
}
|
||||
|
||||
std::pair<u32, u32> GetBindlessCBuf() const {
|
||||
return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)};
|
||||
}
|
||||
|
||||
bool operator<(const Sampler& rhs) const {
|
||||
return std::tie(index, offset, type, is_array, is_shadow, is_bindless) <
|
||||
std::tie(rhs.index, rhs.offset, rhs.type, rhs.is_array, rhs.is_shadow,
|
||||
rhs.is_bindless);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Offset in TSC memory from which to read the sampler object, as specified by the sampling
|
||||
/// instruction.
|
||||
std::size_t offset{};
|
||||
std::size_t index{}; ///< Value used to index into the generated GLSL sampler array.
|
||||
Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc)
|
||||
bool is_array{}; ///< Whether the texture is being sampled as an array texture or not.
|
||||
bool is_shadow{}; ///< Whether the texture is being sampled as a depth texture or not.
|
||||
bool is_bindless{}; ///< Whether this sampler belongs to a bindless texture or not.
|
||||
};
|
||||
|
||||
class ConstBuffer {
|
||||
public:
|
||||
explicit ConstBuffer(u32 max_offset, bool is_indirect)
|
||||
@@ -305,268 +66,11 @@ private:
|
||||
bool is_indirect{};
|
||||
};
|
||||
|
||||
struct GlobalMemoryBase {
|
||||
u32 cbuf_index{};
|
||||
u32 cbuf_offset{};
|
||||
|
||||
bool operator<(const GlobalMemoryBase& rhs) const {
|
||||
return std::tie(cbuf_index, cbuf_offset) < std::tie(rhs.cbuf_index, rhs.cbuf_offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct GlobalMemoryUsage {
|
||||
bool is_read{};
|
||||
bool is_written{};
|
||||
};
|
||||
|
||||
struct MetaArithmetic {
|
||||
bool precise{};
|
||||
};
|
||||
|
||||
struct MetaTexture {
|
||||
const Sampler& sampler;
|
||||
Node array{};
|
||||
Node depth_compare{};
|
||||
std::vector<Node> aoffi;
|
||||
Node bias{};
|
||||
Node lod{};
|
||||
Node component{};
|
||||
u32 element{};
|
||||
};
|
||||
|
||||
constexpr MetaArithmetic PRECISE = {true};
|
||||
constexpr MetaArithmetic NO_PRECISE = {false};
|
||||
|
||||
using Meta = std::variant<MetaArithmetic, MetaTexture, Tegra::Shader::HalfType>;
|
||||
|
||||
/// Holds any kind of operation that can be done in the IR
|
||||
class OperationNode final {
|
||||
public:
|
||||
explicit OperationNode(OperationCode code) : code{code} {}
|
||||
|
||||
explicit OperationNode(OperationCode code, Meta&& meta) : code{code}, meta{std::move(meta)} {}
|
||||
|
||||
template <typename... T>
|
||||
explicit OperationNode(OperationCode code, const T*... operands)
|
||||
: OperationNode(code, {}, operands...) {}
|
||||
|
||||
template <typename... T>
|
||||
explicit OperationNode(OperationCode code, Meta&& meta, const T*... operands_)
|
||||
: code{code}, meta{std::move(meta)}, operands{operands_...} {}
|
||||
|
||||
explicit OperationNode(OperationCode code, Meta&& meta, std::vector<Node>&& operands)
|
||||
: code{code}, meta{meta}, operands{std::move(operands)} {}
|
||||
|
||||
explicit OperationNode(OperationCode code, std::vector<Node>&& operands)
|
||||
: code{code}, operands{std::move(operands)} {}
|
||||
|
||||
OperationCode GetCode() const {
|
||||
return code;
|
||||
}
|
||||
|
||||
const Meta& GetMeta() const {
|
||||
return meta;
|
||||
}
|
||||
|
||||
std::size_t GetOperandsCount() const {
|
||||
return operands.size();
|
||||
}
|
||||
|
||||
Node operator[](std::size_t operand_index) const {
|
||||
return operands.at(operand_index);
|
||||
}
|
||||
|
||||
private:
|
||||
const OperationCode code;
|
||||
const Meta meta;
|
||||
std::vector<Node> operands;
|
||||
};
|
||||
|
||||
/// Encloses inside any kind of node that returns a boolean conditionally-executed code
|
||||
class ConditionalNode final {
|
||||
public:
|
||||
explicit ConditionalNode(Node condition, std::vector<Node>&& code)
|
||||
: condition{condition}, code{std::move(code)} {}
|
||||
|
||||
Node GetCondition() const {
|
||||
return condition;
|
||||
}
|
||||
|
||||
const std::vector<Node>& GetCode() const {
|
||||
return code;
|
||||
}
|
||||
|
||||
private:
|
||||
const Node condition; ///< Condition to be satisfied
|
||||
std::vector<Node> code; ///< Code to execute
|
||||
};
|
||||
|
||||
/// A general purpose register
|
||||
class GprNode final {
|
||||
public:
|
||||
explicit constexpr GprNode(Tegra::Shader::Register index) : index{index} {}
|
||||
|
||||
u32 GetIndex() const {
|
||||
return static_cast<u32>(index);
|
||||
}
|
||||
|
||||
private:
|
||||
const Tegra::Shader::Register index;
|
||||
};
|
||||
|
||||
/// A 32-bits value that represents an immediate value
|
||||
class ImmediateNode final {
|
||||
public:
|
||||
explicit constexpr ImmediateNode(u32 value) : value{value} {}
|
||||
|
||||
u32 GetValue() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
const u32 value;
|
||||
};
|
||||
|
||||
/// One of Maxwell's internal flags
|
||||
class InternalFlagNode final {
|
||||
public:
|
||||
explicit constexpr InternalFlagNode(InternalFlag flag) : flag{flag} {}
|
||||
|
||||
InternalFlag GetFlag() const {
|
||||
return flag;
|
||||
}
|
||||
|
||||
private:
|
||||
const InternalFlag flag;
|
||||
};
|
||||
|
||||
/// A predicate register, it can be negated without additional nodes
|
||||
class PredicateNode final {
|
||||
public:
|
||||
explicit constexpr PredicateNode(Tegra::Shader::Pred index, bool negated)
|
||||
: index{index}, negated{negated} {}
|
||||
|
||||
Tegra::Shader::Pred GetIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
bool IsNegated() const {
|
||||
return negated;
|
||||
}
|
||||
|
||||
private:
|
||||
const Tegra::Shader::Pred index;
|
||||
const bool negated;
|
||||
};
|
||||
|
||||
/// Attribute buffer memory (known as attributes or varyings in GLSL terms)
|
||||
class AbufNode final {
|
||||
public:
|
||||
// Initialize for standard attributes (index is explicit).
|
||||
explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element,
|
||||
Node buffer = {})
|
||||
: buffer{buffer}, index{index}, element{element} {}
|
||||
|
||||
// Initialize for physical attributes (index is a variable value).
|
||||
explicit constexpr AbufNode(Node physical_address, Node buffer = {})
|
||||
: physical_address{physical_address}, buffer{buffer} {}
|
||||
|
||||
Tegra::Shader::Attribute::Index GetIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
u32 GetElement() const {
|
||||
return element;
|
||||
}
|
||||
|
||||
Node GetBuffer() const {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool IsPhysicalBuffer() const {
|
||||
return physical_address != nullptr;
|
||||
}
|
||||
|
||||
Node GetPhysicalAddress() const {
|
||||
return physical_address;
|
||||
}
|
||||
|
||||
private:
|
||||
Node physical_address{};
|
||||
Node buffer{};
|
||||
Tegra::Shader::Attribute::Index index{};
|
||||
u32 element{};
|
||||
};
|
||||
|
||||
/// Constant buffer node, usually mapped to uniform buffers in GLSL
|
||||
class CbufNode final {
|
||||
public:
|
||||
explicit constexpr CbufNode(u32 index, Node offset) : index{index}, offset{offset} {}
|
||||
|
||||
u32 GetIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
Node GetOffset() const {
|
||||
return offset;
|
||||
}
|
||||
|
||||
private:
|
||||
const u32 index;
|
||||
const Node offset;
|
||||
};
|
||||
|
||||
/// Local memory node
|
||||
class LmemNode final {
|
||||
public:
|
||||
explicit constexpr LmemNode(Node address) : address{address} {}
|
||||
|
||||
Node GetAddress() const {
|
||||
return address;
|
||||
}
|
||||
|
||||
private:
|
||||
const Node address;
|
||||
};
|
||||
|
||||
/// Global memory node
|
||||
class GmemNode final {
|
||||
public:
|
||||
explicit constexpr GmemNode(Node real_address, Node base_address,
|
||||
const GlobalMemoryBase& descriptor)
|
||||
: real_address{real_address}, base_address{base_address}, descriptor{descriptor} {}
|
||||
|
||||
Node GetRealAddress() const {
|
||||
return real_address;
|
||||
}
|
||||
|
||||
Node GetBaseAddress() const {
|
||||
return base_address;
|
||||
}
|
||||
|
||||
const GlobalMemoryBase& GetDescriptor() const {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
private:
|
||||
const Node real_address;
|
||||
const Node base_address;
|
||||
const GlobalMemoryBase descriptor;
|
||||
};
|
||||
|
||||
/// Commentary, can be dropped
|
||||
class CommentNode final {
|
||||
public:
|
||||
explicit CommentNode(std::string text) : text{std::move(text)} {}
|
||||
|
||||
const std::string& GetText() const {
|
||||
return text;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string text;
|
||||
};
|
||||
|
||||
class ShaderIR final {
|
||||
public:
|
||||
explicit ShaderIR(const ProgramCode& program_code, u32 main_offset);
|
||||
@@ -663,26 +167,6 @@ private:
|
||||
u32 DecodeXmad(NodeBlock& bb, u32 pc);
|
||||
u32 DecodeOther(NodeBlock& bb, u32 pc);
|
||||
|
||||
/// Internalizes node's data and returns a managed pointer to a clone of that node
|
||||
Node StoreNode(NodeData&& node_data);
|
||||
|
||||
/// Creates a conditional node
|
||||
Node Conditional(Node condition, std::vector<Node>&& code);
|
||||
/// Creates a commentary
|
||||
Node Comment(std::string text);
|
||||
/// Creates an u32 immediate
|
||||
Node Immediate(u32 value);
|
||||
/// Creates a s32 immediate
|
||||
Node Immediate(s32 value) {
|
||||
return Immediate(static_cast<u32>(value));
|
||||
}
|
||||
/// Creates a f32 immediate
|
||||
Node Immediate(f32 value) {
|
||||
u32 integral;
|
||||
std::memcpy(&integral, &value, sizeof(u32));
|
||||
return Immediate(integral);
|
||||
}
|
||||
|
||||
/// Generates a node for a passed register.
|
||||
Node GetRegister(Tegra::Shader::Register reg);
|
||||
/// Generates a node representing a 19-bit immediate value
|
||||
@@ -827,37 +311,6 @@ private:
|
||||
std::tuple<Node, Node, GlobalMemoryBase> TrackAndGetGlobalMemory(
|
||||
NodeBlock& bb, Tegra::Shader::Instruction instr, bool is_write);
|
||||
|
||||
template <typename... T>
|
||||
Node Operation(OperationCode code, const T*... operands) {
|
||||
return StoreNode(OperationNode(code, operands...));
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
Node Operation(OperationCode code, Meta&& meta, const T*... operands) {
|
||||
return StoreNode(OperationNode(code, std::move(meta), operands...));
|
||||
}
|
||||
|
||||
Node Operation(OperationCode code, std::vector<Node>&& operands) {
|
||||
return StoreNode(OperationNode(code, std::move(operands)));
|
||||
}
|
||||
|
||||
Node Operation(OperationCode code, Meta&& meta, std::vector<Node>&& operands) {
|
||||
return StoreNode(OperationNode(code, std::move(meta), std::move(operands)));
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
Node SignedOperation(OperationCode code, bool is_signed, const T*... operands) {
|
||||
return StoreNode(OperationNode(SignedToUnsignedCode(code, is_signed), operands...));
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
Node SignedOperation(OperationCode code, bool is_signed, Meta&& meta, const T*... operands) {
|
||||
return StoreNode(
|
||||
OperationNode(SignedToUnsignedCode(code, is_signed), std::move(meta), operands...));
|
||||
}
|
||||
|
||||
static OperationCode SignedToUnsignedCode(OperationCode operation_code, bool is_signed);
|
||||
|
||||
const ProgramCode& program_code;
|
||||
const u32 main_offset;
|
||||
|
||||
@@ -868,8 +321,6 @@ private:
|
||||
std::map<u32, NodeBlock> basic_blocks;
|
||||
NodeBlock global_code;
|
||||
|
||||
std::vector<std::unique_ptr<NodeData>> stored_nodes;
|
||||
|
||||
std::set<u32> used_registers;
|
||||
std::set<Tegra::Shader::Pred> used_predicates;
|
||||
std::set<Tegra::Shader::Attribute::Index> used_input_attributes;
|
||||
|
||||
@@ -16,12 +16,12 @@ std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor,
|
||||
OperationCode operation_code) {
|
||||
for (; cursor >= 0; --cursor) {
|
||||
const Node node = code.at(cursor);
|
||||
if (const auto operation = std::get_if<OperationNode>(node)) {
|
||||
if (const auto operation = std::get_if<OperationNode>(&*node)) {
|
||||
if (operation->GetCode() == operation_code) {
|
||||
return {node, cursor};
|
||||
}
|
||||
}
|
||||
if (const auto conditional = std::get_if<ConditionalNode>(node)) {
|
||||
if (const auto conditional = std::get_if<ConditionalNode>(&*node)) {
|
||||
const auto& conditional_code = conditional->GetCode();
|
||||
const auto [found, internal_cursor] = FindOperation(
|
||||
conditional_code, static_cast<s64>(conditional_code.size() - 1), operation_code);
|
||||
@@ -35,11 +35,11 @@ std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor,
|
||||
} // namespace
|
||||
|
||||
Node ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const {
|
||||
if (const auto cbuf = std::get_if<CbufNode>(tracked)) {
|
||||
if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) {
|
||||
// Cbuf found, but it has to be immediate
|
||||
return std::holds_alternative<ImmediateNode>(*cbuf->GetOffset()) ? tracked : nullptr;
|
||||
}
|
||||
if (const auto gpr = std::get_if<GprNode>(tracked)) {
|
||||
if (const auto gpr = std::get_if<GprNode>(&*tracked)) {
|
||||
if (gpr->GetIndex() == Tegra::Shader::Register::ZeroIndex) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -51,7 +51,7 @@ Node ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const
|
||||
}
|
||||
return TrackCbuf(source, code, new_cursor);
|
||||
}
|
||||
if (const auto operation = std::get_if<OperationNode>(tracked)) {
|
||||
if (const auto operation = std::get_if<OperationNode>(&*tracked)) {
|
||||
for (std::size_t i = 0; i < operation->GetOperandsCount(); ++i) {
|
||||
if (const auto found = TrackCbuf((*operation)[i], code, cursor)) {
|
||||
// Cbuf found in operand
|
||||
@@ -60,7 +60,7 @@ Node ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
if (const auto conditional = std::get_if<ConditionalNode>(tracked)) {
|
||||
if (const auto conditional = std::get_if<ConditionalNode>(&*tracked)) {
|
||||
const auto& conditional_code = conditional->GetCode();
|
||||
return TrackCbuf(tracked, conditional_code, static_cast<s64>(conditional_code.size()));
|
||||
}
|
||||
@@ -75,7 +75,7 @@ std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code,
|
||||
if (!found) {
|
||||
return {};
|
||||
}
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(found)) {
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(&*found)) {
|
||||
return immediate->GetValue();
|
||||
}
|
||||
return {};
|
||||
@@ -88,11 +88,11 @@ std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeB
|
||||
if (!found_node) {
|
||||
return {};
|
||||
}
|
||||
const auto operation = std::get_if<OperationNode>(found_node);
|
||||
const auto operation = std::get_if<OperationNode>(&*found_node);
|
||||
ASSERT(operation);
|
||||
|
||||
const auto& target = (*operation)[0];
|
||||
if (const auto gpr_target = std::get_if<GprNode>(target)) {
|
||||
if (const auto gpr_target = std::get_if<GprNode>(&*target)) {
|
||||
if (gpr_target->GetIndex() == tracked->GetIndex()) {
|
||||
return {(*operation)[1], new_cursor};
|
||||
}
|
||||
|
||||
@@ -159,6 +159,15 @@ target_compile_definitions(yuzu PRIVATE
|
||||
# Disable implicit conversions from/to C strings
|
||||
-DQT_NO_CAST_FROM_ASCII
|
||||
-DQT_NO_CAST_TO_ASCII
|
||||
|
||||
# Disable implicit type narrowing in signal/slot connect() calls.
|
||||
-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT
|
||||
|
||||
# Disable unsafe overloads of QProcess' start() function.
|
||||
-DQT_NO_PROCESS_COMBINED_ARGUMENT_START
|
||||
|
||||
# Disable implicit QString->QUrl conversions to enforce use of proper resolving functions.
|
||||
-DQT_NO_URL_CAST_FROM_STRING
|
||||
)
|
||||
|
||||
if (YUZU_ENABLE_COMPATIBILITY_REPORTING)
|
||||
|
||||
@@ -122,21 +122,15 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
|
||||
QtProfileSelectionDialog::~QtProfileSelectionDialog() = default;
|
||||
|
||||
void QtProfileSelectionDialog::accept() {
|
||||
ok = true;
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void QtProfileSelectionDialog::reject() {
|
||||
ok = false;
|
||||
user_index = 0;
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
bool QtProfileSelectionDialog::GetStatus() const {
|
||||
return ok;
|
||||
}
|
||||
|
||||
u32 QtProfileSelectionDialog::GetIndex() const {
|
||||
int QtProfileSelectionDialog::GetIndex() const {
|
||||
return user_index;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,15 +30,13 @@ public:
|
||||
void accept() override;
|
||||
void reject() override;
|
||||
|
||||
bool GetStatus() const;
|
||||
u32 GetIndex() const;
|
||||
int GetIndex() const;
|
||||
|
||||
private:
|
||||
bool ok = false;
|
||||
u32 user_index = 0;
|
||||
|
||||
void SelectUser(const QModelIndex& index);
|
||||
|
||||
int user_index = 0;
|
||||
|
||||
QVBoxLayout* layout;
|
||||
QTreeView* tree_view;
|
||||
QStandardItemModel* item_model;
|
||||
|
||||
@@ -104,13 +104,11 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog(
|
||||
QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default;
|
||||
|
||||
void QtSoftwareKeyboardDialog::accept() {
|
||||
ok = true;
|
||||
text = line_edit->text().toStdU16String();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void QtSoftwareKeyboardDialog::reject() {
|
||||
ok = false;
|
||||
text.clear();
|
||||
QDialog::reject();
|
||||
}
|
||||
@@ -119,10 +117,6 @@ std::u16string QtSoftwareKeyboardDialog::GetText() const {
|
||||
return text;
|
||||
}
|
||||
|
||||
bool QtSoftwareKeyboardDialog::GetStatus() const {
|
||||
return ok;
|
||||
}
|
||||
|
||||
QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) {
|
||||
connect(this, &QtSoftwareKeyboard::MainWindowGetText, &main_window,
|
||||
&GMainWindow::SoftwareKeyboardGetText, Qt::QueuedConnection);
|
||||
|
||||
@@ -36,10 +36,8 @@ public:
|
||||
void reject() override;
|
||||
|
||||
std::u16string GetText() const;
|
||||
bool GetStatus() const;
|
||||
|
||||
private:
|
||||
bool ok = false;
|
||||
std::u16string text;
|
||||
|
||||
QDialogButtonBox* buttons;
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {}
|
||||
|
||||
EmuThread::~EmuThread() = default;
|
||||
|
||||
void EmuThread::run() {
|
||||
render_window->MakeCurrent();
|
||||
|
||||
@@ -185,7 +187,7 @@ private:
|
||||
bool do_painting;
|
||||
};
|
||||
|
||||
GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
|
||||
GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread)
|
||||
: QWidget(parent), emu_thread(emu_thread) {
|
||||
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
|
||||
.arg(QString::fromUtf8(Common::g_build_name),
|
||||
@@ -194,8 +196,7 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
|
||||
setAttribute(Qt::WA_AcceptTouchEvents);
|
||||
|
||||
InputCommon::Init();
|
||||
connect(this, &GRenderWindow::FirstFrameDisplayed, static_cast<GMainWindow*>(parent),
|
||||
&GMainWindow::OnLoadComplete);
|
||||
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
|
||||
}
|
||||
|
||||
GRenderWindow::~GRenderWindow() {
|
||||
@@ -247,9 +248,9 @@ void GRenderWindow::PollEvents() {}
|
||||
void GRenderWindow::OnFramebufferSizeChanged() {
|
||||
// Screen changes potentially incur a change in screen DPI, hence we should update the
|
||||
// framebuffer size
|
||||
qreal pixelRatio = GetWindowPixelRatio();
|
||||
unsigned width = child->QPaintDevice::width() * pixelRatio;
|
||||
unsigned height = child->QPaintDevice::height() * pixelRatio;
|
||||
const qreal pixel_ratio = GetWindowPixelRatio();
|
||||
const u32 width = child->QPaintDevice::width() * pixel_ratio;
|
||||
const u32 height = child->QPaintDevice::height() * pixel_ratio;
|
||||
UpdateCurrentFramebufferLayout(width, height);
|
||||
}
|
||||
|
||||
@@ -266,7 +267,7 @@ void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) {
|
||||
}
|
||||
|
||||
void GRenderWindow::BackupGeometry() {
|
||||
geometry = ((QWidget*)this)->saveGeometry();
|
||||
geometry = QWidget::saveGeometry();
|
||||
}
|
||||
|
||||
void GRenderWindow::RestoreGeometry() {
|
||||
@@ -283,10 +284,11 @@ void GRenderWindow::restoreGeometry(const QByteArray& geometry) {
|
||||
QByteArray GRenderWindow::saveGeometry() {
|
||||
// If we are a top-level widget, store the current geometry
|
||||
// otherwise, store the last backup
|
||||
if (parent() == nullptr)
|
||||
return ((QWidget*)this)->saveGeometry();
|
||||
else
|
||||
return geometry;
|
||||
if (parent() == nullptr) {
|
||||
return QWidget::saveGeometry();
|
||||
}
|
||||
|
||||
return geometry;
|
||||
}
|
||||
|
||||
qreal GRenderWindow::GetWindowPixelRatio() const {
|
||||
@@ -294,10 +296,10 @@ qreal GRenderWindow::GetWindowPixelRatio() const {
|
||||
return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
|
||||
}
|
||||
|
||||
std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const {
|
||||
std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const {
|
||||
const qreal pixel_ratio = GetWindowPixelRatio();
|
||||
return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
|
||||
static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
|
||||
return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
|
||||
static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
|
||||
}
|
||||
|
||||
void GRenderWindow::closeEvent(QCloseEvent* event) {
|
||||
@@ -353,7 +355,7 @@ void GRenderWindow::focusOutEvent(QFocusEvent* event) {
|
||||
InputCommon::GetKeyboard()->ReleaseAllKeys();
|
||||
}
|
||||
|
||||
void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) {
|
||||
void GRenderWindow::OnClientAreaResized(u32 width, u32 height) {
|
||||
NotifyClientAreaSizeChanged(std::make_pair(width, height));
|
||||
}
|
||||
|
||||
@@ -394,7 +396,7 @@ void GRenderWindow::InitRenderTarget() {
|
||||
context->setShareContext(shared_context.get());
|
||||
context->setFormat(fmt);
|
||||
context->create();
|
||||
fmt.setSwapInterval(false);
|
||||
fmt.setSwapInterval(0);
|
||||
|
||||
child = new GGLWidgetInternal(this, shared_context.get());
|
||||
container = QWidget::createWindowContainer(child, this);
|
||||
@@ -424,23 +426,29 @@ void GRenderWindow::InitRenderTarget() {
|
||||
BackupGeometry();
|
||||
}
|
||||
|
||||
void GRenderWindow::CaptureScreenshot(u16 res_scale, const QString& screenshot_path) {
|
||||
void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {
|
||||
auto& renderer = Core::System::GetInstance().Renderer();
|
||||
|
||||
if (!res_scale)
|
||||
if (res_scale == 0) {
|
||||
res_scale = VideoCore::GetResolutionScaleFactor(renderer);
|
||||
}
|
||||
|
||||
const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)};
|
||||
screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
|
||||
renderer.RequestScreenshot(screenshot_image.bits(),
|
||||
[=] {
|
||||
screenshot_image.mirrored(false, true).save(screenshot_path);
|
||||
LOG_INFO(Frontend, "The screenshot is saved.");
|
||||
},
|
||||
layout);
|
||||
renderer.RequestScreenshot(
|
||||
screenshot_image.bits(),
|
||||
[=] {
|
||||
const std::string std_screenshot_path = screenshot_path.toStdString();
|
||||
if (screenshot_image.mirrored(false, true).save(screenshot_path)) {
|
||||
LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path);
|
||||
} else {
|
||||
LOG_ERROR(Frontend, "Failed to save screenshot to \"{}\"", std_screenshot_path);
|
||||
}
|
||||
},
|
||||
layout);
|
||||
}
|
||||
|
||||
void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) {
|
||||
void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) {
|
||||
setMinimumSize(minimal_size.first, minimal_size.second);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,11 +27,12 @@ namespace VideoCore {
|
||||
enum class LoadCallbackStage;
|
||||
}
|
||||
|
||||
class EmuThread : public QThread {
|
||||
class EmuThread final : public QThread {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit EmuThread(GRenderWindow* render_window);
|
||||
~EmuThread() override;
|
||||
|
||||
/**
|
||||
* Start emulation (on new thread)
|
||||
@@ -114,7 +115,7 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GRenderWindow(QWidget* parent, EmuThread* emu_thread);
|
||||
GRenderWindow(GMainWindow* parent, EmuThread* emu_thread);
|
||||
~GRenderWindow() override;
|
||||
|
||||
// EmuWindow implementation
|
||||
@@ -133,17 +134,17 @@ public:
|
||||
QByteArray saveGeometry(); // overridden
|
||||
|
||||
qreal GetWindowPixelRatio() const;
|
||||
std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const;
|
||||
std::pair<u32, u32> ScaleTouch(QPointF pos) const;
|
||||
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
bool event(QEvent* event) override;
|
||||
void focusOutEvent(QFocusEvent* event) override;
|
||||
|
||||
void OnClientAreaResized(unsigned width, unsigned height);
|
||||
void OnClientAreaResized(u32 width, u32 height);
|
||||
|
||||
void InitRenderTarget();
|
||||
|
||||
void CaptureScreenshot(u16 res_scale, const QString& screenshot_path);
|
||||
void CaptureScreenshot(u32 res_scale, const QString& screenshot_path);
|
||||
|
||||
public slots:
|
||||
void moveContext(); // overridden
|
||||
@@ -162,7 +163,7 @@ private:
|
||||
void TouchUpdateEvent(const QTouchEvent* event);
|
||||
void TouchEndEvent();
|
||||
|
||||
void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override;
|
||||
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
|
||||
|
||||
QWidget* container = nullptr;
|
||||
GGLWidgetInternal* child = nullptr;
|
||||
|
||||
@@ -22,11 +22,11 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
|
||||
}
|
||||
|
||||
connect(ui->volume_slider, &QSlider::valueChanged, this,
|
||||
&ConfigureAudio::setVolumeIndicatorText);
|
||||
&ConfigureAudio::SetVolumeIndicatorText);
|
||||
|
||||
this->setConfiguration();
|
||||
SetConfiguration();
|
||||
connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureAudio::updateAudioDevices);
|
||||
&ConfigureAudio::UpdateAudioDevices);
|
||||
|
||||
const bool is_powered_on = Core::System::GetInstance().IsPoweredOn();
|
||||
ui->output_sink_combo_box->setEnabled(!is_powered_on);
|
||||
@@ -35,20 +35,20 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
|
||||
|
||||
ConfigureAudio::~ConfigureAudio() = default;
|
||||
|
||||
void ConfigureAudio::setConfiguration() {
|
||||
setOutputSinkFromSinkID();
|
||||
void ConfigureAudio::SetConfiguration() {
|
||||
SetOutputSinkFromSinkID();
|
||||
|
||||
// The device list cannot be pre-populated (nor listed) until the output sink is known.
|
||||
updateAudioDevices(ui->output_sink_combo_box->currentIndex());
|
||||
UpdateAudioDevices(ui->output_sink_combo_box->currentIndex());
|
||||
|
||||
setAudioDeviceFromDeviceID();
|
||||
SetAudioDeviceFromDeviceID();
|
||||
|
||||
ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching);
|
||||
ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum());
|
||||
setVolumeIndicatorText(ui->volume_slider->sliderPosition());
|
||||
SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
|
||||
}
|
||||
|
||||
void ConfigureAudio::setOutputSinkFromSinkID() {
|
||||
void ConfigureAudio::SetOutputSinkFromSinkID() {
|
||||
int new_sink_index = 0;
|
||||
|
||||
const QString sink_id = QString::fromStdString(Settings::values.sink_id);
|
||||
@@ -62,7 +62,7 @@ void ConfigureAudio::setOutputSinkFromSinkID() {
|
||||
ui->output_sink_combo_box->setCurrentIndex(new_sink_index);
|
||||
}
|
||||
|
||||
void ConfigureAudio::setAudioDeviceFromDeviceID() {
|
||||
void ConfigureAudio::SetAudioDeviceFromDeviceID() {
|
||||
int new_device_index = -1;
|
||||
|
||||
const QString device_id = QString::fromStdString(Settings::values.audio_device_id);
|
||||
@@ -76,11 +76,11 @@ void ConfigureAudio::setAudioDeviceFromDeviceID() {
|
||||
ui->audio_device_combo_box->setCurrentIndex(new_device_index);
|
||||
}
|
||||
|
||||
void ConfigureAudio::setVolumeIndicatorText(int percentage) {
|
||||
void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
|
||||
ui->volume_indicator->setText(tr("%1%", "Volume percentage (e.g. 50%)").arg(percentage));
|
||||
}
|
||||
|
||||
void ConfigureAudio::applyConfiguration() {
|
||||
void ConfigureAudio::ApplyConfiguration() {
|
||||
Settings::values.sink_id =
|
||||
ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
|
||||
.toStdString();
|
||||
@@ -92,7 +92,7 @@ void ConfigureAudio::applyConfiguration() {
|
||||
static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum();
|
||||
}
|
||||
|
||||
void ConfigureAudio::updateAudioDevices(int sink_index) {
|
||||
void ConfigureAudio::UpdateAudioDevices(int sink_index) {
|
||||
ui->audio_device_combo_box->clear();
|
||||
ui->audio_device_combo_box->addItem(QString::fromUtf8(AudioCore::auto_device_name));
|
||||
|
||||
@@ -102,6 +102,6 @@ void ConfigureAudio::updateAudioDevices(int sink_index) {
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureAudio::retranslateUi() {
|
||||
void ConfigureAudio::RetranslateUI() {
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
|
||||
@@ -18,16 +18,16 @@ public:
|
||||
explicit ConfigureAudio(QWidget* parent = nullptr);
|
||||
~ConfigureAudio() override;
|
||||
|
||||
void applyConfiguration();
|
||||
void retranslateUi();
|
||||
void ApplyConfiguration();
|
||||
void RetranslateUI();
|
||||
|
||||
private:
|
||||
void updateAudioDevices(int sink_index);
|
||||
void UpdateAudioDevices(int sink_index);
|
||||
|
||||
void setConfiguration();
|
||||
void setOutputSinkFromSinkID();
|
||||
void setAudioDeviceFromDeviceID();
|
||||
void setVolumeIndicatorText(int percentage);
|
||||
void SetConfiguration();
|
||||
void SetOutputSinkFromSinkID();
|
||||
void SetAudioDeviceFromDeviceID();
|
||||
void SetVolumeIndicatorText(int percentage);
|
||||
|
||||
std::unique_ptr<Ui::ConfigureAudio> ui;
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<widget class="QLabel" name="label_1">
|
||||
<property name="text">
|
||||
<string>Output Engine:</string>
|
||||
</property>
|
||||
@@ -44,7 +44,7 @@
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Audio Device:</string>
|
||||
</property>
|
||||
@@ -61,7 +61,7 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Volume:</string>
|
||||
</property>
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
|
||||
ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureDebug) {
|
||||
ui->setupUi(this);
|
||||
this->setConfiguration();
|
||||
SetConfiguration();
|
||||
|
||||
connect(ui->open_log_button, &QPushButton::pressed, []() {
|
||||
QString path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LogDir));
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
@@ -25,7 +26,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::Co
|
||||
|
||||
ConfigureDebug::~ConfigureDebug() = default;
|
||||
|
||||
void ConfigureDebug::setConfiguration() {
|
||||
void ConfigureDebug::SetConfiguration() {
|
||||
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub);
|
||||
ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub);
|
||||
ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port);
|
||||
@@ -37,7 +38,7 @@ void ConfigureDebug::setConfiguration() {
|
||||
ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso);
|
||||
}
|
||||
|
||||
void ConfigureDebug::applyConfiguration() {
|
||||
void ConfigureDebug::ApplyConfiguration() {
|
||||
Settings::values.use_gdbstub = ui->toggle_gdbstub->isChecked();
|
||||
Settings::values.gdbstub_port = ui->gdbport_spinbox->value();
|
||||
UISettings::values.show_console = ui->toggle_console->isChecked();
|
||||
|
||||
@@ -18,10 +18,10 @@ public:
|
||||
explicit ConfigureDebug(QWidget* parent = nullptr);
|
||||
~ConfigureDebug() override;
|
||||
|
||||
void applyConfiguration();
|
||||
void ApplyConfiguration();
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
void SetConfiguration();
|
||||
|
||||
std::unique_ptr<Ui::ConfigureDebug> ui;
|
||||
};
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<widget class="QLabel" name="label_1">
|
||||
<property name="text">
|
||||
<string>Port:</string>
|
||||
</property>
|
||||
@@ -70,11 +70,11 @@
|
||||
<property name="title">
|
||||
<string>Logging</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Global Log Filter</string>
|
||||
</property>
|
||||
@@ -86,7 +86,7 @@
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_console">
|
||||
<property name="text">
|
||||
@@ -111,11 +111,11 @@
|
||||
<property name="title">
|
||||
<string>Homebrew</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Arguments String</string>
|
||||
</property>
|
||||
@@ -134,7 +134,7 @@
|
||||
<property name="title">
|
||||
<string>Dump</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="dump_decompressed_nso">
|
||||
<property name="whatsThis">
|
||||
|
||||
@@ -15,11 +15,11 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
|
||||
: QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) {
|
||||
ui->setupUi(this);
|
||||
ui->hotkeysTab->Populate(registry);
|
||||
this->setConfiguration();
|
||||
this->PopulateSelectionList();
|
||||
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
SetConfiguration();
|
||||
PopulateSelectionList();
|
||||
|
||||
connect(ui->selectorList, &QListWidget::itemSelectionChanged, this,
|
||||
&ConfigureDialog::UpdateVisibleTabs);
|
||||
|
||||
@@ -29,19 +29,19 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
|
||||
|
||||
ConfigureDialog::~ConfigureDialog() = default;
|
||||
|
||||
void ConfigureDialog::setConfiguration() {}
|
||||
void ConfigureDialog::SetConfiguration() {}
|
||||
|
||||
void ConfigureDialog::applyConfiguration() {
|
||||
ui->generalTab->applyConfiguration();
|
||||
ui->gameListTab->applyConfiguration();
|
||||
ui->systemTab->applyConfiguration();
|
||||
ui->profileManagerTab->applyConfiguration();
|
||||
ui->inputTab->applyConfiguration();
|
||||
ui->hotkeysTab->applyConfiguration(registry);
|
||||
ui->graphicsTab->applyConfiguration();
|
||||
ui->audioTab->applyConfiguration();
|
||||
ui->debugTab->applyConfiguration();
|
||||
ui->webTab->applyConfiguration();
|
||||
void ConfigureDialog::ApplyConfiguration() {
|
||||
ui->generalTab->ApplyConfiguration();
|
||||
ui->gameListTab->ApplyConfiguration();
|
||||
ui->systemTab->ApplyConfiguration();
|
||||
ui->profileManagerTab->ApplyConfiguration();
|
||||
ui->inputTab->ApplyConfiguration();
|
||||
ui->hotkeysTab->ApplyConfiguration(registry);
|
||||
ui->graphicsTab->ApplyConfiguration();
|
||||
ui->audioTab->ApplyConfiguration();
|
||||
ui->debugTab->ApplyConfiguration();
|
||||
ui->webTab->ApplyConfiguration();
|
||||
Settings::Apply();
|
||||
Settings::LogSettings();
|
||||
}
|
||||
|
||||
@@ -20,10 +20,10 @@ public:
|
||||
explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry);
|
||||
~ConfigureDialog() override;
|
||||
|
||||
void applyConfiguration();
|
||||
void ApplyConfiguration();
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
void SetConfiguration();
|
||||
void UpdateVisibleTabs();
|
||||
void PopulateSelectionList();
|
||||
|
||||
|
||||
@@ -12,20 +12,20 @@
|
||||
#include "yuzu/ui_settings.h"
|
||||
|
||||
namespace {
|
||||
constexpr std::array<std::pair<u32, const char*>, 5> default_icon_sizes{{
|
||||
constexpr std::array default_icon_sizes{
|
||||
std::make_pair(0, QT_TR_NOOP("None")),
|
||||
std::make_pair(32, QT_TR_NOOP("Small (32x32)")),
|
||||
std::make_pair(64, QT_TR_NOOP("Standard (64x64)")),
|
||||
std::make_pair(128, QT_TR_NOOP("Large (128x128)")),
|
||||
std::make_pair(256, QT_TR_NOOP("Full Size (256x256)")),
|
||||
}};
|
||||
};
|
||||
|
||||
constexpr std::array<const char*, 4> row_text_names{{
|
||||
constexpr std::array row_text_names{
|
||||
QT_TR_NOOP("Filename"),
|
||||
QT_TR_NOOP("Filetype"),
|
||||
QT_TR_NOOP("Title ID"),
|
||||
QT_TR_NOOP("Title Name"),
|
||||
}};
|
||||
};
|
||||
} // Anonymous namespace
|
||||
|
||||
ConfigureGameList::ConfigureGameList(QWidget* parent)
|
||||
@@ -35,7 +35,7 @@ ConfigureGameList::ConfigureGameList(QWidget* parent)
|
||||
InitializeIconSizeComboBox();
|
||||
InitializeRowComboBoxes();
|
||||
|
||||
this->setConfiguration();
|
||||
SetConfiguration();
|
||||
|
||||
// Force game list reload if any of the relevant settings are changed.
|
||||
connect(ui->show_unknown, &QCheckBox::stateChanged, this,
|
||||
@@ -50,7 +50,7 @@ ConfigureGameList::ConfigureGameList(QWidget* parent)
|
||||
|
||||
ConfigureGameList::~ConfigureGameList() = default;
|
||||
|
||||
void ConfigureGameList::applyConfiguration() {
|
||||
void ConfigureGameList::ApplyConfiguration() {
|
||||
UISettings::values.show_unknown = ui->show_unknown->isChecked();
|
||||
UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
|
||||
UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt();
|
||||
@@ -63,7 +63,7 @@ void ConfigureGameList::RequestGameListUpdate() {
|
||||
UISettings::values.is_game_list_reload_pending.exchange(true);
|
||||
}
|
||||
|
||||
void ConfigureGameList::setConfiguration() {
|
||||
void ConfigureGameList::SetConfiguration() {
|
||||
ui->show_unknown->setChecked(UISettings::values.show_unknown);
|
||||
ui->show_add_ons->setChecked(UISettings::values.show_add_ons);
|
||||
ui->icon_size_combobox->setCurrentIndex(
|
||||
|
||||
@@ -18,12 +18,12 @@ public:
|
||||
explicit ConfigureGameList(QWidget* parent = nullptr);
|
||||
~ConfigureGameList() override;
|
||||
|
||||
void applyConfiguration();
|
||||
void ApplyConfiguration();
|
||||
|
||||
private:
|
||||
void RequestGameListUpdate();
|
||||
|
||||
void setConfiguration();
|
||||
void SetConfiguration();
|
||||
|
||||
void changeEvent(QEvent*) override;
|
||||
void RetranslateUI();
|
||||
|
||||
@@ -18,7 +18,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
||||
QString::fromUtf8(theme.second));
|
||||
}
|
||||
|
||||
this->setConfiguration();
|
||||
SetConfiguration();
|
||||
|
||||
connect(ui->toggle_deepscan, &QCheckBox::stateChanged, this,
|
||||
[] { UISettings::values.is_game_list_reload_pending.exchange(true); });
|
||||
@@ -28,7 +28,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
||||
|
||||
ConfigureGeneral::~ConfigureGeneral() = default;
|
||||
|
||||
void ConfigureGeneral::setConfiguration() {
|
||||
void ConfigureGeneral::SetConfiguration() {
|
||||
ui->toggle_deepscan->setChecked(UISettings::values.game_directory_deepscan);
|
||||
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
|
||||
ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot);
|
||||
@@ -36,7 +36,7 @@ void ConfigureGeneral::setConfiguration() {
|
||||
ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
|
||||
}
|
||||
|
||||
void ConfigureGeneral::applyConfiguration() {
|
||||
void ConfigureGeneral::ApplyConfiguration() {
|
||||
UISettings::values.game_directory_deepscan = ui->toggle_deepscan->isChecked();
|
||||
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
|
||||
UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
|
||||
|
||||
@@ -20,10 +20,10 @@ public:
|
||||
explicit ConfigureGeneral(QWidget* parent = nullptr);
|
||||
~ConfigureGeneral() override;
|
||||
|
||||
void applyConfiguration();
|
||||
void ApplyConfiguration();
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
void SetConfiguration();
|
||||
|
||||
std::unique_ptr<Ui::ConfigureGeneral> ui;
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user