baldurk / renderdoc

RenderDoc is a stand-alone graphics debugging tool.
https://renderdoc.org
MIT License
8.86k stars 1.33k forks source link

Textures can't be dumped on Android #2081

Closed wbarnesSamsung closed 3 years ago

wbarnesSamsung commented 3 years ago

Description

I have a python script that uses renderdoc to dump a texture on an android device given a renderdoc file. The script baseline is mostly taken from https://renderdoc.org/docs/python_api/examples/renderdoc/remote_capture.html and https://renderdoc.org/docs/python_api/examples/renderdoc/save_texture.html The script has conditionals to support all OS, but all differences are in the opening and closing of the capture.

The script complete without crash or critical error. Opening, parsing, and closing the capture is successful without issue. However, controller.SaveTexture() fails aka returns false Adding breakpoints to and diving into the renderdoc code the internal RDC error is: RDCERR("Couldn't get Live ID for %s getting texture data", ToStr(sd.resourceId).c_str()); in bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path) The sd.resourceId is the correct value. In ResourceId ReplayProxy::Proxied_GetLiveID(ParamSerialiser &paramser, ReturnSerialiser &retser, ResourceId id) m_LiveIDs is effectively empty (0 only) m_LocalTextures is actually empty if(paramser.IsErrored() || retser.IsErrored() || m_IsErrored) are all true. I stopped being able to easily follow the debug here.

Saving the same resource via renderdoc gui instead of the python library works without issue. The same SaveTexture in same script works just fine in a Windows replay. This seems to be an issue for all Android rdc files and resources.

Steps to reproduce

Will send an rdc to baldurk@baldurk.org If necessary I can recreate a trimmed down version of the script by cobbling together the replay portion of https://renderdoc.org/docs/python_api/examples/renderdoc/remote_capture.html and https://renderdoc.org/docs/python_api/examples/renderdoc/save_texture.html.
The connection to Android type I used was adb.
protocol_to_use = 'adb'

Rough outline: Take https://renderdoc.org/docs/python_api/examples/renderdoc/save_texture.html

Replace def loadCapture(filename): code with something like

protocols = rd.GetSupportedDeviceProtocols()
print(f"Supported device protocols: {protocols}")
protocol_to_use = 'adb'

# 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")

cap_path = remote.CopyCaptureToRemote(args.r, 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(status)}")
#****NOTE**** remote replaces cap
return remote, controller

then replace cap.Shutdown() with remote.ShutdownServerAndConnection()

Environment

Tested on a Pixel 4.

baldurk commented 3 years ago

Due to Android being a unstable and unreliable platform I don't support python scripting with Android, only using the UI itself. The scripting may work, but I can't help if it breaks. If you can reproduce this bug without Android I can look into it, or if you investigate and find a bug then any fixes are welcome.

wbarnesSamsung commented 3 years ago

Can you post that info as part of the the bug filing information or similar to not waste others time?

baldurk commented 3 years ago

I could perhaps add a note to the documentation, but it doesn't seem that bad since this is a very niche use case and it doesn't waste anyone's time to have an issue closed with an explanation. I would also expect anyone working with android to expect things to be intermittently broken and partially working since that is typical for the platform.

wbarnesSamsung commented 3 years ago

I want to push back on that thinking. Filing the issue in the first place took time. Time I could have spent debugging the issue. I wouldn't have filed the issue if I knew it was an unsupported feature, though I am not surprised. It disincentivizes adding detailed issues up front, as they may be summarily dismissed. Please add a note somewhere.