mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-05 01:20:42 +00:00
197 lines
6.6 KiB
Python
197 lines
6.6 KiB
Python
import renderdoc as rd
|
|
import threading
|
|
import time
|
|
|
|
# This sample is intended as an example of how to do remote capture and replay
|
|
# as well as using device protocols to automatically enumerate remote targets.
|
|
#
|
|
# It is not complete since it requires filling in with custom logic to select
|
|
# the executable and trigger the capture at the desired time
|
|
raise RuntimeError("This sample should not be run directly, read the source")
|
|
|
|
protocols = rd.GetSupportedDeviceProtocols()
|
|
|
|
print(f"Supported device protocols: {protocols}")
|
|
|
|
# Protocols are optional - they allow automatic detection and management of
|
|
# devices.
|
|
if protocol_to_use is not None:
|
|
# the protocol must be supported
|
|
if protocol_to_use not in protocols:
|
|
raise RuntimeError(f"{protocol_to_use} protocol not supported")
|
|
|
|
protocol = rd.GetDeviceProtocolController(protocol_to_use)
|
|
|
|
devices = protocol.GetDevices()
|
|
|
|
if len(devices) == 0:
|
|
raise RuntimeError(f"no {protocol_to_use} devices connected")
|
|
|
|
# Choose the first device
|
|
dev = devices[0]
|
|
name = protocol.GetFriendlyName(dev)
|
|
|
|
print(f"Running test on {dev} - named {name}")
|
|
|
|
URL = protocol.GetProtocolName() + "://" + dev
|
|
|
|
# Protocols can enumerate devices which are not supported. Capture/replay
|
|
# is not guaranteed to work on these devices
|
|
if not protocol.IsSupported(URL):
|
|
raise RuntimeError(f"{dev} doesn't support capture/replay - too old?")
|
|
|
|
# Protocol devices may be single-use and not support multiple captured programs
|
|
# If so, trying to execute a program for capture is an error
|
|
if not protocol.SupportsMultiplePrograms(URL):
|
|
# check to see if anything is running. Just use the URL
|
|
ident = rd.EnumerateRemoteTargets(URL, 0)
|
|
|
|
if ident != 0:
|
|
raise RuntimeError(f"{name} already has a program running on {ident}")
|
|
else:
|
|
# If you're not using a protocol then the URL can simply be a hostname.
|
|
# The remote server must be running already - how that is done is up
|
|
# to you. Everything else will work the same over a normal TCP connection
|
|
protocol = None
|
|
URL = hostname
|
|
|
|
# Let's try to connect
|
|
status,remote = rd.CreateRemoteServerConnection(URL)
|
|
|
|
if status == rd.ReplayStatus.NetworkIOFailed and protocol is not None:
|
|
# If there's just no I/O, most likely the server is not running. If we have
|
|
# a protocol, we can try to start the remote server
|
|
print("Couldn't connect to remote server, trying to start it")
|
|
|
|
status = protocol.StartRemoteServer(URL)
|
|
|
|
if status != rd.ReplayStatus.Succeeded:
|
|
raise RuntimeError(f"Couldn't launch remote server, got error {str(status)}")
|
|
|
|
# Try to connect again!
|
|
status,remote = rd.CreateRemoteServerConnection(URL)
|
|
|
|
if status != rd.ReplayStatus.Succeeded:
|
|
raise RuntimeError(f"Couldn't connect to remote server, got error {str(status)}")
|
|
|
|
# We now have a remote connection. This works regardless of whether it's a device
|
|
# with a protocol or not. In fact we are done with the protocol at this point
|
|
protocol = None
|
|
|
|
print("Got connection to remote server")
|
|
|
|
# GetHomeFolder() gives you a good default path to start with.
|
|
# ListFolder() lists the contents of a folder and can recursively
|
|
# browse the remote filesystem.
|
|
home = remote.GetHomeFolder()
|
|
paths = remote.ListFolder(home)
|
|
|
|
print(f"Executables in home folder '{home}':")
|
|
|
|
for p in paths:
|
|
print(" - " + p.filename)
|
|
|
|
# Select your executable, perhaps hardcoded or browsing using the above
|
|
# functions
|
|
exe,workingDir,cmdLine,env,opts = select_executable()
|
|
|
|
print(f"Running {exe}")
|
|
|
|
result = remote.ExecuteAndInject(exe, workingDir, cmdLine, env, opts)
|
|
|
|
if result.status != rd.ReplayStatus.Succeeded:
|
|
remote.ShutdownServerAndConnection()
|
|
raise RuntimeError(f"Couldn't launch {exe}, got error {str(result.status)}")
|
|
|
|
# Spin up a thread to keep the remote server connection alive while we make a capture,
|
|
# as it will time out after 5 seconds of inactivity
|
|
def ping_remote(remote, kill):
|
|
success = True
|
|
while success and not kill.is_set():
|
|
success = remote.Ping()
|
|
time.sleep(1)
|
|
|
|
kill = threading.Event()
|
|
ping_thread = threading.Thread(target=ping_remote, args=(remote,kill))
|
|
ping_thread.start()
|
|
|
|
# Create target control connection
|
|
target = rd.CreateTargetControl(URL, result.ident, 'remote_capture.py', True)
|
|
|
|
if target is None:
|
|
kill.set()
|
|
ping_thread.join()
|
|
remote.ShutdownServerAndConnection()
|
|
raise RuntimeError(f"Couldn't connect to target control for {exe}")
|
|
|
|
print("Connected - waiting for desired capture")
|
|
|
|
# Wait for the capture condition we want
|
|
capture_condition()
|
|
|
|
print("Triggering capture")
|
|
|
|
target.TriggerCapture(1)
|
|
|
|
# Pump messages, keep waiting until we get a capture message. Time out after 30 seconds
|
|
msg = None
|
|
start = time.clock()
|
|
while msg is None or msg.type != rd.TargetControlMessageType.NewCapture:
|
|
msg = target.ReceiveMessage(None)
|
|
|
|
if time.clock() - start > 30:
|
|
break
|
|
|
|
# Close the target connection, we're done either way
|
|
target.Shutdown()
|
|
target = None
|
|
|
|
# Stop the background ping thread
|
|
kill.set()
|
|
ping_thread.join()
|
|
|
|
# If we didn't get a capture, error now
|
|
if msg.type != rd.TargetControlMessageType.NewCapture:
|
|
remote.ShutdownServerAndConnection()
|
|
raise RuntimeError("Didn't get new capture notification after triggering capture")
|
|
|
|
cap_path = msg.newCapture.path
|
|
cap_id = msg.newCapture.captureId
|
|
|
|
print(f"Got new capture at {cap_path} which is frame {msg.newCapture.frameNumber} with {msg.newCapture.api}")
|
|
|
|
# We could save the capture locally
|
|
# remote.CopyCaptureFromRemote(cap_path, local_path, None)
|
|
|
|
|
|
# Open a replay. It's recommended to set no proxy preference, but you could
|
|
# call remote.LocalProxies and choose an index.
|
|
#
|
|
# The path must be remote - if the capture isn't freshly created then you need
|
|
# to copy it with remote.CopyCaptureToRemote()
|
|
status,controller = remote.OpenCapture(rd.RemoteServer.NoPreference, cap_path, rd.ReplayOptions(), None)
|
|
|
|
if status != rd.ReplayStatus.Succeeded:
|
|
remote.ShutdownServerAndConnection()
|
|
raise RuntimeError(f"Couldn't open {cap_path}, got error {str(result.status)}")
|
|
|
|
# We can now use replay as normal.
|
|
#
|
|
# The replay is tunnelled over the remote connection, so you don't have to keep
|
|
# pinging the remote connection while using the controller. Use of the remote
|
|
# connection and controller can be interleaved though you should only access
|
|
# them from one thread at once. If they are both unused for 5 seconds though,
|
|
# the timeout will happen, so if the controller is idle it's advisable to ping
|
|
# the remote connection
|
|
|
|
sampleCode(controller)
|
|
|
|
print("Shutting down")
|
|
|
|
controller.Shutdown()
|
|
|
|
# We can still use remote here - e.g. capture again, replay something else,
|
|
# save the capture, etc
|
|
|
|
remote.ShutdownServerAndConnection()
|