mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-07-10 09:37:08 +00:00
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.
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user