From 99adcc614fd1c5cd41a27a239274e99a0f479894 Mon Sep 17 00:00:00 2001 From: baldurk Date: Tue, 15 Feb 2022 15:59:46 +0000 Subject: [PATCH] Allow multiple remote servers on one hostname, on different ports * When a port is specified (with the usual :12345 suffix on the hostname) we use that for remote replay connections. We disable target control enumeration since that requires a port _range_ and captured applications self-assign those ports. Those can still be accessed via a normal unsuffixed remote specifier - even if there is no remote server running on the default port. --- docs/how/how_network_capture_replay.rst | 4 ++++ renderdoc/api/replay/renderdoc_replay.h | 7 ++++--- renderdoc/core/remote_server.cpp | 21 +++++++++++++++++++++ renderdoc/core/target_control.cpp | 12 ++++++++++++ renderdoc/replay/entry_points.cpp | 20 +++++++++++++++----- renderdoccmd/renderdoccmd.cpp | 13 ++++++++++--- 6 files changed, 66 insertions(+), 11 deletions(-) diff --git a/docs/how/how_network_capture_replay.rst b/docs/how/how_network_capture_replay.rst index 22c2fb9fd..b2cf67c96 100644 --- a/docs/how/how_network_capture_replay.rst +++ b/docs/how/how_network_capture_replay.rst @@ -31,6 +31,10 @@ This manager allows you to configure both which remote hosts are available, as w To add a new host, simply type its hostname in the hostname box and click ``Add``. It will be added to the list of known remote hosts, and immediately a network lookup will happen to see if the remote server or any running RenderDoc-injected applications are alive. +.. note:: + + You can run multiple remote servers on a single hostname by listening on different ports and specifying that in the hostname as with ``hostname:12345``. + At minimum, this is all you must configure. However it is recommended that you configure a command which can be run on the **local** machine which will **remotely** start the remote server on that host. The reason for this is that it allows easy capture each time with zero manual extra steps. .. figure:: ../imgs/Screenshots/RemoteHostConfigure.png diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index fa3177bc9..0f4906a08 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -1872,14 +1872,15 @@ This function will block until a remote connection tells the server to shut down ``killReplay`` callback returns ``True``. :param str listenhost: The name of the interface to listen on. +:param int port: The port to listen on, or ``0`` to listen on the default port. :param KillCallback killReplay: A callback that returns a ``bool`` indicating if the server should be shut down or not. :param PreviewWindowCallback previewWindow: A callback that returns information for a preview window when the server wants to display some preview of the ongoing replay. )"); -extern "C" RENDERDOC_API void RENDERDOC_CC -RENDERDOC_BecomeRemoteServer(const rdcstr &listenhost, RENDERDOC_KillCallback killReplay, - RENDERDOC_PreviewWindowCallback previewWindow); +extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_BecomeRemoteServer( + const rdcstr &listenhost, uint16_t port, RENDERDOC_KillCallback killReplay, + RENDERDOC_PreviewWindowCallback previewWindow); ////////////////////////////////////////////////////////////////////////// // Injection/execution capture functions. diff --git a/renderdoc/core/remote_server.cpp b/renderdoc/core/remote_server.cpp index 525a4c4e7..95dc4965c 100644 --- a/renderdoc/core/remote_server.cpp +++ b/renderdoc/core/remote_server.cpp @@ -289,6 +289,8 @@ static void ActiveRemoteClientThread(ClientThread *threadData, writer.SetStreamingMode(true); reader.SetStreamingMode(true); + uint32_t captureNum = 0; + while(client) { if(client && !client->Connected()) @@ -396,6 +398,13 @@ static void ActiveRemoteClientThread(ClientThread *threadData, rdcstr dummy, dummy2; FileIO::GetDefaultFiles("remotecopy", path, dummy, dummy2); + // remove the .rdc + path.erase(path.size() - 4, 4); + + // append a process- and capture- specific suffix to avoid clashes + path += StringFormat::Fmt("_remotecopy_%u_%u.rdc", Process::GetCurrentPID(), captureNum); + captureNum++; + RDCLOG("Copying file to local path '%s'.", path.c_str()); FileIO::CreateParentDirectory(path); @@ -1147,6 +1156,18 @@ RENDERDOC_CreateRemoteServerConnection(const rdcstr &URL, IRemoteServer **rend) port = protocol->RemapPort(deviceID, port); } + else + { + int32_t idx = deviceID.indexOf(':'); + if(idx > 0) + { + host = deviceID.substr(0, idx); + port = atoi(deviceID.substr(idx + 1).c_str()) & 0xffff; + } + } + + if(port == 0) + return ReplayStatus::NetworkIOFailed; Network::Socket *sock = Network::CreateClientSocket(host, port, 750); diff --git a/renderdoc/core/target_control.cpp b/renderdoc/core/target_control.cpp index 1da00b584..29ca9305f 100644 --- a/renderdoc/core/target_control.cpp +++ b/renderdoc/core/target_control.cpp @@ -945,6 +945,18 @@ extern "C" RENDERDOC_API ITargetControl *RENDERDOC_CC RENDERDOC_CreateTargetCont port = protocol->RemapPort(deviceID, port); } + else + { + int32_t idx = deviceID.indexOf(':'); + if(idx > 0) + { + host = deviceID.substr(0, idx); + port = atoi(deviceID.substr(idx + 1).c_str()) & 0xffff; + } + } + + if(port == 0) + return NULL; Network::Socket *sock = Network::CreateClientSocket(host, port, 750); diff --git a/renderdoc/replay/entry_points.cpp b/renderdoc/replay/entry_points.cpp index 16f6e3c4f..e178ea6b2 100644 --- a/renderdoc/replay/entry_points.cpp +++ b/renderdoc/replay/entry_points.cpp @@ -429,6 +429,13 @@ extern "C" RENDERDOC_API uint32_t RENDERDOC_CC RENDERDOC_EnumerateRemoteTargets( if(host.empty()) return 0; } + else + { + // hosts specified with a port are supported only for replay, do not enumerate targets on those + // hosts + if(URL.contains(':')) + return 0; + } for(; nextIdent <= RenderDoc_LastTargetControlPort; nextIdent++) { @@ -475,9 +482,9 @@ RENDERDOC_GetDeviceProtocolController(const rdcstr &protocol) return RenderDoc::Inst().GetDeviceProtocol(protocol); } -extern "C" RENDERDOC_API void RENDERDOC_CC -RENDERDOC_BecomeRemoteServer(const rdcstr &listenhost, RENDERDOC_KillCallback killReplay, - RENDERDOC_PreviewWindowCallback previewWindow) +extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_BecomeRemoteServer( + const rdcstr &listenhost, uint16_t port, RENDERDOC_KillCallback killReplay, + RENDERDOC_PreviewWindowCallback previewWindow) { // ensure a sensible default if no callback is provided, that just never kills if(!killReplay) @@ -490,8 +497,11 @@ RENDERDOC_BecomeRemoteServer(const rdcstr &listenhost, RENDERDOC_KillCallback ki return ret; }; - RenderDoc::Inst().BecomeRemoteServer(listenhost.empty() ? "0.0.0.0" : listenhost, - RenderDoc_RemoteServerPort, killReplay, previewWindow); + if(port == 0) + port = RenderDoc_RemoteServerPort; + + RenderDoc::Inst().BecomeRemoteServer(listenhost.empty() ? "0.0.0.0" : listenhost, port, + killReplay, previewWindow); } extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_StartSelfHostCapture(const rdcstr &dllname) diff --git a/renderdoccmd/renderdoccmd.cpp b/renderdoccmd/renderdoccmd.cpp index 269399243..68082a309 100644 --- a/renderdoccmd/renderdoccmd.cpp +++ b/renderdoccmd/renderdoccmd.cpp @@ -456,6 +456,7 @@ private: std::string host; bool daemon = false; bool preview = false; + uint16_t port = 0; public: RemoteServerCommand() : Command() {} @@ -465,6 +466,9 @@ public: parser.add( "host", 'h', "The interface to listen on. By default listens on all interfaces", false, ""); parser.add("preview", 'v', "Display a preview window when a replay is active."); + parser.add( + "port", 'p', + "The port to listen on. Default is 0, which listens on RenderDoc's default port.", false, 0); } virtual const char *Description() { @@ -478,12 +482,15 @@ public: host = parser.get("host"); daemon = parser.exist("daemon"); preview = parser.exist("preview"); + port = parser.get("port") & 0xffff; return true; } virtual int Execute(const CaptureOptions &) { - std::cerr << "Spawning a replay host listening on " << (host.empty() ? "*" : host) << "..." - << std::endl; + std::cerr << "Spawning a replay host listening on " << (host.empty() ? "*" : host); + if(port != 0) + std::cerr << ":" << port; + std::cerr << "..." << std::endl; if(daemon) { @@ -504,7 +511,7 @@ public: if(DisplayRemoteServerPreview(false, {}).system != WindowingSystem::Unknown) previewWindow = &DisplayRemoteServerPreview; - RENDERDOC_BecomeRemoteServer(conv(host), []() { return killSignal; }, previewWindow); + RENDERDOC_BecomeRemoteServer(conv(host), port, []() { return killSignal; }, previewWindow); std::cerr << std::endl << "Cleaning up from replay hosting." << std::endl;