Files
renderdoc/docs/python_api/examples/renderdoc/remote_capture.py
T
baldurk e2704fa2eb Add an abstracted interface around android-specific handling
* This makes it easier to use the same kind of interface to manage other kinds
  of devices.
2019-07-31 17:51:13 +01:00

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()