diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/CMakeLists.txt | 9 | ||||
-rw-r--r-- | src/core/core.cpp | 13 | ||||
-rw-r--r-- | src/core/core.h | 6 | ||||
-rw-r--r-- | src/core/hle/service/am/am.cpp | 14 | ||||
-rw-r--r-- | src/core/hle/service/am/am.h | 1 | ||||
-rw-r--r-- | src/core/hle/service/ngc/ngc.cpp | 150 | ||||
-rw-r--r-- | src/core/hle/service/ngc/ngc.h (renamed from src/core/hle/service/ngct/ngct.h) | 4 | ||||
-rw-r--r-- | src/core/hle/service/ngct/ngct.cpp | 62 | ||||
-rw-r--r-- | src/core/hle/service/service.cpp | 4 | ||||
-rw-r--r-- | src/core/hle/service/sockets/bsd.cpp | 2 | ||||
-rw-r--r-- | src/core/internal_network/network.cpp | 89 | ||||
-rw-r--r-- | src/core/internal_network/network.h | 3 | ||||
-rw-r--r-- | src/core/tools/renderdoc.cpp | 55 | ||||
-rw-r--r-- | src/core/tools/renderdoc.h | 22 |
14 files changed, 360 insertions, 74 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c33910ade..30d2f7df6 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -627,8 +627,8 @@ add_library(core STATIC hle/service/nfp/nfp_interface.h hle/service/nfp/nfp_result.h hle/service/nfp/nfp_types.h - hle/service/ngct/ngct.cpp - hle/service/ngct/ngct.h + hle/service/ngc/ngc.cpp + hle/service/ngc/ngc.h hle/service/nifm/nifm.cpp hle/service/nifm/nifm.h hle/service/nim/nim.cpp @@ -864,6 +864,8 @@ add_library(core STATIC telemetry_session.h tools/freezer.cpp tools/freezer.h + tools/renderdoc.cpp + tools/renderdoc.h ) if (MSVC) @@ -879,6 +881,7 @@ else() -Werror=conversion -Wno-sign-conversion + -Wno-cast-function-type $<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation> ) @@ -887,7 +890,7 @@ endif() create_target_directory_groups(core) target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core nx_tzdb) -target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus) +target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus renderdoc) if (MINGW) target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) endif() diff --git a/src/core/core.cpp b/src/core/core.cpp index f075ae7fa..e8300cd05 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -51,6 +51,7 @@ #include "core/reporter.h" #include "core/telemetry_session.h" #include "core/tools/freezer.h" +#include "core/tools/renderdoc.h" #include "network/network.h" #include "video_core/host1x/host1x.h" #include "video_core/renderer_base.h" @@ -281,6 +282,10 @@ struct System::Impl { microprofile_cpu[2] = MICROPROFILE_TOKEN(ARM_CPU2); microprofile_cpu[3] = MICROPROFILE_TOKEN(ARM_CPU3); + if (Settings::values.enable_renderdoc_hotkey) { + renderdoc_api = std::make_unique<Tools::RenderdocAPI>(); + } + LOG_DEBUG(Core, "Initialized OK"); return SystemResultStatus::Success; @@ -406,6 +411,7 @@ struct System::Impl { gpu_core->NotifyShutdown(); } + Network::CancelPendingSocketOperations(); kernel.SuspendApplication(true); if (services) { services->KillNVNFlinger(); @@ -427,6 +433,7 @@ struct System::Impl { debugger.reset(); kernel.Shutdown(); memory.Reset(); + Network::RestartSocketOperations(); if (auto room_member = room_network.GetRoomMember().lock()) { Network::GameInfo game_info{}; @@ -519,6 +526,8 @@ struct System::Impl { std::unique_ptr<Tools::Freezer> memory_freezer; std::array<u8, 0x20> build_id{}; + std::unique_ptr<Tools::RenderdocAPI> renderdoc_api; + /// Frontend applets Service::AM::Applets::AppletManager applet_manager; @@ -1022,6 +1031,10 @@ const Network::RoomNetwork& System::GetRoomNetwork() const { return impl->room_network; } +Tools::RenderdocAPI& System::GetRenderdocAPI() { + return *impl->renderdoc_api; +} + void System::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) { return impl->kernel.RunServer(std::move(server_manager)); } diff --git a/src/core/core.h b/src/core/core.h index fba312125..df20f26f3 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -102,6 +102,10 @@ namespace Network { class RoomNetwork; } +namespace Tools { +class RenderdocAPI; +} + namespace Core { class ARM_Interface; @@ -413,6 +417,8 @@ public: /// Gets an immutable reference to the Room Network. [[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const; + [[nodiscard]] Tools::RenderdocAPI& GetRenderdocAPI(); + void SetExitLocked(bool locked); bool GetExitLocked() const; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index f9c4f9678..8ffdd19e7 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1386,7 +1386,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"}, {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"}, {27, &IApplicationFunctions::CreateCacheStorage, "CreateCacheStorage"}, - {28, nullptr, "GetSaveDataSizeMax"}, + {28, &IApplicationFunctions::GetSaveDataSizeMax, "GetSaveDataSizeMax"}, {29, nullptr, "GetCacheStorageMax"}, {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"}, {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, @@ -1821,6 +1821,18 @@ void IApplicationFunctions::CreateCacheStorage(HLERequestContext& ctx) { rb.PushRaw(resp); } +void IApplicationFunctions::GetSaveDataSizeMax(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + constexpr u64 size_max_normal = 0xFFFFFFF; + constexpr u64 size_max_journal = 0xFFFFFFF; + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.Push(size_max_normal); + rb.Push(size_max_journal); +} + void IApplicationFunctions::QueryApplicationPlayStatistics(HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index f75a665b2..f86841c60 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -316,6 +316,7 @@ private: void ExtendSaveData(HLERequestContext& ctx); void GetSaveDataSize(HLERequestContext& ctx); void CreateCacheStorage(HLERequestContext& ctx); + void GetSaveDataSizeMax(HLERequestContext& ctx); void BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx); void EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx); void BeginBlockingHomeButton(HLERequestContext& ctx); diff --git a/src/core/hle/service/ngc/ngc.cpp b/src/core/hle/service/ngc/ngc.cpp new file mode 100644 index 000000000..c26019ec0 --- /dev/null +++ b/src/core/hle/service/ngc/ngc.cpp @@ -0,0 +1,150 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/string_util.h" +#include "core/core.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/ngc/ngc.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Service::NGC { + +class NgctServiceImpl final : public ServiceFramework<NgctServiceImpl> { +public: + explicit NgctServiceImpl(Core::System& system_) : ServiceFramework{system_, "ngct:u"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &NgctServiceImpl::Match, "Match"}, + {1, &NgctServiceImpl::Filter, "Filter"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void Match(HLERequestContext& ctx) { + const auto buffer = ctx.ReadBuffer(); + const auto text = Common::StringFromFixedZeroTerminatedBuffer( + reinterpret_cast<const char*>(buffer.data()), buffer.size()); + + LOG_WARNING(Service_NGC, "(STUBBED) called, text={}", text); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + // Return false since we don't censor anything + rb.Push(false); + } + + void Filter(HLERequestContext& ctx) { + const auto buffer = ctx.ReadBuffer(); + const auto text = Common::StringFromFixedZeroTerminatedBuffer( + reinterpret_cast<const char*>(buffer.data()), buffer.size()); + + LOG_WARNING(Service_NGC, "(STUBBED) called, text={}", text); + + // Return the same string since we don't censor anything + ctx.WriteBuffer(buffer); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } +}; + +class NgcServiceImpl final : public ServiceFramework<NgcServiceImpl> { +public: + explicit NgcServiceImpl(Core::System& system_) : ServiceFramework(system_, "ngc:u") { + // clang-format off + static const FunctionInfo functions[] = { + {0, &NgcServiceImpl::GetContentVersion, "GetContentVersion"}, + {1, &NgcServiceImpl::Check, "Check"}, + {2, &NgcServiceImpl::Mask, "Mask"}, + {3, &NgcServiceImpl::Reload, "Reload"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + static constexpr u32 NgcContentVersion = 1; + + // This is nn::ngc::detail::ProfanityFilterOption + struct ProfanityFilterOption { + INSERT_PADDING_BYTES_NOINIT(0x20); + }; + static_assert(sizeof(ProfanityFilterOption) == 0x20, + "ProfanityFilterOption has incorrect size"); + + void GetContentVersion(HLERequestContext& ctx) { + LOG_INFO(Service_NGC, "(STUBBED) called"); + + // This calls nn::ngc::ProfanityFilter::GetContentVersion + const u32 version = NgcContentVersion; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(version); + } + + void Check(HLERequestContext& ctx) { + LOG_INFO(Service_NGC, "(STUBBED) called"); + + struct InputParameters { + u32 flags; + ProfanityFilterOption option; + }; + + IPC::RequestParser rp{ctx}; + [[maybe_unused]] const auto params = rp.PopRaw<InputParameters>(); + [[maybe_unused]] const auto input = ctx.ReadBuffer(0); + + // This calls nn::ngc::ProfanityFilter::CheckProfanityWords + const u32 out_flags = 0; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(out_flags); + } + + void Mask(HLERequestContext& ctx) { + LOG_INFO(Service_NGC, "(STUBBED) called"); + + struct InputParameters { + u32 flags; + ProfanityFilterOption option; + }; + + IPC::RequestParser rp{ctx}; + [[maybe_unused]] const auto params = rp.PopRaw<InputParameters>(); + const auto input = ctx.ReadBuffer(0); + + // This calls nn::ngc::ProfanityFilter::MaskProfanityWordsInText + const u32 out_flags = 0; + ctx.WriteBuffer(input); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(out_flags); + } + + void Reload(HLERequestContext& ctx) { + LOG_INFO(Service_NGC, "(STUBBED) called"); + + // This reloads the database. + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } +}; + +void LoopProcess(Core::System& system) { + auto server_manager = std::make_unique<ServerManager>(system); + + server_manager->RegisterNamedService("ngct:u", std::make_shared<NgctServiceImpl>(system)); + server_manager->RegisterNamedService("ngc:u", std::make_shared<NgcServiceImpl>(system)); + ServerManager::RunServer(std::move(server_manager)); +} + +} // namespace Service::NGC diff --git a/src/core/hle/service/ngct/ngct.h b/src/core/hle/service/ngc/ngc.h index 27c34dad4..823b1aa81 100644 --- a/src/core/hle/service/ngct/ngct.h +++ b/src/core/hle/service/ngc/ngc.h @@ -7,8 +7,8 @@ namespace Core { class System; } -namespace Service::NGCT { +namespace Service::NGC { void LoopProcess(Core::System& system); -} // namespace Service::NGCT +} // namespace Service::NGC diff --git a/src/core/hle/service/ngct/ngct.cpp b/src/core/hle/service/ngct/ngct.cpp deleted file mode 100644 index 493c80ed2..000000000 --- a/src/core/hle/service/ngct/ngct.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/string_util.h" -#include "core/core.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/ngct/ngct.h" -#include "core/hle/service/server_manager.h" -#include "core/hle/service/service.h" - -namespace Service::NGCT { - -class IService final : public ServiceFramework<IService> { -public: - explicit IService(Core::System& system_) : ServiceFramework{system_, "ngct:u"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IService::Match, "Match"}, - {1, &IService::Filter, "Filter"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void Match(HLERequestContext& ctx) { - const auto buffer = ctx.ReadBuffer(); - const auto text = Common::StringFromFixedZeroTerminatedBuffer( - reinterpret_cast<const char*>(buffer.data()), buffer.size()); - - LOG_WARNING(Service_NGCT, "(STUBBED) called, text={}", text); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - // Return false since we don't censor anything - rb.Push(false); - } - - void Filter(HLERequestContext& ctx) { - const auto buffer = ctx.ReadBuffer(); - const auto text = Common::StringFromFixedZeroTerminatedBuffer( - reinterpret_cast<const char*>(buffer.data()), buffer.size()); - - LOG_WARNING(Service_NGCT, "(STUBBED) called, text={}", text); - - // Return the same string since we don't censor anything - ctx.WriteBuffer(buffer); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } -}; - -void LoopProcess(Core::System& system) { - auto server_manager = std::make_unique<ServerManager>(system); - - server_manager->RegisterNamedService("ngct:u", std::make_shared<IService>(system)); - ServerManager::RunServer(std::move(server_manager)); -} - -} // namespace Service::NGCT diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 69cdb5918..0ad607391 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -43,7 +43,7 @@ #include "core/hle/service/ncm/ncm.h" #include "core/hle/service/nfc/nfc.h" #include "core/hle/service/nfp/nfp.h" -#include "core/hle/service/ngct/ngct.h" +#include "core/hle/service/ngc/ngc.h" #include "core/hle/service/nifm/nifm.h" #include "core/hle/service/nim/nim.h" #include "core/hle/service/npns/npns.h" @@ -257,7 +257,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system kernel.RunOnGuestCoreProcess("NCM", [&] { NCM::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("nfc", [&] { NFC::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("nfp", [&] { NFP::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("ngct", [&] { NGCT::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("ngc", [&] { NGC::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("nifm", [&] { NIFM::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("nim", [&] { NIM::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("npns", [&] { NPNS::LoopProcess(system); }); diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index d8509c1dd..85849d5f3 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -170,7 +170,7 @@ void BSD::Socket(HLERequestContext& ctx) { } void BSD::Select(HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + LOG_DEBUG(Service, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 4}; diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp index 5d28300e6..a983f23ea 100644 --- a/src/core/internal_network/network.cpp +++ b/src/core/internal_network/network.cpp @@ -48,15 +48,32 @@ enum class CallType { using socklen_t = int; +SOCKET interrupt_socket = static_cast<SOCKET>(-1); + +void InterruptSocketOperations() { + closesocket(interrupt_socket); +} + +void AcknowledgeInterrupt() { + interrupt_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); +} + void Initialize() { WSADATA wsa_data; (void)WSAStartup(MAKEWORD(2, 2), &wsa_data); + + AcknowledgeInterrupt(); } void Finalize() { + InterruptSocketOperations(); WSACleanup(); } +SOCKET GetInterruptSocket() { + return interrupt_socket; +} + sockaddr TranslateFromSockAddrIn(SockAddrIn input) { sockaddr_in result; @@ -157,9 +174,42 @@ constexpr int SD_RECEIVE = SHUT_RD; constexpr int SD_SEND = SHUT_WR; constexpr int SD_BOTH = SHUT_RDWR; -void Initialize() {} +int interrupt_pipe_fd[2] = {-1, -1}; -void Finalize() {} +void Initialize() { + if (pipe(interrupt_pipe_fd) != 0) { + LOG_ERROR(Network, "Failed to create interrupt pipe!"); + } + int flags = fcntl(interrupt_pipe_fd[0], F_GETFL); + ASSERT_MSG(fcntl(interrupt_pipe_fd[0], F_SETFL, flags | O_NONBLOCK) == 0, + "Failed to set nonblocking state for interrupt pipe"); +} + +void Finalize() { + if (interrupt_pipe_fd[0] >= 0) { + close(interrupt_pipe_fd[0]); + } + if (interrupt_pipe_fd[1] >= 0) { + close(interrupt_pipe_fd[1]); + } +} + +void InterruptSocketOperations() { + u8 value = 0; + ASSERT(write(interrupt_pipe_fd[1], &value, sizeof(value)) == 1); +} + +void AcknowledgeInterrupt() { + u8 value = 0; + ssize_t ret = read(interrupt_pipe_fd[0], &value, sizeof(value)); + if (ret != 1 && errno != EAGAIN && errno != EWOULDBLOCK) { + LOG_ERROR(Network, "Failed to acknowledge interrupt on shutdown"); + } +} + +SOCKET GetInterruptSocket() { + return interrupt_pipe_fd[0]; +} sockaddr TranslateFromSockAddrIn(SockAddrIn input) { sockaddr_in result; @@ -490,6 +540,14 @@ NetworkInstance::~NetworkInstance() { Finalize(); } +void CancelPendingSocketOperations() { + InterruptSocketOperations(); +} + +void RestartSocketOperations() { + AcknowledgeInterrupt(); +} + std::optional<IPv4Address> GetHostIPv4Address() { const auto network_interface = Network::GetSelectedNetworkInterface(); if (!network_interface.has_value()) { @@ -560,7 +618,14 @@ std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { return result; }); - const int result = WSAPoll(host_pollfds.data(), static_cast<ULONG>(num), timeout); + host_pollfds.push_back(WSAPOLLFD{ + .fd = GetInterruptSocket(), + .events = POLLIN, + .revents = 0, + }); + + const int result = + WSAPoll(host_pollfds.data(), static_cast<ULONG>(host_pollfds.size()), timeout); if (result == 0) { ASSERT(std::all_of(host_pollfds.begin(), host_pollfds.end(), [](WSAPOLLFD fd) { return fd.revents == 0; })); @@ -627,6 +692,24 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() { sockaddr_in addr; socklen_t addrlen = sizeof(addr); + + std::vector<WSAPOLLFD> host_pollfds{ + WSAPOLLFD{fd, POLLIN, 0}, + WSAPOLLFD{GetInterruptSocket(), POLLIN, 0}, + }; + + while (true) { + const int pollres = + WSAPoll(host_pollfds.data(), static_cast<ULONG>(host_pollfds.size()), -1); + if (host_pollfds[1].revents != 0) { + // Interrupt signaled before a client could be accepted, break + return {AcceptResult{}, Errno::AGAIN}; + } + if (pollres > 0) { + break; + } + } + const SOCKET new_socket = accept(fd, reinterpret_cast<sockaddr*>(&addr), &addrlen); if (new_socket == INVALID_SOCKET) { diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h index c7e20ae34..b7b7d773a 100644 --- a/src/core/internal_network/network.h +++ b/src/core/internal_network/network.h @@ -96,6 +96,9 @@ public: ~NetworkInstance(); }; +void CancelPendingSocketOperations(); +void RestartSocketOperations(); + #ifdef _WIN32 constexpr IPv4Address TranslateIPv4(in_addr addr) { auto& bytes = addr.S_un.S_un_b; diff --git a/src/core/tools/renderdoc.cpp b/src/core/tools/renderdoc.cpp new file mode 100644 index 000000000..44d24822a --- /dev/null +++ b/src/core/tools/renderdoc.cpp @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <renderdoc_app.h> + +#include "common/assert.h" +#include "common/dynamic_library.h" +#include "core/tools/renderdoc.h" + +#ifdef WIN32 +#include <windows.h> +#else +#include <dlfcn.h> +#endif + +namespace Tools { + +RenderdocAPI::RenderdocAPI() { +#ifdef WIN32 + if (HMODULE mod = GetModuleHandleA("renderdoc.dll")) { + const auto RENDERDOC_GetAPI = + reinterpret_cast<pRENDERDOC_GetAPI>(GetProcAddress(mod, "RENDERDOC_GetAPI")); + const s32 ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_6_0, (void**)&rdoc_api); + ASSERT(ret == 1); + } +#else +#ifdef ANDROID + static constexpr const char RENDERDOC_LIB[] = "libVkLayer_GLES_RenderDoc.so"; +#else + static constexpr const char RENDERDOC_LIB[] = "librenderdoc.so"; +#endif + if (void* mod = dlopen(RENDERDOC_LIB, RTLD_NOW | RTLD_NOLOAD)) { + const auto RENDERDOC_GetAPI = + reinterpret_cast<pRENDERDOC_GetAPI>(dlsym(mod, "RENDERDOC_GetAPI")); + const s32 ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_6_0, (void**)&rdoc_api); + ASSERT(ret == 1); + } +#endif +} + +RenderdocAPI::~RenderdocAPI() = default; + +void RenderdocAPI::ToggleCapture() { + if (!rdoc_api) [[unlikely]] { + return; + } + if (!is_capturing) { + rdoc_api->StartFrameCapture(NULL, NULL); + } else { + rdoc_api->EndFrameCapture(NULL, NULL); + } + is_capturing = !is_capturing; +} + +} // namespace Tools diff --git a/src/core/tools/renderdoc.h b/src/core/tools/renderdoc.h new file mode 100644 index 000000000..0e5e43da5 --- /dev/null +++ b/src/core/tools/renderdoc.h @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +struct RENDERDOC_API_1_6_0; + +namespace Tools { + +class RenderdocAPI { +public: + explicit RenderdocAPI(); + ~RenderdocAPI(); + + void ToggleCapture(); + +private: + RENDERDOC_API_1_6_0* rdoc_api{}; + bool is_capturing{false}; +}; + +} // namespace Tools |