mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-05 09:30:44 +00:00
e2704fa2eb
* This makes it easier to use the same kind of interface to manage other kinds of devices.
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, 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()
|