Merge branch 'master' into BGRA8-Framebuffer-Format

This commit is contained in:
greggameplayer
2019-02-13 07:13:41 +01:00
committed by GitHub
200 changed files with 10334 additions and 5781 deletions
+16 -5
View File
@@ -1,16 +1,27 @@
<!--
Please keep in mind yuzu is EXPERIMENTAL SOFTWARE.
Please read the FAQ: https://yuzu-emu.org/wiki/faq/
Please read the FAQ:
https://yuzu-emu.org/wiki/faq/
When submitting an issue, please do the following:
THIS IS NOT A SUPPORT FORUM, FOR SUPPORT GO TO:
https://community.citra-emu.org/
- Provide the version (commit hash) of yuzu you are using.
- Provide sufficient detail for the issue to be reproduced.
- Provide:
If the FAQ does not answer your question, please go to:
https://community.citra-emu.org/
When submitting an issue, please check the following:
- You have read the above.
- You have provided the version (commit hash) of yuzu you are using.
- You have provided sufficient detail for the issue to be reproduced.
- You have provided system specs (if relevant).
- Please also provide:
- For any issues, a log file
- For crashes, a backtrace.
- For graphical issues, comparison screenshots with real hardware.
- For emulation inaccuracies, a test-case (if able).
-->
+2
View File
@@ -6,6 +6,8 @@ TRAVIS_BRANCH
TRAVIS_BUILD_ID
TRAVIS_BUILD_NUMBER
TRAVIS_COMMIT
TRAVIS_COMMIT_RANGE
TRAVIS_EVENT_TYPE
TRAVIS_JOB_ID
TRAVIS_JOB_NUMBER
TRAVIS_REPO_SLUG
-13
View File
@@ -419,19 +419,6 @@ function(create_target_directory_groups target_name)
endforeach()
endfunction()
# Gets a UTC timstamp and sets the provided variable to it
function(get_timestamp _var)
string(TIMESTAMP timestamp UTC)
set(${_var} "${timestamp}" PARENT_SCOPE)
endfunction()
# generate git/build information
include(GetGitRevisionDescription)
get_git_head_revision(GIT_REF_SPEC GIT_REV)
git_describe(GIT_DESC --always --long --dirty)
git_branch_name(GIT_BRANCH)
get_timestamp(BUILD_DATE)
enable_testing()
add_subdirectory(externals)
add_subdirectory(src)
+4 -1
View File
@@ -45,5 +45,8 @@ function(copy_yuzu_Qt5_deps target_dir)
windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*)
windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS} qjpeg$<$<CONFIG:Debug>:d>.*)
windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS}
qjpeg$<$<CONFIG:Debug>:d>.*
qgif$<$<CONFIG:Debug>:d>.*
)
endfunction(copy_yuzu_Qt5_deps)
+94
View File
@@ -0,0 +1,94 @@
# Gets a UTC timstamp and sets the provided variable to it
function(get_timestamp _var)
string(TIMESTAMP timestamp UTC)
set(${_var} "${timestamp}" PARENT_SCOPE)
endfunction()
list(APPEND CMAKE_MODULE_PATH "${SRC_DIR}/externals/cmake-modules")
# generate git/build information
include(GetGitRevisionDescription)
get_git_head_revision(GIT_REF_SPEC GIT_REV)
git_describe(GIT_DESC --always --long --dirty)
git_branch_name(GIT_BRANCH)
get_timestamp(BUILD_DATE)
# Generate cpp with Git revision from template
# Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well
set(REPO_NAME "")
set(BUILD_VERSION "0")
if (BUILD_REPOSITORY)
# regex capture the string nightly or canary into CMAKE_MATCH_1
string(REGEX MATCH "yuzu-emu/yuzu-?(.*)" OUTVAR ${BUILD_REPOSITORY})
if (${CMAKE_MATCH_COUNT} GREATER 0)
# capitalize the first letter of each word in the repo name.
string(REPLACE "-" ";" REPO_NAME_LIST ${CMAKE_MATCH_1})
foreach(WORD ${REPO_NAME_LIST})
string(SUBSTRING ${WORD} 0 1 FIRST_LETTER)
string(SUBSTRING ${WORD} 1 -1 REMAINDER)
string(TOUPPER ${FIRST_LETTER} FIRST_LETTER)
set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER}")
endforeach()
if (BUILD_TAG)
string(REGEX MATCH "${CMAKE_MATCH_1}-([0-9]+)" OUTVAR ${BUILD_TAG})
if (${CMAKE_MATCH_COUNT} GREATER 0)
set(BUILD_VERSION ${CMAKE_MATCH_1})
endif()
if (BUILD_VERSION)
# This leaves a trailing space on the last word, but we actually want that
# because of how it's styled in the title bar.
set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ")
else()
set(BUILD_FULLNAME "")
endif()
endif()
endif()
endif()
# The variable SRC_DIR must be passed into the script (since it uses the current build directory for all values of CMAKE_*_DIR)
set(VIDEO_CORE "${SRC_DIR}/src/video_core")
set(HASH_FILES
"${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp"
"${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h"
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp"
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.h"
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp"
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h"
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.cpp"
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h"
"${VIDEO_CORE}/shader/decode/arithmetic.cpp"
"${VIDEO_CORE}/shader/decode/arithmetic_half.cpp"
"${VIDEO_CORE}/shader/decode/arithmetic_half_immediate.cpp"
"${VIDEO_CORE}/shader/decode/arithmetic_immediate.cpp"
"${VIDEO_CORE}/shader/decode/arithmetic_integer.cpp"
"${VIDEO_CORE}/shader/decode/arithmetic_integer_immediate.cpp"
"${VIDEO_CORE}/shader/decode/bfe.cpp"
"${VIDEO_CORE}/shader/decode/bfi.cpp"
"${VIDEO_CORE}/shader/decode/conversion.cpp"
"${VIDEO_CORE}/shader/decode/ffma.cpp"
"${VIDEO_CORE}/shader/decode/float_set.cpp"
"${VIDEO_CORE}/shader/decode/float_set_predicate.cpp"
"${VIDEO_CORE}/shader/decode/half_set.cpp"
"${VIDEO_CORE}/shader/decode/half_set_predicate.cpp"
"${VIDEO_CORE}/shader/decode/hfma2.cpp"
"${VIDEO_CORE}/shader/decode/integer_set.cpp"
"${VIDEO_CORE}/shader/decode/integer_set_predicate.cpp"
"${VIDEO_CORE}/shader/decode/memory.cpp"
"${VIDEO_CORE}/shader/decode/other.cpp"
"${VIDEO_CORE}/shader/decode/predicate_set_predicate.cpp"
"${VIDEO_CORE}/shader/decode/predicate_set_register.cpp"
"${VIDEO_CORE}/shader/decode/register_set_predicate.cpp"
"${VIDEO_CORE}/shader/decode/shift.cpp"
"${VIDEO_CORE}/shader/decode/video.cpp"
"${VIDEO_CORE}/shader/decode/xmad.cpp"
"${VIDEO_CORE}/shader/decode.cpp"
"${VIDEO_CORE}/shader/shader_ir.cpp"
"${VIDEO_CORE}/shader/shader_ir.h"
"${VIDEO_CORE}/shader/track.cpp"
)
set(COMBINED "")
foreach (F IN LISTS HASH_FILES)
file(READ ${F} TMP)
set(COMBINED "${COMBINED}${TMP}")
endforeach()
string(MD5 SHADER_CACHE_VERSION "${COMBINED}")
configure_file("${SRC_DIR}/src/common/scm_rev.cpp.in" "scm_rev.cpp" @ONLY)
+5 -4
View File
@@ -37,7 +37,7 @@ Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callbac
: sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
sink_stream{sink_stream}, name{std::move(name_)} {
release_event = CoreTiming::RegisterEvent(
release_event = Core::Timing::RegisterEvent(
name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
}
@@ -57,7 +57,7 @@ Stream::State Stream::GetState() const {
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
return Core::Timing::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
}
static void VolumeAdjustSamples(std::vector<s16>& samples) {
@@ -68,7 +68,7 @@ static void VolumeAdjustSamples(std::vector<s16>& samples) {
}
// Implementation of a volume slider with a dynamic range of 60 dB
const float volume_scale_factor{std::exp(6.90775f * volume) * 0.001f};
const float volume_scale_factor = volume == 0 ? 0 : std::exp(6.90775f * volume) * 0.001f;
for (auto& sample : samples) {
sample = static_cast<s16>(sample * volume_scale_factor);
}
@@ -99,7 +99,8 @@ void Stream::PlayNextBuffer() {
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
Core::Timing::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event,
{});
}
void Stream::ReleaseActiveBuffer() {
+11 -11
View File
@@ -13,7 +13,7 @@
#include "audio_core/buffer.h"
#include "common/common_types.h"
namespace CoreTiming {
namespace Core::Timing {
struct EventType;
}
@@ -91,16 +91,16 @@ private:
/// Gets the number of core cycles when the specified buffer will be released
s64 GetBufferReleaseCycles(const Buffer& buffer) const;
u32 sample_rate; ///< Sample rate of the stream
Format format; ///< Format of the stream
ReleaseCallback release_callback; ///< Buffer release callback for the stream
State state{State::Stopped}; ///< Playback state of the stream
CoreTiming::EventType* release_event{}; ///< Core timing release event for the stream
BufferPtr active_buffer; ///< Actively playing buffer in the stream
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
SinkStream& sink_stream; ///< Output sink for the stream
std::string name; ///< Name of the stream, must be unique
u32 sample_rate; ///< Sample rate of the stream
Format format; ///< Format of the stream
ReleaseCallback release_callback; ///< Buffer release callback for the stream
State state{State::Stopped}; ///< Playback state of the stream
Core::Timing::EventType* release_event{}; ///< Core timing release event for the stream
BufferPtr active_buffer; ///< Actively playing buffer in the stream
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
SinkStream& sink_stream; ///< Output sink for the stream
std::string name; ///< Name of the stream, must be unique
};
using StreamPtr = std::shared_ptr<Stream>;
+2 -2
View File
@@ -53,8 +53,8 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
// Place a lower limit of 5% speed. When a game boots up, there will be
// many silence samples. These do not need to be timestretched.
// Place a lower limit of 5% speed. When a game boots up, there will be
// many silence samples. These do not need to be timestretched.
m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
m_sound_touch.setTempo(m_stretch_ratio);
+60 -33
View File
@@ -1,42 +1,69 @@
# Generate cpp with Git revision from template
# Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well
set(REPO_NAME "")
set(BUILD_VERSION "0")
if ($ENV{CI})
if ($ENV{TRAVIS})
# Add a custom command to generate a new shader_cache_version hash when any of the following files change
# NOTE: This is an approximation of what files affect shader generation, its possible something else
# could affect the result, but much more unlikely than the following files. Keeping a list of files
# like this allows for much better caching since it doesn't force the user to recompile binary shaders every update
set(VIDEO_CORE "${CMAKE_SOURCE_DIR}/src/video_core")
if (DEFINED ENV{CI})
if (DEFINED ENV{TRAVIS})
set(BUILD_REPOSITORY $ENV{TRAVIS_REPO_SLUG})
set(BUILD_TAG $ENV{TRAVIS_TAG})
elseif($ENV{APPVEYOR})
elseif(DEFINED ENV{APPVEYOR})
set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME})
set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME})
endif()
# regex capture the string nightly or canary into CMAKE_MATCH_1
string(REGEX MATCH "yuzu-emu/yuzu-?(.*)" OUTVAR ${BUILD_REPOSITORY})
if (${CMAKE_MATCH_COUNT} GREATER 0)
# capitalize the first letter of each word in the repo name.
string(REPLACE "-" ";" REPO_NAME_LIST ${CMAKE_MATCH_1})
foreach(WORD ${REPO_NAME_LIST})
string(SUBSTRING ${WORD} 0 1 FIRST_LETTER)
string(SUBSTRING ${WORD} 1 -1 REMAINDER)
string(TOUPPER ${FIRST_LETTER} FIRST_LETTER)
set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER}")
endforeach()
if (BUILD_TAG)
string(REGEX MATCH "${CMAKE_MATCH_1}-([0-9]+)" OUTVAR ${BUILD_TAG})
if (${CMAKE_MATCH_COUNT} GREATER 0)
set(BUILD_VERSION ${CMAKE_MATCH_1})
endif()
if (BUILD_VERSION)
# This leaves a trailing space on the last word, but we actually want that
# because of how it's styled in the title bar.
set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ")
else()
set(BUILD_FULLNAME "")
endif()
endif()
endif()
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp" @ONLY)
add_custom_command(OUTPUT scm_rev.cpp
COMMAND ${CMAKE_COMMAND}
-DSRC_DIR="${CMAKE_SOURCE_DIR}"
-DBUILD_REPOSITORY="${BUILD_REPOSITORY}"
-DBUILD_TAG="${BUILD_TAG}"
-P "${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake"
DEPENDS
# WARNING! It was too much work to try and make a common location for this list,
# so if you need to change it, please update CMakeModules/GenerateSCMRev.cmake as well
"${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp"
"${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h"
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp"
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.h"
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp"
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h"
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.cpp"
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h"
"${VIDEO_CORE}/shader/decode/arithmetic.cpp"
"${VIDEO_CORE}/shader/decode/arithmetic_half.cpp"
"${VIDEO_CORE}/shader/decode/arithmetic_half_immediate.cpp"
"${VIDEO_CORE}/shader/decode/arithmetic_immediate.cpp"
"${VIDEO_CORE}/shader/decode/arithmetic_integer.cpp"
"${VIDEO_CORE}/shader/decode/arithmetic_integer_immediate.cpp"
"${VIDEO_CORE}/shader/decode/bfe.cpp"
"${VIDEO_CORE}/shader/decode/bfi.cpp"
"${VIDEO_CORE}/shader/decode/conversion.cpp"
"${VIDEO_CORE}/shader/decode/ffma.cpp"
"${VIDEO_CORE}/shader/decode/float_set.cpp"
"${VIDEO_CORE}/shader/decode/float_set_predicate.cpp"
"${VIDEO_CORE}/shader/decode/half_set.cpp"
"${VIDEO_CORE}/shader/decode/half_set_predicate.cpp"
"${VIDEO_CORE}/shader/decode/hfma2.cpp"
"${VIDEO_CORE}/shader/decode/integer_set.cpp"
"${VIDEO_CORE}/shader/decode/integer_set_predicate.cpp"
"${VIDEO_CORE}/shader/decode/memory.cpp"
"${VIDEO_CORE}/shader/decode/other.cpp"
"${VIDEO_CORE}/shader/decode/predicate_set_predicate.cpp"
"${VIDEO_CORE}/shader/decode/predicate_set_register.cpp"
"${VIDEO_CORE}/shader/decode/register_set_predicate.cpp"
"${VIDEO_CORE}/shader/decode/shift.cpp"
"${VIDEO_CORE}/shader/decode/video.cpp"
"${VIDEO_CORE}/shader/decode/xmad.cpp"
"${VIDEO_CORE}/shader/decode.cpp"
"${VIDEO_CORE}/shader/shader_ir.cpp"
"${VIDEO_CORE}/shader/shader_ir.h"
"${VIDEO_CORE}/shader/track.cpp"
# and also check that the scm_rev files haven't changed
"${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in"
"${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.h"
# technically we should regenerate if the git version changed, but its not worth the effort imo
"${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake"
)
add_library(common STATIC
alignment.h
+1
View File
@@ -35,6 +35,7 @@
#define KEYS_DIR "keys"
#define LOAD_DIR "load"
#define DUMP_DIR "dump"
#define SHADER_DIR "shader"
#define LOG_DIR "log"
// Filenames
+1
View File
@@ -710,6 +710,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP);
paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP);
paths.emplace(UserPath::ShaderDir, user_path + SHADER_DIR DIR_SEP);
paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP);
// TODO: Put the logs in a better location for each OS
+1
View File
@@ -31,6 +31,7 @@ enum class UserPath {
SDMCDir,
LoadDir,
DumpDir,
ShaderDir,
SysDataDir,
UserDir,
};
+2
View File
@@ -11,6 +11,7 @@
#define BUILD_DATE "@BUILD_DATE@"
#define BUILD_FULLNAME "@BUILD_FULLNAME@"
#define BUILD_VERSION "@BUILD_VERSION@"
#define SHADER_CACHE_VERSION "@SHADER_CACHE_VERSION@"
namespace Common {
@@ -21,6 +22,7 @@ const char g_build_name[] = BUILD_NAME;
const char g_build_date[] = BUILD_DATE;
const char g_build_fullname[] = BUILD_FULLNAME;
const char g_build_version[] = BUILD_VERSION;
const char g_shader_cache_version[] = SHADER_CACHE_VERSION;
} // namespace
+1
View File
@@ -13,5 +13,6 @@ extern const char g_build_name[];
extern const char g_build_date[];
extern const char g_build_fullname[];
extern const char g_build_version[];
extern const char g_shader_cache_version[];
} // namespace Common
+2 -2
View File
@@ -95,6 +95,8 @@ add_library(core STATIC
frontend/framebuffer_layout.cpp
frontend/framebuffer_layout.h
frontend/input.h
frontend/scope_acquire_window_context.cpp
frontend/scope_acquire_window_context.h
gdbstub/gdbstub.cpp
gdbstub/gdbstub.h
hle/ipc.h
@@ -138,8 +140,6 @@ add_library(core STATIC
hle/kernel/svc_wrap.h
hle/kernel/thread.cpp
hle/kernel/thread.h
hle/kernel/timer.cpp
hle/kernel/timer.h
hle/kernel/vm_manager.cpp
hle/kernel/vm_manager.h
hle/kernel/wait_object.cpp
+3 -3
View File
@@ -112,14 +112,14 @@ public:
// Always execute at least one tick.
amortized_ticks = std::max<u64>(amortized_ticks, 1);
CoreTiming::AddTicks(amortized_ticks);
Timing::AddTicks(amortized_ticks);
num_interpreted_instructions = 0;
}
u64 GetTicksRemaining() override {
return std::max(CoreTiming::GetDowncount(), 0);
return std::max(Timing::GetDowncount(), 0);
}
u64 GetCNTPCT() override {
return CoreTiming::GetTicks();
return Timing::GetTicks();
}
ARM_Dynarmic& parent;
+2 -2
View File
@@ -177,7 +177,7 @@ void ARM_Unicorn::Run() {
if (GDBStub::IsServerEnabled()) {
ExecuteInstructions(std::max(4000000, 0));
} else {
ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0));
ExecuteInstructions(std::max(Timing::GetDowncount(), 0));
}
}
@@ -190,7 +190,7 @@ MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
CoreTiming::AddTicks(num_instructions);
Timing::AddTicks(num_instructions);
if (GDBStub::IsServerEnabled()) {
if (last_bkpt_hit) {
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
+15 -10
View File
@@ -94,7 +94,7 @@ struct System::Impl {
ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
LOG_DEBUG(HW_Memory, "initialized OK");
CoreTiming::Init();
Timing::Init();
kernel.Initialize();
const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
@@ -123,7 +123,7 @@ struct System::Impl {
Service::Init(service_manager, *virtual_filesystem);
GDBStub::Init();
renderer = VideoCore::CreateRenderer(emu_window);
renderer = VideoCore::CreateRenderer(emu_window, system);
if (!renderer->Init()) {
return ResultStatus::ErrorVideoCore;
}
@@ -175,6 +175,7 @@ struct System::Impl {
return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
static_cast<u32>(load_result));
}
status = ResultStatus::Success;
return status;
}
@@ -204,7 +205,7 @@ struct System::Impl {
// Shutdown kernel and core timing
kernel.Shutdown();
CoreTiming::Shutdown();
Timing::Shutdown();
// Close app loader
app_loader.reset();
@@ -231,7 +232,7 @@ struct System::Impl {
}
PerfStatsResults GetAndResetPerfStats() {
return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs());
return perf_stats.GetAndResetStats(Timing::GetGlobalTimeUs());
}
Kernel::KernelCore kernel;
@@ -443,27 +444,31 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
return impl->virtual_filesystem;
}
void System::SetProfileSelector(std::unique_ptr<Core::Frontend::ProfileSelectApplet> applet) {
void System::SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet) {
impl->profile_selector = std::move(applet);
}
const Core::Frontend::ProfileSelectApplet& System::GetProfileSelector() const {
const Frontend::ProfileSelectApplet& System::GetProfileSelector() const {
return *impl->profile_selector;
}
void System::SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet) {
void System::SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet) {
impl->software_keyboard = std::move(applet);
}
const Core::Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
const Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
return *impl->software_keyboard;
}
void System::SetWebBrowser(std::unique_ptr<Core::Frontend::WebBrowserApplet> applet) {
void System::SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet) {
impl->web_browser = std::move(applet);
}
const Core::Frontend::WebBrowserApplet& System::GetWebBrowser() const {
Frontend::WebBrowserApplet& System::GetWebBrowser() {
return *impl->web_browser;
}
const Frontend::WebBrowserApplet& System::GetWebBrowser() const {
return *impl->web_browser;
}
+7 -6
View File
@@ -243,17 +243,18 @@ public:
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
void SetProfileSelector(std::unique_ptr<Core::Frontend::ProfileSelectApplet> applet);
void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet);
const Core::Frontend::ProfileSelectApplet& GetProfileSelector() const;
const Frontend::ProfileSelectApplet& GetProfileSelector() const;
void SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet);
void SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet);
const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
const Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
void SetWebBrowser(std::unique_ptr<Core::Frontend::WebBrowserApplet> applet);
void SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet);
const Core::Frontend::WebBrowserApplet& GetWebBrowser() const;
Frontend::WebBrowserApplet& GetWebBrowser();
const Frontend::WebBrowserApplet& GetWebBrowser() const;
private:
System();
+3 -3
View File
@@ -93,14 +93,14 @@ void Cpu::RunLoop(bool tight_loop) {
if (IsMainCore()) {
// TODO(Subv): Only let CoreTiming idle if all 4 cores are idling.
CoreTiming::Idle();
CoreTiming::Advance();
Timing::Idle();
Timing::Advance();
}
PrepareReschedule();
} else {
if (IsMainCore()) {
CoreTiming::Advance();
Timing::Advance();
}
if (tight_loop) {
+2 -2
View File
@@ -15,7 +15,7 @@
#include "common/threadsafe_queue.h"
#include "core/core_timing_util.h"
namespace CoreTiming {
namespace Core::Timing {
static s64 global_timer;
static int slice_length;
@@ -242,4 +242,4 @@ int GetDowncount() {
return downcount;
}
} // namespace CoreTiming
} // namespace Core::Timing
+2 -2
View File
@@ -22,7 +22,7 @@
#include <string>
#include "common/common_types.h"
namespace CoreTiming {
namespace Core::Timing {
struct EventType;
@@ -92,4 +92,4 @@ std::chrono::microseconds GetGlobalTimeUs();
int GetDowncount();
} // namespace CoreTiming
} // namespace Core::Timing
+2 -2
View File
@@ -8,7 +8,7 @@
#include <limits>
#include "common/logging/log.h"
namespace CoreTiming {
namespace Core::Timing {
constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / BASE_CLOCK_RATE;
@@ -60,4 +60,4 @@ s64 nsToCycles(u64 ns) {
return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000;
}
} // namespace CoreTiming
} // namespace Core::Timing
+2 -2
View File
@@ -6,7 +6,7 @@
#include "common/common_types.h"
namespace CoreTiming {
namespace Core::Timing {
// The below clock rate is based on Switch's clockspeed being widely known as 1.020GHz
// The exact value used is of course unverified.
@@ -61,4 +61,4 @@ inline u64 cyclesToMs(s64 cycles) {
return cycles * 1000 / BASE_CLOCK_RATE;
}
} // namespace CoreTiming
} // namespace Core::Timing
+6
View File
@@ -359,6 +359,8 @@ bool NCA::ReadPFS0Section(const NCASectionHeader& section, const NCASectionTable
dirs.push_back(std::move(npfs));
if (IsDirectoryExeFS(dirs.back()))
exefs = dirs.back();
else if (IsDirectoryLogoPartition(dirs.back()))
logo = dirs.back();
} else {
if (has_rights_id)
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
@@ -546,4 +548,8 @@ u64 NCA::GetBaseIVFCOffset() const {
return ivfc_offset;
}
VirtualDir NCA::GetLogoPartition() const {
return logo;
}
} // namespace FileSys
+10
View File
@@ -74,6 +74,13 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
}
inline bool IsDirectoryLogoPartition(const VirtualDir& pfs) {
// NintendoLogo is the static image in the top left corner while StartupMovie is the animation
// in the bottom right corner.
return pfs->GetFile("NintendoLogo.png") != nullptr &&
pfs->GetFile("StartupMovie.gif") != nullptr;
}
// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
// After construction, use GetStatus to determine if the file is valid and ready to be used.
class NCA : public ReadOnlyVfsDirectory {
@@ -102,6 +109,8 @@ public:
// Returns the base ivfc offset used in BKTR patching.
u64 GetBaseIVFCOffset() const;
VirtualDir GetLogoPartition() const;
private:
bool CheckSupportedNCA(const NCAHeader& header);
bool HandlePotentialHeaderDecryption();
@@ -122,6 +131,7 @@ private:
VirtualFile romfs = nullptr;
VirtualDir exefs = nullptr;
VirtualDir logo = nullptr;
VirtualFile file;
VirtualFile bktr_base_romfs;
u64 ivfc_offset = 0;
-23
View File
@@ -39,27 +39,4 @@ static_assert(sizeof(Entry) == 0x310, "Directory Entry struct isn't exactly 0x31
static_assert(offsetof(Entry, type) == 0x304, "Wrong offset for type in Entry.");
static_assert(offsetof(Entry, file_size) == 0x308, "Wrong offset for file_size in Entry.");
class DirectoryBackend : NonCopyable {
public:
DirectoryBackend() {}
virtual ~DirectoryBackend() {}
/**
* List files contained in the directory
* @param count Number of entries to return at once in entries
* @param entries Buffer to read data into
* @return Number of entries listed
*/
virtual u64 Read(const u64 count, Entry* entries) = 0;
/// Returns the number of entries still left to read.
virtual u64 GetEntryCount() const = 0;
/**
* Close the directory
* @return true if the directory closed correctly
*/
virtual bool Close() const = 0;
};
} // namespace FileSys
+1 -1
View File
@@ -13,7 +13,7 @@ DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default;
void DefaultWebBrowserApplet::OpenPage(std::string_view filename,
std::function<void()> unpack_romfs_callback,
std::function<void()> finished_callback) const {
std::function<void()> finished_callback) {
LOG_INFO(Service_AM,
"(STUBBED) called - No suitable web browser implementation found to open website page "
"at '{}'!",
+2 -2
View File
@@ -14,7 +14,7 @@ public:
virtual ~WebBrowserApplet();
virtual void OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback,
std::function<void()> finished_callback) const = 0;
std::function<void()> finished_callback) = 0;
};
class DefaultWebBrowserApplet final : public WebBrowserApplet {
@@ -22,7 +22,7 @@ public:
~DefaultWebBrowserApplet() override;
void OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback,
std::function<void()> finished_callback) const override;
std::function<void()> finished_callback) override;
};
} // namespace Core::Frontend
@@ -0,0 +1,18 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/frontend/emu_window.h"
#include "core/frontend/scope_acquire_window_context.h"
namespace Core::Frontend {
ScopeAcquireWindowContext::ScopeAcquireWindowContext(Core::Frontend::EmuWindow& emu_window_)
: emu_window{emu_window_} {
emu_window.MakeCurrent();
}
ScopeAcquireWindowContext::~ScopeAcquireWindowContext() {
emu_window.DoneCurrent();
}
} // namespace Core::Frontend
@@ -0,0 +1,23 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace Core::Frontend {
class EmuWindow;
/// Helper class to acquire/release window context within a given scope
class ScopeAcquireWindowContext : NonCopyable {
public:
explicit ScopeAcquireWindowContext(Core::Frontend::EmuWindow& window);
~ScopeAcquireWindowContext();
private:
Core::Frontend::EmuWindow& emu_window;
};
} // namespace Core::Frontend
+10 -4
View File
@@ -507,8 +507,11 @@ static void RemoveBreakpoint(BreakpointType type, VAddr addr) {
LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}",
bp->second.len, bp->second.addr, static_cast<int>(type));
Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size());
Core::System::GetInstance().InvalidateCpuInstructionCaches();
if (type == BreakpointType::Execute) {
Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size());
Core::System::GetInstance().InvalidateCpuInstructionCaches();
}
p.erase(addr);
}
@@ -1057,9 +1060,12 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
breakpoint.addr = addr;
breakpoint.len = len;
Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
static constexpr std::array<u8, 4> btrap{0x00, 0x7d, 0x20, 0xd4};
Memory::WriteBlock(addr, btrap.data(), btrap.size());
Core::System::GetInstance().InvalidateCpuInstructionCaches();
if (type == BreakpointType::Execute) {
Memory::WriteBlock(addr, btrap.data(), btrap.size());
Core::System::GetInstance().InvalidateCpuInstructionCaches();
}
p.insert({addr, breakpoint});
LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}",
+21
View File
@@ -216,6 +216,11 @@ private:
/// Push ///
template <>
inline void ResponseBuilder::Push(s32 value) {
cmdbuf[index++] = static_cast<u32>(value);
}
template <>
inline void ResponseBuilder::Push(u32 value) {
cmdbuf[index++] = value;
@@ -234,6 +239,22 @@ inline void ResponseBuilder::Push(ResultCode value) {
Push<u32>(0);
}
template <>
inline void ResponseBuilder::Push(s8 value) {
PushRaw(value);
}
template <>
inline void ResponseBuilder::Push(s16 value) {
PushRaw(value);
}
template <>
inline void ResponseBuilder::Push(s64 value) {
Push(static_cast<u32>(value));
Push(static_cast<u32>(value >> 32));
}
template <>
inline void ResponseBuilder::Push(u8 value) {
PushRaw(value);
+3 -45
View File
@@ -18,7 +18,6 @@
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
@@ -86,27 +85,12 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
}
}
/// The timer callback event, called when a timer is fired
static void TimerCallback(u64 timer_handle, int cycles_late) {
const auto proper_handle = static_cast<Handle>(timer_handle);
const auto& system = Core::System::GetInstance();
SharedPtr<Timer> timer = system.Kernel().RetrieveTimerFromCallbackHandleTable(proper_handle);
if (timer == nullptr) {
LOG_CRITICAL(Kernel, "Callback fired for invalid timer {:016X}", timer_handle);
return;
}
timer->Signal(cycles_late);
}
struct KernelCore::Impl {
void Initialize(KernelCore& kernel) {
Shutdown();
InitializeSystemResourceLimit(kernel);
InitializeThreads();
InitializeTimers();
}
void Shutdown() {
@@ -122,9 +106,6 @@ struct KernelCore::Impl {
thread_wakeup_callback_handle_table.Clear();
thread_wakeup_event_type = nullptr;
timer_callback_handle_table.Clear();
timer_callback_event_type = nullptr;
named_ports.clear();
}
@@ -143,12 +124,7 @@ struct KernelCore::Impl {
void InitializeThreads() {
thread_wakeup_event_type =
CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
}
void InitializeTimers() {
timer_callback_handle_table.Clear();
timer_callback_event_type = CoreTiming::RegisterEvent("TimerCallback", TimerCallback);
Core::Timing::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
}
std::atomic<u32> next_object_id{0};
@@ -161,13 +137,7 @@ struct KernelCore::Impl {
SharedPtr<ResourceLimit> system_resource_limit;
/// The event type of the generic timer callback event
CoreTiming::EventType* timer_callback_event_type = nullptr;
// TODO(yuriks): This can be removed if Timer objects are explicitly pooled in the future,
// allowing us to simply use a pool index or similar.
Kernel::HandleTable timer_callback_handle_table;
CoreTiming::EventType* thread_wakeup_event_type = nullptr;
Core::Timing::EventType* thread_wakeup_event_type = nullptr;
// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future,
// allowing us to simply use a pool index or similar.
Kernel::HandleTable thread_wakeup_callback_handle_table;
@@ -198,10 +168,6 @@ SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle
return impl->thread_wakeup_callback_handle_table.Get<Thread>(handle);
}
SharedPtr<Timer> KernelCore::RetrieveTimerFromCallbackHandleTable(Handle handle) const {
return impl->timer_callback_handle_table.Get<Timer>(handle);
}
void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
impl->process_list.push_back(std::move(process));
}
@@ -247,18 +213,10 @@ u64 KernelCore::CreateNewProcessID() {
return impl->next_process_id++;
}
ResultVal<Handle> KernelCore::CreateTimerCallbackHandle(const SharedPtr<Timer>& timer) {
return impl->timer_callback_handle_table.Create(timer);
}
CoreTiming::EventType* KernelCore::ThreadWakeupCallbackEventType() const {
Core::Timing::EventType* KernelCore::ThreadWakeupCallbackEventType() const {
return impl->thread_wakeup_event_type;
}
CoreTiming::EventType* KernelCore::TimerCallbackEventType() const {
return impl->timer_callback_event_type;
}
Kernel::HandleTable& KernelCore::ThreadWakeupCallbackHandleTable() {
return impl->thread_wakeup_callback_handle_table;
}
+2 -13
View File
@@ -11,7 +11,7 @@
template <typename T>
class ResultVal;
namespace CoreTiming {
namespace Core::Timing {
struct EventType;
}
@@ -22,7 +22,6 @@ class HandleTable;
class Process;
class ResourceLimit;
class Thread;
class Timer;
/// Represents a single instance of the kernel.
class KernelCore {
@@ -51,9 +50,6 @@ public:
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
SharedPtr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const;
/// Retrieves a shared pointer to a Timer instance within the timer callback handle table.
SharedPtr<Timer> RetrieveTimerFromCallbackHandleTable(Handle handle) const;
/// Adds the given shared pointer to an internal list of active processes.
void AppendNewProcess(SharedPtr<Process> process);
@@ -82,7 +78,6 @@ private:
friend class Object;
friend class Process;
friend class Thread;
friend class Timer;
/// Creates a new object ID, incrementing the internal object ID counter.
u32 CreateNewObjectID();
@@ -93,14 +88,8 @@ private:
/// Creates a new thread ID, incrementing the internal thread ID counter.
u64 CreateNewThreadID();
/// Creates a timer callback handle for the given timer.
ResultVal<Handle> CreateTimerCallbackHandle(const SharedPtr<Timer>& timer);
/// Retrieves the event type used for thread wakeup callbacks.
CoreTiming::EventType* ThreadWakeupCallbackEventType() const;
/// Retrieves the event type used for timer callbacks.
CoreTiming::EventType* TimerCallbackEventType() const;
Core::Timing::EventType* ThreadWakeupCallbackEventType() const;
/// Provides a reference to the thread wakeup callback handle table.
Kernel::HandleTable& ThreadWakeupCallbackHandleTable();
-1
View File
@@ -16,7 +16,6 @@ bool Object::IsWaitable() const {
case HandleType::ReadableEvent:
case HandleType::Thread:
case HandleType::Process:
case HandleType::Timer:
case HandleType::ServerPort:
case HandleType::ServerSession:
return true;
-1
View File
@@ -25,7 +25,6 @@ enum class HandleType : u32 {
Thread,
Process,
AddressArbiter,
Timer,
ResourceLimit,
ClientPort,
ServerPort,
-4
View File
@@ -44,8 +44,4 @@ ResultCode ReadableEvent::Reset() {
return RESULT_SUCCESS;
}
void ReadableEvent::WakeupAllWaitingThreads() {
WaitObject::WakeupAllWaitingThreads();
}
} // namespace Kernel
-2
View File
@@ -39,8 +39,6 @@ public:
bool ShouldWait(Thread* thread) const override;
void Acquire(Thread* thread) override;
void WakeupAllWaitingThreads() override;
/// Unconditionally clears the readable event's state.
void Clear();
+1 -1
View File
@@ -111,7 +111,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
const u64 prev_switch_ticks = last_context_switch_time;
const u64 most_recent_switch_ticks = CoreTiming::GetTicks();
const u64 most_recent_switch_ticks = Core::Timing::GetTicks();
const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
if (thread != nullptr) {
+8 -4
View File
@@ -597,6 +597,7 @@ enum class BreakType : u32 {
PostNROLoad = 4,
PreNROUnload = 5,
PostNROUnload = 6,
CppException = 7,
};
struct BreakReason {
@@ -669,6 +670,9 @@ static void Break(u32 reason, u64 info1, u64 info2) {
"Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
info2);
break;
case BreakType::CppException:
LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered.");
break;
default:
LOG_WARNING(
Debug_Emulated,
@@ -923,9 +927,9 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
out_ticks = thread_ticks + (CoreTiming::GetTicks() - prev_ctx_ticks);
out_ticks = thread_ticks + (Core::Timing::GetTicks() - prev_ctx_ticks);
} else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
out_ticks = CoreTiming::GetTicks() - prev_ctx_ticks;
out_ticks = Core::Timing::GetTicks() - prev_ctx_ticks;
}
*result = out_ticks;
@@ -1542,10 +1546,10 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to
static u64 GetSystemTick() {
LOG_TRACE(Kernel_SVC, "called");
const u64 result{CoreTiming::GetTicks()};
const u64 result{Core::Timing::GetTicks()};
// Advance time to defeat dumb games that busy-wait for the frame to end.
CoreTiming::AddTicks(400);
Core::Timing::AddTicks(400);
return result;
}
+7 -6
View File
@@ -43,7 +43,7 @@ Thread::~Thread() = default;
void Thread::Stop() {
// Cancel any outstanding wakeup events for this thread
CoreTiming::UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), callback_handle);
Core::Timing::UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), callback_handle);
kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle);
callback_handle = 0;
@@ -85,12 +85,13 @@ 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.
CoreTiming::ScheduleEventThreadsafe(CoreTiming::nsToCycles(nanoseconds),
kernel.ThreadWakeupCallbackEventType(), callback_handle);
Core::Timing::ScheduleEventThreadsafe(Core::Timing::nsToCycles(nanoseconds),
kernel.ThreadWakeupCallbackEventType(), callback_handle);
}
void Thread::CancelWakeupTimer() {
CoreTiming::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), callback_handle);
Core::Timing::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(),
callback_handle);
}
static std::optional<s32> GetNextProcessorId(u64 mask) {
@@ -197,7 +198,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
thread->stack_top = stack_top;
thread->tpidr_el0 = 0;
thread->nominal_priority = thread->current_priority = priority;
thread->last_running_ticks = CoreTiming::GetTicks();
thread->last_running_ticks = Core::Timing::GetTicks();
thread->processor_id = processor_id;
thread->ideal_core = processor_id;
thread->affinity_mask = 1ULL << processor_id;
@@ -257,7 +258,7 @@ void Thread::SetStatus(ThreadStatus new_status) {
}
if (status == ThreadStatus::Running) {
last_running_ticks = CoreTiming::GetTicks();
last_running_ticks = Core::Timing::GetTicks();
}
status = new_status;
-88
View File
@@ -1,88 +0,0 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h"
namespace Kernel {
Timer::Timer(KernelCore& kernel) : WaitObject{kernel} {}
Timer::~Timer() = default;
SharedPtr<Timer> Timer::Create(KernelCore& kernel, ResetType reset_type, std::string name) {
SharedPtr<Timer> timer(new Timer(kernel));
timer->reset_type = reset_type;
timer->signaled = false;
timer->name = std::move(name);
timer->initial_delay = 0;
timer->interval_delay = 0;
timer->callback_handle = kernel.CreateTimerCallbackHandle(timer).Unwrap();
return timer;
}
bool Timer::ShouldWait(Thread* thread) const {
return !signaled;
}
void Timer::Acquire(Thread* thread) {
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
if (reset_type == ResetType::OneShot)
signaled = false;
}
void Timer::Set(s64 initial, s64 interval) {
// Ensure we get rid of any previous scheduled event
Cancel();
initial_delay = initial;
interval_delay = interval;
if (initial == 0) {
// Immediately invoke the callback
Signal(0);
} else {
CoreTiming::ScheduleEvent(CoreTiming::nsToCycles(initial), kernel.TimerCallbackEventType(),
callback_handle);
}
}
void Timer::Cancel() {
CoreTiming::UnscheduleEvent(kernel.TimerCallbackEventType(), callback_handle);
}
void Timer::Clear() {
signaled = false;
}
void Timer::WakeupAllWaitingThreads() {
WaitObject::WakeupAllWaitingThreads();
}
void Timer::Signal(int cycles_late) {
LOG_TRACE(Kernel, "Timer {} fired", GetObjectId());
signaled = true;
// Resume all waiting threads
WakeupAllWaitingThreads();
if (interval_delay != 0) {
// Reschedule the timer with the interval delay
CoreTiming::ScheduleEvent(CoreTiming::nsToCycles(interval_delay) - cycles_late,
kernel.TimerCallbackEventType(), callback_handle);
}
}
} // namespace Kernel
-90
View File
@@ -1,90 +0,0 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/wait_object.h"
namespace Kernel {
class KernelCore;
class Timer final : public WaitObject {
public:
/**
* Creates a timer
* @param kernel The kernel instance to create the timer callback handle for.
* @param reset_type ResetType describing how to create the timer
* @param name Optional name of timer
* @return The created Timer
*/
static SharedPtr<Timer> Create(KernelCore& kernel, ResetType reset_type,
std::string name = "Unknown");
std::string GetTypeName() const override {
return "Timer";
}
std::string GetName() const override {
return name;
}
static const HandleType HANDLE_TYPE = HandleType::Timer;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
ResetType GetResetType() const {
return reset_type;
}
u64 GetInitialDelay() const {
return initial_delay;
}
u64 GetIntervalDelay() const {
return interval_delay;
}
bool ShouldWait(Thread* thread) const override;
void Acquire(Thread* thread) override;
void WakeupAllWaitingThreads() override;
/**
* Starts the timer, with the specified initial delay and interval.
* @param initial Delay until the timer is first fired
* @param interval Delay until the timer is fired after the first time
*/
void Set(s64 initial, s64 interval);
void Cancel();
void Clear();
/**
* Signals the timer, waking up any waiting threads and rescheduling it
* for the next interval.
* This method should not be called from outside the timer callback handler,
* lest multiple callback events get scheduled.
*/
void Signal(int cycles_late);
private:
explicit Timer(KernelCore& kernel);
~Timer() override;
ResetType reset_type; ///< The ResetType of this timer
u64 initial_delay; ///< The delay until the timer fires for the first time
u64 interval_delay; ///< The delay until the timer fires after the first time
bool signaled; ///< Whether the timer has been signaled or not
std::string name; ///< Name of timer (optional)
/// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
Handle callback_handle;
};
} // namespace Kernel
+3 -3
View File
@@ -33,19 +33,19 @@ public:
* Add a thread to wait on this object
* @param thread Pointer to thread to add
*/
virtual void AddWaitingThread(SharedPtr<Thread> thread);
void AddWaitingThread(SharedPtr<Thread> thread);
/**
* Removes a thread from waiting on this object (e.g. if it was resumed already)
* @param thread Pointer to thread to remove
*/
virtual void RemoveWaitingThread(Thread* thread);
void RemoveWaitingThread(Thread* thread);
/**
* Wake up all threads waiting on this object that can be awoken, in priority order,
* and set the synchronization result and output of the thread.
*/
virtual void WakeupAllWaitingThreads();
void WakeupAllWaitingThreads();
/**
* Wakes up a single thread waiting on this object.
+4 -3
View File
@@ -322,14 +322,15 @@ void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& c
void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
// TODO(Subv): Find out how AM determines the display to use, for now just
// create the layer in the Default display.
u64 display_id = nvflinger->OpenDisplay("Default");
u64 layer_id = nvflinger->CreateLayer(display_id);
const auto display_id = nvflinger->OpenDisplay("Default");
const auto layer_id = nvflinger->CreateLayer(*display_id);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(layer_id);
rb.Push(*layer_id);
}
void ISelfController::SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx) {
+2 -1
View File
@@ -249,7 +249,8 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
{300, nullptr, "OpenOverlayAppletProxy"},
{350, nullptr, "OpenSystemApplicationProxy"},
{400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"},
{401, nullptr, "GetSystemAppletControllerForDebug"},
{410, nullptr, "GetSystemAppletControllerForDebug"},
{1000, nullptr, "GetDebugFunctions"},
};
// clang-format on
@@ -2,9 +2,16 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <cstring>
#include <vector>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/backend.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
@@ -12,7 +19,6 @@
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/vfs_types.h"
#include "core/frontend/applets/web_browser.h"
#include "core/hle/kernel/process.h"
@@ -146,7 +152,7 @@ void WebBrowser::Execute() {
return;
}
const auto& frontend{Core::System::GetInstance().GetWebBrowser()};
auto& frontend{Core::System::GetInstance().GetWebBrowser()};
frontend.OpenPage(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
}
+11 -2
View File
@@ -12,6 +12,7 @@ namespace Service::Audio {
class IAudioIn final : public ServiceFramework<IAudioIn> {
public:
IAudioIn() : ServiceFramework("IAudioIn") {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetAudioInState"},
{1, nullptr, "StartAudioIn"},
@@ -28,16 +29,24 @@ public:
{12, nullptr, "SetAudioInDeviceGain"},
{13, nullptr, "GetAudioInDeviceGain"},
};
// clang-format on
RegisterHandlers(functions);
}
~IAudioIn() = default;
};
AudInU::AudInU() : ServiceFramework("audin:u") {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "ListAudioIns"}, {1, nullptr, "OpenAudioIn"}, {2, nullptr, "Unknown"},
{3, nullptr, "OpenAudioInAuto"}, {4, nullptr, "ListAudioInsAuto"},
{0, nullptr, "ListAudioIns"},
{1, nullptr, "OpenAudioIn"},
{2, nullptr, "Unknown"},
{3, nullptr, "OpenAudioInAuto"},
{4, nullptr, "ListAudioInsAuto"},
};
// clang-format on
RegisterHandlers(functions);
}
+5 -1
View File
@@ -12,6 +12,7 @@ namespace Service::Audio {
class IFinalOutputRecorder final : public ServiceFramework<IFinalOutputRecorder> {
public:
IFinalOutputRecorder() : ServiceFramework("IFinalOutputRecorder") {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetFinalOutputRecorderState"},
{1, nullptr, "StartFinalOutputRecorder"},
@@ -20,10 +21,13 @@ public:
{4, nullptr, "RegisterBufferEvent"},
{5, nullptr, "GetReleasedFinalOutputRecorderBuffer"},
{6, nullptr, "ContainsFinalOutputRecorderBuffer"},
{7, nullptr, "Unknown"},
{7, nullptr, "GetFinalOutputRecorderBufferEndTime"},
{8, nullptr, "AppendFinalOutputRecorderBufferAuto"},
{9, nullptr, "GetReleasedFinalOutputRecorderBufferAuto"},
{10, nullptr, "FlushFinalOutputRecorderBuffers"},
};
// clang-format on
RegisterHandlers(functions);
}
~IFinalOutputRecorder() = default;
+6 -4
View File
@@ -229,14 +229,16 @@ private:
}; // namespace Audio
AudRenU::AudRenU() : ServiceFramework("audren:u") {
// clang-format off
static const FunctionInfo functions[] = {
{0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
{1, &AudRenU::GetAudioRendererWorkBufferSize, "GetAudioRendererWorkBufferSize"},
{2, &AudRenU::GetAudioDevice, "GetAudioDevice"},
{2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"},
{3, nullptr, "OpenAudioRendererAuto"},
{4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo,
"GetAudioDeviceServiceWithRevisionInfo"},
{4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"},
};
// clang-format on
RegisterHandlers(functions);
}
@@ -313,7 +315,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", output_sz);
}
void AudRenU::GetAudioDevice(Kernel::HLERequestContext& ctx) {
void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+1 -1
View File
@@ -20,7 +20,7 @@ public:
private:
void OpenAudioRenderer(Kernel::HLERequestContext& ctx);
void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx);
void GetAudioDevice(Kernel::HLERequestContext& ctx);
void GetAudioDeviceService(Kernel::HLERequestContext& ctx);
void GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx);
enum class AudioFeatures : u32 {
+76 -43
View File
@@ -5,7 +5,6 @@
#include <chrono>
#include <cstring>
#include <memory>
#include <optional>
#include <vector>
#include <opus.h>
@@ -30,48 +29,66 @@ public:
u32 channel_count)
: ServiceFramework("IHardwareOpusDecoderManager"), decoder(std::move(decoder)),
sample_rate(sample_rate), channel_count(channel_count) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
{0, &IHardwareOpusDecoderManager::DecodeInterleavedOld, "DecodeInterleavedOld"},
{1, nullptr, "SetContext"},
{2, nullptr, "DecodeInterleavedForMultiStream"},
{2, nullptr, "DecodeInterleavedForMultiStreamOld"},
{3, nullptr, "SetContextForMultiStream"},
{4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerformance,
"DecodeInterleavedWithPerformance"},
{5, nullptr, "Unknown5"},
{6, nullptr, "Unknown6"},
{7, nullptr, "Unknown7"},
{4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"},
{5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"},
{6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
{7, nullptr, "DecodeInterleavedForMultiStream"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
/// Describes extra behavior that may be asked of the decoding context.
enum class ExtraBehavior {
/// No extra behavior.
None,
/// Resets the decoder context back to a freshly initialized state.
ResetContext,
};
void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Audio, "called");
DecodeInterleavedHelper(ctx, nullptr, ExtraBehavior::None);
}
void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Audio, "called");
u64 performance = 0;
DecodeInterleavedHelper(ctx, &performance, ExtraBehavior::None);
}
void DecodeInterleaved(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Audio, "called");
u32 consumed = 0;
u32 sample_count = 0;
std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16));
if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples)) {
LOG_ERROR(Audio, "Failed to decode opus data");
IPC::ResponseBuilder rb{ctx, 2};
// TODO(ogniK): Use correct error code
rb.Push(ResultCode(-1));
return;
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(consumed);
rb.Push<u32>(sample_count);
ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
IPC::RequestParser rp{ctx};
const auto extra_behavior =
rp.Pop<bool>() ? ExtraBehavior::ResetContext : ExtraBehavior::None;
u64 performance = 0;
DecodeInterleavedHelper(ctx, &performance, extra_behavior);
}
void DecodeInterleavedWithPerformance(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Audio, "called");
void DecodeInterleavedHelper(Kernel::HLERequestContext& ctx, u64* performance,
ExtraBehavior extra_behavior) {
u32 consumed = 0;
u32 sample_count = 0;
u64 performance = 0;
std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16));
if (extra_behavior == ExtraBehavior::ResetContext) {
ResetDecoderContext();
}
if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples,
performance)) {
LOG_ERROR(Audio, "Failed to decode opus data");
@@ -80,25 +97,28 @@ private:
rb.Push(ResultCode(-1));
return;
}
IPC::ResponseBuilder rb{ctx, 6};
const u32 param_size = performance != nullptr ? 6 : 4;
IPC::ResponseBuilder rb{ctx, param_size};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(consumed);
rb.Push<u32>(sample_count);
rb.Push<u64>(performance);
if (performance) {
rb.Push<u64>(*performance);
}
ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
}
bool Decoder_DecodeInterleaved(
u32& consumed, u32& sample_count, const std::vector<u8>& input,
std::vector<opus_int16>& output,
std::optional<std::reference_wrapper<u64>> performance_time = std::nullopt) {
bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input,
std::vector<opus_int16>& output, u64* out_performance_time) {
const auto start_time = std::chrono::high_resolution_clock::now();
std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
const std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
if (sizeof(OpusHeader) > input.size()) {
LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}",
sizeof(OpusHeader), input.size());
return false;
}
OpusHeader hdr{};
std::memcpy(&hdr, input.data(), sizeof(OpusHeader));
if (sizeof(OpusHeader) + static_cast<u32>(hdr.sz) > input.size()) {
@@ -106,8 +126,9 @@ private:
sizeof(OpusHeader) + static_cast<u32>(hdr.sz), input.size());
return false;
}
auto frame = input.data() + sizeof(OpusHeader);
auto decoded_sample_count = opus_packet_get_nb_samples(
const auto frame = input.data() + sizeof(OpusHeader);
const auto decoded_sample_count = opus_packet_get_nb_samples(
frame, static_cast<opus_int32>(input.size() - sizeof(OpusHeader)),
static_cast<opus_int32>(sample_rate));
if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) {
@@ -117,8 +138,9 @@ private:
decoded_sample_count * channel_count * sizeof(u16), raw_output_sz);
return false;
}
const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count));
auto out_sample_count =
const auto out_sample_count =
opus_decode(decoder.get(), frame, hdr.sz, output.data(), frame_size, 0);
if (out_sample_count < 0) {
LOG_ERROR(Audio,
@@ -127,16 +149,24 @@ private:
out_sample_count, frame_size, static_cast<u32>(hdr.sz));
return false;
}
const auto end_time = std::chrono::high_resolution_clock::now() - start_time;
sample_count = out_sample_count;
consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz);
if (performance_time.has_value()) {
performance_time->get() =
if (out_performance_time != nullptr) {
*out_performance_time =
std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count();
}
return true;
}
void ResetDecoderContext() {
ASSERT(decoder != nullptr);
opus_decoder_ctl(decoder.get(), OPUS_RESET_STATE);
}
struct OpusHeader {
u32_be sz; // Needs to be BE for some odd reason
INSERT_PADDING_WORDS(1);
@@ -157,6 +187,7 @@ void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto sample_rate = rp.Pop<u32>();
const auto channel_count = rp.Pop<u32>();
LOG_DEBUG(Audio, "called with sample_rate={}, channel_count={}", sample_rate, channel_count);
ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
@@ -174,9 +205,10 @@ void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto sample_rate = rp.Pop<u32>();
auto channel_count = rp.Pop<u32>();
auto buffer_sz = rp.Pop<u32>();
const auto sample_rate = rp.Pop<u32>();
const auto channel_count = rp.Pop<u32>();
const auto buffer_sz = rp.Pop<u32>();
LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}, buffer_size={}", sample_rate,
channel_count, buffer_sz);
@@ -185,8 +217,9 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
"Invalid sample rate");
ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
std::size_t worker_sz = WorkerBufferSize(channel_count);
const std::size_t worker_sz = WorkerBufferSize(channel_count);
ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large");
std::unique_ptr<OpusDecoder, OpusDeleter> decoder{
static_cast<OpusDecoder*>(operator new(worker_sz))};
if (const int err = opus_decoder_init(decoder.get(), sample_rate, channel_count)) {
+101 -46
View File
@@ -19,16 +19,16 @@ public:
explicit Bt() : ServiceFramework{"bt"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Unknown0"},
{1, nullptr, "Unknown1"},
{2, nullptr, "Unknown2"},
{3, nullptr, "Unknown3"},
{4, nullptr, "Unknown4"},
{5, nullptr, "Unknown5"},
{6, nullptr, "Unknown6"},
{7, nullptr, "Unknown7"},
{8, nullptr, "Unknown8"},
{9, &Bt::RegisterEvent, "RegisterEvent"},
{0, nullptr, "LeClientReadCharacteristic"},
{1, nullptr, "LeClientReadDescriptor"},
{2, nullptr, "LeClientWriteCharacteristic"},
{3, nullptr, "LeClientWriteDescriptor"},
{4, nullptr, "LeClientRegisterNotification"},
{5, nullptr, "LeClientDeregisterNotification"},
{6, nullptr, "SetLeResponse"},
{7, nullptr, "LeSendIndication"},
{8, nullptr, "GetLeEventInfo"},
{9, &Bt::RegisterBleEvent, "RegisterBleEvent"},
};
// clang-format on
RegisterHandlers(functions);
@@ -39,7 +39,7 @@ public:
}
private:
void RegisterEvent(Kernel::HLERequestContext& ctx) {
void RegisterBleEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_BTM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
@@ -55,11 +55,11 @@ public:
explicit BtDrv() : ServiceFramework{"btdrv"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Unknown"},
{1, nullptr, "Init"},
{2, nullptr, "Enable"},
{3, nullptr, "Disable"},
{4, nullptr, "CleanupAndShutdown"},
{0, nullptr, "InitializeBluetoothDriver"},
{1, nullptr, "InitializeBluetooth"},
{2, nullptr, "EnableBluetooth"},
{3, nullptr, "DisableBluetooth"},
{4, nullptr, "CleanupBluetooth"},
{5, nullptr, "GetAdapterProperties"},
{6, nullptr, "GetAdapterProperty"},
{7, nullptr, "SetAdapterProperty"},
@@ -70,36 +70,91 @@ public:
{12, nullptr, "CancelBond"},
{13, nullptr, "PinReply"},
{14, nullptr, "SspReply"},
{15, nullptr, "Unknown2"},
{16, nullptr, "InitInterfaces"},
{17, nullptr, "HidHostInterface_Connect"},
{18, nullptr, "HidHostInterface_Disconnect"},
{19, nullptr, "HidHostInterface_SendData"},
{20, nullptr, "HidHostInterface_SendData2"},
{21, nullptr, "HidHostInterface_SetReport"},
{22, nullptr, "HidHostInterface_GetReport"},
{23, nullptr, "HidHostInterface_WakeController"},
{24, nullptr, "HidHostInterface_AddPairedDevice"},
{25, nullptr, "HidHostInterface_GetPairedDevice"},
{26, nullptr, "HidHostInterface_CleanupAndShutdown"},
{27, nullptr, "Unknown3"},
{28, nullptr, "ExtInterface_SetTSI"},
{29, nullptr, "ExtInterface_SetBurstMode"},
{30, nullptr, "ExtInterface_SetZeroRetran"},
{31, nullptr, "ExtInterface_SetMcMode"},
{32, nullptr, "ExtInterface_StartLlrMode"},
{33, nullptr, "ExtInterface_ExitLlrMode"},
{34, nullptr, "ExtInterface_SetRadio"},
{35, nullptr, "ExtInterface_SetVisibility"},
{36, nullptr, "Unknown4"},
{37, nullptr, "Unknown5"},
{38, nullptr, "HidHostInterface_GetLatestPlr"},
{39, nullptr, "ExtInterface_GetPendingConnections"},
{40, nullptr, "HidHostInterface_GetChannelMap"},
{41, nullptr, "SetIsBluetoothBoostEnabled"},
{42, nullptr, "GetIsBluetoothBoostEnabled"},
{43, nullptr, "SetIsBluetoothAfhEnabled"},
{44, nullptr, "GetIsBluetoothAfhEnabled"},
{15, nullptr, "GetEventInfo"},
{16, nullptr, "InitializeHid"},
{17, nullptr, "HidConnect"},
{18, nullptr, "HidDisconnect"},
{19, nullptr, "HidSendData"},
{20, nullptr, "HidSendData2"},
{21, nullptr, "HidSetReport"},
{22, nullptr, "HidGetReport"},
{23, nullptr, "HidWakeController"},
{24, nullptr, "HidAddPairedDevice"},
{25, nullptr, "HidGetPairedDevice"},
{26, nullptr, "CleanupHid"},
{27, nullptr, "HidGetEventInfo"},
{28, nullptr, "ExtSetTsi"},
{29, nullptr, "ExtSetBurstMode"},
{30, nullptr, "ExtSetZeroRetran"},
{31, nullptr, "ExtSetMcMode"},
{32, nullptr, "ExtStartLlrMode"},
{33, nullptr, "ExtExitLlrMode"},
{34, nullptr, "ExtSetRadio"},
{35, nullptr, "ExtSetVisibility"},
{36, nullptr, "ExtSetTbfcScan"},
{37, nullptr, "RegisterHidReportEvent"},
{38, nullptr, "HidGetReportEventInfo"},
{39, nullptr, "GetLatestPlr"},
{40, nullptr, "ExtGetPendingConnections"},
{41, nullptr, "GetChannelMap"},
{42, nullptr, "EnableBluetoothBoostSetting"},
{43, nullptr, "IsBluetoothBoostSettingEnabled"},
{44, nullptr, "EnableBluetoothAfhSetting"},
{45, nullptr, "IsBluetoothAfhSettingEnabled"},
{46, nullptr, "InitializeBluetoothLe"},
{47, nullptr, "EnableBluetoothLe"},
{48, nullptr, "DisableBluetoothLe"},
{49, nullptr, "CleanupBluetoothLe"},
{50, nullptr, "SetLeVisibility"},
{51, nullptr, "SetLeConnectionParameter"},
{52, nullptr, "SetLeDefaultConnectionParameter"},
{53, nullptr, "SetLeAdvertiseData"},
{54, nullptr, "SetLeAdvertiseParameter"},
{55, nullptr, "StartLeScan"},
{56, nullptr, "StopLeScan"},
{57, nullptr, "AddLeScanFilterCondition"},
{58, nullptr, "DeleteLeScanFilterCondition"},
{59, nullptr, "DeleteLeScanFilter"},
{60, nullptr, "ClearLeScanFilters"},
{61, nullptr, "EnableLeScanFilter"},
{62, nullptr, "RegisterLeClient"},
{63, nullptr, "UnregisterLeClient"},
{64, nullptr, "UnregisterLeClientAll"},
{65, nullptr, "LeClientConnect"},
{66, nullptr, "LeClientCancelConnection"},
{67, nullptr, "LeClientDisconnect"},
{68, nullptr, "LeClientGetAttributes"},
{69, nullptr, "LeClientDiscoverService"},
{70, nullptr, "LeClientConfigureMtu"},
{71, nullptr, "RegisterLeServer"},
{72, nullptr, "UnregisterLeServer"},
{73, nullptr, "LeServerConnect"},
{74, nullptr, "LeServerDisconnect"},
{75, nullptr, "CreateLeService"},
{76, nullptr, "StartLeService"},
{77, nullptr, "AddLeCharacteristic"},
{78, nullptr, "AddLeDescriptor"},
{79, nullptr, "GetLeCoreEventInfo"},
{80, nullptr, "LeGetFirstCharacteristic"},
{81, nullptr, "LeGetNextCharacteristic"},
{82, nullptr, "LeGetFirstDescriptor"},
{83, nullptr, "LeGetNextDescriptor"},
{84, nullptr, "RegisterLeCoreDataPath"},
{85, nullptr, "UnregisterLeCoreDataPath"},
{86, nullptr, "RegisterLeHidDataPath"},
{87, nullptr, "UnregisterLeHidDataPath"},
{88, nullptr, "RegisterLeDataPath"},
{89, nullptr, "UnregisterLeDataPath"},
{90, nullptr, "LeClientReadCharacteristic"},
{91, nullptr, "LeClientReadDescriptor"},
{92, nullptr, "LeClientWriteCharacteristic"},
{93, nullptr, "LeClientWriteDescriptor"},
{94, nullptr, "LeClientRegisterNotification"},
{95, nullptr, "LeClientDeregisterNotification"},
{96, nullptr, "GetLeHidEventInfo"},
{97, nullptr, "RegisterBleHidEvent"},
{98, nullptr, "SetLeScanParameter"},
{256, nullptr, "GetIsManufacturingMode"}
};
// clang-format on
+97 -55
View File
@@ -20,38 +20,38 @@ public:
explicit IBtmUserCore() : ServiceFramework{"IBtmUserCore"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IBtmUserCore::GetScanEvent, "GetScanEvent"},
{1, nullptr, "Unknown1"},
{2, nullptr, "Unknown2"},
{3, nullptr, "Unknown3"},
{4, nullptr, "Unknown4"},
{5, nullptr, "Unknown5"},
{6, nullptr, "Unknown6"},
{7, nullptr, "Unknown7"},
{8, nullptr, "Unknown8"},
{9, nullptr, "Unknown9"},
{10, nullptr, "Unknown10"},
{17, &IBtmUserCore::GetConnectionEvent, "GetConnectionEvent"},
{18, nullptr, "Unknown18"},
{19, nullptr, "Unknown19"},
{20, nullptr, "Unknown20"},
{21, nullptr, "Unknown21"},
{22, nullptr, "Unknown22"},
{23, nullptr, "Unknown23"},
{24, nullptr, "Unknown24"},
{25, nullptr, "Unknown25"},
{26, &IBtmUserCore::GetDiscoveryEvent, "AcquireBleServiceDiscoveryEventImpl"},
{27, nullptr, "Unknown27"},
{28, nullptr, "Unknown28"},
{29, nullptr, "Unknown29"},
{30, nullptr, "Unknown30"},
{31, nullptr, "Unknown31"},
{32, nullptr, "Unknown32"},
{33, &IBtmUserCore::GetConfigEvent, "GetConfigEvent"},
{34, nullptr, "Unknown34"},
{35, nullptr, "Unknown35"},
{36, nullptr, "Unknown36"},
{37, nullptr, "Unknown37"},
{0, &IBtmUserCore::AcquireBleScanEvent, "AcquireBleScanEvent"},
{1, nullptr, "GetBleScanFilterParameter"},
{2, nullptr, "GetBleScanFilterParameter2"},
{3, nullptr, "StartBleScanForGeneral"},
{4, nullptr, "StopBleScanForGeneral"},
{5, nullptr, "GetBleScanResultsForGeneral"},
{6, nullptr, "StartBleScanForPaired"},
{7, nullptr, "StopBleScanForPaired"},
{8, nullptr, "StartBleScanForSmartDevice"},
{9, nullptr, "StopBleScanForSmartDevice"},
{10, nullptr, "GetBleScanResultsForSmartDevice"},
{17, &IBtmUserCore::AcquireBleConnectionEvent, "AcquireBleConnectionEvent"},
{18, nullptr, "BleConnect"},
{19, nullptr, "BleDisconnect"},
{20, nullptr, "BleGetConnectionState"},
{21, nullptr, "AcquireBlePairingEvent"},
{22, nullptr, "BlePairDevice"},
{23, nullptr, "BleUnPairDevice"},
{24, nullptr, "BleUnPairDevice2"},
{25, nullptr, "BleGetPairedDevices"},
{26, &IBtmUserCore::AcquireBleServiceDiscoveryEvent, "AcquireBleServiceDiscoveryEvent"},
{27, nullptr, "GetGattServices"},
{28, nullptr, "GetGattService"},
{29, nullptr, "GetGattIncludedServices"},
{30, nullptr, "GetBelongingGattService"},
{31, nullptr, "GetGattCharacteristics"},
{32, nullptr, "GetGattDescriptors"},
{33, &IBtmUserCore::AcquireBleMtuConfigEvent, "AcquireBleMtuConfigEvent"},
{34, nullptr, "ConfigureBleMtu"},
{35, nullptr, "GetBleMtu"},
{36, nullptr, "RegisterBleGattDataPath"},
{37, nullptr, "UnregisterBleGattDataPath"},
};
// clang-format on
RegisterHandlers(functions);
@@ -68,7 +68,7 @@ public:
}
private:
void GetScanEvent(Kernel::HLERequestContext& ctx) {
void AcquireBleScanEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_BTM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
@@ -76,7 +76,7 @@ private:
rb.PushCopyObjects(scan_event.readable);
}
void GetConnectionEvent(Kernel::HLERequestContext& ctx) {
void AcquireBleConnectionEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_BTM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
@@ -84,7 +84,7 @@ private:
rb.PushCopyObjects(connection_event.readable);
}
void GetDiscoveryEvent(Kernel::HLERequestContext& ctx) {
void AcquireBleServiceDiscoveryEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_BTM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
@@ -92,7 +92,7 @@ private:
rb.PushCopyObjects(service_discovery.readable);
}
void GetConfigEvent(Kernel::HLERequestContext& ctx) {
void AcquireBleMtuConfigEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_BTM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
@@ -111,14 +111,14 @@ public:
explicit BTM_USR() : ServiceFramework{"btm:u"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &BTM_USR::GetCoreImpl, "GetCoreImpl"},
{0, &BTM_USR::GetCore, "GetCore"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void GetCoreImpl(Kernel::HLERequestContext& ctx) {
void GetCore(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_BTM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -134,26 +134,64 @@ public:
static const FunctionInfo functions[] = {
{0, nullptr, "Unknown1"},
{1, nullptr, "Unknown2"},
{2, nullptr, "RegisterSystemEventForConnectedDeviceConditionImpl"},
{2, nullptr, "RegisterSystemEventForConnectedDeviceCondition"},
{3, nullptr, "Unknown3"},
{4, nullptr, "Unknown4"},
{5, nullptr, "Unknown5"},
{6, nullptr, "Unknown6"},
{7, nullptr, "Unknown7"},
{8, nullptr, "RegisterSystemEventForRegisteredDeviceInfoImpl"},
{8, nullptr, "RegisterSystemEventForRegisteredDeviceInfo"},
{9, nullptr, "Unknown8"},
{10, nullptr, "Unknown9"},
{11, nullptr, "Unknown10"},
{12, nullptr, "Unknown11"},
{13, nullptr, "Unknown12"},
{14, nullptr, "EnableRadioImpl"},
{15, nullptr, "DisableRadioImpl"},
{14, nullptr, "EnableRadio"},
{15, nullptr, "DisableRadio"},
{16, nullptr, "Unknown13"},
{17, nullptr, "Unknown14"},
{18, nullptr, "Unknown15"},
{19, nullptr, "Unknown16"},
{20, nullptr, "Unknown17"},
{21, nullptr, "Unknown18"},
{22, nullptr, "Unknown19"},
{23, nullptr, "Unknown20"},
{24, nullptr, "Unknown21"},
{25, nullptr, "Unknown22"},
{26, nullptr, "Unknown23"},
{27, nullptr, "Unknown24"},
{28, nullptr, "Unknown25"},
{29, nullptr, "Unknown26"},
{30, nullptr, "Unknown27"},
{31, nullptr, "Unknown28"},
{32, nullptr, "Unknown29"},
{33, nullptr, "Unknown30"},
{34, nullptr, "Unknown31"},
{35, nullptr, "Unknown32"},
{36, nullptr, "Unknown33"},
{37, nullptr, "Unknown34"},
{38, nullptr, "Unknown35"},
{39, nullptr, "Unknown36"},
{40, nullptr, "Unknown37"},
{41, nullptr, "Unknown38"},
{42, nullptr, "Unknown39"},
{43, nullptr, "Unknown40"},
{44, nullptr, "Unknown41"},
{45, nullptr, "Unknown42"},
{46, nullptr, "Unknown43"},
{47, nullptr, "Unknown44"},
{48, nullptr, "Unknown45"},
{49, nullptr, "Unknown46"},
{50, nullptr, "Unknown47"},
{51, nullptr, "Unknown48"},
{52, nullptr, "Unknown49"},
{53, nullptr, "Unknown50"},
{54, nullptr, "Unknown51"},
{55, nullptr, "Unknown52"},
{56, nullptr, "Unknown53"},
{57, nullptr, "Unknown54"},
{58, nullptr, "Unknown55"},
{59, nullptr, "Unknown56"},
};
// clang-format on
@@ -166,7 +204,7 @@ public:
explicit BTM_DBG() : ServiceFramework{"btm:dbg"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "RegisterSystemEventForDiscoveryImpl"},
{0, nullptr, "RegisterSystemEventForDiscovery"},
{1, nullptr, "Unknown1"},
{2, nullptr, "Unknown2"},
{3, nullptr, "Unknown3"},
@@ -175,6 +213,10 @@ public:
{6, nullptr, "Unknown6"},
{7, nullptr, "Unknown7"},
{8, nullptr, "Unknown8"},
{9, nullptr, "Unknown9"},
{10, nullptr, "Unknown10"},
{11, nullptr, "Unknown11"},
{12, nullptr, "Unknown11"},
};
// clang-format on
@@ -187,16 +229,16 @@ public:
explicit IBtmSystemCore() : ServiceFramework{"IBtmSystemCore"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "StartGamepadPairingImpl"},
{1, nullptr, "CancelGamepadPairingImpl"},
{2, nullptr, "ClearGamepadPairingDatabaseImpl"},
{3, nullptr, "GetPairedGamepadCountImpl"},
{4, nullptr, "EnableRadioImpl"},
{5, nullptr, "DisableRadioImpl"},
{6, nullptr, "GetRadioOnOffImpl"},
{7, nullptr, "AcquireRadioEventImpl"},
{8, nullptr, "AcquireGamepadPairingEventImpl"},
{9, nullptr, "IsGamepadPairingStartedImpl"},
{0, nullptr, "StartGamepadPairing"},
{1, nullptr, "CancelGamepadPairing"},
{2, nullptr, "ClearGamepadPairingDatabase"},
{3, nullptr, "GetPairedGamepadCount"},
{4, nullptr, "EnableRadio"},
{5, nullptr, "DisableRadio"},
{6, nullptr, "GetRadioOnOff"},
{7, nullptr, "AcquireRadioEvent"},
{8, nullptr, "AcquireGamepadPairingEvent"},
{9, nullptr, "IsGamepadPairingStarted"},
};
// clang-format on
@@ -209,7 +251,7 @@ public:
explicit BTM_SYS() : ServiceFramework{"btm:sys"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &BTM_SYS::GetCoreImpl, "GetCoreImpl"},
{0, &BTM_SYS::GetCore, "GetCore"},
};
// clang-format on
@@ -217,7 +259,7 @@ public:
}
private:
void GetCoreImpl(Kernel::HLERequestContext& ctx) {
void GetCore(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_BTM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+20 -12
View File
@@ -627,8 +627,8 @@ private:
FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "MountContent"},
{1, &FSP_SRV::Initialize, "Initialize"},
{0, nullptr, "OpenFileSystem"},
{1, &FSP_SRV::SetCurrentProcess, "SetCurrentProcess"},
{2, nullptr, "OpenDataFileSystemByCurrentProcess"},
{7, &FSP_SRV::OpenFileSystemWithPatch, "OpenFileSystemWithPatch"},
{8, nullptr, "OpenFileSystemWithId"},
@@ -637,10 +637,10 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
{12, nullptr, "OpenBisStorage"},
{13, nullptr, "InvalidateBisCache"},
{17, nullptr, "OpenHostFileSystem"},
{18, &FSP_SRV::MountSdCard, "MountSdCard"},
{18, &FSP_SRV::OpenSdCardFileSystem, "OpenSdCardFileSystem"},
{19, nullptr, "FormatSdCardFileSystem"},
{21, nullptr, "DeleteSaveDataFileSystem"},
{22, &FSP_SRV::CreateSaveData, "CreateSaveData"},
{22, &FSP_SRV::CreateSaveDataFileSystem, "CreateSaveDataFileSystem"},
{23, nullptr, "CreateSaveDataFileSystemBySystemSaveDataId"},
{24, nullptr, "RegisterSaveDataFileSystemAtomicDeletion"},
{25, nullptr, "DeleteSaveDataFileSystemBySaveDataSpaceId"},
@@ -652,7 +652,8 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
{32, nullptr, "ExtendSaveDataFileSystem"},
{33, nullptr, "DeleteCacheStorage"},
{34, nullptr, "GetCacheStorageSize"},
{51, &FSP_SRV::MountSaveData, "MountSaveData"},
{35, nullptr, "CreateSaveDataFileSystemByHashSalt"},
{51, &FSP_SRV::OpenSaveDataFileSystem, "OpenSaveDataFileSystem"},
{52, nullptr, "OpenSaveDataFileSystemBySystemSaveDataId"},
{53, &FSP_SRV::OpenReadOnlySaveDataFileSystem, "OpenReadOnlySaveDataFileSystem"},
{57, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataSpaceId"},
@@ -664,21 +665,26 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
{64, nullptr, "OpenSaveDataInternalStorageFileSystem"},
{65, nullptr, "UpdateSaveDataMacForDebug"},
{66, nullptr, "WriteSaveDataFileSystemExtraData2"},
{67, nullptr, "FindSaveDataWithFilter"},
{68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"},
{80, nullptr, "OpenSaveDataMetaFile"},
{81, nullptr, "OpenSaveDataTransferManager"},
{82, nullptr, "OpenSaveDataTransferManagerVersion2"},
{83, nullptr, "OpenSaveDataTransferProhibiterForCloudBackUp"},
{84, nullptr, "ListApplicationAccessibleSaveDataOwnerId"},
{100, nullptr, "OpenImageDirectoryFileSystem"},
{110, nullptr, "OpenContentStorageFileSystem"},
{120, nullptr, "OpenCloudBackupWorkStorageFileSystem"},
{200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"},
{201, nullptr, "OpenDataStorageByProgramId"},
{202, &FSP_SRV::OpenDataStorageByDataId, "OpenDataStorageByDataId"},
{203, &FSP_SRV::OpenRomStorage, "OpenRomStorage"},
{203, &FSP_SRV::OpenPatchDataStorageByCurrentProcess, "OpenPatchDataStorageByCurrentProcess"},
{400, nullptr, "OpenDeviceOperator"},
{500, nullptr, "OpenSdCardDetectionEventNotifier"},
{501, nullptr, "OpenGameCardDetectionEventNotifier"},
{510, nullptr, "OpenSystemDataUpdateEventNotifier"},
{511, nullptr, "NotifySystemDataUpdateEvent"},
{520, nullptr, "SimulateGameCardDetectionEvent"},
{600, nullptr, "SetCurrentPosixTime"},
{601, nullptr, "QuerySaveDataTotalSize"},
{602, nullptr, "VerifySaveDataFileSystem"},
@@ -717,6 +723,8 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
{1008, nullptr, "OpenRegisteredUpdatePartition"},
{1009, nullptr, "GetAndClearMemoryReportInfo"},
{1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
{1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"},
{1200, nullptr, "OpenMultiCommitManager"},
};
// clang-format on
RegisterHandlers(functions);
@@ -724,7 +732,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
FSP_SRV::~FSP_SRV() = default;
void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
void FSP_SRV::SetCurrentProcess(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
@@ -743,7 +751,7 @@ void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
rb.Push(ResultCode(-1));
}
void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
IFileSystem filesystem(OpenSDMC().Unwrap());
@@ -753,7 +761,7 @@ void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
}
void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) {
void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
@@ -767,7 +775,7 @@ void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto space_id = rp.PopRaw<FileSys::SaveDataSpaceId>();
@@ -793,7 +801,7 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem");
MountSaveData(ctx);
OpenSaveDataFileSystem(ctx);
}
void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx) {
@@ -881,7 +889,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
rb.PushIpcInterface<IStorage>(std::move(storage));
}
void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
void FSP_SRV::OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto storage_id = rp.PopRaw<FileSys::StorageId>();
+5 -5
View File
@@ -19,17 +19,17 @@ public:
~FSP_SRV() override;
private:
void Initialize(Kernel::HLERequestContext& ctx);
void SetCurrentProcess(Kernel::HLERequestContext& ctx);
void OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx);
void MountSdCard(Kernel::HLERequestContext& ctx);
void CreateSaveData(Kernel::HLERequestContext& ctx);
void MountSaveData(Kernel::HLERequestContext& ctx);
void OpenSdCardFileSystem(Kernel::HLERequestContext& ctx);
void CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx);
void OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx);
void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx);
void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx);
void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);
void OpenRomStorage(Kernel::HLERequestContext& ctx);
void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
FileSys::VirtualFile romfs;
};
@@ -22,7 +22,7 @@ void Controller_DebugPad::OnInit() {}
void Controller_DebugPad::OnRelease() {}
void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
shared_memory.header.timestamp = CoreTiming::GetTicks();
shared_memory.header.timestamp = Core::Timing::GetTicks();
shared_memory.header.total_entry_count = 17;
if (!IsControllerActivated()) {
@@ -18,7 +18,7 @@ void Controller_Gesture::OnInit() {}
void Controller_Gesture::OnRelease() {}
void Controller_Gesture::OnUpdate(u8* data, std::size_t size) {
shared_memory.header.timestamp = CoreTiming::GetTicks();
shared_memory.header.timestamp = Core::Timing::GetTicks();
shared_memory.header.total_entry_count = 17;
if (!IsControllerActivated()) {
@@ -20,7 +20,7 @@ void Controller_Keyboard::OnInit() {}
void Controller_Keyboard::OnRelease() {}
void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) {
shared_memory.header.timestamp = CoreTiming::GetTicks();
shared_memory.header.timestamp = Core::Timing::GetTicks();
shared_memory.header.total_entry_count = 17;
if (!IsControllerActivated()) {
@@ -18,7 +18,7 @@ void Controller_Mouse::OnInit() {}
void Controller_Mouse::OnRelease() {}
void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
shared_memory.header.timestamp = CoreTiming::GetTicks();
shared_memory.header.timestamp = Core::Timing::GetTicks();
shared_memory.header.total_entry_count = 17;
if (!IsControllerActivated()) {
@@ -308,7 +308,7 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
const auto& last_entry =
main_controller->npad[main_controller->common.last_entry_index];
main_controller->common.timestamp = CoreTiming::GetTicks();
main_controller->common.timestamp = Core::Timing::GetTicks();
main_controller->common.last_entry_index =
(main_controller->common.last_entry_index + 1) % 17;
@@ -22,7 +22,7 @@ void Controller_Stubbed::OnUpdate(u8* data, std::size_t size) {
}
CommonHeader header{};
header.timestamp = CoreTiming::GetTicks();
header.timestamp = Core::Timing::GetTicks();
header.total_entry_count = 17;
header.entry_count = 0;
header.last_entry_index = 0;
@@ -21,7 +21,7 @@ void Controller_Touchscreen::OnInit() {}
void Controller_Touchscreen::OnRelease() {}
void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
shared_memory.header.timestamp = CoreTiming::GetTicks();
shared_memory.header.timestamp = Core::Timing::GetTicks();
shared_memory.header.total_entry_count = 17;
if (!IsControllerActivated()) {
@@ -48,7 +48,7 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
const u64 tick = CoreTiming::GetTicks();
const u64 tick = Core::Timing::GetTicks();
touch_entry.delta_time = tick - last_touch;
last_touch = tick;
touch_entry.finger = Settings::values.touchscreen.finger;
@@ -19,7 +19,7 @@ void Controller_XPad::OnRelease() {}
void Controller_XPad::OnUpdate(u8* data, std::size_t size) {
for (auto& xpad_entry : shared_memory.shared_memory_entries) {
xpad_entry.header.timestamp = CoreTiming::GetTicks();
xpad_entry.header.timestamp = Core::Timing::GetTicks();
xpad_entry.header.total_entry_count = 17;
if (!IsControllerActivated()) {
+9 -10
View File
@@ -36,9 +36,9 @@ namespace Service::HID {
// Updating period for each HID device.
// TODO(ogniK): Find actual polling rate of hid
constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 66;
constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr u64 pad_update_ticks = Core::Timing::BASE_CLOCK_RATE / 66;
constexpr u64 accelerometer_update_ticks = Core::Timing::BASE_CLOCK_RATE / 100;
constexpr u64 gyroscope_update_ticks = Core::Timing::BASE_CLOCK_RATE / 100;
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
@@ -73,14 +73,13 @@ IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
// Register update callbacks
pad_update_event =
CoreTiming::RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, int cycles_late) {
UpdateControllers(userdata, cycles_late);
});
pad_update_event = Core::Timing::RegisterEvent(
"HID::UpdatePadCallback",
[this](u64 userdata, int cycles_late) { UpdateControllers(userdata, cycles_late); });
// TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
Core::Timing::ScheduleEvent(pad_update_ticks, pad_update_event);
ReloadInputDevices();
}
@@ -94,7 +93,7 @@ void IAppletResource::DeactivateController(HidController controller) {
}
IAppletResource ::~IAppletResource() {
CoreTiming::UnscheduleEvent(pad_update_event, 0);
Core::Timing::UnscheduleEvent(pad_update_event, 0);
}
void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
@@ -114,7 +113,7 @@ void IAppletResource::UpdateControllers(u64 userdata, int cycles_late) {
controller->OnUpdate(shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
}
CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
Core::Timing::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
}
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
+2 -2
View File
@@ -7,7 +7,7 @@
#include "controllers/controller_base.h"
#include "core/hle/service/service.h"
namespace CoreTiming {
namespace Core::Timing {
struct EventType;
}
@@ -66,7 +66,7 @@ private:
Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
CoreTiming::EventType* pad_update_event;
Core::Timing::EventType* pad_update_event;
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
controllers{};
+1 -1
View File
@@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u64>(CoreTiming::GetTicks());
rb.PushRaw<u64>(Core::Timing::GetTicks());
rb.PushRaw<u32>(0);
}
+4 -4
View File
@@ -40,10 +40,10 @@ public:
{6, nullptr, "CloseContentStorageForcibly"},
{7, nullptr, "CloseContentMetaDatabaseForcibly"},
{8, nullptr, "CleanupContentMetaDatabase"},
{9, nullptr, "OpenContentStorage2"},
{10, nullptr, "CloseContentStorage"},
{11, nullptr, "OpenContentMetaDatabase2"},
{12, nullptr, "CloseContentMetaDatabase"},
{9, nullptr, "ActivateContentStorage"},
{10, nullptr, "InactivateContentStorage"},
{11, nullptr, "ActivateContentMetaDatabase"},
{12, nullptr, "InactivateContentMetaDatabase"},
};
// clang-format on
+20 -14
View File
@@ -43,7 +43,7 @@ public:
{11, nullptr, "CalculateApplicationOccupiedSize"},
{16, nullptr, "PushApplicationRecord"},
{17, nullptr, "ListApplicationRecordContentMeta"},
{19, nullptr, "LaunchApplication"},
{19, nullptr, "LaunchApplicationOld"},
{21, nullptr, "GetApplicationContentPath"},
{22, nullptr, "TerminateApplication"},
{23, nullptr, "ResolveApplicationContentPath"},
@@ -96,10 +96,10 @@ public:
{86, nullptr, "EnableApplicationCrashReport"},
{87, nullptr, "IsApplicationCrashReportEnabled"},
{90, nullptr, "BoostSystemMemoryResourceLimit"},
{91, nullptr, "Unknown1"},
{92, nullptr, "Unknown2"},
{91, nullptr, "DeprecatedLaunchApplication"},
{92, nullptr, "GetRunningApplicationProgramId"},
{93, nullptr, "GetMainApplicationProgramIndex"},
{94, nullptr, "LaunchApplication2"},
{94, nullptr, "LaunchApplication"},
{95, nullptr, "GetApplicationLaunchInfo"},
{96, nullptr, "AcquireApplicationLaunchInfo"},
{97, nullptr, "GetMainApplicationProgramIndex2"},
@@ -163,7 +163,7 @@ public:
{907, nullptr, "WithdrawApplicationUpdateRequest"},
{908, nullptr, "ListApplicationRecordInstalledContentMeta"},
{909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
{910, nullptr, "Unknown3"},
{910, nullptr, "HasApplicationRecord"},
{911, nullptr, "SetPreInstalledApplication"},
{912, nullptr, "ClearPreInstalledApplicationFlag"},
{1000, nullptr, "RequestVerifyApplicationDeprecated"},
@@ -219,10 +219,10 @@ public:
{2015, nullptr, "CompareSystemDeliveryInfo"},
{2016, nullptr, "ListNotCommittedContentMeta"},
{2017, nullptr, "CreateDownloadTask"},
{2018, nullptr, "Unknown4"},
{2050, nullptr, "Unknown5"},
{2100, nullptr, "Unknown6"},
{2101, nullptr, "Unknown7"},
{2018, nullptr, "GetApplicationDeliveryInfoHash"},
{2050, nullptr, "GetApplicationRightsOnClient"},
{2100, nullptr, "GetApplicationTerminateResult"},
{2101, nullptr, "GetRawApplicationTerminateResult"},
{2150, nullptr, "CreateRightsEnvironment"},
{2151, nullptr, "DestroyRightsEnvironment"},
{2152, nullptr, "ActivateRightsEnvironment"},
@@ -237,10 +237,10 @@ public:
{2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
{2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
{2199, nullptr, "GetRightsEnvironmentCountForDebug"},
{2200, nullptr, "Unknown8"},
{2201, nullptr, "Unknown9"},
{2250, nullptr, "Unknown10"},
{2300, nullptr, "Unknown11"},
{2200, nullptr, "GetGameCardApplicationCopyIdentifier"},
{2201, nullptr, "GetInstalledApplicationCopyIdentifier"},
{2250, nullptr, "RequestReportActiveELicence"},
{2300, nullptr, "ListEventLog"},
};
// clang-format on
@@ -355,6 +355,7 @@ public:
static const FunctionInfo functions[] = {
{21, nullptr, "GetApplicationContentPath"},
{23, nullptr, "ResolveApplicationContentPath"},
{93, nullptr, "GetRunningApplicationProgramId"},
};
// clang-format on
@@ -389,6 +390,11 @@ public:
// 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
@@ -403,7 +409,7 @@ public:
static const FunctionInfo functions[] = {
{100, nullptr, "ResetToFactorySettings"},
{101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
{102, nullptr, "ResetToFactorySettingsForRefurbishment "},
{102, nullptr, "ResetToFactorySettingsForRefurbishment"},
};
// clang-format on
@@ -25,9 +25,9 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform,
const MathUtil::Rectangle<int>& crop_rect) {
VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
LOG_WARNING(Service,
"Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
addr, offset, width, height, stride, format);
LOG_TRACE(Service,
"Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
addr, offset, width, height, stride, format);
using PixelFormat = Tegra::FramebufferConfig::PixelFormat;
const Tegra::FramebufferConfig framebuffer{
@@ -184,7 +184,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o
IoctlGetGpuTime params{};
std::memcpy(&params, input.data(), input.size());
params.gpu_time = CoreTiming::cyclesToNs(CoreTiming::GetTicks());
params.gpu_time = Core::Timing::cyclesToNs(Core::Timing::GetTicks());
std::memcpy(output.data(), &params, output.size());
return 0;
}
@@ -13,10 +13,6 @@
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/writable_event.h"
namespace CoreTiming {
struct EventType;
}
namespace Service::NVFlinger {
struct IGBPBuffer {
+91 -45
View File
@@ -5,7 +5,6 @@
#include <algorithm>
#include <optional>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
@@ -22,98 +21,145 @@
#include "core/hle/service/nvflinger/nvflinger.h"
#include "core/perf_stats.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
namespace Service::NVFlinger {
constexpr std::size_t SCREEN_REFRESH_RATE = 60;
constexpr u64 frame_ticks = static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
constexpr u64 frame_ticks = static_cast<u64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
NVFlinger::NVFlinger() {
// Add the different displays to the list of displays.
displays.emplace_back(0, "Default");
displays.emplace_back(1, "External");
displays.emplace_back(2, "Edid");
displays.emplace_back(3, "Internal");
// Schedule the screen composition events
composition_event =
CoreTiming::RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
Core::Timing::RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
Compose();
CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event);
Core::Timing::ScheduleEvent(frame_ticks - cycles_late, composition_event);
});
CoreTiming::ScheduleEvent(frame_ticks, composition_event);
Core::Timing::ScheduleEvent(frame_ticks, composition_event);
}
NVFlinger::~NVFlinger() {
CoreTiming::UnscheduleEvent(composition_event, 0);
Core::Timing::UnscheduleEvent(composition_event, 0);
}
void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
nvdrv = std::move(instance);
}
u64 NVFlinger::OpenDisplay(std::string_view name) {
LOG_WARNING(Service, "Opening display {}", name);
std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
LOG_DEBUG(Service, "Opening \"{}\" display", name);
// TODO(Subv): Currently we only support the Default display.
ASSERT(name == "Default");
auto itr = std::find_if(displays.begin(), displays.end(),
[&](const Display& display) { return display.name == name; });
ASSERT(itr != displays.end());
const auto itr = std::find_if(displays.begin(), displays.end(),
[&](const Display& display) { return display.name == name; });
if (itr == displays.end()) {
return {};
}
return itr->id;
}
u64 NVFlinger::CreateLayer(u64 display_id) {
auto& display = GetDisplay(display_id);
std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
auto* const display = FindDisplay(display_id);
ASSERT_MSG(display.layers.empty(), "Only one layer is supported per display at the moment");
if (display == nullptr) {
return {};
}
u64 layer_id = next_layer_id++;
u32 buffer_queue_id = next_buffer_queue_id++;
ASSERT_MSG(display->layers.empty(), "Only one layer is supported per display at the moment");
const u64 layer_id = next_layer_id++;
const u32 buffer_queue_id = next_buffer_queue_id++;
auto buffer_queue = std::make_shared<BufferQueue>(buffer_queue_id, layer_id);
display.layers.emplace_back(layer_id, buffer_queue);
display->layers.emplace_back(layer_id, buffer_queue);
buffer_queues.emplace_back(std::move(buffer_queue));
return layer_id;
}
u32 NVFlinger::GetBufferQueueId(u64 display_id, u64 layer_id) {
const auto& layer = GetLayer(display_id, layer_id);
return layer.buffer_queue->GetId();
std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) const {
const auto* const layer = FindLayer(display_id, layer_id);
if (layer == nullptr) {
return {};
}
return layer->buffer_queue->GetId();
}
Kernel::SharedPtr<Kernel::ReadableEvent> NVFlinger::GetVsyncEvent(u64 display_id) {
return GetDisplay(display_id).vsync_event.readable;
Kernel::SharedPtr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const {
auto* const display = FindDisplay(display_id);
if (display == nullptr) {
return nullptr;
}
return display->vsync_event.readable;
}
std::shared_ptr<BufferQueue> NVFlinger::GetBufferQueue(u32 id) const {
auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
[&](const auto& queue) { return queue->GetId() == id; });
std::shared_ptr<BufferQueue> NVFlinger::FindBufferQueue(u32 id) const {
const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
[&](const auto& queue) { return queue->GetId() == id; });
ASSERT(itr != buffer_queues.end());
return *itr;
}
Display& NVFlinger::GetDisplay(u64 display_id) {
auto itr = std::find_if(displays.begin(), displays.end(),
[&](const Display& display) { return display.id == display_id; });
Display* NVFlinger::FindDisplay(u64 display_id) {
const auto itr = std::find_if(displays.begin(), displays.end(),
[&](const Display& display) { return display.id == display_id; });
ASSERT(itr != displays.end());
return *itr;
if (itr == displays.end()) {
return nullptr;
}
return &*itr;
}
Layer& NVFlinger::GetLayer(u64 display_id, u64 layer_id) {
auto& display = GetDisplay(display_id);
const Display* NVFlinger::FindDisplay(u64 display_id) const {
const auto itr = std::find_if(displays.begin(), displays.end(),
[&](const Display& display) { return display.id == display_id; });
auto itr = std::find_if(display.layers.begin(), display.layers.end(),
[&](const Layer& layer) { return layer.id == layer_id; });
if (itr == displays.end()) {
return nullptr;
}
ASSERT(itr != display.layers.end());
return *itr;
return &*itr;
}
Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) {
auto* const display = FindDisplay(display_id);
if (display == nullptr) {
return nullptr;
}
const auto itr = std::find_if(display->layers.begin(), display->layers.end(),
[&](const Layer& layer) { return layer.id == layer_id; });
if (itr == display->layers.end()) {
return nullptr;
}
return &*itr;
}
const Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) const {
const auto* const display = FindDisplay(display_id);
if (display == nullptr) {
return nullptr;
}
const auto itr = std::find_if(display->layers.begin(), display->layers.end(),
[&](const Layer& layer) { return layer.id == layer_id; });
if (itr == display->layers.end()) {
return nullptr;
}
return &*itr;
}
void NVFlinger::Compose() {
@@ -145,7 +191,7 @@ void NVFlinger::Compose() {
continue;
}
auto& igbp_buffer = buffer->get().igbp_buffer;
const auto& igbp_buffer = buffer->get().igbp_buffer;
// Now send the buffer to the GPU for drawing.
// TODO(Subv): Support more than just disp0. The display device selection is probably based
+39 -17
View File
@@ -4,7 +4,9 @@
#pragma once
#include <array>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
@@ -12,7 +14,7 @@
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
namespace CoreTiming {
namespace Core::Timing {
struct EventType;
}
@@ -56,35 +58,55 @@ public:
/// Sets the NVDrv module instance to use to send buffers to the GPU.
void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance);
/// Opens the specified display and returns the id.
u64 OpenDisplay(std::string_view name);
/// Opens the specified display and returns the ID.
///
/// If an invalid display name is provided, then an empty optional is returned.
std::optional<u64> OpenDisplay(std::string_view name);
/// Creates a layer on the specified display and returns the layer id.
u64 CreateLayer(u64 display_id);
/// Creates a layer on the specified display and returns the layer ID.
///
/// If an invalid display ID is specified, then an empty optional is returned.
std::optional<u64> CreateLayer(u64 display_id);
/// Gets the buffer queue id of the specified layer in the specified display.
u32 GetBufferQueueId(u64 display_id, u64 layer_id);
/// Finds the buffer queue ID of the specified layer in the specified display.
///
/// If an invalid display ID or layer ID is provided, then an empty optional is returned.
std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id) const;
/// Gets the vsync event for the specified display.
Kernel::SharedPtr<Kernel::ReadableEvent> GetVsyncEvent(u64 display_id);
///
/// If an invalid display ID is provided, then nullptr is returned.
Kernel::SharedPtr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const;
/// Obtains a buffer queue identified by the id.
std::shared_ptr<BufferQueue> GetBufferQueue(u32 id) const;
/// Obtains a buffer queue identified by the ID.
std::shared_ptr<BufferQueue> FindBufferQueue(u32 id) const;
/// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
/// finished.
void Compose();
private:
/// Returns the display identified by the specified id.
Display& GetDisplay(u64 display_id);
/// Finds the display identified by the specified ID.
Display* FindDisplay(u64 display_id);
/// Returns the layer identified by the specified id in the desired display.
Layer& GetLayer(u64 display_id, u64 layer_id);
/// Finds the display identified by the specified ID.
const Display* FindDisplay(u64 display_id) const;
/// Finds the layer identified by the specified ID in the desired display.
Layer* FindLayer(u64 display_id, u64 layer_id);
/// Finds the layer identified by the specified ID in the desired display.
const Layer* FindLayer(u64 display_id, u64 layer_id) const;
std::shared_ptr<Nvidia::Module> nvdrv;
std::vector<Display> displays;
std::array<Display, 5> displays{{
{0, "Default"},
{1, "External"},
{2, "Edid"},
{3, "Internal"},
{4, "Null"},
}};
std::vector<std::shared_ptr<BufferQueue>> buffer_queues;
/// Id to use for the next layer that is created, this counter is shared among all displays.
@@ -93,8 +115,8 @@ private:
/// layers.
u32 next_buffer_queue_id = 1;
/// CoreTiming event that handles screen composition.
CoreTiming::EventType* composition_event;
/// Event that handles screen composition.
Core::Timing::EventType* composition_event;
};
} // namespace Service::NVFlinger
+13 -2
View File
@@ -13,7 +13,7 @@ public:
explicit BootMode() : ServiceFramework{"pm:bm"} {
static const FunctionInfo functions[] = {
{0, &BootMode::GetBootMode, "GetBootMode"},
{1, nullptr, "SetMaintenanceBoot"},
{1, &BootMode::SetMaintenanceBoot, "SetMaintenanceBoot"},
};
RegisterHandlers(functions);
}
@@ -24,8 +24,19 @@ private:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(static_cast<u32>(SystemBootMode::Normal)); // Normal boot mode
rb.PushEnum(boot_mode);
}
void SetMaintenanceBoot(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_PM, "called");
boot_mode = SystemBootMode::Maintenance;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
SystemBootMode boot_mode = SystemBootMode::Normal;
};
class DebugMonitor final : public ServiceFramework<DebugMonitor> {
+6 -1
View File
@@ -9,7 +9,12 @@ class ServiceManager;
}
namespace Service::PM {
enum class SystemBootMode : u32 { Normal = 0, Maintenance = 1 };
enum class SystemBootMode {
Normal,
Maintenance,
};
/// Registers all PM services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
+9 -8
View File
@@ -17,13 +17,13 @@ public:
explicit PSC_C() : ServiceFramework{"psc:c"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Unknown1"},
{1, nullptr, "Unknown2"},
{2, nullptr, "Unknown3"},
{3, nullptr, "Unknown4"},
{4, nullptr, "Unknown5"},
{5, nullptr, "Unknown6"},
{6, nullptr, "Unknown7"},
{0, nullptr, "Initialize"},
{1, nullptr, "DispatchRequest"},
{2, nullptr, "GetResult"},
{3, nullptr, "GetState"},
{4, nullptr, "Cancel"},
{5, nullptr, "PrintModuleInformation"},
{6, nullptr, "GetModuleInformation"},
};
// clang-format on
@@ -39,7 +39,8 @@ public:
{0, nullptr, "Initialize"},
{1, nullptr, "GetRequest"},
{2, nullptr, "Acknowledge"},
{3, nullptr, "Unknown1"},
{3, nullptr, "Finalize"},
{4, nullptr, "AcknowledgeEx"},
};
// clang-format on
+3 -3
View File
@@ -106,8 +106,8 @@ private:
void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
SteadyClockTimePoint steady_clock_time_point{
CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000};
const SteadyClockTimePoint steady_clock_time_point{
Core::Timing::cyclesToMs(Core::Timing::GetTicks()) / 1000};
IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(steady_clock_time_point);
@@ -282,7 +282,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
}
const SteadyClockTimePoint steady_clock_time_point{
CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000, {}};
Core::Timing::cyclesToMs(Core::Timing::GetTicks()) / 1000, {}};
CalendarTime calendar_time{};
calendar_time.year = tm->tm_year + 1900;
+57 -16
View File
@@ -34,6 +34,7 @@ namespace Service::VI {
constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::VI, 1};
constexpr ResultCode ERR_UNSUPPORTED{ErrorModule::VI, 6};
constexpr ResultCode ERR_NOT_FOUND{ErrorModule::VI, 7};
struct DisplayInfo {
/// The name of this particular display.
@@ -524,7 +525,7 @@ private:
LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
static_cast<u32>(transaction), flags);
auto buffer_queue = nv_flinger->GetBufferQueue(id);
auto buffer_queue = nv_flinger->FindBufferQueue(id);
if (transaction == TransactionId::Connect) {
IGBPConnectRequestParcel request{ctx.ReadBuffer()};
@@ -558,7 +559,7 @@ private:
[=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
Kernel::ThreadWakeupReason reason) {
// Repeat TransactParcel DequeueBuffer when a buffer is available
auto buffer_queue = nv_flinger->GetBufferQueue(id);
auto buffer_queue = nv_flinger->FindBufferQueue(id);
std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
ASSERT_MSG(slot != std::nullopt, "Could not dequeue buffer.");
@@ -628,7 +629,7 @@ private:
LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
const auto buffer_queue = nv_flinger->GetBufferQueue(id);
const auto buffer_queue = nv_flinger->FindBufferQueue(id);
// TODO(Subv): Find out what this actually is.
IPC::ResponseBuilder rb{ctx, 2, 1};
@@ -704,13 +705,14 @@ private:
rb.Push(RESULT_SUCCESS);
}
// This function currently does nothing but return a success error code in
// the vi library itself, so do the same thing, but log out the passed in values.
void SetLayerVisibility(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 layer_id = rp.Pop<u64>();
const bool visibility = rp.Pop<bool>();
LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id,
visibility);
LOG_DEBUG(Service_VI, "called, layer_id=0x{:08X}, visibility={}", layer_id, visibility);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -837,11 +839,16 @@ private:
"(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}",
unknown, display, aruid);
const u64 layer_id = nv_flinger->CreateLayer(display);
const auto layer_id = nv_flinger->CreateLayer(display);
if (!layer_id) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NOT_FOUND);
return;
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(layer_id);
rb.Push(*layer_id);
}
void AddToLayerStack(Kernel::HLERequestContext& ctx) {
@@ -949,9 +956,16 @@ private:
ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
const auto display_id = nv_flinger->OpenDisplay(name);
if (!display_id) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NOT_FOUND);
return;
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(nv_flinger->OpenDisplay(name));
rb.Push<u64>(*display_id);
}
void CloseDisplay(Kernel::HLERequestContext& ctx) {
@@ -1042,10 +1056,21 @@ private:
LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid);
const u64 display_id = nv_flinger->OpenDisplay(display_name);
const u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
const auto display_id = nv_flinger->OpenDisplay(display_name);
if (!display_id) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NOT_FOUND);
return;
}
NativeWindow native_window{buffer_queue_id};
const auto buffer_queue_id = nv_flinger->FindBufferQueueId(*display_id, layer_id);
if (!buffer_queue_id) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NOT_FOUND);
return;
}
NativeWindow native_window{*buffer_queue_id};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
@@ -1061,13 +1086,24 @@ private:
// TODO(Subv): What's the difference between a Stray and a Managed layer?
const u64 layer_id = nv_flinger->CreateLayer(display_id);
const u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
const auto layer_id = nv_flinger->CreateLayer(display_id);
if (!layer_id) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NOT_FOUND);
return;
}
NativeWindow native_window{buffer_queue_id};
const auto buffer_queue_id = nv_flinger->FindBufferQueueId(display_id, *layer_id);
if (!buffer_queue_id) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NOT_FOUND);
return;
}
NativeWindow native_window{*buffer_queue_id};
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
rb.Push(layer_id);
rb.Push(*layer_id);
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
}
@@ -1087,7 +1123,12 @@ private:
LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id);
const auto vsync_event = nv_flinger->GetVsyncEvent(display_id);
const auto vsync_event = nv_flinger->FindVsyncEvent(display_id);
if (!vsync_event) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NOT_FOUND);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
+4
View File
@@ -178,6 +178,8 @@ public:
/**
* Get the banner (typically banner section) of the application
* In the context of NX, this is the animation that displays in the bottom right of the screen
* when a game boots. Stored in GIF format.
* @param buffer Reference to buffer to store data
* @return ResultStatus result of function
*/
@@ -187,6 +189,8 @@ public:
/**
* Get the logo (typically logo section) of the application
* In the context of NX, this is the static image that displays in the top left of the screen
* when a game boots. Stored in JPEG format.
* @param buffer Reference to buffer to store data
* @return ResultStatus result of function
*/
+9
View File
@@ -79,4 +79,13 @@ u64 AppLoader_NAX::ReadRomFSIVFCOffset() const {
ResultStatus AppLoader_NAX::ReadProgramId(u64& out_program_id) {
return nca_loader->ReadProgramId(out_program_id);
}
ResultStatus AppLoader_NAX::ReadBanner(std::vector<u8>& buffer) {
return nca_loader->ReadBanner(buffer);
}
ResultStatus AppLoader_NAX::ReadLogo(std::vector<u8>& buffer) {
return nca_loader->ReadLogo(buffer);
}
} // namespace Loader
+3
View File
@@ -39,6 +39,9 @@ public:
u64 ReadRomFSIVFCOffset() const override;
ResultStatus ReadProgramId(u64& out_program_id) override;
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
private:
std::unique_ptr<FileSys::NAX> nax;
std::unique_ptr<AppLoader_NCA> nca_loader;
+19
View File
@@ -84,4 +84,23 @@ ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) {
return ResultStatus::Success;
}
ResultStatus AppLoader_NCA::ReadBanner(std::vector<u8>& buffer) {
if (nca == nullptr || nca->GetStatus() != ResultStatus::Success)
return ResultStatus::ErrorNotInitialized;
const auto logo = nca->GetLogoPartition();
if (logo == nullptr)
return ResultStatus::ErrorNoIcon;
buffer = logo->GetFile("StartupMovie.gif")->ReadAllBytes();
return ResultStatus::Success;
}
ResultStatus AppLoader_NCA::ReadLogo(std::vector<u8>& buffer) {
if (nca == nullptr || nca->GetStatus() != ResultStatus::Success)
return ResultStatus::ErrorNotInitialized;
const auto logo = nca->GetLogoPartition();
if (logo == nullptr)
return ResultStatus::ErrorNoIcon;
buffer = logo->GetFile("NintendoLogo.png")->ReadAllBytes();
return ResultStatus::Success;
}
} // namespace Loader
+3
View File
@@ -39,6 +39,9 @@ public:
u64 ReadRomFSIVFCOffset() const override;
ResultStatus ReadProgramId(u64& out_program_id) override;
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
private:
std::unique_ptr<FileSys::NCA> nca;
std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader;
+9
View File
@@ -166,4 +166,13 @@ ResultStatus AppLoader_NSP::ReadManualRomFS(FileSys::VirtualFile& file) {
file = nca->GetRomFS();
return file == nullptr ? ResultStatus::ErrorNoRomFS : ResultStatus::Success;
}
ResultStatus AppLoader_NSP::ReadBanner(std::vector<u8>& buffer) {
return secondary_loader->ReadBanner(buffer);
}
ResultStatus AppLoader_NSP::ReadLogo(std::vector<u8>& buffer) {
return secondary_loader->ReadLogo(buffer);
}
} // namespace Loader
+3
View File
@@ -46,6 +46,9 @@ public:
ResultStatus ReadControlData(FileSys::NACP& nacp) override;
ResultStatus ReadManualRomFS(FileSys::VirtualFile& file) override;
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
private:
std::unique_ptr<FileSys::NSP> nsp;
std::unique_ptr<AppLoader> secondary_loader;
+8
View File
@@ -137,4 +137,12 @@ ResultStatus AppLoader_XCI::ReadManualRomFS(FileSys::VirtualFile& file) {
return file == nullptr ? ResultStatus::ErrorNoRomFS : ResultStatus::Success;
}
ResultStatus AppLoader_XCI::ReadBanner(std::vector<u8>& buffer) {
return nca_loader->ReadBanner(buffer);
}
ResultStatus AppLoader_XCI::ReadLogo(std::vector<u8>& buffer) {
return nca_loader->ReadLogo(buffer);
}
} // namespace Loader
+3
View File
@@ -46,6 +46,9 @@ public:
ResultStatus ReadControlData(FileSys::NACP& control) override;
ResultStatus ReadManualRomFS(FileSys::VirtualFile& file) override;
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
private:
std::unique_ptr<FileSys::XCI> xci;
std::unique_ptr<AppLoader_NCA> nca_loader;
+29
View File
@@ -74,4 +74,33 @@ void Apply() {
Service::HID::ReloadInputDevices();
}
template <typename T>
void LogSetting(const std::string& name, const T& value) {
LOG_INFO(Config, "{}: {}", name, value);
}
void LogSettings() {
LOG_INFO(Config, "yuzu Configuration:");
LogSetting("System_UseDockedMode", Settings::values.use_docked_mode);
LogSetting("System_EnableNfc", Settings::values.enable_nfc);
LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0));
LogSetting("System_CurrentUser", Settings::values.current_user);
LogSetting("System_LanguageIndex", Settings::values.language_index);
LogSetting("Core_UseCpuJit", Settings::values.use_cpu_jit);
LogSetting("Core_UseMultiCore", Settings::values.use_multi_core);
LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
LogSetting("Renderer_FrameLimit", Settings::values.frame_limit);
LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation);
LogSetting("Audio_OutputEngine", Settings::values.sink_id);
LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd);
LogSetting("DataStorage_NandDir", Settings::values.nand_dir);
LogSetting("DataStorage_SdmcDir", Settings::values.sdmc_dir);
LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub);
LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port);
LogSetting("Debugging_ProgramArgs", Settings::values.program_args);
}
} // namespace Settings
+2
View File
@@ -391,6 +391,7 @@ struct Values {
float resolution_factor;
bool use_frame_limit;
u16 frame_limit;
bool use_disk_shader_cache;
bool use_accurate_gpu_emulation;
float bg_red;
@@ -425,4 +426,5 @@ struct Values {
} extern values;
void Apply();
void LogSettings();
} // namespace Settings
+2
View File
@@ -158,6 +158,8 @@ TelemetrySession::TelemetrySession() {
AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit",
Settings::values.use_frame_limit);
AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit);
AddField(Telemetry::FieldType::UserConfig, "Renderer_UseDiskShaderCache",
Settings::values.use_disk_shader_cache);
AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation",
Settings::values.use_accurate_gpu_emulation);
AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode",
+85 -84
View File
@@ -31,10 +31,10 @@ void CallbackTemplate(u64 userdata, s64 cycles_late) {
class ScopeInit final {
public:
ScopeInit() {
CoreTiming::Init();
Core::Timing::Init();
}
~ScopeInit() {
CoreTiming::Shutdown();
Core::Timing::Shutdown();
}
};
@@ -44,37 +44,37 @@ static void AdvanceAndCheck(u32 idx, int downcount, int expected_lateness = 0,
expected_callback = CB_IDS[idx];
lateness = expected_lateness;
CoreTiming::AddTicks(CoreTiming::GetDowncount() -
cpu_downcount); // Pretend we executed X cycles of instructions.
CoreTiming::Advance();
// Pretend we executed X cycles of instructions.
Core::Timing::AddTicks(Core::Timing::GetDowncount() - cpu_downcount);
Core::Timing::Advance();
REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags);
REQUIRE(downcount == CoreTiming::GetDowncount());
REQUIRE(downcount == Core::Timing::GetDowncount());
}
TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
ScopeInit guard;
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>);
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>);
CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", CallbackTemplate<2>);
CoreTiming::EventType* cb_d = CoreTiming::RegisterEvent("callbackD", CallbackTemplate<3>);
CoreTiming::EventType* cb_e = CoreTiming::RegisterEvent("callbackE", CallbackTemplate<4>);
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>);
Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", CallbackTemplate<3>);
Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", CallbackTemplate<4>);
// Enter slice 0
CoreTiming::Advance();
Core::Timing::Advance();
// D -> B -> C -> A -> E
CoreTiming::ScheduleEvent(1000, cb_a, CB_IDS[0]);
REQUIRE(1000 == CoreTiming::GetDowncount());
CoreTiming::ScheduleEvent(500, cb_b, CB_IDS[1]);
REQUIRE(500 == CoreTiming::GetDowncount());
CoreTiming::ScheduleEvent(800, cb_c, CB_IDS[2]);
REQUIRE(500 == CoreTiming::GetDowncount());
CoreTiming::ScheduleEvent(100, cb_d, CB_IDS[3]);
REQUIRE(100 == CoreTiming::GetDowncount());
CoreTiming::ScheduleEvent(1200, cb_e, CB_IDS[4]);
REQUIRE(100 == CoreTiming::GetDowncount());
Core::Timing::ScheduleEvent(1000, cb_a, CB_IDS[0]);
REQUIRE(1000 == Core::Timing::GetDowncount());
Core::Timing::ScheduleEvent(500, cb_b, CB_IDS[1]);
REQUIRE(500 == Core::Timing::GetDowncount());
Core::Timing::ScheduleEvent(800, cb_c, CB_IDS[2]);
REQUIRE(500 == Core::Timing::GetDowncount());
Core::Timing::ScheduleEvent(100, cb_d, CB_IDS[3]);
REQUIRE(100 == Core::Timing::GetDowncount());
Core::Timing::ScheduleEvent(1200, cb_e, CB_IDS[4]);
REQUIRE(100 == Core::Timing::GetDowncount());
AdvanceAndCheck(3, 400);
AdvanceAndCheck(1, 300);
@@ -86,36 +86,36 @@ TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
TEST_CASE("CoreTiming[Threadsave]", "[core]") {
ScopeInit guard;
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>);
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>);
CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", CallbackTemplate<2>);
CoreTiming::EventType* cb_d = CoreTiming::RegisterEvent("callbackD", CallbackTemplate<3>);
CoreTiming::EventType* cb_e = CoreTiming::RegisterEvent("callbackE", CallbackTemplate<4>);
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>);
Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", CallbackTemplate<3>);
Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", CallbackTemplate<4>);
// Enter slice 0
CoreTiming::Advance();
Core::Timing::Advance();
// D -> B -> C -> A -> E
CoreTiming::ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]);
Core::Timing::ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]);
// Manually force since ScheduleEventThreadsafe doesn't call it
CoreTiming::ForceExceptionCheck(1000);
REQUIRE(1000 == CoreTiming::GetDowncount());
CoreTiming::ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]);
Core::Timing::ForceExceptionCheck(1000);
REQUIRE(1000 == Core::Timing::GetDowncount());
Core::Timing::ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]);
// Manually force since ScheduleEventThreadsafe doesn't call it
CoreTiming::ForceExceptionCheck(500);
REQUIRE(500 == CoreTiming::GetDowncount());
CoreTiming::ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]);
Core::Timing::ForceExceptionCheck(500);
REQUIRE(500 == Core::Timing::GetDowncount());
Core::Timing::ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]);
// Manually force since ScheduleEventThreadsafe doesn't call it
CoreTiming::ForceExceptionCheck(800);
REQUIRE(500 == CoreTiming::GetDowncount());
CoreTiming::ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]);
Core::Timing::ForceExceptionCheck(800);
REQUIRE(500 == Core::Timing::GetDowncount());
Core::Timing::ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]);
// Manually force since ScheduleEventThreadsafe doesn't call it
CoreTiming::ForceExceptionCheck(100);
REQUIRE(100 == CoreTiming::GetDowncount());
CoreTiming::ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]);
Core::Timing::ForceExceptionCheck(100);
REQUIRE(100 == Core::Timing::GetDowncount());
Core::Timing::ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]);
// Manually force since ScheduleEventThreadsafe doesn't call it
CoreTiming::ForceExceptionCheck(1200);
REQUIRE(100 == CoreTiming::GetDowncount());
Core::Timing::ForceExceptionCheck(1200);
REQUIRE(100 == Core::Timing::GetDowncount());
AdvanceAndCheck(3, 400);
AdvanceAndCheck(1, 300);
@@ -143,42 +143,42 @@ TEST_CASE("CoreTiming[SharedSlot]", "[core]") {
ScopeInit guard;
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", FifoCallback<0>);
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", FifoCallback<1>);
CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", FifoCallback<2>);
CoreTiming::EventType* cb_d = CoreTiming::RegisterEvent("callbackD", FifoCallback<3>);
CoreTiming::EventType* cb_e = CoreTiming::RegisterEvent("callbackE", FifoCallback<4>);
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", FifoCallback<0>);
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", FifoCallback<1>);
Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", FifoCallback<2>);
Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", FifoCallback<3>);
Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", FifoCallback<4>);
CoreTiming::ScheduleEvent(1000, cb_a, CB_IDS[0]);
CoreTiming::ScheduleEvent(1000, cb_b, CB_IDS[1]);
CoreTiming::ScheduleEvent(1000, cb_c, CB_IDS[2]);
CoreTiming::ScheduleEvent(1000, cb_d, CB_IDS[3]);
CoreTiming::ScheduleEvent(1000, cb_e, CB_IDS[4]);
Core::Timing::ScheduleEvent(1000, cb_a, CB_IDS[0]);
Core::Timing::ScheduleEvent(1000, cb_b, CB_IDS[1]);
Core::Timing::ScheduleEvent(1000, cb_c, CB_IDS[2]);
Core::Timing::ScheduleEvent(1000, cb_d, CB_IDS[3]);
Core::Timing::ScheduleEvent(1000, cb_e, CB_IDS[4]);
// Enter slice 0
CoreTiming::Advance();
REQUIRE(1000 == CoreTiming::GetDowncount());
Core::Timing::Advance();
REQUIRE(1000 == Core::Timing::GetDowncount());
callbacks_ran_flags = 0;
counter = 0;
lateness = 0;
CoreTiming::AddTicks(CoreTiming::GetDowncount());
CoreTiming::Advance();
REQUIRE(MAX_SLICE_LENGTH == CoreTiming::GetDowncount());
Core::Timing::AddTicks(Core::Timing::GetDowncount());
Core::Timing::Advance();
REQUIRE(MAX_SLICE_LENGTH == Core::Timing::GetDowncount());
REQUIRE(0x1FULL == callbacks_ran_flags.to_ullong());
}
TEST_CASE("CoreTiming[PredictableLateness]", "[core]") {
TEST_CASE("Core::Timing[PredictableLateness]", "[core]") {
ScopeInit guard;
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>);
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>);
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
// Enter slice 0
CoreTiming::Advance();
Core::Timing::Advance();
CoreTiming::ScheduleEvent(100, cb_a, CB_IDS[0]);
CoreTiming::ScheduleEvent(200, cb_b, CB_IDS[1]);
Core::Timing::ScheduleEvent(100, cb_a, CB_IDS[0]);
Core::Timing::ScheduleEvent(200, cb_b, CB_IDS[1]);
AdvanceAndCheck(0, 90, 10, -10); // (100 - 10)
AdvanceAndCheck(1, MAX_SLICE_LENGTH, 50, -50);
@@ -192,9 +192,10 @@ static void RescheduleCallback(u64 userdata, s64 cycles_late) {
REQUIRE(reschedules >= 0);
REQUIRE(lateness == cycles_late);
if (reschedules > 0)
CoreTiming::ScheduleEvent(1000, reinterpret_cast<CoreTiming::EventType*>(userdata),
userdata);
if (reschedules > 0) {
Core::Timing::ScheduleEvent(1000, reinterpret_cast<Core::Timing::EventType*>(userdata),
userdata);
}
}
} // namespace ChainSchedulingTest
@@ -203,35 +204,35 @@ TEST_CASE("CoreTiming[ChainScheduling]", "[core]") {
ScopeInit guard;
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>);
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>);
CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", CallbackTemplate<2>);
CoreTiming::EventType* cb_rs =
CoreTiming::RegisterEvent("callbackReschedule", RescheduleCallback);
Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>);
Core::Timing::EventType* cb_rs =
Core::Timing::RegisterEvent("callbackReschedule", RescheduleCallback);
// Enter slice 0
CoreTiming::Advance();
Core::Timing::Advance();
CoreTiming::ScheduleEvent(800, cb_a, CB_IDS[0]);
CoreTiming::ScheduleEvent(1000, cb_b, CB_IDS[1]);
CoreTiming::ScheduleEvent(2200, cb_c, CB_IDS[2]);
CoreTiming::ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs));
REQUIRE(800 == CoreTiming::GetDowncount());
Core::Timing::ScheduleEvent(800, cb_a, CB_IDS[0]);
Core::Timing::ScheduleEvent(1000, cb_b, CB_IDS[1]);
Core::Timing::ScheduleEvent(2200, cb_c, CB_IDS[2]);
Core::Timing::ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs));
REQUIRE(800 == Core::Timing::GetDowncount());
reschedules = 3;
AdvanceAndCheck(0, 200); // cb_a
AdvanceAndCheck(1, 1000); // cb_b, cb_rs
REQUIRE(2 == reschedules);
CoreTiming::AddTicks(CoreTiming::GetDowncount());
CoreTiming::Advance(); // cb_rs
Core::Timing::AddTicks(Core::Timing::GetDowncount());
Core::Timing::Advance(); // cb_rs
REQUIRE(1 == reschedules);
REQUIRE(200 == CoreTiming::GetDowncount());
REQUIRE(200 == Core::Timing::GetDowncount());
AdvanceAndCheck(2, 800); // cb_c
CoreTiming::AddTicks(CoreTiming::GetDowncount());
CoreTiming::Advance(); // cb_rs
Core::Timing::AddTicks(Core::Timing::GetDowncount());
Core::Timing::Advance(); // cb_rs
REQUIRE(0 == reschedules);
REQUIRE(MAX_SLICE_LENGTH == CoreTiming::GetDowncount());
REQUIRE(MAX_SLICE_LENGTH == Core::Timing::GetDowncount());
}
+34 -3
View File
@@ -5,12 +5,12 @@ add_library(video_core STATIC
debug_utils/debug_utils.h
engines/fermi_2d.cpp
engines/fermi_2d.h
engines/kepler_compute.cpp
engines/kepler_compute.h
engines/kepler_memory.cpp
engines/kepler_memory.h
engines/maxwell_3d.cpp
engines/maxwell_3d.h
engines/maxwell_compute.cpp
engines/maxwell_compute.h
engines/maxwell_dma.cpp
engines/maxwell_dma.h
engines/shader_bytecode.h
@@ -44,6 +44,8 @@ add_library(video_core STATIC
renderer_opengl/gl_shader_cache.h
renderer_opengl/gl_shader_decompiler.cpp
renderer_opengl/gl_shader_decompiler.h
renderer_opengl/gl_shader_disk_cache.cpp
renderer_opengl/gl_shader_disk_cache.h
renderer_opengl/gl_shader_gen.cpp
renderer_opengl/gl_shader_gen.h
renderer_opengl/gl_shader_manager.cpp
@@ -59,6 +61,35 @@ add_library(video_core STATIC
renderer_opengl/renderer_opengl.h
renderer_opengl/utils.cpp
renderer_opengl/utils.h
shader/decode/arithmetic.cpp
shader/decode/arithmetic_immediate.cpp
shader/decode/bfe.cpp
shader/decode/bfi.cpp
shader/decode/shift.cpp
shader/decode/arithmetic_integer.cpp
shader/decode/arithmetic_integer_immediate.cpp
shader/decode/arithmetic_half.cpp
shader/decode/arithmetic_half_immediate.cpp
shader/decode/ffma.cpp
shader/decode/hfma2.cpp
shader/decode/conversion.cpp
shader/decode/memory.cpp
shader/decode/float_set_predicate.cpp
shader/decode/integer_set_predicate.cpp
shader/decode/half_set_predicate.cpp
shader/decode/predicate_set_register.cpp
shader/decode/predicate_set_predicate.cpp
shader/decode/register_set_predicate.cpp
shader/decode/float_set.cpp
shader/decode/integer_set.cpp
shader/decode/half_set.cpp
shader/decode/video.cpp
shader/decode/xmad.cpp
shader/decode/other.cpp
shader/decode.cpp
shader/shader_ir.cpp
shader/shader_ir.h
shader/track.cpp
surface.cpp
surface.h
textures/astc.cpp
@@ -73,4 +104,4 @@ add_library(video_core STATIC
create_target_directory_groups(video_core)
target_link_libraries(video_core PUBLIC common core)
target_link_libraries(video_core PRIVATE glad)
target_link_libraries(video_core PRIVATE glad lz4_static)
+4 -2
View File
@@ -35,8 +35,10 @@ void DmaPusher::DispatchCalls() {
bool DmaPusher::Step() {
if (dma_get != dma_put) {
// Push buffer non-empty, read a word
const CommandHeader command_header{
Memory::Read32(*gpu.MemoryManager().GpuToCpuAddress(dma_get))};
const auto address = gpu.MemoryManager().GpuToCpuAddress(dma_get);
ASSERT_MSG(address, "Invalid GPU address");
const CommandHeader command_header{Memory::Read32(*address)};
dma_get += sizeof(u32);
+1 -1
View File
@@ -83,7 +83,7 @@ private:
u32 subchannel; ///< Current subchannel
u32 method_count; ///< Current method count
u32 length_pending; ///< Large NI command length pending
bool non_incrementing; ///< Current commands NI flag
bool non_incrementing; ///< Current command's NI flag
};
DmaState dma_state{};
+15 -45
View File
@@ -21,7 +21,9 @@ void Fermi2D::CallMethod(const GPU::MethodCall& method_call) {
regs.reg_array[method_call.method] = method_call.argument;
switch (method_call.method) {
case FERMI2D_REG_INDEX(trigger): {
// Trigger the surface copy on the last register write. This is blit_src_y, but this is 64-bit,
// so trigger on the second 32-bit write.
case FERMI2D_REG_INDEX(blit_src_y) + 1: {
HandleSurfaceCopy();
break;
}
@@ -32,55 +34,23 @@ void Fermi2D::HandleSurfaceCopy() {
LOG_WARNING(HW_GPU, "Requested a surface copy with operation {}",
static_cast<u32>(regs.operation));
const GPUVAddr source = regs.src.Address();
const GPUVAddr dest = regs.dst.Address();
// TODO(Subv): Only same-format and same-size copies are allowed for now.
ASSERT(regs.src.format == regs.dst.format);
ASSERT(regs.src.width * regs.src.height == regs.dst.width * regs.dst.height);
// TODO(Subv): Only raw copies are implemented.
ASSERT(regs.operation == Regs::Operation::SrcCopy);
const VAddr source_cpu = *memory_manager.GpuToCpuAddress(source);
const VAddr dest_cpu = *memory_manager.GpuToCpuAddress(dest);
const u32 src_blit_x1{static_cast<u32>(regs.blit_src_x >> 32)};
const u32 src_blit_y1{static_cast<u32>(regs.blit_src_y >> 32)};
const u32 src_blit_x2{
static_cast<u32>((regs.blit_src_x + (regs.blit_dst_width * regs.blit_du_dx)) >> 32)};
const u32 src_blit_y2{
static_cast<u32>((regs.blit_src_y + (regs.blit_dst_height * regs.blit_dv_dy)) >> 32)};
u32 src_bytes_per_pixel = RenderTargetBytesPerPixel(regs.src.format);
u32 dst_bytes_per_pixel = RenderTargetBytesPerPixel(regs.dst.format);
const MathUtil::Rectangle<u32> src_rect{src_blit_x1, src_blit_y1, src_blit_x2, src_blit_y2};
const MathUtil::Rectangle<u32> dst_rect{regs.blit_dst_x, regs.blit_dst_y,
regs.blit_dst_x + regs.blit_dst_width,
regs.blit_dst_y + regs.blit_dst_height};
if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst)) {
// All copies here update the main memory, so mark all rasterizer states as invalid.
Core::System::GetInstance().GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
rasterizer.FlushRegion(source_cpu, src_bytes_per_pixel * regs.src.width * regs.src.height);
// We have to invalidate the destination region to evict any outdated surfaces from the
// cache. We do this before actually writing the new data because the destination address
// might contain a dirty surface that will have to be written back to memory.
rasterizer.InvalidateRegion(dest_cpu,
dst_bytes_per_pixel * regs.dst.width * regs.dst.height);
if (regs.src.linear == regs.dst.linear) {
// If the input layout and the output layout are the same, just perform a raw copy.
ASSERT(regs.src.BlockHeight() == regs.dst.BlockHeight());
Memory::CopyBlock(dest_cpu, source_cpu,
src_bytes_per_pixel * regs.dst.width * regs.dst.height);
return;
}
u8* src_buffer = Memory::GetPointer(source_cpu);
u8* dst_buffer = Memory::GetPointer(dest_cpu);
if (!regs.src.linear && regs.dst.linear) {
// If the input is tiled and the output is linear, deswizzle the input and copy it over.
Texture::CopySwizzledData(regs.src.width, regs.src.height, regs.src.depth,
src_bytes_per_pixel, dst_bytes_per_pixel, src_buffer,
dst_buffer, true, regs.src.BlockHeight(),
regs.src.BlockDepth(), 0);
} else {
// If the input is linear and the output is tiled, swizzle the input and copy it over.
Texture::CopySwizzledData(regs.src.width, regs.src.height, regs.src.depth,
src_bytes_per_pixel, dst_bytes_per_pixel, dst_buffer,
src_buffer, false, regs.dst.BlockHeight(),
regs.dst.BlockDepth(), 0);
}
if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst, src_rect, dst_rect)) {
UNIMPLEMENTED();
}
}
+24 -5
View File
@@ -94,12 +94,22 @@ public:
Operation operation;
INSERT_PADDING_WORDS(0x9);
INSERT_PADDING_WORDS(0x177);
// TODO(Subv): This is only a guess.
u32 trigger;
u32 blit_control;
INSERT_PADDING_WORDS(0x1A3);
INSERT_PADDING_WORDS(0x8);
u32 blit_dst_x;
u32 blit_dst_y;
u32 blit_dst_width;
u32 blit_dst_height;
u64 blit_du_dx;
u64 blit_dv_dy;
u64 blit_src_x;
u64 blit_src_y;
INSERT_PADDING_WORDS(0x21);
};
std::array<u32, NUM_REGS> reg_array;
};
@@ -122,7 +132,16 @@ private:
ASSERT_REG_POSITION(dst, 0x80);
ASSERT_REG_POSITION(src, 0x8C);
ASSERT_REG_POSITION(operation, 0xAB);
ASSERT_REG_POSITION(trigger, 0xB5);
ASSERT_REG_POSITION(blit_control, 0x223);
ASSERT_REG_POSITION(blit_dst_x, 0x22c);
ASSERT_REG_POSITION(blit_dst_y, 0x22d);
ASSERT_REG_POSITION(blit_dst_width, 0x22e);
ASSERT_REG_POSITION(blit_dst_height, 0x22f);
ASSERT_REG_POSITION(blit_du_dx, 0x230);
ASSERT_REG_POSITION(blit_dv_dy, 0x232);
ASSERT_REG_POSITION(blit_src_x, 0x234);
ASSERT_REG_POSITION(blit_src_y, 0x236);
#undef ASSERT_REG_POSITION
} // namespace Tegra::Engines
+34
View File
@@ -0,0 +1,34 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/core.h"
#include "core/memory.h"
#include "video_core/engines/kepler_compute.h"
#include "video_core/memory_manager.h"
namespace Tegra::Engines {
KeplerCompute::KeplerCompute(MemoryManager& memory_manager) : memory_manager{memory_manager} {}
KeplerCompute::~KeplerCompute() = default;
void KeplerCompute::CallMethod(const GPU::MethodCall& method_call) {
ASSERT_MSG(method_call.method < Regs::NUM_REGS,
"Invalid KeplerCompute register, increase the size of the Regs structure");
regs.reg_array[method_call.method] = method_call.argument;
switch (method_call.method) {
case KEPLER_COMPUTE_REG_INDEX(launch):
// Abort execution since compute shaders can be used to alter game memory (e.g. CUDA
// kernels)
UNREACHABLE_MSG("Compute shaders are not implemented");
break;
default:
break;
}
}
} // namespace Tegra::Engines
@@ -10,47 +10,48 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
namespace Tegra::Engines {
#define MAXWELL_COMPUTE_REG_INDEX(field_name) \
(offsetof(Tegra::Engines::MaxwellCompute::Regs, field_name) / sizeof(u32))
#define KEPLER_COMPUTE_REG_INDEX(field_name) \
(offsetof(Tegra::Engines::KeplerCompute::Regs, field_name) / sizeof(u32))
class MaxwellCompute final {
class KeplerCompute final {
public:
MaxwellCompute() = default;
~MaxwellCompute() = default;
explicit KeplerCompute(MemoryManager& memory_manager);
~KeplerCompute();
static constexpr std::size_t NumConstBuffers = 8;
struct Regs {
static constexpr std::size_t NUM_REGS = 0xCF8;
union {
struct {
INSERT_PADDING_WORDS(0x281);
INSERT_PADDING_WORDS(0xAF);
union {
u32 compute_end;
BitField<0, 1, u32> unknown;
} compute;
u32 launch;
INSERT_PADDING_WORDS(0xA76);
INSERT_PADDING_WORDS(0xC48);
};
std::array<u32, NUM_REGS> reg_array;
};
} regs{};
static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32),
"MaxwellCompute Regs has wrong size");
"KeplerCompute Regs has wrong size");
MemoryManager& memory_manager;
/// Write the value to the register identified by method.
void CallMethod(const GPU::MethodCall& method_call);
};
#define ASSERT_REG_POSITION(field_name, position) \
static_assert(offsetof(MaxwellCompute::Regs, field_name) == position * 4, \
static_assert(offsetof(KeplerCompute::Regs, field_name) == position * 4, \
"Field " #field_name " has invalid position")
ASSERT_REG_POSITION(compute, 0x281);
ASSERT_REG_POSITION(launch, 0xAF);
#undef ASSERT_REG_POSITION

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