diff --git a/docs/how/how_android_capture.rst b/docs/how/how_android_capture.rst index ebea3efdf..d90d7172f 100644 --- a/docs/how/how_android_capture.rst +++ b/docs/how/how_android_capture.rst @@ -61,5 +61,3 @@ If you have Android Studio open, it will interfere with RenderDoc's debugging by RenderDoc does its best to locate or provide necessary Android tools from the Android SDK. On Windows, these tools are shipped with the distributions and all that's required is java - either in your ``PATH`` or via the ``JAVA_HOME`` environment variable. If these tools aren't present then RenderDoc searches through ``PATH`` and other variables like ``ANDROID_HOME`` or ``ANDROID_SDK_ROOT`` to find the SDK. If you don't have those environment variables set, you can browse to the SDK and JDK folders in the :doc:`settings window <../window/settings_window>` under the :guilabel:`Android` section. If something goes wrong with these steps, please `open an issue on GitHub `__! The process should be as smooth as possible given Android's platform limitations, so if you encounter problems then it may well be fixable. - -Often when an operation fails, more information is available via :guilabel:`Help` → :guilabel:`View Diagnostic Log`. diff --git a/docs/how/how_network_capture_replay.rst b/docs/how/how_network_capture_replay.rst index b2cf67c96..0600c8971 100644 --- a/docs/how/how_network_capture_replay.rst +++ b/docs/how/how_network_capture_replay.rst @@ -99,17 +99,17 @@ Capture files will all be kept on the target system by default. They will only b .. note:: - There is a case where temporary captures can be 'leaked' and not cleaned up. If you do not have a remote server run command configured and have captures left over when the program closes, there will be no way to either save or delete the temporary log files. This *doesn't* apply to deleting or saving captures while the program is still running. + There is a case where temporary captures can be 'leaked' and not cleaned up. If you do not have a remote server run command configured and have captures left over when the program closes, there will be no way to either save or delete the temporary capture files. This *doesn't* apply to deleting or saving captures while the program is still running. The capture connection will warn you about this case and let you know when you are leaking temporary captures - you can delete them by hand if necessary, or you can switch to a replay context on that host and then you will be able to save and delete them as normal. Note that this is the same as if a program is run locally without any connection to the UI made at all - the captures will leak with nothing left to take ownership of them. The difference is that if a connection is made, because the files are on the local machine they can be deleted or saved directly by the UI even after the program has closed. -Capture files made with a recent version of RenderDoc will store a coarse type of machine that was used at capture time, such as 'Linux 64-bit' or 'Windows 32-bit'. If you have the local replay context active and the machine you are running on differs significantly from the machine that the capture was made on, the UI will prompt you to ask if you really want to replay it locally (which may or may not work depending on the API and contents of the log), or switch to a remote context. +Capture files made with a recent version of RenderDoc will store a coarse type of machine that was used at capture time, such as 'Linux 64-bit' or 'Windows 32-bit'. If you have the local replay context active and the machine you are running on differs significantly from the machine that the capture was made on, the UI will prompt you to ask if you really want to replay it locally (which may or may not work depending on the API and contents of the capture), or switch to a remote context. .. figure:: ../imgs/Screenshots/RemoteHostCapturePrompt.png - Remote Hosts: Prompting for remote replay of a notably different log + Remote Hosts: Prompting for remote replay of a notably different capture Configuring the Remote Server ----------------------------- diff --git a/docs/python_api/examples/renderdoc/decode_mesh.py b/docs/python_api/examples/renderdoc/decode_mesh.py index 59184af91..7df2498a2 100644 --- a/docs/python_api/examples/renderdoc/decode_mesh.py +++ b/docs/python_api/examples/renderdoc/decode_mesh.py @@ -253,21 +253,21 @@ def loadCapture(filename): cap = rd.OpenCaptureFile() # Open a particular file - see also OpenBuffer to load from memory - status = cap.OpenFile(filename, '', None) + result = cap.OpenFile(filename, '', None) # Make sure the file opened successfully - if status != rd.ReplayStatus.Succeeded: - raise RuntimeError("Couldn't open file: " + str(status)) + if result != rd.ResultCode.Succeeded: + raise RuntimeError("Couldn't open file: " + str(result)) # Make sure we can replay if not cap.LocalReplaySupport(): raise RuntimeError("Capture cannot be replayed") # Initialise the replay - status,controller = cap.OpenCapture(rd.ReplayOptions(), None) + result,controller = cap.OpenCapture(rd.ReplayOptions(), None) - if status != rd.ReplayStatus.Succeeded: - raise RuntimeError("Couldn't initialise replay: " + str(status)) + if result != rd.ResultCode.Succeeded: + raise RuntimeError("Couldn't initialise replay: " + str(result)) return (cap, controller) diff --git a/docs/python_api/examples/renderdoc/display_window.py b/docs/python_api/examples/renderdoc/display_window.py index 2b1fb232c..0993851fd 100644 --- a/docs/python_api/examples/renderdoc/display_window.py +++ b/docs/python_api/examples/renderdoc/display_window.py @@ -12,21 +12,21 @@ def loadCapture(filename): cap = rd.OpenCaptureFile() # Open a particular file - see also OpenBuffer to load from memory - status = cap.OpenFile(filename, '', None) + result = cap.OpenFile(filename, '', None) # Make sure the file opened successfully - if status != rd.ReplayStatus.Succeeded: - raise RuntimeError("Couldn't open file: " + str(status)) + if result != rd.ResultCode.Succeeded: + raise RuntimeError("Couldn't open file: " + str(result)) # Make sure we can replay if not cap.LocalReplaySupport(): raise RuntimeError("Capture cannot be replayed") # Initialise the replay - status,controller = cap.OpenCapture(rd.ReplayOptions(), None) + result,controller = cap.OpenCapture(rd.ReplayOptions(), None) - if status != rd.ReplayStatus.Succeeded: - raise RuntimeError("Couldn't initialise replay: " + str(status)) + if result != rd.ResultCode.Succeeded: + raise RuntimeError("Couldn't initialise replay: " + str(result)) return cap,controller diff --git a/docs/python_api/examples/renderdoc/fetch_counters.py b/docs/python_api/examples/renderdoc/fetch_counters.py index e45f7e9dc..1899ef7bc 100644 --- a/docs/python_api/examples/renderdoc/fetch_counters.py +++ b/docs/python_api/examples/renderdoc/fetch_counters.py @@ -69,21 +69,21 @@ def loadCapture(filename): cap = rd.OpenCaptureFile() # Open a particular file - see also OpenBuffer to load from memory - status = cap.OpenFile(filename, '', None) + result = cap.OpenFile(filename, '', None) # Make sure the file opened successfully - if status != rd.ReplayStatus.Succeeded: - raise RuntimeError("Couldn't open file: " + str(status)) + if result != rd.ResultCode.Succeeded: + raise RuntimeError("Couldn't open file: " + str(result)) # Make sure we can replay if not cap.LocalReplaySupport(): raise RuntimeError("Capture cannot be replayed") # Initialise the replay - status,controller = cap.OpenCapture(rd.ReplayOptions(), None) + result,controller = cap.OpenCapture(rd.ReplayOptions(), None) - if status != rd.ReplayStatus.Succeeded: - raise RuntimeError("Couldn't initialise replay: " + str(status)) + if result != rd.ResultCode.Succeeded: + raise RuntimeError("Couldn't initialise replay: " + str(result)) return cap,controller diff --git a/docs/python_api/examples/renderdoc/fetch_shader.py b/docs/python_api/examples/renderdoc/fetch_shader.py index 1639285db..32191f8b3 100644 --- a/docs/python_api/examples/renderdoc/fetch_shader.py +++ b/docs/python_api/examples/renderdoc/fetch_shader.py @@ -60,21 +60,21 @@ def loadCapture(filename): cap = rd.OpenCaptureFile() # Open a particular file - see also OpenBuffer to load from memory - status = cap.OpenFile(filename, '', None) + result = cap.OpenFile(filename, '', None) # Make sure the file opened successfully - if status != rd.ReplayStatus.Succeeded: - raise RuntimeError("Couldn't open file: " + str(status)) + if result != rd.ResultCode.Succeeded: + raise RuntimeError("Couldn't open file: " + str(result)) # Make sure we can replay if not cap.LocalReplaySupport(): raise RuntimeError("Capture cannot be replayed") # Initialise the replay - status,controller = cap.OpenCapture(rd.ReplayOptions(), None) + result,controller = cap.OpenCapture(rd.ReplayOptions(), None) - if status != rd.ReplayStatus.Succeeded: - raise RuntimeError("Couldn't initialise replay: " + str(status)) + if result != rd.ResultCode.Succeeded: + raise RuntimeError("Couldn't initialise replay: " + str(result)) return (cap, controller) diff --git a/docs/python_api/examples/renderdoc/iter_actions.py b/docs/python_api/examples/renderdoc/iter_actions.py index f9e58685b..b0b8276cb 100644 --- a/docs/python_api/examples/renderdoc/iter_actions.py +++ b/docs/python_api/examples/renderdoc/iter_actions.py @@ -63,21 +63,21 @@ def loadCapture(filename): cap = rd.OpenCaptureFile() # Open a particular file - see also OpenBuffer to load from memory - status = cap.OpenFile(filename, '', None) + result = cap.OpenFile(filename, '', None) # Make sure the file opened successfully - if status != rd.ReplayStatus.Succeeded: - raise RuntimeError("Couldn't open file: " + str(status)) + if result != rd.ResultCode.Succeeded: + raise RuntimeError("Couldn't open file: " + str(result)) # Make sure we can replay if not cap.LocalReplaySupport(): raise RuntimeError("Capture cannot be replayed") # Initialise the replay - status,controller = cap.OpenCapture(rd.ReplayOptions(), None) + result,controller = cap.OpenCapture(rd.ReplayOptions(), None) - if status != rd.ReplayStatus.Succeeded: - raise RuntimeError("Couldn't initialise replay: " + str(status)) + if result != rd.ResultCode.Succeeded: + raise RuntimeError("Couldn't initialise replay: " + str(result)) return cap,controller diff --git a/docs/python_api/examples/renderdoc/remote_capture.py b/docs/python_api/examples/renderdoc/remote_capture.py index 1dc34b6bb..c3cf50592 100644 --- a/docs/python_api/examples/renderdoc/remote_capture.py +++ b/docs/python_api/examples/renderdoc/remote_capture.py @@ -58,23 +58,23 @@ else: URL = hostname # Let's try to connect -status,remote = rd.CreateRemoteServerConnection(URL) +result,remote = rd.CreateRemoteServerConnection(URL) -if status == rd.ReplayStatus.NetworkIOFailed and protocol is not None: +if result == rd.ResultCode.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) + result = protocol.StartRemoteServer(URL) - if status != rd.ReplayStatus.Succeeded: - raise RuntimeError(f"Couldn't launch remote server, got error {str(status)}") + if result != rd.ResultCode.Succeeded: + raise RuntimeError(f"Couldn't launch remote server, got error {str(result)}") # Try to connect again! - status,remote = rd.CreateRemoteServerConnection(URL) + result,remote = rd.CreateRemoteServerConnection(URL) -if status != rd.ReplayStatus.Succeeded: - raise RuntimeError(f"Couldn't connect to remote server, got error {str(status)}") +if result != rd.ResultCode.Succeeded: + raise RuntimeError(f"Couldn't connect to remote server, got error {str(result)}") # 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 @@ -101,9 +101,9 @@ print(f"Running {exe}") result = remote.ExecuteAndInject(exe, workingDir, cmdLine, env, opts) -if result.status != rd.ReplayStatus.Succeeded: +if result.result != rd.ResultCode.Succeeded: remote.ShutdownServerAndConnection() - raise RuntimeError(f"Couldn't launch {exe}, got error {str(result.status)}") + raise RuntimeError(f"Couldn't launch {exe}, got error {str(result.result)}") # 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 @@ -171,11 +171,11 @@ print(f"Got new capture at {cap_path} which is frame {msg.newCapture.frameNumber # # 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) +result,controller = remote.OpenCapture(rd.RemoteServer.NoPreference, cap_path, rd.ReplayOptions(), None) -if status != rd.ReplayStatus.Succeeded: +if result != rd.ResultCode.Succeeded: remote.ShutdownServerAndConnection() - raise RuntimeError(f"Couldn't open {cap_path}, got error {str(result.status)}") + raise RuntimeError(f"Couldn't open {cap_path}, got error {str(result)}") # We can now use replay as normal. # diff --git a/docs/python_api/examples/renderdoc/remote_capture.rst b/docs/python_api/examples/renderdoc/remote_capture.rst index 69e846d0b..b70d148c4 100644 --- a/docs/python_api/examples/renderdoc/remote_capture.rst +++ b/docs/python_api/examples/renderdoc/remote_capture.rst @@ -43,18 +43,18 @@ If the connection fails, normally we must fail but if we have a device protocol .. highlight:: python .. code:: python - if status == rd.ReplayStatus.NetworkIOFailed and protocol is not None: + if result == rd.ResultCode.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) + result = protocol.StartRemoteServer(URL) - if status != rd.ReplayStatus.Succeeded: - raise RuntimeError(f"Couldn't launch remote server, got error {str(status)}") + if result != rd.ResultCode.Succeeded: + raise RuntimeError(f"Couldn't launch remote server, got error {str(result)}") # Try to connect again! - status,remote = rd.CreateRemoteServerConnection(URL) + result,remote = rd.CreateRemoteServerConnection(URL) .. note:: diff --git a/docs/python_api/examples/renderdoc/save_texture.py b/docs/python_api/examples/renderdoc/save_texture.py index 9b5d97822..0004ed3a3 100644 --- a/docs/python_api/examples/renderdoc/save_texture.py +++ b/docs/python_api/examples/renderdoc/save_texture.py @@ -78,21 +78,21 @@ def loadCapture(filename): cap = rd.OpenCaptureFile() # Open a particular file - see also OpenBuffer to load from memory - status = cap.OpenFile(filename, '', None) + result = cap.OpenFile(filename, '', None) # Make sure the file opened successfully - if status != rd.ReplayStatus.Succeeded: - raise RuntimeError("Couldn't open file: " + str(status)) + if result != rd.ResultCode.Succeeded: + raise RuntimeError("Couldn't open file: " + str(result)) # Make sure we can replay if not cap.LocalReplaySupport(): raise RuntimeError("Capture cannot be replayed") # Initialise the replay - status,controller = cap.OpenCapture(rd.ReplayOptions(), None) + result,controller = cap.OpenCapture(rd.ReplayOptions(), None) - if status != rd.ReplayStatus.Succeeded: - raise RuntimeError("Couldn't initialise replay: " + str(status)) + if result != rd.ResultCode.Succeeded: + raise RuntimeError("Couldn't initialise replay: " + str(result)) return (cap, controller) diff --git a/docs/python_api/examples/renderdoc_intro.py b/docs/python_api/examples/renderdoc_intro.py index 7e93fd4d7..b0358ae58 100644 --- a/docs/python_api/examples/renderdoc_intro.py +++ b/docs/python_api/examples/renderdoc_intro.py @@ -6,21 +6,21 @@ rd.InitialiseReplay(rd.GlobalEnvironment(), []) cap = rd.OpenCaptureFile() # Open a particular file - see also OpenBuffer to load from memory -status = cap.OpenFile('test.rdc', '', None) +result = cap.OpenFile('test.rdc', '', None) # Make sure the file opened successfully -if status != rd.ReplayStatus.Succeeded: - raise RuntimeError("Couldn't open file: " + str(status)) +if result != rd.ResultCode.Succeeded: + raise RuntimeError("Couldn't open file: " + str(result)) # Make sure we can replay if not cap.LocalReplaySupport(): raise RuntimeError("Capture cannot be replayed") # Initialise the replay -status,controller = cap.OpenCapture(rd.ReplayOptions(), None) +result,controller = cap.OpenCapture(rd.ReplayOptions(), None) -if status != rd.ReplayStatus.Succeeded: - raise RuntimeError("Couldn't initialise replay: " + str(status)) +if result != rd.ResultCode.Succeeded: + raise RuntimeError("Couldn't initialise replay: " + str(result)) # Now we can use the controller! print("%d top-level actions" % len(controller.GetRootActions())) diff --git a/docs/python_api/examples/renderdoc_intro.rst b/docs/python_api/examples/renderdoc_intro.rst index f8c87592e..3895748c2 100644 --- a/docs/python_api/examples/renderdoc_intro.rst +++ b/docs/python_api/examples/renderdoc_intro.rst @@ -63,7 +63,7 @@ Before doing anything, we must initialise the replay API. We do that by calling To begin with, we use :py:meth:`~renderdoc.OpenCaptureFile` to obtain a :py:class:`~renderdoc.CaptureFile` instance. This gives us access to control over a capture file at a meta level. For more information see the :py:class:`CaptureFile` reference - the interface can also be used to create. -To open a file, use :py:meth:`~renderdoc.CaptureFile.OpenFile` on the :py:class:`~renderdoc.CaptureFile` instance. This function allows conversion from other formats via an importer, but here we'll use it just for opening a regular ``rdc`` file. It returns a :py:class:`~renderdoc.ReplayStatus` which can be used to determine what went wrong in the event that there was a problem. We then check that the capture uses an API which can be replayed locally - for example not every platform supports ``D3D11``, so on linux this would return no local replay support. +To open a file, use :py:meth:`~renderdoc.CaptureFile.OpenFile` on the :py:class:`~renderdoc.CaptureFile` instance. This function allows conversion from other formats via an importer, but here we'll use it just for opening a regular ``rdc`` file. It returns a :py:class:`~renderdoc.ResultDetails` which can be used to determine what went wrong in the event that there was a problem. We then check that the capture uses an API which can be replayed locally - for example not every platform supports ``D3D11``, so on linux this would return no local replay support. .. highlight:: python .. code:: python @@ -72,11 +72,11 @@ To open a file, use :py:meth:`~renderdoc.CaptureFile.OpenFile` on the :py:class: cap = rd.OpenCaptureFile() # Open a particular file - see also OpenBuffer to load from memory - status = cap.OpenFile('test.rdc', '', None) + result = cap.OpenFile('test.rdc', '', None) # Make sure the file opened successfully - if status != rd.ReplayStatus.Succeeded: - raise RuntimeError("Couldn't open file: " + str(status)) + if result != rd.ResultCode.Succeeded: + raise RuntimeError("Couldn't open file: " + str(result)) # Make sure we can replay if not cap.LocalReplaySupport(): @@ -85,7 +85,7 @@ To open a file, use :py:meth:`~renderdoc.CaptureFile.OpenFile` on the :py:class: Accessing Capture Analysis -------------------------- -Once the capture has been loaded, we can now begin the replay analysis. To do that we use :py:meth:`~renderdoc.CaptureFile.OpenCapture` which returns a tuple of :py:class:`~renderdoc.ReplayStatus` and :py:class:`~renderdoc.ReplayController`. +Once the capture has been loaded, we can now begin the replay analysis. To do that we use :py:meth:`~renderdoc.CaptureFile.OpenCapture` which returns a tuple of :py:class:`~renderdoc.ResultDetails` and :py:class:`~renderdoc.ReplayController`. This function call will open the capture and begin to replay it, and initialise the analysis. The :py:class:`~renderdoc.ReplayController` returned is the interface to the majority of RenderDoc's replaying functionality. @@ -93,10 +93,10 @@ This function call will open the capture and begin to replay it, and initialise .. code:: python # Initialise the replay - status,controller = cap.OpenCapture(rd.ReplayOptions(), None) + result,controller = cap.OpenCapture(rd.ReplayOptions(), None) - if status != rd.ReplayStatus.Succeeded: - raise RuntimeError("Couldn't initialise replay: " + str(status)) + if result != rd.ResultCode.Succeeded: + raise RuntimeError("Couldn't initialise replay: " + str(result)) # Now we can use the controller! print("%d top-level actions" % len(controller.GetRootActions())) diff --git a/docs/python_api/renderdoc/replay.rst b/docs/python_api/renderdoc/replay.rst index 97211a832..57ca5970a 100644 --- a/docs/python_api/renderdoc/replay.rst +++ b/docs/python_api/renderdoc/replay.rst @@ -14,6 +14,12 @@ Initialisation and Shutdown .. autoclass:: renderdoc.GlobalEnvironment :members: +.. autoclass:: renderdoc.ResultCode + :members: + +.. autoclass:: renderdoc.ResultDetails + :members: + Capture File Access ------------------- @@ -25,9 +31,6 @@ Capture File Access .. autoclass:: renderdoc.CaptureFile :members: -.. autoclass:: renderdoc.ReplayStatus - :members: - .. autoclass:: renderdoc.ReplaySupport :members: diff --git a/docs/window/api_inspector.rst b/docs/window/api_inspector.rst index 3828826b6..84fbe6c13 100644 --- a/docs/window/api_inspector.rst +++ b/docs/window/api_inspector.rst @@ -17,7 +17,7 @@ API Calls This section lists the series of API calls made between the preceding action and the currently selected action. The current action is always the last element in this list and is highlighted in bold. By default it is also the selected element. -Each API call can be expanded to see the parameters that were passed to it, in the form that they were serialised out to the log file. +Each API call can be expanded to see the parameters that were passed to it, in the form that they were serialised out to the capture. .. figure:: ../imgs/Screenshots/APIList.png diff --git a/docs/window/capture_connection.rst b/docs/window/capture_connection.rst index a085faa77..b06415278 100644 --- a/docs/window/capture_connection.rst +++ b/docs/window/capture_connection.rst @@ -63,7 +63,7 @@ In this example we have a connection window open to the debugmarker sample from From here you can save these captures out - as currently they are only temporary copies that will be cleaned up on close. You can also manually delete any capture you wish to discard. -Double clicking on any capture will close any current open capture in the RenderDoc UI, and open up that capture for inspection. You may also right click or use the drop-down menu on the open button to launch a new instance of RenderDoc for viewing the log. This is mostly useful if you want to compare two captures side-by-side easily. +Double clicking on any capture will close any current open capture in the RenderDoc UI, and open up that capture for inspection. You may also right click or use the drop-down menu on the open button to launch a new instance of RenderDoc for viewing the capture. This is mostly useful if you want to compare two captures side-by-side easily. You can press F2 or single click on a selected capture (not double click) to rename the default label given to each capture. This can be useful if you're changing something as you go or toggling an option and you want to remember which capture is which. diff --git a/docs/window/debug_messages.rst b/docs/window/debug_messages.rst index f38a1ea91..e8a476833 100644 --- a/docs/window/debug_messages.rst +++ b/docs/window/debug_messages.rst @@ -3,12 +3,12 @@ Debug Messages The Debug Messages window shows messages from the API including warnings about performance issues or concerns about potential hazards, as well as errors on invalid use of the API. -RenderDoc will alert you if there are any debug messages to show from the log by flashing in the status bar. Sometimes RenderDoc will also add new alerts there that aren't part of the original log if there are warnings or problems that it encounters while processing and analysing the log. +RenderDoc will alert you if there are any debug messages to show from the capture by flashing in the status bar. Sometimes RenderDoc will also add new alerts there that aren't part of the original capture if there are warnings or problems that it encounters while processing and analysing the capture. Capturing with debug messages included -------------------------------------- -To include these debug messages in a log, check on the :guilabel:`Enable API validation` option in the capture options. For more information see :doc:`capture_attach` +To include these debug messages in a capture, check on the :guilabel:`Enable API validation` option in the capture options. For more information see :doc:`capture_attach` Debug Messages -------------- diff --git a/qrenderdoc/Code/CaptureContext.cpp b/qrenderdoc/Code/CaptureContext.cpp index 59e775f26..85d48c635 100644 --- a/qrenderdoc/Code/CaptureContext.cpp +++ b/qrenderdoc/Code/CaptureContext.cpp @@ -101,47 +101,54 @@ CaptureContext::CaptureContext(PersistantConfig &cfg) : m_Config(cfg) m_Replay.SetFatalErrorCallback([this]() { GUIInvoke::call(m_MainWindow, [this]() { - RefreshUIStatus({}, true, true); + ResultDetails err = GetFatalError(); - ReplayStatus err = GetFatalError(); + // if we encountered a fatal error during shutdown just ignore it, there's not much point in + // alerting the user and the recorded error will be gone already + if(!m_Replay.IsRunning()) + return; + + RefreshUIStatus({}, true, true); QString title; QString text; - if(err == ReplayStatus::RemoteServerConnectionLost) + if(err.code == ResultCode::RemoteServerConnectionLost) { QString serverName = Replay().CurrentRemote().Name(); title = tr("Connection lost to %1").arg(serverName); text = - tr("Connection to %1 was lost while analysing capture.\n" + tr("%1.\n\n" "This is most commonly caused by a crash, but that can't be verified and it may be " "a connection issue.") - .arg(serverName); + .arg(err.Message()); } - else if(err == ReplayStatus::ReplayDeviceLost) + else if(err.code == ResultCode::ReplayDeviceLost) { title = tr("Device Lost error"); - text = - tr("The capture analysis encountered a device lost error. This may be due to an " - "application bug, a RenderDoc bug, or insufficient resources on the system to " - "analyse the capture.\n\n" - "It is recommended that you run your application with API validation enabled, as " - "API use errors can cause this kind of problem."); + text = tr("%1.\n\n" + "This may be due to an application bug, a RenderDoc bug, or insufficient " + "resources on the system to analyse the capture.\n\n" + "It is recommended that you run your application with API validation enabled, as " + "API usage errors can cause this kind of problem.") + .arg(err.Message()); } - else if(err == ReplayStatus::ReplayOutOfMemory) + else if(err.code == ResultCode::ReplayOutOfMemory) { - title = tr("Out of GPU memory"); - text = tr( - "While analysing the capture it ran out of GPU memory. This is most likely caused by " - "insufficient resources on the system to analyse the capture, which is possible if " - "there is memory pressure from other applications or if the capture is heavyweight."); + title = tr("Out of memory"); + text = + tr("%1.\n\n" + "This is most likely caused by " + "insufficient resources on the system to analyse the capture, which is possible if " + "there is memory pressure from other applications or if the capture is heavyweight.") + .arg(err.Message()); } else { title = tr("Unrecoverable error"); text = tr("While analysing the capture we encountered an unrecoverable error:\n\n%1") - .arg(QString(ToStr(err))); + .arg(err.Message()); } text += @@ -445,9 +452,9 @@ bool CaptureContext::IsExtensionLoaded(rdcstr name) return m_ExtensionObjects.contains(name); } -bool CaptureContext::LoadExtension(rdcstr name) +rdcstr CaptureContext::LoadExtension(rdcstr name) { - bool ret = false; + QString ret; PythonContext::ProcessExtensionWork([this, &ret, name]() { for(QObject *o : m_ExtensionObjects[name]) @@ -457,7 +464,7 @@ bool CaptureContext::LoadExtension(rdcstr name) ret = PythonContext::LoadExtension(*this, name); - if(ret) + if(ret.isEmpty()) { m_ExtensionObjects[name].swap(m_PendingExtensionObjects); } @@ -944,17 +951,15 @@ void CaptureContext::LoadCaptureThreaded(const QString &captureFile, const Repla // this function call will block until the capture is either loaded, or there's some failure m_Replay.OpenCapture(captureFile, opts, [this](float p) { m_LoadProgress = p; }); + QString filename = QFileInfo(origFilename).fileName(); + // if the renderer isn't running, we hit a failure case so display an error message if(!m_Replay.IsRunning()) { - QString errmsg = ToQStr(m_Replay.GetCreateStatus()); - - QString messageText = tr("%1\nFailed to open capture for replay: %2.\n\n" - "Check diagnostic log in Help menu for more details.") - .arg(captureFile) - .arg(errmsg); - - RDDialog::critical(NULL, tr("Error opening capture"), messageText); + RDDialog::critical(NULL, tr("Error opening capture"), + tr("Failed to open '%1' for replay\n\n%2") + .arg(filename) + .arg(m_Replay.GetCreateStatus().Message())); m_LoadInProgress = false; return; @@ -1343,7 +1348,9 @@ bool CaptureContext::SaveCaptureTo(const rdcstr &captureFile) if(capFile) { // this will overwrite - success = capFile->CopyFileTo(captureFile); + ResultDetails result = capFile->CopyFileTo(captureFile); + success = result.OK(); + error = result.Message(); } else { @@ -1351,18 +1358,17 @@ bool CaptureContext::SaveCaptureTo(const rdcstr &captureFile) // prompted for overwrite) QFile::remove(captureFile); success = QFile::copy(GetCaptureFilename(), captureFile); + error = tr("File move failed"); } - error = tr("Couldn't save to %1").arg(captureFile); + error = tr("Couldn't save to %1: %2").arg(captureFile).arg(error); } } else { - RDDialog::critical( - NULL, tr("File not found"), - error = - tr("Capture '%1' couldn't be found on disk, cannot save.").arg(GetCaptureFilename())); + error = tr("Capture '%1' couldn't be found on disk, cannot save.").arg(GetCaptureFilename()); success = false; + RDDialog::critical(NULL, tr("File not found"), error); } } else @@ -1463,29 +1469,27 @@ bool CaptureContext::ImportCapture(const CaptureFileFormat &fmt, const rdcstr &i QString ext = fmt.extension; - ReplayStatus status = ReplayStatus::UnknownError; - QString message; + ResultDetails result; // shorten the filename after here for error messages QString filename = QFileInfo(importfile).fileName(); float progress = 0.0f; - LambdaThread *th = new LambdaThread([rdcfile, importfile, ext, &message, &progress, &status]() { + LambdaThread *th = new LambdaThread([rdcfile, importfile, ext, &progress, &result]() { ICaptureFile *file = RENDERDOC_OpenCaptureFile(); - status = file->OpenFile(importfile, ext.toUtf8().data(), + result = file->OpenFile(importfile, ext.toUtf8().data(), [&progress](float p) { progress = p * 0.5f; }); - if(status != ReplayStatus::Succeeded) + if(result.code != ResultCode::Succeeded) { - message = file->ErrorString(); file->Shutdown(); return; } - status = + result = file->Convert(rdcfile, "rdc", NULL, [&progress](float p) { progress = 0.5f + p * 0.5f; }); file->Shutdown(); }); @@ -1500,15 +1504,10 @@ bool CaptureContext::ImportCapture(const CaptureFileFormat &fmt, const rdcstr &i } th->deleteLater(); - if(status != ReplayStatus::Succeeded) + if(!result.OK()) { - QString text = tr("Couldn't convert file '%1'\n").arg(filename); - if(message.isEmpty()) - text += tr("%1").arg(ToQStr(status)); - else - text += tr("%1: %2").arg(ToQStr(status)).arg(message); - - RDDialog::critical(m_MainWindow, tr("Error converting capture"), text); + RDDialog::critical(m_MainWindow, tr("Error converting capture"), + tr("Couldn't convert file '%1'\n%2").arg(filename).arg(result.Message())); return false; } @@ -1524,7 +1523,7 @@ void CaptureContext::ExportCapture(const CaptureFileFormat &fmt, const rdcstr &e ICaptureFile *local = NULL; ICaptureFile *file = NULL; - ReplayStatus status = ReplayStatus::Succeeded; + ResultDetails result = {ResultCode::Succeeded}; const SDFile *sdfile = NULL; @@ -1538,21 +1537,16 @@ void CaptureContext::ExportCapture(const CaptureFileFormat &fmt, const rdcstr &e if(!file) { local = file = RENDERDOC_OpenCaptureFile(); - status = file->OpenFile(m_CaptureFile, "rdc", NULL); + result = file->OpenFile(m_CaptureFile, "rdc", NULL); } QString filename = QFileInfo(m_CaptureFile).fileName(); - if(status != ReplayStatus::Succeeded) + if(!result.OK()) { - QString text = tr("Couldn't open file '%1' for export\n").arg(filename); - QString message = file->ErrorString(); - if(message.isEmpty()) - text += tr("%1").arg(ToQStr(status)); - else - text += tr("%1: %2").arg(ToQStr(status)).arg(message); - - RDDialog::critical(m_MainWindow, tr("Error opening file"), text); + RDDialog::critical( + m_MainWindow, tr("Error opening file"), + tr("Couldn't open file '%1' for export\n%2").arg(filename).arg(result.Message())); if(local) local->Shutdown(); @@ -1561,8 +1555,8 @@ void CaptureContext::ExportCapture(const CaptureFileFormat &fmt, const rdcstr &e float progress = 0.0f; - LambdaThread *th = new LambdaThread([file, sdfile, ext, exportfile, &progress, &status]() { - status = file->Convert(exportfile, ext, sdfile, [&progress](float p) { progress = p; }); + LambdaThread *th = new LambdaThread([file, sdfile, ext, exportfile, &progress, &result]() { + result = file->Convert(exportfile, ext, sdfile, [&progress](float p) { progress = p; }); }); th->setName(lit("ExportCapture")); th->start(); @@ -1576,19 +1570,13 @@ void CaptureContext::ExportCapture(const CaptureFileFormat &fmt, const rdcstr &e } th->deleteLater(); - QString message = file->ErrorString(); if(local) local->Shutdown(); - if(status != ReplayStatus::Succeeded) + if(!result.OK()) { - QString text = tr("Couldn't convert file '%1'\n").arg(filename); - if(message.isEmpty()) - text += tr("%1").arg(ToQStr(status)); - else - text += tr("%1: %2").arg(ToQStr(status)).arg(message); - - RDDialog::critical(m_MainWindow, tr("Error converting capture"), text); + RDDialog::critical(m_MainWindow, tr("Error converting capture"), + tr("Couldn't convert file '%1'\n%2").arg(filename).arg(result.Message())); } } @@ -1840,7 +1828,7 @@ bool CaptureContext::SaveRenames() props.type = SectionType::ResourceRenames; props.version = 1; - return Replay().GetCaptureAccess()->WriteSection(props, json.toUtf8()); + return Replay().GetCaptureAccess()->WriteSection(props, json.toUtf8()).OK(); } void CaptureContext::LoadRenames(const QString &data) @@ -1892,7 +1880,7 @@ bool CaptureContext::SaveBookmarks() props.type = SectionType::Bookmarks; props.version = 1; - return Replay().GetCaptureAccess()->WriteSection(props, json.toUtf8()); + return Replay().GetCaptureAccess()->WriteSection(props, json.toUtf8()).OK(); } void CaptureContext::LoadBookmarks(const QString &data) @@ -1932,7 +1920,7 @@ bool CaptureContext::SaveNotes() ANALYTIC_SET(UIFeatures.CaptureComments, true); - return Replay().GetCaptureAccess()->WriteSection(props, json.toUtf8()); + return Replay().GetCaptureAccess()->WriteSection(props, json.toUtf8()).OK(); } void CaptureContext::LoadNotes(const QString &data) @@ -1967,7 +1955,7 @@ bool CaptureContext::SaveEdits() props.type = SectionType::EditedShaders; props.version = 1; - return Replay().GetCaptureAccess()->WriteSection(props, json.toUtf8()); + return Replay().GetCaptureAccess()->WriteSection(props, json.toUtf8()).OK(); } void CaptureContext::LoadEdits(const QString &data) @@ -2655,10 +2643,6 @@ QWidget *CaptureContext::CreateBuiltinWindow(const rdcstr &objectName) { return GetDebugMessageView()->Widget(); } - else if(objectName == "diagnosticLogView") - { - return GetDiagnosticLogView()->Widget(); - } else if(objectName == "commentView") { return GetCommentView()->Widget(); diff --git a/qrenderdoc/Code/CaptureContext.h b/qrenderdoc/Code/CaptureContext.h index bba532f56..44f03f5c5 100644 --- a/qrenderdoc/Code/CaptureContext.h +++ b/qrenderdoc/Code/CaptureContext.h @@ -76,7 +76,7 @@ public: rdcarray GetInstalledExtensions() override; bool IsExtensionLoaded(rdcstr name) override; - bool LoadExtension(rdcstr name) override; + rdcstr LoadExtension(rdcstr name) override; void RegisterWindowMenu(WindowMenu base, const rdcarray &submenus, ExtensionCallback callback) override; @@ -149,7 +149,7 @@ public: bool IsCaptureLocal() override { return m_CaptureLocal; } bool IsCaptureTemporary() override { return m_CaptureTemporary; } bool IsCaptureLoading() override { return m_LoadInProgress; } - ReplayStatus GetFatalError() override { return m_Replay.GetFatalError(); } + ResultDetails GetFatalError() override { return m_Replay.GetFatalError(); } rdcstr GetCaptureFilename() override { return m_CaptureFile; } CaptureModifications GetCaptureModifications() override { return m_CaptureMods; } const FrameDescription &FrameInfo() override { return m_FrameInfo; } diff --git a/qrenderdoc/Code/Interface/Extensions.h b/qrenderdoc/Code/Interface/Extensions.h index b5f006d72..a6ae10642 100644 --- a/qrenderdoc/Code/Interface/Extensions.h +++ b/qrenderdoc/Code/Interface/Extensions.h @@ -953,10 +953,11 @@ struct IExtensionManager DOCUMENT(R"(Enable an extension by name. If the extension is already enabled, this will reload it. :param str name: The qualified name of the extension, e.g. ``foo.bar`` -:return: If the extension loaded successfully or not. -:rtype: bool +:return: If the extension loaded successfully, an empty string, otherwise the errors encountered + while loading it. +:rtype: str )"); - virtual bool LoadExtension(rdcstr name) = 0; + virtual rdcstr LoadExtension(rdcstr name) = 0; ////////////////////////////////////////////////////////////////////////// // UI hook/callback registration diff --git a/qrenderdoc/Code/Interface/QRDInterface.h b/qrenderdoc/Code/Interface/QRDInterface.h index 53334ef60..29ec903a8 100644 --- a/qrenderdoc/Code/Interface/QRDInterface.h +++ b/qrenderdoc/Code/Interface/QRDInterface.h @@ -1369,9 +1369,9 @@ struct IReplayManager :param RemoteHost host: The host to connect to. :return: Whether or not the connection was successful. -:rtype: renderdoc.ReplayStatus +:rtype: renderdoc.ResultDetails )"); - virtual ReplayStatus ConnectToRemoteServer(RemoteHost host) = 0; + virtual ResultDetails ConnectToRemoteServer(RemoteHost host) = 0; DOCUMENT("Disconnect from the server the manager is currently connected to."); virtual void DisconnectFromRemoteServer() = 0; @@ -1894,9 +1894,9 @@ temporary and treated like any other capture. DOCUMENT(R"(If a capture is loaded, return the current fatal error status. :return: If a capture is currently loaded, return the fatal error status. -:rtype: renderdoc.ReplayStatus +:rtype: renderdoc.ResultDetails )"); - virtual ReplayStatus GetFatalError() = 0; + virtual ResultDetails GetFatalError() = 0; DOCUMENT(R"(Retrieve the filename for the currently loaded capture. diff --git a/qrenderdoc/Code/Interface/RemoteHost.cpp b/qrenderdoc/Code/Interface/RemoteHost.cpp index b5f57a3ba..00b135985 100644 --- a/qrenderdoc/Code/Interface/RemoteHost.cpp +++ b/qrenderdoc/Code/Interface/RemoteHost.cpp @@ -128,7 +128,7 @@ void RemoteHost::CheckStatus() UpdateStatus(RENDERDOC_CheckRemoteServerConnection(m_hostname)); } -ReplayStatus RemoteHost::Connect(IRemoteServer **server) +ResultDetails RemoteHost::Connect(IRemoteServer **server) { QMutexLocker autolock(&m_data->mutex); return RENDERDOC_CreateRemoteServerConnection(m_hostname, server); @@ -148,23 +148,23 @@ void RemoteHost::SetShutdown() m_data->m_busy = false; } -void RemoteHost::UpdateStatus(ReplayStatus status) +void RemoteHost::UpdateStatus(ResultDetails result) { { QMutexLocker autolock(&m_data->mutex); - if(status == ReplayStatus::Succeeded) + if(result.code == ResultCode::Succeeded) { m_data->m_serverRunning = true; m_data->m_versionMismatch = m_data->m_busy = false; } - else if(status == ReplayStatus::NetworkRemoteBusy) + else if(result.code == ResultCode::NetworkRemoteBusy) { m_data->m_serverRunning = true; m_data->m_busy = true; m_data->m_versionMismatch = false; } - else if(status == ReplayStatus::NetworkVersionMismatch) + else if(result.code == ResultCode::NetworkVersionMismatch) { m_data->m_serverRunning = true; m_data->m_busy = true; @@ -186,16 +186,11 @@ void RemoteHost::UpdateStatus(ReplayStatus status) QThread::msleep(15); } -ReplayStatus RemoteHost::Launch() +ResultDetails RemoteHost::Launch() { - ReplayStatus status = ReplayStatus::Succeeded; - if(m_protocol) - { // this is blocking - status = m_protocol->StartRemoteServer(m_hostname); - return status; - } + return m_protocol->StartRemoteServer(m_hostname); rdcstr run = RunCommand(); @@ -204,7 +199,7 @@ ReplayStatus RemoteHost::Launch() process.waitForFinished(2000); process.detach(); - return status; + return {ResultCode::Succeeded}; } bool RemoteHost::IsServerRunning() const diff --git a/qrenderdoc/Code/Interface/RemoteHost.h b/qrenderdoc/Code/Interface/RemoteHost.h index 31912b681..398f385c0 100644 --- a/qrenderdoc/Code/Interface/RemoteHost.h +++ b/qrenderdoc/Code/Interface/RemoteHost.h @@ -61,12 +61,12 @@ public: void CheckStatus(); DOCUMENT(R"(Runs the command specified in :data:`runCommand`. Returns -:class:`~renderdoc.ReplayStatus` which indicates success or the type of failure. +:class:`~renderdoc.ResultDetails` which indicates success or the type of failure. :return: The result from launching the remote server. -:rtype: renderdoc.ReplayStatus +:rtype: renderdoc.ResultDetails )"); - ReplayStatus Launch(); + ResultDetails Launch(); DOCUMENT(R"( :return: ``True`` if a remote server is currently running on this host. @@ -131,9 +131,9 @@ public: :return: The status of opening the capture, whether success or failure, and a :class:`RemoteServer` instance if it were successful -:rtype: Tuple[renderdoc.ReplayStatus, renderdoc.RemoteServer] +:rtype: Tuple[renderdoc.ResultDetails, renderdoc.RemoteServer] )"); - ReplayStatus Connect(IRemoteServer **server); + ResultDetails Connect(IRemoteServer **server); DOCUMENT(R"( :return: The :class:`~renderdoc.DeviceProtocolController` for this host, or ``None`` if no protocol @@ -181,7 +181,7 @@ private: void SetConnected(bool connected); void SetShutdown(); - void UpdateStatus(ReplayStatus status); + void UpdateStatus(ResultDetails result); }; DECLARE_REFLECTION_STRUCT(RemoteHost); diff --git a/qrenderdoc/Code/ReplayManager.cpp b/qrenderdoc/Code/ReplayManager.cpp index e131532a6..e7e1e7fce 100644 --- a/qrenderdoc/Code/ReplayManager.cpp +++ b/qrenderdoc/Code/ReplayManager.cpp @@ -48,7 +48,8 @@ void ReplayManager::OpenCapture(const QString &capturefile, const ReplayOptions if(m_Running) return; - m_FatalError = ReplayStatus::Succeeded; + m_FatalError = {}; + m_FatalError.code = ResultCode::Succeeded; // TODO maybe we could expose this choice to the user? int proxyRenderer = -1; @@ -298,7 +299,8 @@ void ReplayManager::CancelReplayLoop() void ReplayManager::CloseThread() { m_Running = false; - m_FatalError = ReplayStatus::Succeeded; + m_FatalError = {}; + m_FatalError.code = ResultCode::Succeeded; m_RenderCondition.wakeAll(); @@ -314,9 +316,9 @@ void ReplayManager::CloseThread() m_Thread = NULL; } -ReplayStatus ReplayManager::ConnectToRemoteServer(RemoteHost host) +ResultDetails ReplayManager::ConnectToRemoteServer(RemoteHost host) { - ReplayStatus status = host.Connect(&m_Remote); + ResultDetails result = host.Connect(&m_Remote); if(host.Protocol() && host.Protocol()->GetProtocolName() == "adb") { @@ -329,13 +331,10 @@ ReplayStatus ReplayManager::ConnectToRemoteServer(RemoteHost host) m_RemoteHost = host; - if(status == ReplayStatus::Succeeded) - { + if(result.OK()) m_RemoteHost.SetConnected(true); - return status; - } - return status; + return result; } void ReplayManager::DisconnectFromRemoteServer() @@ -383,7 +382,7 @@ void ReplayManager::PingRemote() { if(!IsRunning() || m_Thread->isCurrentThread()) { - if(!m_Remote->Ping()) + if(!m_Remote->Ping().OK()) m_RemoteHost.SetShutdown(); } m_RemoteLock.unlock(); @@ -440,17 +439,17 @@ void ReplayManager::run(int proxyRenderer, const QString &capturefile, const Rep if(m_Remote) { - rdctie(m_CreateStatus, m_Renderer) = + rdctie(m_CreateResult, m_Renderer) = m_Remote->OpenCapture(proxyRenderer, capturefile, opts, progress); } else { m_CaptureFile = RENDERDOC_OpenCaptureFile(); - m_CreateStatus = m_CaptureFile->OpenFile(capturefile, "rdc", NULL); + m_CreateResult = m_CaptureFile->OpenFile(capturefile, "rdc", NULL); - if(m_CreateStatus == ReplayStatus::Succeeded) - rdctie(m_CreateStatus, m_Renderer) = m_CaptureFile->OpenCapture(opts, progress); + if(m_CreateResult.OK()) + rdctie(m_CreateResult, m_Renderer) = m_CaptureFile->OpenCapture(opts, progress); } if(m_Renderer == NULL) @@ -495,8 +494,8 @@ void ReplayManager::run(int proxyRenderer, const QString &capturefile, const Rep cmd->method(m_Renderer); - ReplayStatus err = m_Renderer->GetFatalErrorStatus(); - if(m_FatalError == ReplayStatus::Succeeded && err != ReplayStatus::Succeeded) + ResultDetails err = m_Renderer->GetFatalErrorStatus(); + if(m_FatalError.OK() && !err.OK()) { m_FatalError = err; m_FatalErrorCallback(); diff --git a/qrenderdoc/Code/ReplayManager.h b/qrenderdoc/Code/ReplayManager.h index 4bbc6b1e3..85002c0a7 100644 --- a/qrenderdoc/Code/ReplayManager.h +++ b/qrenderdoc/Code/ReplayManager.h @@ -53,9 +53,9 @@ public: void DeleteCapture(const rdcstr &capturefile, bool local); bool IsRunning(); - ReplayStatus GetCreateStatus() { return m_CreateStatus; } + ResultDetails GetCreateStatus() { return m_CreateResult; } void SetFatalErrorCallback(std::function cb) { m_FatalErrorCallback = cb; } - ReplayStatus GetFatalError() { return m_FatalError; } + ResultDetails GetFatalError() { return m_FatalError; } float GetCurrentProcessingTime(); QString GetCurrentProcessingTag(); // this tagged version is for cases when we might send a request - e.g. to pick a vertex or pixel @@ -72,7 +72,7 @@ public: void CloseThread(); - ReplayStatus ConnectToRemoteServer(RemoteHost host); + ResultDetails ConnectToRemoteServer(RemoteHost host); void DisconnectFromRemoteServer(); void ShutdownServer(); void PingRemote(); @@ -139,8 +139,8 @@ private: IRemoteServer *m_OrphanedRemote = NULL; std::function m_FatalErrorCallback; - ReplayStatus m_FatalError = ReplayStatus::Succeeded; + ResultDetails m_FatalError = {ResultCode::Succeeded}; volatile bool m_Running; LambdaThread *m_Thread; - ReplayStatus m_CreateStatus = ReplayStatus::Succeeded; + ResultDetails m_CreateResult = {ResultCode::Succeeded}; }; diff --git a/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp b/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp index f6dd45b0f..5bd566b58 100644 --- a/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp +++ b/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp @@ -577,8 +577,10 @@ void PythonContext::ProcessExtensionWork(std::function callback) PyGILState_Release(gil); } -bool PythonContext::LoadExtension(ICaptureContext &ctx, const rdcstr &extension) +QString PythonContext::LoadExtension(ICaptureContext &ctx, const rdcstr &extension) { + QString ret; + PyObject *sysobj = PyDict_GetItemString(main_dict, "sys"); PyObject *syspath = PyObject_GetAttrString(sysobj, "path"); @@ -649,6 +651,7 @@ bool PythonContext::LoadExtension(ICaptureContext &ctx, const rdcstr &extension) if(mod == NULL) { qCritical() << "Failed to reload " << keystr; + ret += tr("Failed to reload submodule '%1'\n").arg(keystr); reloadSuccess = false; break; } @@ -704,19 +707,28 @@ bool PythonContext::LoadExtension(ICaptureContext &ctx, const rdcstr &extension) else { qCritical() << "Internal error passing pyrenderdoc to extension register()"; + ret += tr("Internal error passing pyrenderdoc to extension register()\n"); } if(retval == NULL) + { + qCritical() << "register() function failed"; + ret += tr("register() function failed\n"); ext = NULL; + } Py_XDECREF(retval); if(ext) { - int ret = PyModule_AddObject(ext, "pyrenderdoc", pyctx); + int pyret = PyModule_AddObject(ext, "pyrenderdoc", pyctx); - if(ret != 0) + if(pyret != 0) + { + qCritical() << "Couldn't set pyrenderdoc global in loaded module"; + ret += tr("Couldn't set pyrenderdoc global in loaded module\n"); ext = NULL; + } } Py_XDECREF(pyctx); @@ -735,17 +747,26 @@ bool PythonContext::LoadExtension(ICaptureContext &ctx, const rdcstr &extension) { FetchException(typeStr, valueStr, finalLine, frames); + ret += lit("\n"); + + ret = ret.trimmed(); + qCritical("Error importing extension module. %s: %s", typeStr.toUtf8().data(), valueStr.toUtf8().data()); + ret += tr("Error importing extension module. %1: %2\n\n").arg(typeStr).arg(valueStr); if(!frames.isEmpty()) { qCritical() << "Traceback (most recent call last):"; + ret += tr("Traceback (most recent call last):\n"); for(const QString &f : frames) { QStringList lines = f.split(QLatin1Char('\n')); for(const QString &line : lines) - qCritical("%s", line.toUtf8().data()); + { + qCritical(" %s", line.toUtf8().data()); + ret += line + lit("\n"); + } } } } @@ -757,7 +778,7 @@ bool PythonContext::LoadExtension(ICaptureContext &ctx, const rdcstr &extension) current_global_handle = NULL; - return ext != NULL; + return ret; } void PythonContext::ConvertPyArgs(const ExtensionCallbackData &data, @@ -833,10 +854,7 @@ void PythonContext::executeString(const QString &filename, const QString &source { if(!initialised()) { - emit exception( - lit("SystemError"), - tr("Python integration failed to initialise, see diagnostic log for more information."), -1, - {}); + emit exception(lit("SystemError"), tr("Python integration failed to initialise."), -1, {}); return; } @@ -934,10 +952,7 @@ void PythonContext::setGlobal(const char *varName, const char *typeName, void *o { if(!initialised()) { - emit exception( - lit("SystemError"), - tr("Python integration failed to initialise, see diagnostic log for more information."), -1, - {}); + emit exception(lit("SystemError"), tr("Python integration failed to initialise."), -1, {}); return; } @@ -1142,10 +1157,7 @@ void PythonContext::setPyGlobal(const char *varName, PyObject *obj) { if(!initialised()) { - emit exception( - lit("SystemError"), - tr("Python integration failed to initialise, see diagnostic log for more information."), -1, - {}); + emit exception(lit("SystemError"), tr("Python integration failed to initialise."), -1, {}); return; } diff --git a/qrenderdoc/Code/pyrenderdoc/PythonContext.h b/qrenderdoc/Code/pyrenderdoc/PythonContext.h index d0b98b057..aa884b200 100644 --- a/qrenderdoc/Code/pyrenderdoc/PythonContext.h +++ b/qrenderdoc/Code/pyrenderdoc/PythonContext.h @@ -60,7 +60,7 @@ public: static QStringList GetApplicationExtensionsPaths(); static void ProcessExtensionWork(std::function callback); - static bool LoadExtension(ICaptureContext &ctx, const rdcstr &extension); + static QString LoadExtension(ICaptureContext &ctx, const rdcstr &extension); static void ConvertPyArgs(const ExtensionCallbackData &data, rdcarray> &args); static void FreePyArgs(rdcarray> &args); diff --git a/qrenderdoc/Code/pyrenderdoc/cosmetics.i b/qrenderdoc/Code/pyrenderdoc/cosmetics.i index fb6f3b820..34980c900 100644 --- a/qrenderdoc/Code/pyrenderdoc/cosmetics.i +++ b/qrenderdoc/Code/pyrenderdoc/cosmetics.i @@ -1,4 +1,7 @@ +%feature("python:tp_str") ResultDetails "result_str"; +%feature("python:tp_repr") ResultDetails "result_str"; + // add some useful builtin functions for ResourceId %feature("python:tp_str") ResourceId "resid_str"; %feature("python:tp_repr") ResourceId "resid_str"; @@ -35,6 +38,22 @@ fail: } // %extend ResourceId %wrapper %{ +static PyObject *result_str(PyObject *resid) +{ + void *resptr = NULL; + ResultDetails *result = NULL; + int res = SWIG_ConvertPtr(resid, &resptr, SWIGTYPE_p_ResultDetails, 0); + if (!SWIG_IsOK(res)) { + SWIG_exception_fail(SWIG_ArgError(res), "in method 'ResultDetails.str', ResultDetails is not correct type"); + } + + result = (ResultDetails *)resptr; + + return PyUnicode_FromFormat("", result->Message().c_str()); +fail: + return NULL; +} + static PyObject *resid_str(PyObject *resid) { void *resptr = NULL; diff --git a/qrenderdoc/Code/pyrenderdoc/qrenderdoc_stub.cpp b/qrenderdoc/Code/pyrenderdoc/qrenderdoc_stub.cpp index dd499281b..fa9a52cb1 100644 --- a/qrenderdoc/Code/pyrenderdoc/qrenderdoc_stub.cpp +++ b/qrenderdoc/Code/pyrenderdoc/qrenderdoc_stub.cpp @@ -187,14 +187,14 @@ void RemoteHost::CheckStatus() { } -ReplayStatus RemoteHost::Connect(IRemoteServer **server) +ResultDetails RemoteHost::Connect(IRemoteServer **server) { - return ReplayStatus::Succeeded; + return {ResultCode::Succeeded}; } -ReplayStatus RemoteHost::Launch() +ResultDetails RemoteHost::Launch() { - return ReplayStatus::Succeeded; + return {ResultCode::Succeeded}; } bool RemoteHost::IsServerRunning() const diff --git a/qrenderdoc/Code/pyrenderdoc/renderdoc.i b/qrenderdoc/Code/pyrenderdoc/renderdoc.i index 52b917593..c6b791ab6 100644 --- a/qrenderdoc/Code/pyrenderdoc/renderdoc.i +++ b/qrenderdoc/Code/pyrenderdoc/renderdoc.i @@ -155,6 +155,11 @@ VA_IGNORE_REST_OF_FILE %ignore StructuredChunkList::StructuredChunkList; %ignore StructuredChunkList::~StructuredChunkList; +// don't allow user code to create ResultDetails objects (they can't allocate the string) +// or access the internal message, which we can't hide as ResultDetails must be POD. +%ignore ResultDetails::ResultDetails; +%ignore ResultDetails::internal_msg; + // these objects return a new copy which the python caller should own. %newobject SDObject::Duplicate; %newobject SDChunk::Duplicate; diff --git a/qrenderdoc/Widgets/CustomPaintWidget.cpp b/qrenderdoc/Widgets/CustomPaintWidget.cpp index 134a8f4b7..3e283fdad 100644 --- a/qrenderdoc/Widgets/CustomPaintWidget.cpp +++ b/qrenderdoc/Widgets/CustomPaintWidget.cpp @@ -97,7 +97,7 @@ void CustomPaintWidget::OnSelectedEventChanged(uint32_t eventId) void CustomPaintWidget::OnEventChanged(uint32_t eventId) { // if we've encountered a fatal error recreate the widget and take over painting - if(m_Rendering && m_Ctx && m_Ctx->GetFatalError() != ReplayStatus::Succeeded) + if(m_Rendering && m_Ctx && !m_Ctx->GetFatalError().OK()) { RecreateInternalWidget(); update(); @@ -136,8 +136,7 @@ void CustomPaintWidget::RecreateInternalWidget() } // if no capture is loaded, or we've encountered a fatal error, we're not rendering anymore. - m_Rendering = m_Rendering && m_Ctx && m_Ctx->IsCaptureLoaded() && - m_Ctx->GetFatalError() == ReplayStatus::Succeeded; + m_Rendering = m_Rendering && m_Ctx && m_Ctx->IsCaptureLoaded() && m_Ctx->GetFatalError().OK(); // we need to recreate the widget if it's not matching out rendering state. if(m_Internal == NULL || m_Rendering != m_Internal->IsRendering()) diff --git a/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp b/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp index 7932fc69c..4d9af8594 100644 --- a/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp +++ b/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp @@ -893,15 +893,15 @@ void CaptureDialog::on_toggleGlobal_clicked() QString capturefile = m_Ctx.TempCaptureFilename(QFileInfo(exe).baseName()); - bool success = RENDERDOC_StartGlobalHook(exe, capturefile, Settings().options); + ResultDetails success = RENDERDOC_StartGlobalHook(exe, capturefile, Settings().options); - if(!success) + if(!success.OK()) { // tidy up and exit - RDDialog::critical( - this, tr("Couldn't start global hook"), - tr("Aborting. Couldn't start global hook. Check diagnostic log in help menu for more " - "information")); + RDDialog::critical(this, tr("Couldn't start global hook"), + tr("Aborting. Couldn't start global hook.\n" + "%1") + .arg(success.Message())); setEnabledMultiple(enableDisableWidgets, true); diff --git a/qrenderdoc/Windows/Dialogs/CrashDialog.cpp b/qrenderdoc/Windows/Dialogs/CrashDialog.cpp index f286723c4..43acb089c 100644 --- a/qrenderdoc/Windows/Dialogs/CrashDialog.cpp +++ b/qrenderdoc/Windows/Dialogs/CrashDialog.cpp @@ -83,9 +83,9 @@ CrashDialog::CrashDialog(PersistantConfig &cfg, QVariantMap crashReportJSON, QWi ICaptureFile *cap = RENDERDOC_OpenCaptureFile(); - ReplayStatus status = cap->OpenFile(capInfo.absoluteFilePath(), "", NULL); + ResultDetails result = cap->OpenFile(capInfo.absoluteFilePath(), "", NULL); - if(status == ReplayStatus::Succeeded) + if(result.OK()) { Thumbnail thumb = cap->GetThumbnail(FileType::Raw, 320); QImage i = QImage(thumb.data.data(), (int)thumb.width, (int)thumb.height, QImage::Format_RGB888) diff --git a/qrenderdoc/Windows/Dialogs/ExtensionManager.cpp b/qrenderdoc/Windows/Dialogs/ExtensionManager.cpp index 1ebcba814..faef25197 100644 --- a/qrenderdoc/Windows/Dialogs/ExtensionManager.cpp +++ b/qrenderdoc/Windows/Dialogs/ExtensionManager.cpp @@ -107,7 +107,8 @@ void ExtensionManager::on_reload_clicked() if(!e.name.isEmpty()) { // if the load succeeds, set us as checked. Otherwise, unchecked - if(m_Ctx.Extensions().LoadExtension(e.package)) + QString errors = m_Ctx.Extensions().LoadExtension(e.package); + if(errors.isEmpty()) { item->setCheckState(2, Qt::Checked); } @@ -115,9 +116,10 @@ void ExtensionManager::on_reload_clicked() { item->setCheckState(2, Qt::Unchecked); RDDialog::critical(this, tr("Failed to load extension"), - tr("Failed to load extension '%1'.\n" - "Check the diagnostic log for python errors") - .arg(e.name)); + tr("Failed to load extension '%1':\n" + "%2") + .arg(e.name) + .arg(errors)); } update_currentItem(item); diff --git a/qrenderdoc/Windows/Dialogs/RemoteManager.cpp b/qrenderdoc/Windows/Dialogs/RemoteManager.cpp index 376e0a5f7..bbca3b846 100644 --- a/qrenderdoc/Windows/Dialogs/RemoteManager.cpp +++ b/qrenderdoc/Windows/Dialogs/RemoteManager.cpp @@ -597,10 +597,10 @@ void RemoteManager::on_connect_clicked() } else { - ReplayStatus status = ReplayStatus::Succeeded; - LambdaThread *th = new LambdaThread([&host, &status]() { + ResultDetails result = {ResultCode::Succeeded}; + LambdaThread *th = new LambdaThread([&host, &result]() { IRemoteServer *server = NULL; - status = host.Connect(&server); + result = host.Connect(&server); if(server) server->ShutdownServerAndConnection(); }); @@ -615,9 +615,9 @@ void RemoteManager::on_connect_clicked() setRemoteServerLive(node, false, false); - if(status != ReplayStatus::Succeeded) + if(!result.OK()) RDDialog::critical(this, tr("Shutdown error"), - tr("Error shutting down remote server: %1").arg(ToQStr(status))); + tr("Error shutting down remote server: %1").arg(result.Message())); } // kick off a thread to check the status diff --git a/qrenderdoc/Windows/MainWindow.cpp b/qrenderdoc/Windows/MainWindow.cpp index 7e8476f7e..f6e21c50a 100644 --- a/qrenderdoc/Windows/MainWindow.cpp +++ b/qrenderdoc/Windows/MainWindow.cpp @@ -670,29 +670,27 @@ void MainWindow::OnCaptureTrigger(const QString &exe, const QString &workingDir, GUIInvoke::call(this, [this, exe, ret, callback]() { - if(ret.status == ReplayStatus::JDWPFailure) + if(ret.result.code == ResultCode::JDWPFailure) { RDDialog::critical( this, tr("Error connecting to debugger"), tr("Error launching %1 for capture.\n\n" "Something went wrong connecting to the debugger on the Android device.\n\n" - "This can happen if the package is not " - "marked as debuggable, if the intent arguments are badly specified, " - "or if another android tool such as Android Studio " - "is interfering with the debug connection.\n\n" + "This can happen if the package is not marked as debuggable, the device is not " + "configured to allow app debugging, if the intent arguments are badly specified, or " + "if another android tool such as Android Studio is interfering with the debug " + "connection.\n\n" "Close all instances of Android Studio or other Android programs " "and try again.") .arg(exe)); return; } - if(ret.status != ReplayStatus::Succeeded) + if(ret.result.code != ResultCode::Succeeded) { - RDDialog::critical(this, tr("Error kicking capture"), - tr("Error launching %1 for capture.\n\n%2. Check the diagnostic log in " - "the help menu for more details") - .arg(exe) - .arg(ToQStr(ret.status))); + RDDialog::critical( + this, tr("Error launching capture"), + tr("Error launching %1 for capture.\n\n%2.").arg(exe).arg(ret.result.Message())); return; } @@ -732,29 +730,11 @@ void MainWindow::OnInjectTrigger(uint32_t PID, const rdcarrayError injecting into process %1 for capture.\n\n" - "Something went wrong connecting to the debugger on the Android device.\n\n" - "This can happen if the package is not " - "marked as debuggable, or if another android tool such as Android Studio " - "is interfering with the debug connection.\n\n" - "Close all instances of Android Studio or other Android programs " - "and try again.") - .arg(PID)); - return; - } - - if(ret.status != ReplayStatus::Succeeded) - { - RDDialog::critical( - this, tr("Error kicking capture"), - tr("Error injecting into process %1 for capture.\n\n%2. Check the diagnostic log in " - "the help menu for more details") - .arg(PID) - .arg(ToQStr(ret.status))); + this, tr("Error injecting into process"), + tr("Error injecting into process %1 for capture.\n\n%2").arg(PID).arg(ret.result.Message())); return; } @@ -792,18 +772,12 @@ void MainWindow::LoadCapture(const QString &filename, const ReplayOptions &opts, { ICaptureFile *file = RENDERDOC_OpenCaptureFile(); - ReplayStatus status = file->OpenFile(filename, "rdc", NULL); + ResultDetails result = file->OpenFile(filename, "rdc", NULL); - if(status != ReplayStatus::Succeeded) + if(!result.OK()) { - QString text = tr("Couldn't open file '%1'\n").arg(filename); - QString message = file->ErrorString(); - if(message.isEmpty()) - text += tr("%1").arg(ToQStr(status)); - else - text += tr("%1: %2").arg(ToQStr(status)).arg(message); - - RDDialog::critical(this, tr("Error opening capture"), text); + RDDialog::critical(this, tr("Error opening capture"), + tr("Couldn't open file '%1'\n%2").arg(filename).arg(result.Message())); file->Shutdown(); return; @@ -2096,10 +2070,10 @@ void MainWindow::setRemoteHost(int hostIdx) statusProgress->setMaximum(0); }); - ReplayStatus launchStatus = host.Launch(); - if(launchStatus != ReplayStatus::Succeeded) + ResultDetails launchResult = host.Launch(); + if(!launchResult.OK()) { - showLaunchError(launchStatus); + showLaunchError(launchResult); } // check if it's running now @@ -2108,22 +2082,22 @@ void MainWindow::setRemoteHost(int hostIdx) GUIInvoke::call(this, [this]() { statusProgress->setVisible(false); }); } - ReplayStatus status = ReplayStatus::Succeeded; + ResultDetails result = {ResultCode::Succeeded}; if(host.IsServerRunning() && !host.IsBusy()) { - status = m_Ctx.Replay().ConnectToRemoteServer(host); + result = m_Ctx.Replay().ConnectToRemoteServer(host); } - GUIInvoke::call(this, [this, host, status]() { + GUIInvoke::call(this, [this, host, result]() { contextChooser->setIcon(host.IsServerRunning() && !host.IsBusy() ? Icons::connect() : Icons::disconnect()); - if(status != ReplayStatus::Succeeded) + if(!result.OK()) { contextChooser->setIcon(Icons::cross()); contextChooser->setText(tr("Replay Context: %1").arg(tr("Local"))); - statusText->setText(tr("Connection failed: %1").arg(ToQStr(status))); + statusText->setText(tr("Connection failed: %1").arg(result.Message())); } else if(host.IsVersionMismatch()) { @@ -2573,15 +2547,14 @@ void MainWindow::on_action_Resolve_Symbols_triggered() bool finished = false; m_Ctx.Replay().AsyncInvoke([this, &progress, &finished](IReplayController *) { - bool success = m_Ctx.Replay().GetCaptureAccess()->InitResolver( + ResultDetails success = m_Ctx.Replay().GetCaptureAccess()->InitResolver( true, [&progress](float p) { progress = p; }); - if(!success) + if(!success.OK()) { RDDialog::critical( this, tr("Error loading symbols"), - tr("Couldn't load symbols for callstack resolution.\n\nCheck diagnostic log in " - "Help menu for more details.")); + tr("Couldn't load symbols for callstack resolution.\n\n%1").arg(success.Message())); } finished = true; @@ -2831,16 +2804,6 @@ void MainWindow::on_action_Show_Tips_triggered() RDDialog::show(&tipsDialog); } -void MainWindow::on_action_View_Diagnostic_Log_File_triggered() -{ - QWidget *logView = m_Ctx.GetDiagnosticLogView()->Widget(); - - if(ui->toolWindowManager->toolWindows().contains(logView)) - ToolWindowManager::raiseToolWindow(logView); - else - ui->toolWindowManager->addToolWindow(logView, mainToolArea()); -} - void MainWindow::on_action_Counter_Viewer_triggered() { QWidget *performanceCounterViewer = m_Ctx.GetPerformanceCounterViewer()->Widget(); @@ -2928,6 +2891,16 @@ void MainWindow::on_action_Check_for_Updates_triggered() }); } +void MainWindow::showDiagnosticLogView() +{ + QWidget *logView = m_Ctx.GetDiagnosticLogView()->Widget(); + + if(ui->toolWindowManager->toolWindows().contains(logView)) + ToolWindowManager::raiseToolWindow(logView); + else + ui->toolWindowManager->addToolWindow(logView, mainToolArea()); +} + void MainWindow::updateAvailable_triggered() { bool mismatch = HandleMismatchedVersions(); @@ -3128,40 +3101,43 @@ bool MainWindow::LoadLayout(int layout) return false; } -void MainWindow::showLaunchError(ReplayStatus status) +void MainWindow::showLaunchError(ResultDetails result) { QString message; - switch(status) + switch(result.code) { - case ReplayStatus::AndroidGrantPermissionsFailed: + case ResultCode::AndroidGrantPermissionsFailed: message = - tr("Failed to automatically grant Android permissions to installed server.\n\n" + tr("%1.\n\n" "Please manually allow the RenderDocCmd program storage permissions on your device " - "to ensure correct functionality."); + "to ensure correct functionality.") + .arg(result.Message()); break; - case ReplayStatus::AndroidABINotFound: - message = - tr("Couldn't determine supported ABIs for your device, please check device connection " - "and status."); + case ResultCode::AndroidABINotFound: + message = tr("%1.\n\nPlease check device connection and result.").arg(result.Message()); break; - case ReplayStatus::AndroidAPKFolderNotFound: - message = tr("Couldn't find APK folder, please check that your installation is complete."); + case ResultCode::AndroidAPKFolderNotFound: message = result.Message(); break; + case ResultCode::AndroidAPKInstallFailed: + message = tr("%1.\n\nlease check that your device is connected and accessible to " + "adb, and that installing APKs over USB is allowed.") + .arg(result.Message()); break; - case ReplayStatus::AndroidAPKInstallFailed: - message = - tr("Couldn't install APK, please check that your device is connected and accessible to " - "adb, and that installing APKs over USB is allowed."); - break; - case ReplayStatus::AndroidAPKVerifyFailed: + case ResultCode::AndroidAPKVerifyFailed: message = tr("Couldn't correctly verify installed APK version.\n\n" - "Please check your installation is not corrupted, or if this is a custom build check " - "that all ABIs are built at the same version as this program."); + "Please check your installation is not corrupted." +#if !RENDERDOC_OFFICIAL_BUILD + " Or if this is a custom build check that all ABIs are built at the same version as " + "this program." +#endif + ); + break; + default: + message = tr("Error encountered launching RenderDoc remote server: %1.").arg(result.Message()); break; - default: message = tr("Unexpected error: %1.").arg(ToQStr(status)); break; } GUIInvoke::call(this, [this, message]() { - RDDialog::warning(this, tr("Problems installing RenderDoc server"), message); + RDDialog::warning(this, tr("Problems launching RenderDoc remote server"), message); }); } diff --git a/qrenderdoc/Windows/MainWindow.h b/qrenderdoc/Windows/MainWindow.h index 9355d45dc..e69b5c71e 100644 --- a/qrenderdoc/Windows/MainWindow.h +++ b/qrenderdoc/Windows/MainWindow.h @@ -133,7 +133,7 @@ public: void showPipelineViewer() { on_action_Pipeline_State_triggered(); } void showCaptureDialog() { on_action_Launch_Application_triggered(); } void showDebugMessageView() { on_action_Errors_and_Warnings_triggered(); } - void showDiagnosticLogView() { on_action_View_Diagnostic_Log_File_triggered(); } + void showDiagnosticLogView(); void showCommentView() { on_action_Comments_triggered(); } void showStatisticsViewer() { on_action_Statistics_Viewer_triggered(); } void showTimelineBar() { on_action_Timeline_triggered(); } @@ -175,8 +175,6 @@ private slots: void on_action_Manage_Remote_Servers_triggered(); void on_action_Settings_triggered(); void on_action_View_Documentation_triggered(); - void on_action_View_Diagnostic_Log_File_triggered(); - void on_action_Diagnostic_Log_triggered() { on_action_View_Diagnostic_Log_File_triggered(); } void on_action_Source_on_GitHub_triggered(); void on_action_Build_Release_Downloads_triggered(); void on_action_Show_Tips_triggered(); @@ -297,7 +295,7 @@ private: void FillRemotesMenu(QMenu *menu, bool includeLocalhost); - void showLaunchError(ReplayStatus status); + void showLaunchError(ResultDetails result); bool isUnshareableDeviceInUse(); }; diff --git a/qrenderdoc/Windows/MainWindow.ui b/qrenderdoc/Windows/MainWindow.ui index 2d0826bc6..1ddce6f3e 100644 --- a/qrenderdoc/Windows/MainWindow.ui +++ b/qrenderdoc/Windows/MainWindow.ui @@ -164,7 +164,6 @@ - @@ -180,7 +179,6 @@ - @@ -377,11 +375,6 @@ View &Documentation - - - View Diagnostic &Log File - - Send &Error Report @@ -502,11 +495,6 @@ Manage Extensions - - - Dia&gnostic Log - - Open Capture with Options diff --git a/qrenderdoc/Windows/PythonShell.cpp b/qrenderdoc/Windows/PythonShell.cpp index 78efbd8ea..46edc0181 100644 --- a/qrenderdoc/Windows/PythonShell.cpp +++ b/qrenderdoc/Windows/PythonShell.cpp @@ -319,7 +319,7 @@ struct ExtensionInvoker : ObjectForwarder // rdcarray GetInstalledExtensions() { return m_Obj.GetInstalledExtensions(); } bool IsExtensionLoaded(rdcstr name) { return m_Obj.IsExtensionLoaded(name); } - bool LoadExtension(rdcstr name) { return m_Obj.LoadExtension(name); } + rdcstr LoadExtension(rdcstr name) { return m_Obj.LoadExtension(name); } IMiniQtHelper &GetMiniQtHelper() { return *m_MiniQt; } // /////////////////////////////////////////////////////////////////////// @@ -414,7 +414,7 @@ struct CaptureContextInvoker : ObjectForwarder virtual bool IsCaptureLocal() override { return m_Obj.IsCaptureLocal(); } virtual bool IsCaptureTemporary() override { return m_Obj.IsCaptureTemporary(); } virtual bool IsCaptureLoading() override { return m_Obj.IsCaptureLoading(); } - virtual ReplayStatus GetFatalError() override { return m_Obj.GetFatalError(); } + virtual ResultDetails GetFatalError() override { return m_Obj.GetFatalError(); } virtual rdcstr GetCaptureFilename() override { return m_Obj.GetCaptureFilename(); } virtual CaptureModifications GetCaptureModifications() override { diff --git a/qrenderdoc/Windows/TextureViewer.cpp b/qrenderdoc/Windows/TextureViewer.cpp index feadb776d..a73d6854c 100644 --- a/qrenderdoc/Windows/TextureViewer.cpp +++ b/qrenderdoc/Windows/TextureViewer.cpp @@ -3929,17 +3929,16 @@ void TextureViewer::on_saveTex_clicked() { ANALYTIC_SET(Export.Texture, true); - bool ret = false; + ResultDetails result = {ResultCode::Succeeded}; QString fn = saveDialog.filename(); m_Ctx.Replay().BlockInvoke( - [this, &ret, fn](IReplayController *r) { ret = r->SaveTexture(m_SaveConfig, fn); }); + [this, &result, fn](IReplayController *r) { result = r->SaveTexture(m_SaveConfig, fn); }); - if(!ret) + if(!result.OK()) { - RDDialog::critical( - NULL, tr("Error saving texture"), - tr("Error saving texture %1.\n\nCheck diagnostic log in Help menu for more details.").arg(fn)); + RDDialog::critical(NULL, tr("Error saving texture"), + tr("Error saving texture %1:\n\n%2").arg(fn).arg(result.Message())); } } } diff --git a/renderdoc/CMakeLists.txt b/renderdoc/CMakeLists.txt index caacfdc4c..99ce9877f 100644 --- a/renderdoc/CMakeLists.txt +++ b/renderdoc/CMakeLists.txt @@ -128,6 +128,7 @@ set(sources common/dds_readwrite.h common/formatting.h common/globalconfig.h + common/result.h common/shader_cache.h common/threading.h common/timing.h diff --git a/renderdoc/android/android.cpp b/renderdoc/android/android.cpp index a2d7290f3..7276cbc24 100644 --- a/renderdoc/android/android.cpp +++ b/renderdoc/android/android.cpp @@ -323,16 +323,16 @@ bool CheckAndroidServerVersion(const rdcstr &deviceID, ABI abi) return false; } -ReplayStatus InstallRenderDocServer(const rdcstr &deviceID) +RDResult InstallRenderDocServer(const rdcstr &deviceID) { - ReplayStatus status = ReplayStatus::Succeeded; + ResultCode result = ResultCode::Succeeded; rdcarray abis = GetSupportedABIs(deviceID); if(abis.empty()) { - RDCERR("Couldn't determine supported ABIs for %s", deviceID.c_str()); - return ReplayStatus::AndroidABINotFound; + RETURN_ERROR_RESULT(ResultCode::AndroidABINotFound, + "Couldn't determine supported ABIs for device %s", deviceID.c_str()); } // Check known paths for RenderDoc server @@ -391,11 +391,18 @@ ReplayStatus InstallRenderDocServer(const rdcstr &deviceID) if(apksFolder.empty()) { - RDCERR( - "APK folder missing! RenderDoc for Android will not work without it. " - "Build your Android ABI in build-android in the root to have it " - "automatically found and installed."); - return ReplayStatus::AndroidAPKFolderNotFound; +#if RENDERDOC_OFFICIAL_BUILD + RETURN_ERROR_RESULT(ResultCode::AndroidAPKFolderNotFound, + "RenderDoc APK not found. Your build of RenderDoc may be incomplete.\n" + "Check that your device is ARM based, other ABIs are not supported."); +#else + RETURN_ERROR_RESULT(ResultCode::AndroidAPKFolderNotFound, + "RenderDoc APK not found. Your build of RenderDoc is incomplete.\n" + "If this is a development build, consult the documentation for building " + "the Android package.\n" + "If this is a release build check that your device is ARM based, other " + "ABIs are not supported."); +#endif } for(ABI abi : abis) @@ -444,14 +451,14 @@ ReplayStatus InstallRenderDocServer(const rdcstr &deviceID) if(success) { // if it succeeded this time, then it was the permission grant that failed - status = ReplayStatus::AndroidGrantPermissionsFailed; + result = ResultCode::AndroidGrantPermissionsFailed; } else { // otherwise something went wrong with verifying. If the install failed completely we'll // return AndroidAPKInstallFailed below, otherwise return a code indicating we couldn't // verify the install properly. - status = ReplayStatus::AndroidAPKVerifyFailed; + result = ResultCode::AndroidAPKVerifyFailed; } } } @@ -462,8 +469,8 @@ ReplayStatus InstallRenderDocServer(const rdcstr &deviceID) if(adbCheck.strStdout.empty()) { - RDCERR("Couldn't find any installed APKs. stderr: %s", adbCheck.strStderror.c_str()); - return ReplayStatus::AndroidAPKInstallFailed; + RETURN_ERROR_RESULT(ResultCode::AndroidAPKInstallFailed, "Couldn't install APK(s). stderr: %s", + adbCheck.strStderror.c_str()); } size_t lines = adbCheck.strStdout.find('\n') == -1 ? 1 : 2; @@ -471,7 +478,7 @@ ReplayStatus InstallRenderDocServer(const rdcstr &deviceID) if(lines != abis.size()) RDCWARN("Installation of some apks failed!"); - return status; + return result; } bool RemoveRenderDocAndroidServer(const rdcstr &deviceID) @@ -555,17 +562,17 @@ struct AndroidRemoteServer : public RemoteServer RemoteServer::ShutdownServerAndConnection(); } - virtual bool Ping() override + virtual ResultDetails Ping() override { if(!Connected()) - return false; + return RDResult(ResultCode::RemoteServerConnectionLost); LazilyStartLogcatThread(); return RemoteServer::Ping(); } - virtual rdcpair OpenCapture( + virtual rdcpair OpenCapture( uint32_t proxyid, const rdcstr &filename, const ReplayOptions &opts, RENDERDOC_ProgressCallback progress) override { @@ -978,18 +985,20 @@ struct AndroidController : public IDeviceProtocolHandler return ret; } - ReplayStatus StartRemoteServer(const rdcstr &URL) override + ResultDetails StartRemoteServer(const rdcstr &URL) override { - ReplayStatus status = ReplayStatus::Succeeded; + RDResult result = ResultCode::Succeeded; - Invoke([this, &status, URL]() { + Invoke([this, &result, URL]() { rdcstr deviceID = GetDeviceID(URL); Device &dev = devices[deviceID]; if(!dev.active) { - status = ReplayStatus::InternalError; + SET_ERROR_RESULT(result, ResultCode::InternalError, + "Android device %s is not active, can't launch server", + Android::GetFriendlyName(deviceID).c_str()); return; } @@ -1009,7 +1018,9 @@ struct AndroidController : public IDeviceProtocolHandler if(abis.empty()) { - status = ReplayStatus::AndroidABINotFound; + SET_ERROR_RESULT(result, ResultCode::AndroidABINotFound, + "Can't determine supported ABIs for device %s", + Android::GetFriendlyName(deviceID).c_str()); return; } @@ -1026,10 +1037,9 @@ struct AndroidController : public IDeviceProtocolHandler } // If server is not detected or has been removed due to incompatibility, install it - status = Android::InstallRenderDocServer(deviceID); - if(status != ReplayStatus::Succeeded && - status != ReplayStatus::AndroidGrantPermissionsFailed && - status != ReplayStatus::AndroidAPKVerifyFailed) + result = Android::InstallRenderDocServer(deviceID); + if(result != ResultCode::Succeeded && result != ResultCode::AndroidGrantPermissionsFailed && + result != ResultCode::AndroidAPKVerifyFailed) { RDCERR("Failed to install RenderDoc server app"); return; @@ -1070,7 +1080,7 @@ struct AndroidController : public IDeviceProtocolHandler // allow the package to start and begin listening before we return Threading::Sleep(1500); - return status; + return result; } rdcstr RemapHostname(const rdcstr &deviceID) override @@ -1150,17 +1160,16 @@ ExecuteResult AndroidRemoteServer::ExecuteAndInject(const rdcstr &packageAndActi Threading::ThreadHandle pingThread = Threading::CreateThread([&done, this]() { Threading::SetCurrentThreadName("Android Ping"); - bool ok = true; - while(ok && Atomic::CmpExch32(&done, 0, 0) == 0) + ResultDetails ok; + ok.code = ResultCode::Succeeded; + while(ok.OK() && Atomic::CmpExch32(&done, 0, 0) == 0) ok = Ping(); }); - ExecuteResult ret; - - AndroidController::m_Inst.Invoke([this, &ret, packageAndActivity, intentArgs, opts]() { - ret.status = ReplayStatus::UnknownError; - ret.ident = RenderDoc_FirstTargetControlPort; + RDResult result; + uint32_t ident = RenderDoc_FirstTargetControlPort; + AndroidController::m_Inst.Invoke([this, &result, &ident, packageAndActivity, intentArgs, opts]() { rdcstr packageName = Android::GetPackageName(packageAndActivity); // Remove leading '/' if any @@ -1189,6 +1198,8 @@ ExecuteResult AndroidRemoteServer::ExecuteAndInject(const rdcstr &packageAndActi bool hookWithJDWP = true; + rdcstr info; + if(Android::SupportsNativeLayers(m_deviceID)) { RDCLOG("Using Android 10 native GPU layering"); @@ -1258,11 +1269,13 @@ ExecuteResult AndroidRemoteServer::ExecuteAndInject(const rdcstr &packageAndActi !checkString.contains("gpu_debug_layers=" RENDERDOC_VULKAN_LAYER_NAME) || !checkString.contains("gpu_debug_layers_gles=" RENDERDOC_ANDROID_LIBRARY)) { - RDCERR( - "Couldn't verify that debug settings are set:\n%s" + info = "Do you have a strange device that requires extra setup?\n" - "E.g. Xiaomi requires a developer account and \"USB debugging (Security Settings)\"\n", - inString.c_str()); + "E.g. Xiaomi requires a developer account and \"USB debugging (Security Settings)\"\n"; + + RDCERR("Couldn't verify that debug settings are set:\n%s\n%s", inString.c_str(), + info.c_str()); + hookWithJDWP = true; // need to tell the hooks to ignore the fact that layers are present because they're not @@ -1340,7 +1353,8 @@ ExecuteResult AndroidRemoteServer::ExecuteAndInject(const rdcstr &packageAndActi int pid = 0; - RDCLOG("Launching package '%s' with activity '%s'", packageName.c_str(), activityName.c_str()); + RDCLOG("Launching package '%s' with activity '%s' and intent args '%s'", packageName.c_str(), + activityName.c_str(), intentArgs.c_str()); if(hookWithJDWP) { @@ -1369,10 +1383,20 @@ ExecuteResult AndroidRemoteServer::ExecuteAndInject(const rdcstr &packageAndActi if(pid == 0) { - RDCERR("Couldn't get PID when launching %s with activity %s and intent args %s", - packageName.c_str(), activityName.c_str(), intentArgs.c_str()); - ret.status = ReplayStatus::InjectionFailed; - ret.ident = 0; + result = RDResult(ResultCode::InjectionFailed); + ident = 0; + + RDCERR("Couldn't get PID"); + + if(!info.empty()) + result.message = + "Couldn't locate the launched activity, either it failed to launch or crashed." + "\n\nAdditional information:\n" + + info; + else + result.message = + "Couldn't locate the launched activity, either it failed to launch or crashed."; + return; } @@ -1388,25 +1412,29 @@ ExecuteResult AndroidRemoteServer::ExecuteAndInject(const rdcstr &packageAndActi if(!injected) { RDCERR("Failed to inject using JDWP"); - ret.status = ReplayStatus::JDWPFailure; - ret.ident = 0; + ident = 0; + result = RDResult(ResultCode::JDWPFailure); + result.message = StringFormat::Fmt( + "Failed to inject using JDWP when launching '%s' with activity '%s' and intent args " + "'%s'", + packageName.c_str(), activityName.c_str(), intentArgs.c_str()); return; } } - ret.status = ReplayStatus::InjectionFailed; + result = RDResult(ResultCode::InjectionFailed, "Timeout was reached waiting for app to start"); uint32_t elapsed = 0, timeout = 1000 * RDCMAX(5U, Android_MaxConnectTimeout()); while(elapsed < timeout) { // Check if the target app has started yet and we can connect to it. ITargetControl *control = RENDERDOC_CreateTargetControl( - AndroidController::m_Inst.GetProtocolName() + "://" + m_deviceID, ret.ident, - "testConnection", false); + AndroidController::m_Inst.GetProtocolName() + "://" + m_deviceID, ident, "testConnection", + false); if(control) { control->Shutdown(); - ret.status = ReplayStatus::Succeeded; + result = ResultCode::Succeeded; break; } @@ -1417,7 +1445,9 @@ ExecuteResult AndroidRemoteServer::ExecuteAndInject(const rdcstr &packageAndActi if(curpid == 0) { - RDCERR("APK has crashed or never opened target control connection before closing."); + SET_ERROR_RESULT( + result, ResultCode::InjectionFailed, + "APK has crashed or never opened target control connection before closing."); break; } @@ -1437,6 +1467,11 @@ ExecuteResult AndroidRemoteServer::ExecuteAndInject(const rdcstr &packageAndActi Threading::JoinThread(pingThread); Threading::CloseThread(pingThread); + ExecuteResult ret = {}; + + ret.result = result; + ret.ident = ident; + return ret; } diff --git a/renderdoc/api/replay/control_types.h b/renderdoc/api/replay/control_types.h index 4333d2589..141f6992f 100644 --- a/renderdoc/api/replay/control_types.h +++ b/renderdoc/api/replay/control_types.h @@ -953,6 +953,67 @@ here. DECLARE_REFLECTION_STRUCT(GlobalEnvironment); +DOCUMENT(R"(A general result from an operation with optional string information for failures. + +This struct can be compared directly to a :class:`ResultCode` for simple checks of status, and when +converted to a string it includes the formatted result code and message as appropriate. + +.. note:: + + The string information is only valid until :func:`ShutdownReplay` is called. After that point, + accessing the string information is invalid and may crash. Care should be taken if + :func:`ShutdownReplay` is called in response to an error, that the error code is read and saved. + + Copying the `ResultDetails` instance is *not* sufficient to preserve the string information. It + should be copied to e.g. a `Tuple[ResultCode, str]` instead. +)") +struct ResultDetails +{ + DOCUMENT(""); + ResultDetails() = default; + ResultDetails(const ResultDetails &) = default; + ResultDetails &operator=(const ResultDetails &) = default; + + ResultDetails &operator=(ResultCode c) + { + code = c; + internal_msg = NULL; + return *this; + } + + DOCUMENT(R"(A simple helper function to check if this result is successful. + +:return Whether or not this result is successful +:rtype bool +)"); + bool OK() const { return code == ResultCode::Succeeded; } + DOCUMENT(""); + explicit operator bool() const { return OK(); } +#if defined(SWIG) || defined(SWIG_GENERATED) + bool operator==(ResultCode resultCode) const { return code == resultCode; } + bool operator!=(ResultCode resultCode) const { return code != resultCode; } +#endif + DOCUMENT( + "The :class:`ResultDetails` resulting from the operation, indicating success or failure."); + ResultCode code; + + DOCUMENT(R"(For error codes, this will contain the stringified error code as well as any optional +extra information that is available about the error. + +.. note:: + + It's not necessary to also display the stringified version of :data:`code` as that is automatically + included in the message. + +:return A formatted message for failure codes, including the code itself. +:rtype str +)"); + rdcstr Message() const { return internal_msg ? *internal_msg : ToStr(code); } + const rdcstr *internal_msg; +}; + +DECLARE_REFLECTION_STRUCT(ResultDetails); + DOCUMENT("The result of executing or injecting into a program.") struct ExecuteResult { @@ -962,8 +1023,8 @@ struct ExecuteResult ExecuteResult &operator=(const ExecuteResult &) = default; DOCUMENT( - "The :class:`ReplayStatus` resulting from the operation, indicating success or failure."); - ReplayStatus status; + "The :class:`ResultDetails` resulting from the operation, indicating success or failure."); + ResultDetails result; DOCUMENT(R"(The ident where the new application is listening for target control, or 0 if something went wrong. )"); diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index 497870078..912038b13 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -286,11 +286,11 @@ Should only be called for texture outputs. type. If set to :data:`CompType.Typeless` then no cast is applied, otherwise where allowed the texture data will be reinterpreted - e.g. from unsigned integers to floats, or to unsigned normalised values. -:return: A boolean indicating if the thumbnail was successfully created. -:rtype: bool +:return: A result indicating if the thumbnail was successfully created. +:rtype: ResultDetails )"); - virtual bool AddThumbnail(WindowingData window, ResourceId textureId, const Subresource &sub, - CompType typeCast) = 0; + virtual ResultDetails AddThumbnail(WindowingData window, ResourceId textureId, + const Subresource &sub, CompType typeCast) = 0; DOCUMENT(R"(Render to the window handle specified when the output was created. @@ -306,10 +306,10 @@ fixed high zoom value and a fixed position, see :meth:`SetPixelContextLocation`. Should only be called for texture outputs. :param WindowingData window: A :class:`WindowingData` describing the native window. -:return: A boolean indicating if the pixel context was successfully configured. -:rtype: bool +:return: A result indicating if the pixel context was successfully configured. +:rtype: ResultDetails )"); - virtual bool SetPixelContext(WindowingData window) = 0; + virtual ResultDetails SetPixelContext(WindowingData window) = 0; DOCUMENT(R"(Sets the pixel that the pixel context should be centred on. @@ -791,14 +791,14 @@ appropriately, such as by displaying a message to the user. The replay controlle remain stable and return null/empty data for the most part, but it's recommended for maximum stability to stop using the controller when a fatal error is encountered. -If there has been no error, this will return :data:`ReplayStatus.Succeeded`. If there has been an +If there has been no error, this will return :data:`ResultCode.Succeeded`. If there has been an error this will return the error code every time, it will not be 'consumed' so it's safe to have multiple things checking it. :return: The current fatal error status. -:rtype: ReplayStatus +:rtype: ResultDetails )"); - virtual ReplayStatus GetFatalErrorStatus() = 0; + virtual ResultDetails GetFatalErrorStatus() = 0; DOCUMENT(R"(Retrieve a list of entry points for a shader. @@ -1006,10 +1006,10 @@ texture to something compatible with the target file format. :param TextureSave saveData: The configuration settings of which texture to save, and how :param str path: The path to save to on disk. -:return: ``True`` if the texture was saved successfully, ``False`` otherwise. -:rtype: bool +:return: The result of the operation. +:rtype: ResultDetails )"); - virtual bool SaveTexture(const TextureSave &saveData, const rdcstr &path) = 0; + virtual ResultDetails SaveTexture(const TextureSave &saveData, const rdcstr &path) = 0; DOCUMENT(R"(Retrieve the generated data from one of the geometry processing shader stages. @@ -1217,10 +1217,10 @@ or name). :param SectionProperties props: The properties of the section to be written. :param bytes contents: The raw contents of the section. -:return: ``True`` if the section was written successfully, ``False`` otherwise. -:rtype: bool +:return: The result of the operation. +:rtype: ResultDetails )"); - virtual bool WriteSection(const SectionProperties &props, const bytebuf &contents) = 0; + virtual ResultDetails WriteSection(const SectionProperties &props, const bytebuf &contents) = 0; DOCUMENT(R"(Query if callstacks are available. @@ -1241,10 +1241,10 @@ separate thread. fail. :param ProgressCallback progress: A callback that will be repeatedly called with an updated progress value for the resolver process. Can be ``None`` if no progress is desired. -:return: ``True`` if the resolver successfully initialised, ``False`` if something went wrong. -:rtype: bool +:return: The result of the operation. +:rtype: ResultDetails )"); - virtual bool InitResolver(bool interactive, RENDERDOC_ProgressCallback progress) = 0; + virtual ResultDetails InitResolver(bool interactive, RENDERDOC_ProgressCallback progress) = 0; DOCUMENT(R"(Retrieve the details of each stackframe in the provided callstack. @@ -1287,11 +1287,10 @@ struct IRemoteServer : public ICaptureAccess DOCUMENT(R"(Pings the remote server to ensure the connection is still alive. -:return: ``True`` if the ping was sent and received successfully, ``False`` if something went wrong - and the connection is no longer alive. -:rtype: bool +:return: The result of the operation - if a failure occurred the connection is no longer alive. +:rtype: ResultDetails )"); - virtual bool Ping() = 0; + virtual ResultDetails Ping() = 0; DOCUMENT(R"(Retrieve a list of renderers available for local proxying. @@ -1410,9 +1409,9 @@ or an error has occurred. value for the opening. Can be ``None`` if no progress is desired. :return: A tuple containing the status of opening the capture, whether success or failure, and the resulting :class:`ReplayController` handle if successful. -:rtype: Tuple[ReplayStatus,ReplayController] +:rtype: Tuple[ResultDetails,ReplayController] )"); - virtual rdcpair OpenCapture( + virtual rdcpair OpenCapture( uint32_t proxyid, const rdcstr &logfile, const ReplayOptions &opts, RENDERDOC_ProgressCallback progress) = 0; @@ -1450,11 +1449,11 @@ empty or unrecognised. :param str filetype: The format of the given file. :param ProgressCallback progress: A callback that will be repeatedly called with an updated progress value if an import step occurs. Can be ``None`` if no progress is desired. -:return: The status of the open operation, whether it succeeded or failed (and how it failed). -:rtype: ReplayStatus +:return: The result of the operation. +:rtype: ResultDetails )"); - virtual ReplayStatus OpenFile(const rdcstr &filename, const rdcstr &filetype, - RENDERDOC_ProgressCallback progress) = 0; + virtual ResultDetails OpenFile(const rdcstr &filename, const rdcstr &filetype, + RENDERDOC_ProgressCallback progress) = 0; DOCUMENT(R"(Initialises the file handle from a raw memory buffer. @@ -1466,11 +1465,11 @@ For the :paramref:`OpenBuffer.filetype` parameter, see :meth:`OpenFile`. :param str filetype: The format of the given file. :param ProgressCallback progress: A callback that will be repeatedly called with an updated progress value if an import step occurs. Can be ``None`` if no progress is desired. -:return: The status of the open operation, whether it succeeded or failed (and how it failed). -:rtype: ReplayStatus +:return: The result of the operation. +:rtype: ResultDetails )"); - virtual ReplayStatus OpenBuffer(const bytebuf &buffer, const rdcstr &filetype, - RENDERDOC_ProgressCallback progress) = 0; + virtual ResultDetails OpenBuffer(const bytebuf &buffer, const rdcstr &filetype, + RENDERDOC_ProgressCallback progress) = 0; DOCUMENT(R"(When a capture file is opened, an exclusive lock is held on the file on disk. This makes it impossible to copy the file to another location at the user's request. Calling this @@ -1481,10 +1480,10 @@ It is invalid to call this function if :meth:`OpenFile` has not previously been file. :param str filename: The filename to copy to. -:return: ``True`` if the operation succeeded. -:rtype: bool +:return: The result of the file copy operation. +:rtype: ResultDetails )"); - virtual bool CopyFileTo(const rdcstr &filename) = 0; + virtual ResultDetails CopyFileTo(const rdcstr &filename) = 0; DOCUMENT(R"(Converts the currently loaded file to a given format and saves it to disk. @@ -1499,21 +1498,11 @@ representation back to native RDC. again. If ``None`` then structured data will be fetched if not already present and used. :param ProgressCallback progress: A callback that will be repeatedly called with an updated progress value for the conversion. Can be ``None`` if no progress is desired. -:return: The status of the conversion operation, whether it succeeded or failed (and how it failed). -:rtype: ReplayStatus +:return: The result of the operation. +:rtype: ResultDetails )"); - virtual ReplayStatus Convert(const rdcstr &filename, const rdcstr &filetype, const SDFile *file, - RENDERDOC_ProgressCallback progress) = 0; - - DOCUMENT(R"(Returns the human-readable error string for the last error received. - -The error string is not reset by calling this function so it's safe to call multiple times. However -any other function call may reset the error string to empty. - -:return: The error string, if one exists, or an empty string. -:rtype: str -)"); - virtual rdcstr ErrorString() = 0; + virtual ResultDetails Convert(const rdcstr &filename, const rdcstr &filetype, const SDFile *file, + RENDERDOC_ProgressCallback progress) = 0; DOCUMENT(R"(Returns the list of capture file formats. @@ -1598,9 +1587,9 @@ by the :class:`ReplayController`. value for the opening. Can be ``None`` if no progress is desired. :return: A tuple containing the status of opening the capture, whether success or failure, and the resulting :class:`ReplayController` handle if successful. -:rtype: Tuple[ReplayStatus,ReplayController] +:rtype: Tuple[ResultDetails,ReplayController] )"); - virtual rdcpair OpenCapture( + virtual rdcpair OpenCapture( const ReplayOptions &opts, RENDERDOC_ProgressCallback progress) = 0; DOCUMENT(R"(Returns the structured data for this capture. @@ -1847,9 +1836,9 @@ DOCUMENT(R"(Create a connection to a remote server running on given hostname. specified then default TCP enumeration happens. :return: The status of opening the connection, whether success or failure, and a :class:`RemoteServer` instance if it were successful -:rtype: Tuple[ReplayStatus,RemoteServer] +:rtype: Tuple[ResultDetails,RemoteServer] )"); -extern "C" RENDERDOC_API ReplayStatus RENDERDOC_CC +extern "C" RENDERDOC_API ResultDetails RENDERDOC_CC RENDERDOC_CreateRemoteServerConnection(const rdcstr &URL, IRemoteServer **rend); DOCUMENT(R"(Check the connection to a remote server running on given hostname. @@ -1860,9 +1849,9 @@ the status can be checked without interfering with making connections. :param str URL: The hostname to connect to, if blank then localhost is used. If no protocol is specified then default TCP enumeration happens. :return: The status of the server. -:rtype: ReplayStatus +:rtype: ResultDetails )"); -extern "C" RENDERDOC_API ReplayStatus RENDERDOC_CC +extern "C" RENDERDOC_API ResultDetails RENDERDOC_CC RENDERDOC_CheckRemoteServerConnection(const rdcstr &URL); DOCUMENT(R"(This launches a remote server which will continually run in a loop to server requests @@ -1901,19 +1890,19 @@ started on the system. This function can only be called if global hooking is supported (see :func:`CanGlobalHook`) and if global hooking is not active (see :func:`IsGlobalHookActive`). +The hook must be closed with :func:`StopGlobalHook` before the application is closed. + This function must be called when the process is running with administrator/superuser permissions. :param str pathmatch: A string to match against each new process's executable path to determine which corresponds to the program we actually want to capture. :param str logfile: Where to store any captures. :param CaptureOptions opts: The capture options to use when injecting into the program. -:return: ``True`` if the hook is active, ``False`` if something went wrong. The hook must be closed - with :func:`StopGlobalHook` before the application is closed. -:rtype: bool +:return: The result of the operation, if the result succeeded the hook is now active. +:rtype: ResultDetails )"); -extern "C" RENDERDOC_API bool RENDERDOC_CC RENDERDOC_StartGlobalHook(const rdcstr &pathmatch, - const rdcstr &logfile, - const CaptureOptions &opts); +extern "C" RENDERDOC_API ResultDetails RENDERDOC_CC RENDERDOC_StartGlobalHook( + const rdcstr &pathmatch, const rdcstr &logfile, const CaptureOptions &opts); DOCUMENT(R"(Stop the global hook that was activated by :func:`StartGlobalHook`. @@ -2239,9 +2228,9 @@ user can be prompted to close an existing program before a new one is launched. :param str URL: The URL of the device in the form ``protocol://host``, with protocol as returned by :func:`GetProtocolName` and host as returned by :func:`GetDevices`. :return: The status of starting the server, whether success or failure. -:rtype: ReplayStatus +:rtype: ResultDetails )"); - virtual ReplayStatus StartRemoteServer(const rdcstr &URL) = 0; + virtual ResultDetails StartRemoteServer(const rdcstr &URL) = 0; protected: IDeviceProtocolController() = default; diff --git a/renderdoc/api/replay/renderdoc_tostr.inl b/renderdoc/api/replay/renderdoc_tostr.inl index 85f89fe24..7052da3d7 100644 --- a/renderdoc/api/replay/renderdoc_tostr.inl +++ b/renderdoc/api/replay/renderdoc_tostr.inl @@ -44,9 +44,9 @@ rdcstr DoStringise(const SDBasic &el) } template <> -rdcstr DoStringise(const ReplayStatus &el) +rdcstr DoStringise(const ResultCode &el) { - BEGIN_ENUM_STRINGISE(ReplayStatus) + BEGIN_ENUM_STRINGISE(ResultCode) { STRINGISE_ENUM_CLASS_NAMED(Succeeded, "Success"); STRINGISE_ENUM_CLASS_NAMED(UnknownError, "Unknown error"); @@ -63,8 +63,8 @@ rdcstr DoStringise(const ReplayStatus &el) FileIncompatibleVersion, "Capture file incompatible due to being made on an different major version of RenderDoc"); STRINGISE_ENUM_CLASS_NAMED(FileCorrupted, "File is corrupted"); - STRINGISE_ENUM_CLASS_NAMED(ImageUnsupported, - "The image file is recognised but the format is unsupported"); + STRINGISE_ENUM_CLASS_NAMED( + ImageUnsupported, "The image file or format is unrecognised or not supported in this form"); STRINGISE_ENUM_CLASS_NAMED(APIUnsupported, "API used in this capture is unsupported"); STRINGISE_ENUM_CLASS_NAMED(APIInitFailed, "API initialisation failed while loading the capture"); @@ -92,8 +92,13 @@ rdcstr DoStringise(const ReplayStatus &el) STRINGISE_ENUM_CLASS_NAMED(AndroidAPKVerifyFailed, "Failed to verify installed Android remote server"); STRINGISE_ENUM_CLASS_NAMED(RemoteServerConnectionLost, "Connection lost to remote server"); - STRINGISE_ENUM_CLASS_NAMED(ReplayOutOfMemory, "Encountered GPU out of memory error"); + STRINGISE_ENUM_CLASS_NAMED(ReplayOutOfMemory, "Encountered an out of memory error"); STRINGISE_ENUM_CLASS_NAMED(ReplayDeviceLost, "Encountered a GPU device lost error"); + STRINGISE_ENUM_CLASS_NAMED(DataNotAvailable, + "Data was requested through RenderDoc's API which is not available"); + STRINGISE_ENUM_CLASS_NAMED(InvalidParameter, + "An invalid parameter was passed to RenderDoc's API"); + STRINGISE_ENUM_CLASS_NAMED(CompressionFailed, "Compression or decompression failed"); } END_ENUM_STRINGISE(); } diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index dae8e82f6..85e745ffd 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -3552,7 +3552,7 @@ enum class ReplaySupport : uint32_t DECLARE_REFLECTION_ENUM(ReplaySupport); -DOCUMENT(R"(The status of a high-level replay operation such as opening a capture or connecting to +DOCUMENT(R"(The result from a replay operation such as opening a capture or connecting to a remote server. .. data:: Succeeded @@ -3605,7 +3605,7 @@ a remote server. .. data:: ImageUnsupported - The image file is recognised but the format is unsupported. + The image file or format is unrecognised or not supported in this form. .. data:: APIUnsupported @@ -3621,7 +3621,7 @@ a remote server. .. data:: APIHardwareUnsupported - The API is not supported on the currently available hardware. + Current replaying hardware unsupported or incompatible with captured hardware. .. data:: APIDataCorrupted @@ -3663,13 +3663,25 @@ a remote server. .. data:: ReplayOutOfMemory - While replaying, a GPU out of memory error was encountered. + While replaying, an out of memory error was encountered. .. data:: ReplayDeviceLost While replaying a device lost fatal error was encountered. + +.. data:: DataNotAvailable + + Data was requested through RenderDoc's API which is not available. + +.. data:: InvalidParameter + + An invalid parameter was passed to RenderDoc's API. + +.. data:: CompressionFailed + + Compression or decompression failed. )"); -enum class ReplayStatus : uint32_t +enum class ResultCode : uint32_t { Succeeded = 0, UnknownError, @@ -3699,9 +3711,16 @@ enum class ReplayStatus : uint32_t RemoteServerConnectionLost, ReplayOutOfMemory, ReplayDeviceLost, + DataNotAvailable, + InvalidParameter, + CompressionFailed, }; -DECLARE_REFLECTION_ENUM(ReplayStatus); +DECLARE_REFLECTION_ENUM(ResultCode); +// need to forward declare this explicitly since ResultCode can be instantiated early in places +// where we're going to later explicitly instantiate it to define it +template <> +rdcstr DoStringise(const ResultCode &el); DOCUMENT(R"(The type of message received from or sent to an application target control connection. diff --git a/renderdoc/common/dds_readwrite.cpp b/renderdoc/common/dds_readwrite.cpp index dd2b33391..00c7abbda 100644 --- a/renderdoc/common/dds_readwrite.cpp +++ b/renderdoc/common/dds_readwrite.cpp @@ -25,6 +25,8 @@ #include "dds_readwrite.h" #include #include "common/common.h" +#include "common/formatting.h" +#include "common/result.h" #include "os/os_specific.h" #include "serialise/streamio.h" @@ -530,9 +532,7 @@ DXGI_FORMAT ResourceFormat2DXGIFormat(ResourceFormat format) case ResourceFormatType::YUV8: case ResourceFormatType::YUV10: case ResourceFormatType::YUV12: - case ResourceFormatType::YUV16: - RDCERR("Unsupported writing format %u", format.type); - return DXGI_FORMAT_UNKNOWN; + case ResourceFormatType::YUV16: return DXGI_FORMAT_UNKNOWN; } } @@ -584,7 +584,6 @@ DXGI_FORMAT ResourceFormat2DXGIFormat(ResourceFormat format) } } } - RDCERR("Unexpected component byte width %u for 4-component type", format.compByteWidth); return DXGI_FORMAT_UNKNOWN; } else if(format.compCount == 3) @@ -598,7 +597,6 @@ DXGI_FORMAT ResourceFormat2DXGIFormat(ResourceFormat format) default: return DXGI_FORMAT_R32G32B32_FLOAT; } } - RDCERR("Unexpected component byte width %u for 3-component type", format.compByteWidth); return DXGI_FORMAT_UNKNOWN; } else if(format.compCount == 2) @@ -633,7 +631,6 @@ DXGI_FORMAT ResourceFormat2DXGIFormat(ResourceFormat format) default: return DXGI_FORMAT_R8G8_UNORM; } } - RDCERR("Unexpected component byte width %u for 2-component type", format.compByteWidth); return DXGI_FORMAT_UNKNOWN; } else if(format.compCount == 1) @@ -669,18 +666,16 @@ DXGI_FORMAT ResourceFormat2DXGIFormat(ResourceFormat format) default: return DXGI_FORMAT_R8_UNORM; } } - RDCERR("Unexpected component byte width %u for 1-component type", format.compByteWidth); return DXGI_FORMAT_UNKNOWN; } - RDCERR("Unexpected component count %u", format.compCount); return DXGI_FORMAT_UNKNOWN; } -bool write_dds_to_file(FILE *f, const write_dds_data &data) +RDResult write_dds_to_file(FILE *f, const write_dds_data &data) { if(!f) - return false; + return RDResult(ResultCode::InvalidParameter, "Missing file handle writing DDS file"); uint32_t magic = dds_fourcc; DDS_HEADER header; @@ -723,8 +718,11 @@ bool write_dds_to_file(FILE *f, const write_dds_data &data) case ResourceFormatType::YUV10: case ResourceFormatType::YUV12: case ResourceFormatType::YUV16: - RDCERR("Unsupported file format, %u", data.format.type); - return false; + { + RETURN_ERROR_RESULT(ResultCode::ImageUnsupported, + "Unsupported file format %s to write to DDS", + ToStr(data.format.type).c_str()); + } default: break; } } @@ -752,8 +750,9 @@ bool write_dds_to_file(FILE *f, const write_dds_data &data) if(headerDXT10.dxgiFormat == DXGI_FORMAT_UNKNOWN) { - RDCERR("Couldn't convert resource format to DXGI format"); - return false; + RETURN_ERROR_RESULT(ResultCode::ImageUnsupported, + "Couldn't convert resource format %s to DDS-compatible DXGI format", + data.format.Name().c_str()); } if(data.cubemap) @@ -797,8 +796,11 @@ bool write_dds_to_file(FILE *f, const write_dds_data &data) case ResourceFormatType::YUV12: case ResourceFormatType::YUV16: case ResourceFormatType::R4G4: - RDCERR("Unsupported file format %u", data.format.type); - return false; + { + RETURN_ERROR_RESULT(ResultCode::ImageUnsupported, + "Unsupported file format %s to write to DDS", + ToStr(data.format.type).c_str()); + } default: bytesPerPixel = data.format.compCount * data.format.compByteWidth; } @@ -947,7 +949,7 @@ bool write_dds_to_file(FILE *f, const write_dds_data &data) } } - return true; + return RDResult(); } bool is_dds_file(byte *headerBuffer, size_t size) @@ -959,11 +961,8 @@ bool is_dds_file(byte *headerBuffer, size_t size) return memcmp(headerBuffer, &dds_fourcc, 4) == 0; } -read_dds_data load_dds_from_file(StreamReader *reader) +RDResult load_dds_from_file(StreamReader *reader, read_dds_data &ret) { - read_dds_data ret = {}; - read_dds_data error = {}; - uint64_t fileSize = reader->GetSize(); uint32_t magic = 0; @@ -1018,8 +1017,9 @@ read_dds_data load_dds_from_file(StreamReader *reader) ret.format = DXGIFormat2ResourceFormat(headerDXT10.dxgiFormat); if(ret.format.type == ResourceFormatType::Undefined) { - RDCWARN("Unsupported DXGI_FORMAT: %u", (uint32_t)headerDXT10.dxgiFormat); - return error; + RETURN_ERROR_RESULT(ResultCode::ImageUnsupported, + "Unsupported DXGI_FORMAT %u loaded from DDS", + uint32_t(headerDXT10.dxgiFormat)); } } else if(header.ddspf.dwFlags & DDPF_FOURCC) @@ -1071,8 +1071,18 @@ read_dds_data load_dds_from_file(StreamReader *reader) case 114: ret.format = DXGIFormat2ResourceFormat(DXGI_FORMAT_R32_FLOAT); break; case 115: ret.format = DXGIFormat2ResourceFormat(DXGI_FORMAT_R32G32_FLOAT); break; case 116: ret.format = DXGIFormat2ResourceFormat(DXGI_FORMAT_R32G32B32A32_FLOAT); break; - case 117: RDCERR("Legacy CxV8U8 format is unsupported"); return error; - default: RDCWARN("Unsupported FourCC: %08x", header.ddspf.dwFourCC); return error; + case 117: + { + RETURN_ERROR_RESULT(ResultCode::ImageUnsupported, + "Legacy CxV8U8 DDS format is unsupported"); + } + default: + { + RETURN_ERROR_RESULT( + ResultCode::ImageUnsupported, "Unsupported DDS FourCC: %c%c%c%c", + char((header.ddspf.dwFourCC >> 0) & 0xff), char((header.ddspf.dwFourCC >> 8) & 0xff), + char((header.ddspf.dwFourCC >> 16) & 0xff), char((header.ddspf.dwFourCC >> 24) & 0xff)); + } } } else @@ -1080,8 +1090,8 @@ read_dds_data load_dds_from_file(StreamReader *reader) if(header.ddspf.dwRGBBitCount != 32 && header.ddspf.dwRGBBitCount != 24 && header.ddspf.dwRGBBitCount != 16 && header.ddspf.dwRGBBitCount != 8) { - RDCWARN("Unsupported RGB bit count: %u", header.ddspf.dwRGBBitCount); - return error; + RETURN_ERROR_RESULT(ResultCode::ImageUnsupported, "Unsupported RGB bit count %u in DDS file", + header.ddspf.dwRGBBitCount); } if(header.ddspf.dwABitMask == 0x0000 && header.ddspf.dwRBitMask == 0xf800 && @@ -1113,9 +1123,10 @@ read_dds_data load_dds_from_file(StreamReader *reader) if((bits[1] != 0 && bits[1] != bits[0]) || (bits[2] != 0 && bits[2] != bits[0]) || (bits[3] != 0 && bits[3] != bits[0])) { - RDCWARN("Unsupported RGBA mask: %08x %08x %08x %08x", header.ddspf.dwRBitMask, - header.ddspf.dwGBitMask, header.ddspf.dwBBitMask, header.ddspf.dwABitMask); - return error; + RETURN_ERROR_RESULT(ResultCode::ImageUnsupported, + "Unsupported RGBA mask: %08x %08x %08x %08x", header.ddspf.dwRBitMask, + header.ddspf.dwGBitMask, header.ddspf.dwBBitMask, + header.ddspf.dwABitMask); } uint32_t bitWidth = bits[0]; @@ -1163,8 +1174,11 @@ read_dds_data load_dds_from_file(StreamReader *reader) case ResourceFormatType::YUV16: case ResourceFormatType::D16S8: case ResourceFormatType::R4G4: - RDCERR("Unsupported file format %u", ret.format.type); - return error; + { + RETURN_ERROR_RESULT(ResultCode::ImageUnsupported, + "Unsupported file format %s to load from DDS", + ToStr(ret.format.type).c_str()); + } default: bytesPerPixel = ret.format.compCount * ret.format.compByteWidth; } @@ -1184,8 +1198,11 @@ read_dds_data load_dds_from_file(StreamReader *reader) case ResourceFormatType::ETC2: case ResourceFormatType::EAC: case ResourceFormatType::ASTC: - RDCERR("Unsupported file format, %u", ret.format.type); - return error; + { + RETURN_ERROR_RESULT(ResultCode::ImageUnsupported, + "Unsupported file format %s to load from DDS", + ToStr(ret.format.type).c_str()); + } default: break; } } @@ -1193,8 +1210,9 @@ read_dds_data load_dds_from_file(StreamReader *reader) if(uint64_t(ret.slices) > fileSize || uint64_t(ret.mips) > fileSize || uint64_t(ret.slices) * ret.mips > fileSize) { - RDCERR("Invalid slice count %u or mip count %u", ret.slices, ret.mips); - return ret; + RETURN_ERROR_RESULT(ResultCode::ImageUnsupported, + "Invalid slice count %u or mip count %u loaded from DDS of size %llu", + ret.slices, ret.mips, fileSize); } // we reserve space for a full mip-chain (twice the size of the top mip) just to be conservative @@ -1289,5 +1307,5 @@ read_dds_data load_dds_from_file(StreamReader *reader) } } - return ret; + return RDResult(); } diff --git a/renderdoc/common/dds_readwrite.h b/renderdoc/common/dds_readwrite.h index aac2d15b0..20233e9e9 100644 --- a/renderdoc/common/dds_readwrite.h +++ b/renderdoc/common/dds_readwrite.h @@ -56,5 +56,5 @@ struct write_dds_data : public dds_data }; extern bool is_dds_file(byte *headerBuffer, size_t size); -extern read_dds_data load_dds_from_file(StreamReader *reader); -extern bool write_dds_to_file(FILE *f, const write_dds_data &data); +extern RDResult load_dds_from_file(StreamReader *reader, read_dds_data &data); +extern RDResult write_dds_to_file(FILE *f, const write_dds_data &data); diff --git a/renderdoc/common/formatting.h b/renderdoc/common/formatting.h index 6cb4aabf5..405b0e2a1 100644 --- a/renderdoc/common/formatting.h +++ b/renderdoc/common/formatting.h @@ -43,5 +43,6 @@ struct Args }; rdcstr Fmt(const char *format, ...); +rdcstr Fmt(rdcliteral format, ...); rdcstr FmtArgs(const char *format, Args &args); }; diff --git a/renderdoc/common/result.h b/renderdoc/common/result.h new file mode 100644 index 000000000..e56332a97 --- /dev/null +++ b/renderdoc/common/result.h @@ -0,0 +1,104 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2022 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#pragma once + +#include "api/replay/stringise.h" +#include "common/globalconfig.h" + +// forward declared so we don't pull in too many headers +enum class ResultCode : uint32_t; +struct ResultDetails; + +struct RDResult +{ + // bit of a hack, rather than including the result code definition just assume 0 is succeeded + RDResult() : code(ResultCode(0)) {} + RDResult(ResultCode code) : code(code) {} + RDResult(ResultCode code, const rdcstr &message) : code(code), message(message) {} + RDResult(ResultCode code, const rdcliteral &message) : code(code), message(message) {} + ResultCode code; + // inflexible string is used here because on desktop it's the size of one pointer, meaning this + // struct is overall two pointers (the smallest we could make it without packing the code into the + // string pointer, or using string tables with an index). Since these results are not returned on + // any very high traffic calls, it's better to prioritise simplicity and directness over tight + // memory optimisations + rdcinflexiblestr message; + + bool operator==(ResultCode result) const { return code == result; } + bool operator!=(ResultCode result) const { return code != result; } + operator ResultDetails() const; +}; + +DECLARE_REFLECTION_STRUCT(RDResult); + +// helper macros since we often want to print the error message that gets returned. +// one helper returns immediately, the other sets a result and prints - to allow cleanup +#define RETURN_ERROR_RESULT_INTERNAL(code, msg, ...) \ + do \ + { \ + RDResult res##__LINE__(code, StringFormat::Fmt(STRING_LITERAL(msg), __VA_ARGS__)); \ + RDCERR("%s", res##__LINE__.message.c_str()); \ + return res##__LINE__; \ + } while((void)0, 0) + +#define SET_ERROR_RESULT_INTERNAL(res, code, msg, ...) \ + do \ + { \ + res = RDResult(code, StringFormat::Fmt(STRING_LITERAL(msg), __VA_ARGS__)); \ + RDCERR("%s", res.message.c_str()); \ + } while((void)0, 0) + +#define RETURN_WARNING_RESULT_INTERNAL(code, msg, ...) \ + do \ + { \ + RDResult res##__LINE__(code, StringFormat::Fmt(STRING_LITERAL(msg), __VA_ARGS__)); \ + RDCWARN("%s", res##__LINE__.message.c_str()); \ + return res##__LINE__; \ + } while((void)0, 0) + +#define SET_WARNING_RESULT_INTERNAL(res, code, msg, ...) \ + do \ + { \ + res = RDResult(code, StringFormat::Fmt(STRING_LITERAL(msg), __VA_ARGS__)); \ + RDCWARN("%s", res.message.c_str()); \ + } while((void)0, 0) + +// VS automatically elides any trailing comma if there are no arguments above. GCC/Clang are +// stricter and may fail, so we add an extra 0 since it won't get processed by the format anyway +#if ENABLED(RDOC_MSVS) + +#define RETURN_ERROR_RESULT RETURN_ERROR_RESULT_INTERNAL +#define RETURN_WARNING_RESULT RETURN_WARNING_RESULT_INTERNAL +#define SET_ERROR_RESULT SET_ERROR_RESULT_INTERNAL +#define SET_WARNING_RESULT SET_WARNING_RESULT_INTERNAL + +#else + +#define RETURN_ERROR_RESULT(...) RETURN_ERROR_RESULT_INTERNAL(__VA_ARGS__, 0) +#define RETURN_WARNING_RESULT(...) RETURN_WARNING_RESULT_INTERNAL(__VA_ARGS__, 0) +#define SET_ERROR_RESULT(...) SET_ERROR_RESULT_INTERNAL(__VA_ARGS__, 0) +#define SET_WARNING_RESULT(...) SET_WARNING_RESULT_INTERNAL(__VA_ARGS__, 0) + +#endif diff --git a/renderdoc/core/core.cpp b/renderdoc/core/core.cpp index 4b819e855..17db50ecd 100644 --- a/renderdoc/core/core.cpp +++ b/renderdoc/core/core.cpp @@ -147,12 +147,23 @@ void DoSerialise(SerialiserType &ser, ResourceId &el) INSTANTIATE_SERIALISE_TYPE(ResourceId); +template +void DoSerialise(SerialiserType &ser, RDResult &el) +{ + SERIALISE_MEMBER(code); + SERIALISE_MEMBER(message); + + SIZE_CHECK(16); +} + +INSTANTIATE_SERIALISE_TYPE(RDResult); + #if ENABLED(RDOC_LINUX) && ENABLED(RDOC_XLIB) #include #endif // from image_viewer.cpp -ReplayStatus IMG_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver); +RDResult IMG_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver); template <> rdcstr DoStringise(const CaptureState &el) @@ -634,9 +645,9 @@ void RenderDoc::InitialiseReplay(GlobalEnvironment env, const rdcarray & continue; IReplayDriver *driver = NULL; - ReplayStatus status = m_ReplayDriverProviders[driverType](NULL, ReplayOptions(), &driver); + RDResult result = m_ReplayDriverProviders[driverType](NULL, ReplayOptions(), &driver); - if(status == ReplayStatus::Succeeded) + if(result == ResultCode::Succeeded) { rdcarray gpus = driver->GetAvailableGPUs(); @@ -661,7 +672,7 @@ void RenderDoc::InitialiseReplay(GlobalEnvironment env, const rdcarray & else { RDCWARN("Couldn't create proxy replay driver for %s: %s", ToStr(driverType).c_str(), - ToStr(status).c_str()); + ResultDetails(result).Message().c_str()); } if(driver) @@ -1278,11 +1289,8 @@ RDCFile *RenderDoc::CreateRDC(RDCDriver driver, uint32_t frameNum, const FramePi ret->Create(m_CurrentLogFile.c_str()); - if(ret->ErrorCode() != ContainerError::NoError) - { - RDCERR("Error creating RDC at '%s'", m_CurrentLogFile.c_str()); + if(ret->Error() != ResultCode::Succeeded) SAFE_DELETE(ret); - } return ret; } @@ -1490,7 +1498,7 @@ bool RenderDoc::HasReplaySupport(RDCDriver driverType) return m_ReplayDriverProviders.find(driverType) != m_ReplayDriverProviders.end(); } -ReplayStatus RenderDoc::CreateProxyReplayDriver(RDCDriver proxyDriver, IReplayDriver **driver) +RDResult RenderDoc::CreateProxyReplayDriver(RDCDriver proxyDriver, IReplayDriver **driver) { SyncAvailableGPUThread(); @@ -1504,15 +1512,14 @@ ReplayStatus RenderDoc::CreateProxyReplayDriver(RDCDriver proxyDriver, IReplayDr if(m_ReplayDriverProviders.find(proxyDriver) != m_ReplayDriverProviders.end()) return m_ReplayDriverProviders[proxyDriver](NULL, ReplayOptions(), driver); - RDCERR("Unsupported replay driver requested: %s", ToStr(proxyDriver).c_str()); - return ReplayStatus::APIUnsupported; + RETURN_ERROR_RESULT(ResultCode::APIUnsupported, "Unsupported replay driver requested: %s", + ToStr(proxyDriver).c_str()); } -ReplayStatus RenderDoc::CreateReplayDriver(RDCFile *rdc, const ReplayOptions &opts, - IReplayDriver **driver) +RDResult RenderDoc::CreateReplayDriver(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver) { if(driver == NULL) - return ReplayStatus::InternalError; + return ResultCode::InvalidParameter; SyncAvailableGPUThread(); @@ -1522,8 +1529,8 @@ ReplayStatus RenderDoc::CreateReplayDriver(RDCFile *rdc, const ReplayOptions &op if(!m_ReplayDriverProviders.empty()) return m_ReplayDriverProviders.begin()->second(NULL, opts, driver); - RDCERR("Request for proxy replay device, but no replay providers are available."); - return ReplayStatus::InternalError; + RETURN_ERROR_RESULT(ResultCode::APIUnsupported, + "Request for proxy replay device, but no replay providers are available."); } RDCDriver driverType = rdc->GetDriver(); @@ -1536,14 +1543,13 @@ ReplayStatus RenderDoc::CreateReplayDriver(RDCFile *rdc, const ReplayOptions &op return m_ReplayDriverProviders[driverType](rdc, opts, driver); RDCERR("Unsupported replay driver requested: %s", ToStr(driverType).c_str()); - return ReplayStatus::APIUnsupported; + return ResultCode::APIUnsupported; } -ReplayStatus RenderDoc::CreateRemoteDriver(RDCFile *rdc, const ReplayOptions &opts, - IRemoteDriver **driver) +RDResult RenderDoc::CreateRemoteDriver(RDCFile *rdc, const ReplayOptions &opts, IRemoteDriver **driver) { if(rdc == NULL || driver == NULL) - return ReplayStatus::InternalError; + return ResultCode::InvalidParameter; SyncAvailableGPUThread(); @@ -1556,18 +1562,18 @@ ReplayStatus RenderDoc::CreateRemoteDriver(RDCFile *rdc, const ReplayOptions &op if(m_ReplayDriverProviders.find(driverType) != m_ReplayDriverProviders.end()) { IReplayDriver *dr = NULL; - ReplayStatus status = m_ReplayDriverProviders[driverType](rdc, opts, &dr); + RDResult result = m_ReplayDriverProviders[driverType](rdc, opts, &dr); - if(status == ReplayStatus::Succeeded) + if(result == ResultCode::Succeeded) *driver = (IRemoteDriver *)dr; else RDCASSERT(dr == NULL); - return status; + return result; } - RDCERR("Unsupported replay driver requested: %s", ToStr(driverType).c_str()); - return ReplayStatus::APIUnsupported; + RETURN_ERROR_RESULT(ResultCode::APIUnsupported, "Unsupported replay driver requested: %s", + ToStr(driverType).c_str()); } void RenderDoc::AddActiveDriver(RDCDriver driver, bool present) @@ -1652,16 +1658,16 @@ DriverInformation RenderDoc::GetDriverInformation(GraphicsAPI api) return ret; IReplayDriver *driver = NULL; - ReplayStatus status = CreateProxyReplayDriver(driverType, &driver); + RDResult result = CreateProxyReplayDriver(driverType, &driver); - if(status == ReplayStatus::Succeeded) + if(result == ResultCode::Succeeded) { ret = driver->GetDriverInfo(); } else { RDCERR("Couldn't create proxy replay driver for %s: %s", ToStr(driverType).c_str(), - ToStr(status).c_str()); + ResultDetails(result).Message().c_str()); } if(driver) diff --git a/renderdoc/core/core.h b/renderdoc/core/core.h index 0e918da43..8fa8f5a81 100644 --- a/renderdoc/core/core.h +++ b/renderdoc/core/core.h @@ -325,18 +325,17 @@ inline constexpr float FakeProgress(uint32_t x, uint32_t maxX) return 1.0f - (1.0f / (x * (4.0f / float(maxX)) + 1)); } -typedef ReplayStatus (*RemoteDriverProvider)(RDCFile *rdc, const ReplayOptions &opts, - IRemoteDriver **driver); -typedef ReplayStatus (*ReplayDriverProvider)(RDCFile *rdc, const ReplayOptions &opts, - IReplayDriver **driver); +typedef RDResult (*RemoteDriverProvider)(RDCFile *rdc, const ReplayOptions &opts, + IRemoteDriver **driver); +typedef RDResult (*ReplayDriverProvider)(RDCFile *rdc, const ReplayOptions &opts, + IReplayDriver **driver); -typedef void (*StructuredProcessor)(RDCFile *rdc, SDFile &structData); +typedef RDResult (*StructuredProcessor)(RDCFile *rdc, SDFile &structData); -typedef ReplayStatus (*CaptureImporter)(const rdcstr &filename, StreamReader &reader, RDCFile *rdc, - SDFile &structData, RENDERDOC_ProgressCallback progress); -typedef ReplayStatus (*CaptureExporter)(const rdcstr &filename, const RDCFile &rdc, - const SDFile &structData, - RENDERDOC_ProgressCallback progress); +typedef RDResult (*CaptureImporter)(const rdcstr &filename, StreamReader &reader, RDCFile *rdc, + SDFile &structData, RENDERDOC_ProgressCallback progress); +typedef RDResult (*CaptureExporter)(const rdcstr &filename, const RDCFile &rdc, + const SDFile &structData, RENDERDOC_ProgressCallback progress); typedef IDeviceProtocolHandler *(*ProtocolHandler)(); typedef bool (*VulkanLayerCheck)(VulkanLayerFlags &flags, rdcarray &myJSONs, @@ -504,9 +503,9 @@ public: void SetDarkCheckerboardColor(const FloatVector &col) { m_DarkChecker = col; } bool IsDarkTheme() { return m_DarkTheme; } void SetDarkTheme(bool dark) { m_DarkTheme = dark; } - ReplayStatus CreateProxyReplayDriver(RDCDriver proxyDriver, IReplayDriver **driver); - ReplayStatus CreateReplayDriver(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver); - ReplayStatus CreateRemoteDriver(RDCFile *rdc, const ReplayOptions &opts, IRemoteDriver **driver); + RDResult CreateProxyReplayDriver(RDCDriver proxyDriver, IReplayDriver **driver); + RDResult CreateReplayDriver(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver); + RDResult CreateRemoteDriver(RDCFile *rdc, const ReplayOptions &opts, IRemoteDriver **driver); bool HasReplaySupport(RDCDriver driverType); diff --git a/renderdoc/core/image_viewer.cpp b/renderdoc/core/image_viewer.cpp index 3068ec829..b1b1e2573 100644 --- a/renderdoc/core/image_viewer.cpp +++ b/renderdoc/core/image_viewer.cpp @@ -23,6 +23,7 @@ ******************************************************************************/ #include "common/dds_readwrite.h" +#include "common/formatting.h" #include "core/core.h" #include "maths/formatpacking.h" #include "replay/dummy_driver.h" @@ -82,8 +83,11 @@ public: } bool IsRemoteProxy() { return true; } - ReplayStatus FatalErrorCheck() + RDResult FatalErrorCheck() { + if(m_Error != ResultCode::Succeeded) + return m_Error; + // check for errors on the underlying proxy driver return m_Proxy->FatalErrorCheck(); } @@ -238,9 +242,9 @@ public: } // other operations are dropped/ignored, to avoid confusion - ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) + RDResult ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } SDFile *GetStructuredFile() { return m_File; } void RenderMesh(uint32_t eventId, const rdcarray &secondaryDraws, const MeshDisplay &cfg) @@ -374,31 +378,34 @@ private: SDFile *m_File; TextureDescription m_TexDetails; + RDResult m_Error; + // if we remapped the texture for display, this contains the real data to return from // GetTextureData() rdcarray m_RealTexData; }; -ReplayStatus IMG_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) +RDResult IMG_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) { if(!rdc) - return ReplayStatus::InternalError; + return ResultCode::InvalidParameter; rdcstr filename; FILE *f = rdc->StealImageFileHandle(filename); + if(!f) + { + RETURN_ERROR_RESULT(ResultCode::InvalidParameter, + "Trying to load invalid handle as image-capture"); + } + byte headerBuffer[4]; const size_t headerSize = FileIO::fread(headerBuffer, 1, 4, f); FileIO::fseek64(f, 0, SEEK_SET); - if(!f) - return ReplayStatus::FileIOFailed; - // make sure the file is a type we recognise before going further if(is_exr_file(f)) { - const char *err = NULL; - FileIO::fseek64(f, 0, SEEK_END); uint64_t size = FileIO::ftell64(f); FileIO::fseek64(f, 0, SEEK_SET); @@ -413,28 +420,57 @@ ReplayStatus IMG_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) if(ret != 0) { - RDCERR("EXR file detected, but couldn't load with ParseEXRVersionFromMemory: %d", ret); FileIO::fclose(f); - return ReplayStatus::ImageUnsupported; + RETURN_ERROR_RESULT(ResultCode::ImageUnsupported, + "EXR file detected, but couldn't load with ParseEXRVersionFromMemory: %d", + ret); } - if(exrVersion.multipart || exrVersion.non_image || exrVersion.tiled) + if(exrVersion.multipart) { - RDCERR("Unsupported EXR file detected - multipart or similar."); FileIO::fclose(f); - return ReplayStatus::ImageUnsupported; + RETURN_ERROR_RESULT(ResultCode::ImageUnsupported, + "Unsupported EXR file detected - multipart EXR."); + } + + if(exrVersion.non_image) + { + FileIO::fclose(f); + RETURN_ERROR_RESULT(ResultCode::ImageUnsupported, + "Unsupported EXR file detected - deep image EXR."); + } + + if(exrVersion.tiled) + { + FileIO::fclose(f); + RETURN_ERROR_RESULT(ResultCode::ImageUnsupported, + "Unsupported EXR file detected - tiled EXR."); } EXRHeader exrHeader; InitEXRHeader(&exrHeader); - ret = ParseEXRHeaderFromMemory(&exrHeader, &exrVersion, buffer.data(), buffer.size(), &err); + rdcstr errString; + + { + const char *err = NULL; + + ret = ParseEXRHeaderFromMemory(&exrHeader, &exrVersion, buffer.data(), buffer.size(), &err); + + if(err) + { + errString = err; + free((void *)err); + } + } if(ret != 0) { - RDCERR("EXR file detected, but couldn't load with ParseEXRHeaderFromMemory %d: '%s'", ret, err); FileIO::fclose(f); - return ReplayStatus::ImageUnsupported; + RETURN_ERROR_RESULT( + ResultCode::ImageUnsupported, + "EXR file detected, but couldn't load with ParseEXRHeaderFromMemory %d: '%s'", ret, + errString.c_str()); } } else if(stbi_is_hdr_from_file(f)) @@ -447,8 +483,8 @@ ReplayStatus IMG_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) if(!data) { FileIO::fclose(f); - RDCERR("HDR file recognised, but couldn't load with stbi_loadf_from_file"); - return ReplayStatus::ImageUnsupported; + RETURN_ERROR_RESULT(ResultCode::ImageUnsupported, + "HDR file recognised, but couldn't load with stbi_loadf_from_file"); } free(data); @@ -457,14 +493,12 @@ ReplayStatus IMG_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) { FileIO::fseek64(f, 0, SEEK_SET); StreamReader reader(f); - read_dds_data read_data = load_dds_from_file(&reader); + read_dds_data read_data; + RDResult res = load_dds_from_file(&reader, read_data); f = NULL; - if(read_data.subresources.empty()) - { - RDCERR("DDS file recognised, but couldn't load"); - return ReplayStatus::ImageUnsupported; - } + if(res != ResultCode::Succeeded) + return res; } else { @@ -476,10 +510,17 @@ ReplayStatus IMG_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) // just in case (we shouldn't have come in here if this weren't true), make sure // the format is supported - if(ret == 0 || width <= 0 || width >= 65536 || height <= 0 || height >= 65536) + if(ret == 0) { FileIO::fclose(f); - return ReplayStatus::ImageUnsupported; + RETURN_ERROR_RESULT(ResultCode::ImageUnsupported, "Image can't be identified by stb"); + } + + if(width <= 0 || width >= 65536 || height <= 0 || height >= 65536) + { + FileIO::fclose(f); + RETURN_ERROR_RESULT(ResultCode::ImageUnsupported, "Image dimensions %ux%u are not supported", + width, height); } byte *data = stbi_load_from_file(f, &ignore, &ignore, &ignore, 4); @@ -487,8 +528,7 @@ ReplayStatus IMG_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) if(!data) { FileIO::fclose(f); - RDCERR("File recognised, but couldn't load with stbi_load_from_file"); - return ReplayStatus::ImageUnsupported; + RETURN_ERROR_RESULT(ResultCode::ImageUnsupported, "File recognised, but couldn't load image"); } free(data); @@ -498,26 +538,28 @@ ReplayStatus IMG_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) FileIO::fclose(f); IReplayDriver *proxy = NULL; - ReplayStatus status = RenderDoc::Inst().CreateProxyReplayDriver(RDCDriver::Unknown, &proxy); + RDResult result = RenderDoc::Inst().CreateProxyReplayDriver(RDCDriver::Unknown, &proxy); - if(status != ReplayStatus::Succeeded || !proxy) + if(result != ResultCode::Succeeded || !proxy) { RDCERR("Couldn't create replay driver to proxy-render images"); if(proxy) proxy->Shutdown(); - return status; + return result; } *driver = new ImageViewer(proxy, filename.c_str()); - if((*driver)->GetResources()[0].resourceId == ResourceId()) + result = (*driver)->FatalErrorCheck(); + + if(result != ResultCode::Succeeded) { (*driver)->Shutdown(); - return ReplayStatus::ImageUnsupported; + return result; } - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } void ImageViewer::RefreshFile() @@ -534,7 +576,9 @@ void ImageViewer::RefreshFile() if(!f) { - RDCERR("Couldn't open %s! Exclusive lock elsewhere?", m_Filename.c_str()); + SET_ERROR_RESULT(m_Error, ResultCode::FileIOFailed, + "Couldn't open %s! Is the file opened exclusively/locked elsewhere?", + m_Filename.c_str()); return; } @@ -594,14 +638,33 @@ void ImageViewer::RefreshFile() if(ret != 0) { - RDCERR("EXR file detected, but couldn't load with ParseEXRVersionFromMemory: %d", ret); + SET_ERROR_RESULT(m_Error, ResultCode::ImageUnsupported, + "EXR file detected, but couldn't load with ParseEXRVersionFromMemory: %d", + ret); FileIO::fclose(f); return; } - if(exrVersion.multipart || exrVersion.non_image || exrVersion.tiled) + if(exrVersion.multipart) { - RDCERR("Unsupported EXR file detected - multipart or similar."); + SET_ERROR_RESULT(m_Error, ResultCode::ImageUnsupported, + "Unsupported EXR file detected - multipart EXR."); + FileIO::fclose(f); + return; + } + + if(exrVersion.non_image) + { + SET_ERROR_RESULT(m_Error, ResultCode::ImageUnsupported, + "Unsupported EXR file detected - deep image EXR."); + FileIO::fclose(f); + return; + } + + if(exrVersion.tiled) + { + SET_ERROR_RESULT(m_Error, ResultCode::ImageUnsupported, + "Unsupported EXR file detected - tiled EXR."); FileIO::fclose(f); return; } @@ -609,12 +672,26 @@ void ImageViewer::RefreshFile() EXRHeader exrHeader; InitEXRHeader(&exrHeader); - const char *err = NULL; + rdcstr errString; + + { + const char *err = NULL; + + ret = ParseEXRHeaderFromMemory(&exrHeader, &exrVersion, buffer.data(), buffer.size(), &err); + + if(err) + { + errString = err; + free((void *)err); + } + } - ret = ParseEXRHeaderFromMemory(&exrHeader, &exrVersion, buffer.data(), buffer.size(), &err); if(ret != 0) { - RDCERR("EXR file detected, but couldn't load with ParseEXRHeaderFromMemory %d: '%s'", ret, err); + SET_ERROR_RESULT( + m_Error, ResultCode::ImageUnsupported, + "EXR file detected, but couldn't load with ParseEXRHeaderFromMemory %d: '%s'", ret, + errString.c_str()); FileIO::fclose(f); return; } @@ -625,10 +702,23 @@ void ImageViewer::RefreshFile() EXRImage exrImage; InitEXRImage(&exrImage); - ret = LoadEXRImageFromMemory(&exrImage, &exrHeader, buffer.data(), buffer.size(), &err); + { + const char *err = NULL; + + ret = LoadEXRImageFromMemory(&exrImage, &exrHeader, buffer.data(), buffer.size(), &err); + + if(err) + { + errString = err; + free((void *)err); + } + } + if(ret != 0) { - RDCERR("EXR file detected, but couldn't load with LoadEXRImageFromMemory %d: '%s'", ret, err); + SET_ERROR_RESULT(m_Error, ResultCode::ImageUnsupported, + "EXR file detected, but couldn't load with LoadEXRImageFromMemory %d: '%s'", + ret, errString.c_str()); FileIO::fclose(f); return; } @@ -641,7 +731,8 @@ void ImageViewer::RefreshFile() if(!data) { - RDCERR("Allocation for %zu bytes failed for EXR data", datasize); + SET_ERROR_RESULT(m_Error, ResultCode::ReplayOutOfMemory, + "Allocation for %zu bytes failed for EXR data", datasize); return; } @@ -679,7 +770,8 @@ void ImageViewer::RefreshFile() if(ret != 0) { free(data); - RDCERR("EXR file detected, but couldn't load with LoadEXRFromMemory %d: '%s'", ret, err); + SET_ERROR_RESULT(m_Error, ResultCode::ImageUnsupported, + "EXR file detected, but failed during parsing"); FileIO::fclose(f); return; } @@ -708,9 +800,19 @@ void ImageViewer::RefreshFile() // just in case (we shouldn't have come in here if this weren't true), make sure // the format is supported - if(ret == 0 || texDetails.width == 0 || texDetails.width == ~0U || texDetails.height == 0 || - texDetails.height == ~0U) + if(ret == 0) { + SET_ERROR_RESULT(m_Error, ResultCode::ImageUnsupported, "Image could not be identified"); + FileIO::fclose(f); + return; + } + + if(texDetails.width == 0 || texDetails.width >= 65536 || texDetails.height == 0 || + texDetails.height >= 65536) + { + SET_ERROR_RESULT(m_Error, ResultCode::ImageUnsupported, + "Image dimensions of %ux%u are not supported", texDetails.width, + texDetails.height); FileIO::fclose(f); return; } @@ -725,6 +827,7 @@ void ImageViewer::RefreshFile() // file was corrupted and we failed to load it if(!dds && data == NULL) { + SET_ERROR_RESULT(m_Error, ResultCode::ImageUnsupported, "Image failed to load"); FileIO::fclose(f); return; } @@ -739,11 +842,14 @@ void ImageViewer::RefreshFile() { FileIO::fseek64(f, 0, SEEK_SET); StreamReader reader(f); - read_data = load_dds_from_file(&reader); + RDResult res = load_dds_from_file(&reader, read_data); f = NULL; - if(read_data.subresources.empty()) + if(res != ResultCode::Succeeded) + { + m_Error = res; return; + } texDetails.cubemap = read_data.cubemap; texDetails.arraysize = read_data.slices; @@ -803,7 +909,10 @@ void ImageViewer::RefreshFile() CreateProxyTexture(texDetails, read_data); if(m_TextureID == ResourceId()) - RDCERR("Couldn't create proxy texture for image file"); + { + SET_ERROR_RESULT(m_Error, ResultCode::APIInitFailed, + "Couldn't create proxy texture for image file"); + } m_TexDetails.resourceId = m_TextureID; m_TexDetails.byteSize = fileSize; diff --git a/renderdoc/core/remote_server.cpp b/renderdoc/core/remote_server.cpp index 76ceb651f..7d1cca0fb 100644 --- a/renderdoc/core/remote_server.cpp +++ b/renderdoc/core/remote_server.cpp @@ -492,27 +492,13 @@ static void ActiveRemoteClientThread(ClientThread *threadData, reader.EndChunk(); RDCASSERT(remoteDriver == NULL && proxy == NULL && rdc == NULL); - ReplayStatus status = ReplayStatus::InternalError; rdc = new RDCFile(); rdc->Open(path); - if(rdc->ErrorCode() != ContainerError::NoError) - { - RDCERR("Failed to open '%s': %d", path.c_str(), rdc->ErrorCode()); + RDResult result = rdc->Error(); - switch(rdc->ErrorCode()) - { - case ContainerError::FileNotFound: status = ReplayStatus::FileNotFound; break; - case ContainerError::FileIO: status = ReplayStatus::FileIOFailed; break; - case ContainerError::Corrupt: status = ReplayStatus::FileCorrupted; break; - case ContainerError::UnsupportedVersion: - status = ReplayStatus::FileIncompatibleVersion; - break; - default: break; - } - } - else + if(result == ResultCode::Succeeded) { if(RenderDoc::Inst().HasRemoteDriver(rdc->GetDriver())) { @@ -536,24 +522,24 @@ static void ActiveRemoteClientThread(ClientThread *threadData, // if we have a replay driver, try to create it so we can display a local preview e.g. if(RenderDoc::Inst().HasReplayDriver(rdc->GetDriver())) { - status = RenderDoc::Inst().CreateReplayDriver(rdc, opts, &replayDriver); + result = RenderDoc::Inst().CreateReplayDriver(rdc, opts, &replayDriver); if(replayDriver) remoteDriver = replayDriver; } else { - status = RenderDoc::Inst().CreateRemoteDriver(rdc, opts, &remoteDriver); + result = RenderDoc::Inst().CreateRemoteDriver(rdc, opts, &remoteDriver); } - if(status != ReplayStatus::Succeeded || remoteDriver == NULL) + if(result != ResultCode::Succeeded || remoteDriver == NULL) { RDCERR("Failed to create remote driver for driver '%s'", rdc->GetDriverName().c_str()); } else { - status = remoteDriver->ReadLogInitialisation(rdc, false); + result = remoteDriver->ReadLogInitialisation(rdc, false); - if(status != ReplayStatus::Succeeded) + if(result != ResultCode::Succeeded) { RDCERR("Failed to initialise remote driver."); @@ -568,23 +554,23 @@ static void ActiveRemoteClientThread(ClientThread *threadData, Threading::JoinThread(ticker); Threading::CloseThread(ticker); - if(status == ReplayStatus::Succeeded && remoteDriver) + if(result == ResultCode::Succeeded && remoteDriver) { proxy = new ReplayProxy(reader, writer, remoteDriver, replayDriver, previewWindow); } } else { - RDCERR("File needs driver for '%s' which isn't supported!", rdc->GetDriverName().c_str()); - - status = ReplayStatus::APIUnsupported; + SET_ERROR_RESULT(result, ResultCode::APIUnsupported, + "File needs driver for '%s' which isn't supported!", + rdc->GetDriverName().c_str()); } } { WRITE_DATA_SCOPE(); SCOPED_SERIALISE_CHUNK(eRemoteServer_LogOpened); - SERIALISE_ELEMENT(status); + SERIALISE_ELEMENT(result); } } else if(type == eRemoteServer_HasCallstacks) @@ -603,7 +589,7 @@ static void ActiveRemoteClientThread(ClientThread *threadData, { reader.EndChunk(); - bool success = false; + RDResult res; int sectionIndex = rdc ? rdc->SectionIndex(SectionType::ResolveDatabase) : -1; @@ -614,11 +600,13 @@ static void ActiveRemoteClientThread(ClientThread *threadData, bytebuf buf; buf.resize((size_t)sectionReader->GetSize()); - success = sectionReader->Read(buf.data(), sectionReader->GetSize()); + bool success = sectionReader->Read(buf.data(), sectionReader->GetSize()); + + res = sectionReader->GetError(); delete sectionReader; - if(success) + if(success && res == ResultCode::Succeeded) { float progress = 0.0f; @@ -642,14 +630,14 @@ static void ActiveRemoteClientThread(ClientThread *threadData, } else { - RDCERR("Failed to read resolve database."); + res.message = "Failed to read resolve database. " + res.message; } } { WRITE_DATA_SCOPE(); SCOPED_SERIALISE_CHUNK(eRemoteServer_InitResolver); - SERIALISE_ELEMENT(success); + SERIALISE_ELEMENT(res); } } else if(type == eRemoteServer_GetResolve) @@ -812,7 +800,7 @@ static void ActiveRemoteClientThread(ClientThread *threadData, reader.EndChunk(); - bool success = false; + RDResult result; if(rdc) { @@ -822,15 +810,22 @@ static void ActiveRemoteClientThread(ClientThread *threadData, { sectionWriter->Write(contents.data(), contents.size()); delete sectionWriter; - - success = true; } + else + { + SET_ERROR_RESULT(result, ResultCode::FileIOFailed, "Failed to write section"); + } + } + else + { + SET_ERROR_RESULT(result, ResultCode::InternalError, + "Attempt to write section with no capture open"); } { WRITE_DATA_SCOPE(); SCOPED_SERIALISE_CHUNK(eRemoteServer_WriteSection); - SERIALISE_ELEMENT(success); + SERIALISE_ELEMENT(result); } } else if(type == eRemoteServer_CloseLog) @@ -864,15 +859,13 @@ static void ActiveRemoteClientThread(ClientThread *threadData, reader.EndChunk(); - ExecuteResult ret = {}; + RDResult res; + uint32_t ident = 0; if(threadData->allowExecution) { - rdcpair status = + rdctie(res, ident) = Process::LaunchAndInjectIntoProcess(app, workingDir, cmdLine, env, "", opts, false); - - ret.status = status.first; - ret.ident = status.second; } else { @@ -882,7 +875,8 @@ static void ActiveRemoteClientThread(ClientThread *threadData, { WRITE_DATA_SCOPE(); SCOPED_SERIALISE_CHUNK(eRemoteServer_ExecuteAndInject); - SERIALISE_ELEMENT(ret); + SERIALISE_ELEMENT(res); + SERIALISE_ELEMENT(ident); } } else if((int)type >= eReplayProxy_First && proxy) @@ -1134,7 +1128,7 @@ void RenderDoc::BecomeRemoteServer(const rdcstr &listenhost, uint16_t port, SAFE_DELETE(sock); } -extern "C" RENDERDOC_API ReplayStatus RENDERDOC_CC +extern "C" RENDERDOC_API ResultDetails RENDERDOC_CC RENDERDOC_CreateRemoteServerConnection(const rdcstr &URL, IRemoteServer **rend) { rdcstr host = "localhost"; @@ -1152,7 +1146,7 @@ RENDERDOC_CreateRemoteServerConnection(const rdcstr &URL, IRemoteServer **rend) deviceID = protocol->GetDeviceID(deviceID); host = protocol->RemapHostname(deviceID); if(host.empty()) - return ReplayStatus::NetworkIOFailed; + return RDResult(ResultCode::NetworkIOFailed); port = protocol->RemapPort(deviceID, port); } @@ -1167,12 +1161,12 @@ RENDERDOC_CreateRemoteServerConnection(const rdcstr &URL, IRemoteServer **rend) } if(port == 0) - return ReplayStatus::NetworkIOFailed; + return RDResult(ResultCode::NetworkIOFailed); Network::Socket *sock = Network::CreateClientSocket(host, port, 750); if(sock == NULL) - return ReplayStatus::NetworkIOFailed; + return RDResult(ResultCode::NetworkIOFailed); uint32_t version = RemoteServerProtocolVersion; @@ -1191,7 +1185,7 @@ RENDERDOC_CreateRemoteServerConnection(const rdcstr &URL, IRemoteServer **rend) } if(!sock->Connected()) - return ReplayStatus::NetworkIOFailed; + return RDResult(ResultCode::NetworkIOFailed); { ReadSerialiser ser(new StreamReader(sock, Ownership::Nothing), Ownership::Stream); @@ -1203,35 +1197,35 @@ RENDERDOC_CreateRemoteServerConnection(const rdcstr &URL, IRemoteServer **rend) if(type == eRemoteServer_Busy) { SAFE_DELETE(sock); - return ReplayStatus::NetworkRemoteBusy; + return RDResult(ResultCode::NetworkRemoteBusy); } if(type == eRemoteServer_VersionMismatch) { SAFE_DELETE(sock); - return ReplayStatus::NetworkVersionMismatch; + return RDResult(ResultCode::NetworkVersionMismatch); } if(ser.IsErrored() || type != eRemoteServer_Handshake) { RDCWARN("Didn't get proper handshake"); SAFE_DELETE(sock); - return ReplayStatus::NetworkIOFailed; + return RDResult(ResultCode::NetworkIOFailed); } } if(rend == NULL) - return ReplayStatus::Succeeded; + return RDResult(ResultCode::Succeeded); if(protocol) *rend = protocol->CreateRemoteServer(sock, deviceID); else *rend = new RemoteServer(sock, deviceID); - return ReplayStatus::Succeeded; + return RDResult(ResultCode::Succeeded); } -extern "C" RENDERDOC_API ReplayStatus RENDERDOC_CC +extern "C" RENDERDOC_API ResultDetails RENDERDOC_CC RENDERDOC_CheckRemoteServerConnection(const rdcstr &URL) { return RENDERDOC_CreateRemoteServerConnection(URL, NULL); @@ -1316,10 +1310,15 @@ bool RemoteServer::Connected() return m_Socket != NULL && m_Socket->Connected(); } -bool RemoteServer::Ping() +ResultDetails RemoteServer::Ping() { + RDResult ret; + if(!Connected()) - return false; + { + ret = ResultCode::RemoteServerConnectionLost; + return ret; + } { WRITE_DATA_SCOPE(); @@ -1334,7 +1333,12 @@ bool RemoteServer::Ping() ser.EndChunk(); } - return type == eRemoteServer_Ping; + if(type == eRemoteServer_Ping) + ret = ResultCode::Succeeded; + else + ret = ResultCode::RemoteServerConnectionLost; + + return ret; } rdcarray RemoteServer::LocalProxies() @@ -1478,7 +1482,11 @@ ExecuteResult RemoteServer::ExecuteAndInject(const rdcstr &app, const rdcstr &wo if(type == eRemoteServer_ExecuteAndInject) { - SERIALISE_ELEMENT(ret); + SERIALISE_ELEMENT_LOCAL(result, RDResult()); + SERIALISE_ELEMENT_LOCAL(ident, uint32_t()); + + ret.result = result; + ret.ident = ident; } else { @@ -1574,12 +1582,12 @@ void RemoteServer::TakeOwnershipCapture(const rdcstr &filename) } } -rdcpair RemoteServer::OpenCapture( +rdcpair RemoteServer::OpenCapture( uint32_t proxyid, const rdcstr &filename, const ReplayOptions &opts, RENDERDOC_ProgressCallback progress) { - rdcpair ret; - ret.first = ReplayStatus::InternalError; + rdcpair ret; + ret.first = ResultCode::InternalError; ret.second = NULL; if(proxyid != ~0U && proxyid >= m_Proxies.size()) @@ -1627,50 +1635,50 @@ rdcpair RemoteServer::OpenCapture( if(reader->IsErrored() || type != eRemoteServer_LogOpened) { RDCERR("Error opening capture"); - ret.first = ReplayStatus::NetworkIOFailed; + ret.first = ResultCode::NetworkIOFailed; return ret; } - ReplayStatus status = ReplayStatus::Succeeded; + RDResult result = ResultCode::Succeeded; { READ_DATA_SCOPE(); - SERIALISE_ELEMENT(status); + SERIALISE_ELEMENT(result); ser.EndChunk(); } if(progress) progress(1.0f); - if(status != ReplayStatus::Succeeded) + if(result != ResultCode::Succeeded) { - RDCERR("Capture open failed: %s", ToStr(status).c_str()); - ret.first = status; + RDCERR("Capture open failed: %s", ResultDetails(result).Message().c_str()); + ret.first = result; return ret; } RDCLOG("Capture ready on replay host"); IReplayDriver *proxyDriver = NULL; - status = RenderDoc::Inst().CreateProxyReplayDriver(proxydrivertype, &proxyDriver); + result = RenderDoc::Inst().CreateProxyReplayDriver(proxydrivertype, &proxyDriver); - if(status != ReplayStatus::Succeeded || !proxyDriver) + if(result != ResultCode::Succeeded || !proxyDriver) { - RDCERR("Creating proxy driver failed: %s", ToStr(status).c_str()); + RDCERR("Creating proxy driver failed: %s", ResultDetails(result).Message().c_str()); if(proxyDriver) proxyDriver->Shutdown(); - ret.first = status; + ret.first = result; return ret; } ReplayController *rend = new ReplayController(); ReplayProxy *proxy = new ReplayProxy(*reader, *writer, proxyDriver); - status = rend->SetDevice(proxy); + result = rend->SetDevice(proxy); - if(status != ReplayStatus::Succeeded) + if(result != ResultCode::Succeeded) { rend->Shutdown(); - ret.first = status; + ret.first = result; return ret; } @@ -1679,7 +1687,7 @@ rdcpair RemoteServer::OpenCapture( RDCLOG("Remote capture open complete & proxy ready"); - ret.first = ReplayStatus::Succeeded; + ret.first = ResultCode::Succeeded; ret.second = rend; return ret; } @@ -1915,10 +1923,15 @@ bytebuf RemoteServer::GetSectionContents(int index) return contents; } -bool RemoteServer::WriteSection(const SectionProperties &props, const bytebuf &contents) +ResultDetails RemoteServer::WriteSection(const SectionProperties &props, const bytebuf &contents) { + RDResult ret; + if(!Connected()) - return false; + { + ret.code = ResultCode::RemoteServerConnectionLost; + return ret; + } { WRITE_DATA_SCOPE(); @@ -1927,7 +1940,7 @@ bool RemoteServer::WriteSection(const SectionProperties &props, const bytebuf &c SERIALISE_ELEMENT(contents); } - bool success = false; + RDResult success; { READ_DATA_SCOPE(); @@ -1979,7 +1992,7 @@ bool RemoteServer::HasCallstacks() return hasCallstacks; } -bool RemoteServer::InitResolver(bool interactive, RENDERDOC_ProgressCallback progress) +ResultDetails RemoteServer::InitResolver(bool interactive, RENDERDOC_ProgressCallback progress) { { WRITE_DATA_SCOPE(); @@ -2007,22 +2020,24 @@ bool RemoteServer::InitResolver(bool interactive, RENDERDOC_ProgressCallback pro RDCLOG("% 3.0f%%...", progressValue * 100.0f); } + RDResult res; + if(reader->IsErrored() || type != eRemoteServer_InitResolver) { - return false; + res = ResultCode::NetworkIOFailed; + return res; } - bool success = false; { READ_DATA_SCOPE(); - SERIALISE_ELEMENT(success); + SERIALISE_ELEMENT(res); ser.EndChunk(); } if(progress) progress(1.0f); - return success; + return res; } rdcarray RemoteServer::GetResolve(const rdcarray &callstack) diff --git a/renderdoc/core/remote_server.h b/renderdoc/core/remote_server.h index ddede78c5..b770a2c30 100644 --- a/renderdoc/core/remote_server.h +++ b/renderdoc/core/remote_server.h @@ -49,7 +49,7 @@ public: virtual void ShutdownServerAndConnection(); virtual bool Connected(); - virtual bool Ping(); + virtual ResultDetails Ping(); virtual rdcarray LocalProxies(); @@ -71,10 +71,10 @@ public: virtual void TakeOwnershipCapture(const rdcstr &filename); - virtual rdcpair OpenCapture(uint32_t proxyid, - const rdcstr &filename, - const ReplayOptions &opts, - RENDERDOC_ProgressCallback progress); + virtual rdcpair OpenCapture(uint32_t proxyid, + const rdcstr &filename, + const ReplayOptions &opts, + RENDERDOC_ProgressCallback progress); virtual void CloseCapture(IReplayController *rend); @@ -92,11 +92,11 @@ public: virtual bytebuf GetSectionContents(int32_t index); - virtual bool WriteSection(const SectionProperties &props, const bytebuf &contents); + virtual ResultDetails WriteSection(const SectionProperties &props, const bytebuf &contents); virtual bool HasCallstacks(); - virtual bool InitResolver(bool interactive, RENDERDOC_ProgressCallback progress); + virtual ResultDetails InitResolver(bool interactive, RENDERDOC_ProgressCallback progress); virtual rdcarray GetResolve(const rdcarray &callstack); diff --git a/renderdoc/core/replay_proxy.cpp b/renderdoc/core/replay_proxy.cpp index 67acddaed..85693f719 100644 --- a/renderdoc/core/replay_proxy.cpp +++ b/renderdoc/core/replay_proxy.cpp @@ -129,37 +129,37 @@ rdcstr DoStringise(const ReplayProxyPacket &el) // begin serialising a return value. We begin a chunk here in either the writing or reading case // since this chunk is used purely to send/receive the return value and is fully handled within the // function. -#define SERIALISE_RETURN(retval) \ - { \ - ReplayStatus fatalStatus = ReplayStatus::Succeeded; \ - if(m_RemoteServer) \ - fatalStatus = m_Remote->FatalErrorCheck(); \ - ReturnSerialiser &ser = retser; \ - PACKET_HEADER(packet); \ - SERIALISE_ELEMENT(retval); \ - GET_SERIALISER.Serialise("fatalStatus"_lit, fatalStatus); \ - GET_SERIALISER.Serialise("packet"_lit, packet); \ - ser.EndChunk(); \ - if(fatalStatus != ReplayStatus::Succeeded && m_FatalError == ReplayStatus::Succeeded) \ - m_FatalError = fatalStatus; \ - CheckError(packet, expectedPacket); \ +#define SERIALISE_RETURN(retval) \ + { \ + RDResult fatalStatus = ResultCode::Succeeded; \ + if(m_RemoteServer) \ + fatalStatus = m_Remote->FatalErrorCheck(); \ + ReturnSerialiser &ser = retser; \ + PACKET_HEADER(packet); \ + SERIALISE_ELEMENT(retval); \ + GET_SERIALISER.Serialise("fatalStatus"_lit, fatalStatus); \ + GET_SERIALISER.Serialise("packet"_lit, packet); \ + ser.EndChunk(); \ + if(fatalStatus != ResultCode::Succeeded && m_FatalError == ResultCode::Succeeded) \ + m_FatalError = fatalStatus; \ + CheckError(packet, expectedPacket); \ } // similar to the above, but for void functions that don't return anything. We still want to check // that both sides of the communication are on the same page. -#define SERIALISE_RETURN_VOID() \ - { \ - ReplayStatus fatalStatus = ReplayStatus::Succeeded; \ - if(m_RemoteServer) \ - fatalStatus = m_Remote->FatalErrorCheck(); \ - ReturnSerialiser &ser = retser; \ - PACKET_HEADER(packet); \ - SERIALISE_ELEMENT(packet); \ - GET_SERIALISER.Serialise("fatalStatus"_lit, fatalStatus); \ - ser.EndChunk(); \ - if(fatalStatus != ReplayStatus::Succeeded && m_FatalError == ReplayStatus::Succeeded) \ - m_FatalError = fatalStatus; \ - CheckError(packet, expectedPacket); \ +#define SERIALISE_RETURN_VOID() \ + { \ + RDResult fatalStatus = ResultCode::Succeeded; \ + if(m_RemoteServer) \ + fatalStatus = m_Remote->FatalErrorCheck(); \ + ReturnSerialiser &ser = retser; \ + PACKET_HEADER(packet); \ + SERIALISE_ELEMENT(packet); \ + GET_SERIALISER.Serialise("fatalStatus"_lit, fatalStatus); \ + ser.EndChunk(); \ + if(fatalStatus != ResultCode::Succeeded && m_FatalError == ResultCode::Succeeded) \ + m_FatalError = fatalStatus; \ + CheckError(packet, expectedPacket); \ } // defines the area where we're executing on the remote host. To avoid timeouts, the remote side @@ -2790,7 +2790,7 @@ void ReplayProxy::RemoteExecutionThreadEntry() } } -ReplayStatus ReplayProxy::FatalErrorCheck() +RDResult ReplayProxy::FatalErrorCheck() { // this isn't proxied since it's called at relatively high frequency. Whenever we proxy a // function, we also return the the remote side's status @@ -2798,13 +2798,13 @@ ReplayStatus ReplayProxy::FatalErrorCheck() { // if we're error'd due to a network issue (i.e. the other side crashed and disconnected) we // won't have a status, set a generic one - if(m_FatalError == ReplayStatus::Succeeded) - m_FatalError = ReplayStatus::RemoteServerConnectionLost; + if(m_FatalError == ResultCode::Succeeded) + m_FatalError = ResultCode::RemoteServerConnectionLost; return m_FatalError; } - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } IReplayDriver *ReplayProxy::MakeDummyDriver() @@ -2825,10 +2825,10 @@ IReplayDriver *ReplayProxy::MakeDummyDriver() bool ReplayProxy::CheckError(ReplayProxyPacket receivedPacket, ReplayProxyPacket expectedPacket) { - if(m_FatalError != ReplayStatus::Succeeded) + if(m_FatalError != ResultCode::Succeeded) { RDCERR("Fatal error detected while processing %s: %s", ToStr(expectedPacket).c_str(), - ToStr(m_FatalError).c_str()); + ResultDetails(m_FatalError).Message().c_str()); m_IsErrored = true; return true; } diff --git a/renderdoc/core/replay_proxy.h b/renderdoc/core/replay_proxy.h index 95e12df15..1467cf618 100644 --- a/renderdoc/core/replay_proxy.h +++ b/renderdoc/core/replay_proxy.h @@ -142,12 +142,12 @@ public: void RemoteExecutionThreadEntry(); bool IsRemoteProxy() { return !m_RemoteServer; } - ReplayStatus FatalErrorCheck(); + RDResult FatalErrorCheck(); IReplayDriver *MakeDummyDriver(); void Shutdown() { delete this; } - ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) + RDResult ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } AMDRGPControl *GetRGPControl() { return NULL; } rdcarray GetSupportedWindowSystems() @@ -710,7 +710,7 @@ private: Threading::ThreadHandle m_RemoteExecutionThread = 0; bool m_IsErrored = false; - ReplayStatus m_FatalError = ReplayStatus::Succeeded; + RDResult m_FatalError = ResultCode::Succeeded; FrameRecord m_FrameRecord; APIProperties m_APIProps; diff --git a/renderdoc/core/target_control.cpp b/renderdoc/core/target_control.cpp index 04b96adf0..327abbf44 100644 --- a/renderdoc/core/target_control.cpp +++ b/renderdoc/core/target_control.cpp @@ -224,7 +224,7 @@ void RenderDoc::TargetControlClientThread(uint32_t version, Network::Socket *cli bytebuf buf; ICaptureFile *file = RENDERDOC_OpenCaptureFile(); - if(file->OpenFile(captures.back().path, "rdc", NULL) == ReplayStatus::Succeeded) + if(file->OpenFile(captures.back().path, "rdc", NULL).OK()) { buf = file->GetThumbnail(FileType::JPG, 0).data; } diff --git a/renderdoc/driver/d3d11/d3d11_context.cpp b/renderdoc/driver/d3d11/d3d11_context.cpp index 81a6249b9..a06fe91ff 100644 --- a/renderdoc/driver/d3d11/d3d11_context.cpp +++ b/renderdoc/driver/d3d11/d3d11_context.cpp @@ -1198,15 +1198,15 @@ void WrappedID3D11DeviceContext::ReplayFakeContext(ResourceId id) m_FakeContext = id; } -ReplayStatus WrappedID3D11DeviceContext::ReplayLog(CaptureState readType, uint32_t startEventID, - uint32_t endEventID, bool partial) +RDResult WrappedID3D11DeviceContext::ReplayLog(CaptureState readType, uint32_t startEventID, + uint32_t endEventID, bool partial) { m_State = readType; if(!m_FrameReader) { - RDCERR("Can't replay context capture without frame reader"); - return ReplayStatus::InternalError; + RETURN_ERROR_RESULT(ResultCode::InvalidParameter, + "Can't replay context capture without frame reader"); } m_FrameReader->SetOffset(0); @@ -1282,8 +1282,8 @@ ReplayStatus WrappedID3D11DeviceContext::ReplayLog(CaptureState readType, uint32 D3D11Chunk chunktype = ser.ReadChunk(); - if(ser.GetReader()->IsErrored()) - return ReplayStatus::APIDataCorrupted; + if(ser.IsErrored()) + return RDResult(ResultCode::APIDataCorrupted, ser.GetError().message); m_ChunkMetadata = ser.ChunkMetadata(); @@ -1291,16 +1291,53 @@ ReplayStatus WrappedID3D11DeviceContext::ReplayLog(CaptureState readType, uint32 ser.EndChunk(); - if(ser.GetReader()->IsErrored()) - return ReplayStatus::APIDataCorrupted; - - if(m_pDevice->HasFatalError()) - return m_pDevice->FatalErrorCheck(); + if(ser.IsErrored()) + return RDResult(ResultCode::APIDataCorrupted, ser.GetError().message); // if there wasn't a serialisation error, but the chunk didn't succeed, then it's an API replay // failure. if(!success) - return m_FailedReplayStatus; + { + rdcstr extra; + + if(m_pDevice->GetInfoQueue()) + { + extra += "\n"; + + for(UINT64 i = 0; + i < m_pDevice->GetInfoQueue()->GetNumStoredMessagesAllowedByRetrievalFilter(); i++) + { + SIZE_T len = 0; + m_pDevice->GetInfoQueue()->GetMessage(i, NULL, &len); + + char *msgbuf = new char[len]; + D3D11_MESSAGE *message = (D3D11_MESSAGE *)msgbuf; + + m_pDevice->GetInfoQueue()->GetMessage(i, message, &len); + + extra += "\n"; + extra += message->pDescription; + + delete[] msgbuf; + } + } + else + { + extra += + "\n\nMore debugging information may be available by enabling API validation on " + "replay"; + } + + if(m_pDevice->HasFatalError()) + { + RDResult result = m_pDevice->FatalErrorCheck(); + result.message = rdcstr(result.message) + extra; + return result; + } + + m_FailedReplayResult.message = rdcstr(m_FailedReplayResult.message) + extra; + return m_FailedReplayResult; + } RenderDoc::Inst().SetProgress( LoadProgress::FrameEventsRead, @@ -1352,7 +1389,7 @@ ReplayStatus WrappedID3D11DeviceContext::ReplayLog(CaptureState readType, uint32 m_DoStateVerify = false; - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } void WrappedID3D11DeviceContext::ClearMaps() diff --git a/renderdoc/driver/d3d11/d3d11_context.h b/renderdoc/driver/d3d11/d3d11_context.h index a0e865f06..bfdb335d5 100644 --- a/renderdoc/driver/d3d11/d3d11_context.h +++ b/renderdoc/driver/d3d11/d3d11_context.h @@ -214,7 +214,7 @@ private: uint32_t m_CurEventID, m_CurActionID; D3D11Chunk m_LastChunk; - ReplayStatus m_FailedReplayStatus = ReplayStatus::APIReplayFailed; + RDResult m_FailedReplayResult = ResultCode::APIReplayFailed; ActionDescription m_ParentAction; std::map m_CmdLists; @@ -310,8 +310,7 @@ public: bool ProcessChunk(ReadSerialiser &ser, D3D11Chunk chunk); void ReplayFakeContext(ResourceId id); - ReplayStatus ReplayLog(CaptureState readType, uint32_t startEventID, uint32_t endEventID, - bool partial); + RDResult ReplayLog(CaptureState readType, uint32_t startEventID, uint32_t endEventID, bool partial); void SetFrameReader(StreamReader *reader) { m_FrameReader = reader; } void MarkResourceReferenced(ResourceId id, FrameRefType refType); diff --git a/renderdoc/driver/d3d11/d3d11_device.cpp b/renderdoc/driver/d3d11/d3d11_device.cpp index e1609bd7a..bee578b5f 100644 --- a/renderdoc/driver/d3d11/d3d11_device.cpp +++ b/renderdoc/driver/d3d11/d3d11_device.cpp @@ -1289,12 +1289,12 @@ bool WrappedID3D11Device::Serialise_CaptureScope(SerialiserType &ser) return true; } -ReplayStatus WrappedID3D11Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) +RDResult WrappedID3D11Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture); if(sectionIdx < 0) - return ReplayStatus::FileCorrupted; + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, "File does not contain captured API data"); StreamReader *reader = rdc->ReadSection(sectionIdx); @@ -1312,8 +1312,9 @@ ReplayStatus WrappedID3D11Device::ReadLogInitialisation(RDCFile *rdc, bool store if(reader->IsErrored()) { + RDResult result = reader->GetError(); delete reader; - return ReplayStatus::FileIOFailed; + return result; } ReadSerialiser ser(reader, Ownership::Stream); @@ -1356,19 +1357,64 @@ ReplayStatus WrappedID3D11Device::ReadLogInitialisation(RDCFile *rdc, bool store chunkIdx++; if(reader->IsErrored()) - return ReplayStatus::APIDataCorrupted; + return RDResult(ResultCode::APIDataCorrupted, ser.GetError().message); + + UINT64 startMessage = 0; + + // store how many messages existed before this chunk. If it fails we'll only print messages + // related to/generated by it + if(m_pInfoQueue) + startMessage = m_pInfoQueue->GetNumStoredMessagesAllowedByRetrievalFilter(); bool success = ProcessChunk(ser, context); ser.EndChunk(); if(reader->IsErrored()) - return ReplayStatus::APIDataCorrupted; + return RDResult(ResultCode::APIDataCorrupted, ser.GetError().message); // if there wasn't a serialisation error, but the chunk didn't succeed, then it's an API replay // failure. if(!success) - return HasFatalError() ? m_FatalError : m_FailedReplayStatus; + { + rdcstr extra; + + if(m_pInfoQueue) + { + extra += "\n"; + + for(UINT64 i = startMessage; + i < m_pInfoQueue->GetNumStoredMessagesAllowedByRetrievalFilter(); i++) + { + SIZE_T len = 0; + m_pInfoQueue->GetMessage(i, NULL, &len); + + char *msgbuf = new char[len]; + D3D11_MESSAGE *message = (D3D11_MESSAGE *)msgbuf; + + m_pInfoQueue->GetMessage(i, message, &len); + + extra += "\n"; + extra += message->pDescription; + + delete[] msgbuf; + } + } + else + { + extra += + "\n\nMore debugging information may be available by enabling API validation on replay"; + } + + if(HasFatalError()) + { + m_FatalError.message = rdcstr(m_FatalError.message) + extra; + return m_FatalError; + } + + m_FailedReplayResult.message = rdcstr(m_FailedReplayResult.message) + extra; + return m_FailedReplayResult; + } uint64_t offsetEnd = reader->GetOffset(); @@ -1398,10 +1444,10 @@ ReplayStatus WrappedID3D11Device::ReadLogInitialisation(RDCFile *rdc, bool store savedDebugMessages.swap(m_DebugMessages); } - ReplayStatus status = m_pImmediateContext->ReplayLog(m_State, 0, 0, false); + RDResult result = m_pImmediateContext->ReplayLog(m_State, 0, 0, false); - if(status != ReplayStatus::Succeeded) - return status; + if(result != ResultCode::Succeeded) + return result; } chunkInfos[context].total += timer.GetMilliseconds(); @@ -1476,9 +1522,10 @@ ReplayStatus WrappedID3D11Device::ReadLogInitialisation(RDCFile *rdc, bool store return m_FatalError; if(m_pDevice && m_pDevice->GetDeviceRemovedReason() != S_OK) - return ReplayStatus::ReplayDeviceLost; + RETURN_ERROR_RESULT(ResultCode::ReplayDeviceLost, "Device lost during load: %s", + ToStr(m_pDevice->GetDeviceRemovedReason()).c_str()); - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } void WrappedID3D11Device::ReplayLog(uint32_t startEventID, uint32_t endEventID, @@ -1506,19 +1553,19 @@ void WrappedID3D11Device::ReplayLog(uint32_t startEventID, uint32_t endEventID, m_ReplayEventCount = 0; - ReplayStatus status = ReplayStatus::Succeeded; + RDResult result = ResultCode::Succeeded; if(replayType == eReplay_Full) - status = m_pImmediateContext->ReplayLog(m_State, startEventID, endEventID, partial); + result = m_pImmediateContext->ReplayLog(m_State, startEventID, endEventID, partial); else if(replayType == eReplay_WithoutDraw) - status = + result = m_pImmediateContext->ReplayLog(m_State, startEventID, RDCMAX(1U, endEventID) - 1, partial); else if(replayType == eReplay_OnlyDraw) - status = m_pImmediateContext->ReplayLog(m_State, endEventID, endEventID, partial); + result = m_pImmediateContext->ReplayLog(m_State, endEventID, endEventID, partial); else RDCFATAL("Unexpected replay type"); - RDCASSERTEQUAL(status, ReplayStatus::Succeeded); + RDCASSERTEQUAL(result.code, ResultCode::Succeeded); // make sure to end any unbalanced replay events if we stopped in the middle of a frame for(int i = 0; i < m_ReplayEventCount; i++) @@ -1527,7 +1574,8 @@ void WrappedID3D11Device::ReplayLog(uint32_t startEventID, uint32_t endEventID, D3D11MarkerRegion::Set("!!!!RenderDoc Internal: Done replay"); if(m_pDevice->GetDeviceRemovedReason() != S_OK) - m_FatalError = ReplayStatus::ReplayDeviceLost; + SET_ERROR_RESULT(m_FatalError, ResultCode::ReplayDeviceLost, "Device lost during replay: %s", + ToStr(m_pDevice->GetDeviceRemovedReason()).c_str()); } void WrappedID3D11Device::NewSwapchainBuffer(IUnknown *backbuffer) @@ -2530,8 +2578,8 @@ void WrappedID3D11Device::CheckHRESULT(HRESULT hr) if(hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET || hr == DXGI_ERROR_DEVICE_HUNG || hr == DXGI_ERROR_DRIVER_INTERNAL_ERROR) { - RDCLOG("Logging device lost fatal error for %s", ToStr(hr).c_str()); - m_FatalError = ReplayStatus::ReplayDeviceLost; + SET_ERROR_RESULT(m_FatalError, ResultCode::ReplayDeviceLost, + "Logging device lost fatal error for %s", ToStr(hr).c_str()); } else if(hr == E_OUTOFMEMORY) { @@ -2541,8 +2589,8 @@ void WrappedID3D11Device::CheckHRESULT(HRESULT hr) } else { - RDCLOG("Logging out of memory fatal error for %s", ToStr(hr).c_str()); - m_FatalError = ReplayStatus::ReplayOutOfMemory; + SET_ERROR_RESULT(m_FatalError, ResultCode::ReplayOutOfMemory, + "Logging out of memory fatal error for %s", ToStr(hr).c_str()); } } else @@ -2862,8 +2910,8 @@ bool WrappedID3D11Device::Serialise_SetShaderExtUAV(SerialiserType &ser, GPUVend { if(!m_ReplayNVAPI) { - RDCERR("This capture uses nvapi extensions but it failed to initialise."); - m_FailedReplayStatus = ReplayStatus::APIUnsupported; + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIUnsupported, + "This capture uses nvapi extensions but it failed to initialise."); return false; } m_ReplayNVAPI->SetShaderExtUAV(~0U, reg, true); @@ -2874,9 +2922,9 @@ bool WrappedID3D11Device::Serialise_SetShaderExtUAV(SerialiserType &ser, GPUVend } else { - RDCERR("This capture uses unexpected %s extensions which are not supported.", - ToStr(vendor).c_str()); - m_FailedReplayStatus = ReplayStatus::APIUnsupported; + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIUnsupported, + "This capture uses %s extensions which are not supported.", + ToStr(vendor).c_str()); return false; } } diff --git a/renderdoc/driver/d3d11/d3d11_device.h b/renderdoc/driver/d3d11/d3d11_device.h index 2b3cabc0c..79eb6c468 100644 --- a/renderdoc/driver/d3d11/d3d11_device.h +++ b/renderdoc/driver/d3d11/d3d11_device.h @@ -561,7 +561,7 @@ private: PerformanceTimer m_CaptureTimer; - ReplayStatus m_FailedReplayStatus = ReplayStatus::APIReplayFailed; + RDResult m_FailedReplayResult = ResultCode::APIReplayFailed; std::set m_CachedStateObjects; @@ -602,7 +602,7 @@ private: int m_OOMHandler = 0; rdcarray m_DebugMessages; - ReplayStatus m_FatalError = ReplayStatus::Succeeded; + RDResult m_FatalError = ResultCode::Succeeded; rdcarray m_CapturedFrames; rdcarray m_Actions; @@ -682,9 +682,9 @@ public: m_OOMHandler--; } void CheckHRESULT(HRESULT hr); - void ReportFatalError(ReplayStatus error) { m_FatalError = error; } - ReplayStatus FatalErrorCheck() { return m_FatalError; } - bool HasFatalError() { return m_FatalError != ReplayStatus::Succeeded; } + void ReportFatalError(RDResult error) { m_FatalError = error; } + RDResult FatalErrorCheck() { return m_FatalError; } + bool HasFatalError() { return m_FatalError != ResultCode::Succeeded; } rdcarray GetDebugMessages(); void AddDebugMessage(DebugMessage msg); void AddDebugMessage(MessageCategory c, MessageSeverity sv, MessageSource src, rdcstr d); @@ -702,6 +702,7 @@ public: bool DiscardFrameCapture(void *dev, void *wnd); ID3DUserDefinedAnnotation *GetAnnotations() { return m_RealAnnotations; } + ID3D11InfoQueue *GetInfoQueue() { return m_pInfoQueue; } // interface for DXGI virtual IUnknown *GetRealIUnknown() { return GetReal(); } void *GetFrameCapturerDevice() { return (ID3D11Device *)this; } @@ -752,7 +753,7 @@ public: m_SectionVersion = sectionVersion; m_State = CaptureState::StructuredExport; } - ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); + RDResult ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); bool ProcessChunk(ReadSerialiser &ser, D3D11Chunk context); void ReplayLog(uint32_t startEventID, uint32_t endEventID, ReplayLogType replayType); diff --git a/renderdoc/driver/d3d11/d3d11_device1_wrap.cpp b/renderdoc/driver/d3d11/d3d11_device1_wrap.cpp index 41c73a565..bd3089c31 100644 --- a/renderdoc/driver/d3d11/d3d11_device1_wrap.cpp +++ b/renderdoc/driver/d3d11/d3d11_device1_wrap.cpp @@ -84,13 +84,20 @@ bool WrappedID3D11Device::Serialise_CreateBlendState1(SerialiserType &ser, HRESULT hr = E_NOINTERFACE; if(m_pDevice1) + { hr = m_pDevice1->CreateBlendState1(&Descriptor, &ret); + } else - RDCERR("Replaying a D3D11.1 device without D3D11.1 available"); + { + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Replaying a D3D11.1 capture without D3D11.1 available"); + return false; + } if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating D3D11.1 blend state, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -195,13 +202,20 @@ bool WrappedID3D11Device::Serialise_CreateRasterizerState1( HRESULT hr = E_NOINTERFACE; if(m_pDevice1) + { hr = m_pDevice1->CreateRasterizerState1(&Descriptor, &ret); + } else - RDCERR("Replaying a D3D11.1 device without D3D11.1 available"); + { + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Replaying a D3D11.1 capture without D3D11.1 available"); + return false; + } if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating D3D11.1 rasterizer state, HRESULT: %s", ToStr(hr).c_str()); return false; } else diff --git a/renderdoc/driver/d3d11/d3d11_device3_wrap.cpp b/renderdoc/driver/d3d11/d3d11_device3_wrap.cpp index 872e1fdb8..c0b7f01be 100644 --- a/renderdoc/driver/d3d11/d3d11_device3_wrap.cpp +++ b/renderdoc/driver/d3d11/d3d11_device3_wrap.cpp @@ -83,12 +83,15 @@ bool WrappedID3D11Device::Serialise_CreateTexture2D1(SerialiserType &ser, } else { - RDCERR("Replaying a D3D11.3 device without D3D11.3 available"); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Replaying a D3D11.3 capture without D3D11.3 available"); + return false; } if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating D3D11.3 2D texture, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -221,12 +224,15 @@ bool WrappedID3D11Device::Serialise_CreateTexture3D1(SerialiserType &ser, } else { - RDCERR("Replaying a D3D11.3 device without D3D11.3 available"); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Replaying a D3D11.3 capture without D3D11.3 available"); + return false; } if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating D3D11.3 3D texture, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -366,12 +372,15 @@ bool WrappedID3D11Device::Serialise_CreateShaderResourceView1( } else { - RDCERR("Replaying a D3D11.3 device without D3D11.3 available"); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Replaying a D3D11.3 capture without D3D11.3 available"); + return false; } if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating D3D11.3 SRV, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -502,12 +511,15 @@ bool WrappedID3D11Device::Serialise_CreateRenderTargetView1(SerialiserType &ser, } else { - RDCERR("Replaying a D3D11.3 device without D3D11.3 available"); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Replaying a D3D11.3 capture without D3D11.3 available"); + return false; } if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating D3D11.3 RTV, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -608,12 +620,15 @@ bool WrappedID3D11Device::Serialise_CreateUnorderedAccessView1( } else { - RDCERR("Replaying a D3D11.3 device without D3D11.3 available"); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Replaying a D3D11.3 capture without D3D11.3 available"); + return false; } if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating D3D11.3 UAV, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -719,13 +734,20 @@ bool WrappedID3D11Device::Serialise_CreateRasterizerState2( HRESULT hr = E_NOINTERFACE; if(m_pDevice3) + { hr = m_pDevice3->CreateRasterizerState2(&Descriptor, &ret); + } else - RDCERR("Replaying a D3D11.3 device without D3D11.3 available"); + { + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Replaying a D3D11.3 capture without D3D11.3 available"); + return false; + } if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating D3D11.3 rasterizer state. HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -832,13 +854,20 @@ bool WrappedID3D11Device::Serialise_CreateQuery1(SerialiserType &ser, HRESULT hr = E_NOINTERFACE; if(m_pDevice3) + { hr = m_pDevice3->CreateQuery1(&Descriptor, &ret); + } else - RDCERR("Replaying a D3D11.3 device without D3D11.3 available"); + { + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Replaying a D3D11.3 capture without D3D11.3 available"); + return false; + } if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating D3D11.3 query. HRESULT: %s", ToStr(hr).c_str()); return false; } else diff --git a/renderdoc/driver/d3d11/d3d11_device_wrap.cpp b/renderdoc/driver/d3d11/d3d11_device_wrap.cpp index d49e8855c..4d3d68e88 100644 --- a/renderdoc/driver/d3d11/d3d11_device_wrap.cpp +++ b/renderdoc/driver/d3d11/d3d11_device_wrap.cpp @@ -119,7 +119,8 @@ bool WrappedID3D11Device::Serialise_CreateBuffer(SerialiserType &ser, const D3D1 if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating buffer, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -427,7 +428,8 @@ bool WrappedID3D11Device::Serialise_CreateTexture1D(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating 1D texture, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -565,7 +567,8 @@ bool WrappedID3D11Device::Serialise_CreateTexture2D(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating 2D texture, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -702,7 +705,8 @@ bool WrappedID3D11Device::Serialise_CreateTexture3D(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating 3D texture, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -842,7 +846,8 @@ bool WrappedID3D11Device::Serialise_CreateShaderResourceView( if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating SRV, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -969,7 +974,8 @@ bool WrappedID3D11Device::Serialise_CreateUnorderedAccessView( if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating UAV, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -1120,7 +1126,8 @@ bool WrappedID3D11Device::Serialise_CreateRenderTargetView(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating RTV, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -1219,7 +1226,8 @@ bool WrappedID3D11Device::Serialise_CreateDepthStencilView( if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating DSV, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -1326,7 +1334,8 @@ bool WrappedID3D11Device::Serialise_CreateInputLayout( if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating input layout, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -1424,7 +1433,8 @@ bool WrappedID3D11Device::Serialise_CreateVertexShader(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating vertex shader, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -1558,7 +1568,8 @@ bool WrappedID3D11Device::Serialise_CreateGeometryShader(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating geometry shader, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -1700,7 +1711,9 @@ bool WrappedID3D11Device::Serialise_CreateGeometryShaderWithStreamOutput( if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating geometry shader with stream output, HRESULT: %s", + ToStr(hr).c_str()); return false; } else @@ -1841,7 +1854,8 @@ bool WrappedID3D11Device::Serialise_CreatePixelShader(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating pixel shader, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -1974,7 +1988,8 @@ bool WrappedID3D11Device::Serialise_CreateHullShader(SerialiserType &ser, const if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating hull shader, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -2109,7 +2124,8 @@ bool WrappedID3D11Device::Serialise_CreateDomainShader(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating domain shader, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -2245,7 +2261,8 @@ bool WrappedID3D11Device::Serialise_CreateComputeShader(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating compute shader, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -2390,7 +2407,8 @@ bool WrappedID3D11Device::Serialise_CreateClassInstance(SerialiserType &ser, LPC if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating class instance, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -2463,7 +2481,8 @@ bool WrappedID3D11Device::Serialise_GetClassInstance(SerialiserType &ser, LPCSTR if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed getting class instance, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -2528,7 +2547,8 @@ bool WrappedID3D11Device::Serialise_CreateClassLinkage(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating class linkage, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -2598,7 +2618,8 @@ bool WrappedID3D11Device::Serialise_CreateBlendState(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating blend state, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -2712,7 +2733,8 @@ bool WrappedID3D11Device::Serialise_CreateDepthStencilState( if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating depth-stencil state, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -2824,7 +2846,8 @@ bool WrappedID3D11Device::Serialise_CreateRasterizerState(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating rasterizer state, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -2936,7 +2959,8 @@ bool WrappedID3D11Device::Serialise_CreateSamplerState(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating sampler state, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -3047,7 +3071,8 @@ bool WrappedID3D11Device::Serialise_CreateQuery(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating query, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -3131,7 +3156,8 @@ bool WrappedID3D11Device::Serialise_CreatePredicate(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating predicate, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -3223,7 +3249,8 @@ bool WrappedID3D11Device::Serialise_CreateCounter(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating counter, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -3306,7 +3333,8 @@ bool WrappedID3D11Device::Serialise_CreateDeferredContext(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating deferred context, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -3434,7 +3462,8 @@ bool WrappedID3D11Device::Serialise_OpenSharedResource(SerialiserType &ser, HAND if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating fake shared buffer, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -3508,7 +3537,8 @@ bool WrappedID3D11Device::Serialise_OpenSharedResource(SerialiserType &ser, HAND if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating fake shared 1D texture, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -3552,7 +3582,8 @@ bool WrappedID3D11Device::Serialise_OpenSharedResource(SerialiserType &ser, HAND if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating fake shared 2D texture, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -3596,7 +3627,8 @@ bool WrappedID3D11Device::Serialise_OpenSharedResource(SerialiserType &ser, HAND if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating fake shared 3D texture, HRESULT: %s", ToStr(hr).c_str()); return false; } else diff --git a/renderdoc/driver/d3d11/d3d11_replay.cpp b/renderdoc/driver/d3d11/d3d11_replay.cpp index 9c6f85f3e..82c65b870 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.cpp +++ b/renderdoc/driver/d3d11/d3d11_replay.cpp @@ -82,7 +82,7 @@ void D3D11Replay::Shutdown() delete m_pDevice; } -ReplayStatus D3D11Replay::FatalErrorCheck() +RDResult D3D11Replay::FatalErrorCheck() { return m_pDevice->FatalErrorCheck(); } @@ -1603,7 +1603,7 @@ void D3D11Replay::SavePipelineState(uint32_t eventId) ret.predication.isPassing = rs->PredicationWouldPass(); } -ReplayStatus D3D11Replay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) +RDResult D3D11Replay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { return m_pDevice->ReadLogInitialisation(rdc, storeStructuredBuffers); } @@ -3919,7 +3919,7 @@ void D3D11Replay::SetProxyBufferData(ResourceId bufid, byte *data, size_t dataSi ID3DDevice *GetD3D11DeviceIfAlloc(IUnknown *dev); -ReplayStatus D3D11_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver) +RDResult D3D11_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver) { RDCDEBUG("Creating a D3D11 replay device"); @@ -3929,8 +3929,7 @@ ReplayStatus D3D11_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I lib = LoadLibraryA("d3d11.dll"); if(lib == NULL) { - RDCERR("Failed to load d3d11.dll"); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Failed to load d3d11.dll"); } PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN CreateDeviceAndSwapChainPtr = @@ -3938,24 +3937,15 @@ ReplayStatus D3D11_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I RealD3D11CreateFunction CreateDeviceAndSwapChain = CreateDeviceAndSwapChainPtr; - lib = LoadLibraryA("d3d9.dll"); - if(lib == NULL) - { - RDCERR("Failed to load d3d9.dll"); - return ReplayStatus::APIInitFailed; - } - lib = LoadLibraryA("dxgi.dll"); if(lib == NULL) { - RDCERR("Failed to load dxgi.dll"); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Failed to load dxgi.dll"); } if(GetD3DCompiler() == NULL) { - RDCERR("Failed to load d3dcompiler_??.dll"); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Failed to load d3dcompiler_??.dll"); } D3D11InitParams initParams; @@ -3971,14 +3961,16 @@ ReplayStatus D3D11_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture); if(sectionIdx < 0) - return ReplayStatus::InternalError; + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, "File does not contain captured API data"); ver = rdc->GetSectionProperties(sectionIdx).version; if(!D3D11InitParams::IsSupportedVersion(ver)) { - RDCERR("Incompatible D3D11 serialise version %llu", ver); - return ReplayStatus::APIIncompatibleVersion; + RETURN_ERROR_RESULT(ResultCode::APIIncompatibleVersion, + "D3D11 capture is incompatible version %llu, newest supported by this " + "build of RenderDoc is %llu", + ver, D3D11InitParams::CurrentVersion); } StreamReader *reader = rdc->ReadSection(sectionIdx); @@ -3991,16 +3983,15 @@ ReplayStatus D3D11_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I if(chunk != SystemChunk::DriverInit) { - RDCERR("Expected to get a DriverInit chunk, instead got %u", chunk); - return ReplayStatus::FileCorrupted; + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, + "Expected to get a DriverInit chunk, instead got %u", chunk); } SERIALISE_ELEMENT(initParams); if(ser.IsErrored()) { - RDCERR("Failed reading driver init params."); - return ReplayStatus::FileIOFailed; + return ser.GetError(); } if(initParams.AdapterDesc.Description[0]) @@ -4054,14 +4045,14 @@ ReplayStatus D3D11_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I if(FAILED(hr) || !factory) { SAFE_RELEASE(factory); - RDCERR("Couldn't create IDXGIFactory: %s", ToStr(hr).c_str()); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, + "Couldn't create DXGI factory from CreateDXGIFactory: %s", + ToStr(hr).c_str()); } } else { - RDCERR("Couldn't get CreateDXGIFactory"); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Couldn't find CreateDXGIFactory in dxgi.dll"); } } @@ -4083,8 +4074,9 @@ ReplayStatus D3D11_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I if(!nvapiDev) { - RDCERR("Capture requires nvapi to replay, but it's not available or can't be initialised"); - return ReplayStatus::APIHardwareUnsupported; + RETURN_ERROR_RESULT( + ResultCode::APIHardwareUnsupported, + "Capture requires nvapi to replay, but it's not available or can't be initialised"); } } else if(initParams.VendorExtensions == GPUVendor::AMD) @@ -4093,8 +4085,9 @@ ReplayStatus D3D11_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I if(!agsDev) { - RDCERR("Capture requires ags to replay, but it's not available or can't be initialised"); - return ReplayStatus::APIHardwareUnsupported; + RETURN_ERROR_RESULT( + ResultCode::APIHardwareUnsupported, + "Capture requires ags to replay, but it's not available or can't be initialised"); } CreateDeviceAndSwapChain = [agsDev]( @@ -4110,11 +4103,11 @@ ReplayStatus D3D11_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I } else if(initParams.VendorExtensions != GPUVendor::Unknown) { - RDCERR( + RETURN_ERROR_RESULT( + ResultCode::APIInitFailed, "Capture requires vendor extensions by %s to replay, but no support for that is " "available.", ToStr(initParams.VendorExtensions).c_str()); - return ReplayStatus::APIInitFailed; } IDXGIAdapter *adapter = NULL; @@ -4298,13 +4291,13 @@ ReplayStatus D3D11_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I BOOL ok = nvapiDev->SetReal(device); if(!ok) { - RDCERR( - "This capture needs nvapi extensions to replay, but device selected for replay can't " - "support nvapi extensions"); SAFE_RELEASE(device); SAFE_RELEASE(nvapiDev); SAFE_RELEASE(factory); - return ReplayStatus::APIHardwareUnsupported; + RETURN_ERROR_RESULT( + ResultCode::APIHardwareUnsupported, + "This capture needs nvapi extensions to replay, but device selected for replay can't " + "support nvapi extensions"); } } @@ -4312,13 +4305,13 @@ ReplayStatus D3D11_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I { if(!agsDev->ExtensionsSupported()) { - RDCERR( - "This capture needs AGS extesnions to replay, but device selected for replay can't " - "support AGS extensions"); SAFE_RELEASE(device); SAFE_RELEASE(nvapiDev); SAFE_RELEASE(factory); - return ReplayStatus::APIHardwareUnsupported; + RETURN_ERROR_RESULT( + ResultCode::APIHardwareUnsupported, + "This capture needs AGS extensions to replay, but device selected for replay can't " + "support nvapi extensions"); } } @@ -4341,37 +4334,39 @@ ReplayStatus D3D11_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I } *driver = (IReplayDriver *)replay; - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } SAFE_RELEASE(factory); - RDCERR("Couldn't create any compatible d3d11 device."); + rdcstr error = "Couldn't create any compatible d3d11 device."; - if((flags & D3D11_CREATE_DEVICE_DEBUG) && !isProxy) - RDCLOG( - "Development RenderDoc builds require D3D debug layers available, " - "ensure you have the windows SDK or windows feature needed."); + if(flags & D3D11_CREATE_DEVICE_DEBUG) + error += + "\n\nDevelopment RenderDoc builds require D3D debug layers available, " + "ensure you have the windows SDK or windows feature needed."; - return ReplayStatus::APIHardwareUnsupported; + RETURN_ERROR_RESULT(ResultCode::APIHardwareUnsupported, "%s", error.c_str()); } static DriverRegistration D3D11DriverRegistration(RDCDriver::D3D11, &D3D11_CreateReplayDevice); -void D3D11_ProcessStructured(RDCFile *rdc, SDFile &output) +RDResult D3D11_ProcessStructured(RDCFile *rdc, SDFile &output) { WrappedID3D11Device device(NULL, D3D11InitParams()); int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture); if(sectionIdx < 0) - return; + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, "File does not contain captured API data"); device.SetStructuredExport(rdc->GetSectionProperties(sectionIdx).version); - ReplayStatus status = device.ReadLogInitialisation(rdc, true); + RDResult result = device.ReadLogInitialisation(rdc, true); - if(status == ReplayStatus::Succeeded) + if(result == ResultCode::Succeeded) device.GetStructuredFile()->Swap(output); + + return result; } static StructuredProcessRegistration D3D11ProcessRegistration(RDCDriver::D3D11, diff --git a/renderdoc/driver/d3d11/d3d11_replay.h b/renderdoc/driver/d3d11/d3d11_replay.h index 2c037b8b5..831e1535c 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.h +++ b/renderdoc/driver/d3d11/d3d11_replay.h @@ -109,7 +109,7 @@ public: bool IsRemoteProxy() { return m_Proxy; } void Shutdown(); - ReplayStatus FatalErrorCheck(); + RDResult FatalErrorCheck(); IReplayDriver *MakeDummyDriver(); void CreateResources(IDXGIFactory *factory); @@ -149,7 +149,7 @@ public: void FreeTargetResource(ResourceId id); void FreeCustomShader(ResourceId id); - ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); + RDResult ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); void ReplayLog(uint32_t endEventID, ReplayLogType replayType); SDFile *GetStructuredFile(); diff --git a/renderdoc/driver/d3d12/d3d12_command_list1_wrap.cpp b/renderdoc/driver/d3d12/d3d12_command_list1_wrap.cpp index e2a329afc..3ba619bd8 100644 --- a/renderdoc/driver/d3d12/d3d12_command_list1_wrap.cpp +++ b/renderdoc/driver/d3d12/d3d12_command_list1_wrap.cpp @@ -47,7 +47,8 @@ bool WrappedID3D12GraphicsCommandList::Serialise_AtomicCopyBufferUINT( { if(GetWrapped(pCommandList)->GetReal1() == NULL) { - RDCERR("Can't replay ID3D12GraphicsCommandList1 command"); + SET_ERROR_RESULT(m_Cmd->m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires ID3D12GraphicsCommandList1 which isn't available"); return false; } @@ -162,7 +163,8 @@ bool WrappedID3D12GraphicsCommandList::Serialise_AtomicCopyBufferUINT64( { if(GetWrapped(pCommandList)->GetReal1() == NULL) { - RDCERR("Can't replay ID3D12GraphicsCommandList1 command"); + SET_ERROR_RESULT(m_Cmd->m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires ID3D12GraphicsCommandList1 which isn't available"); return false; } @@ -270,13 +272,24 @@ bool WrappedID3D12GraphicsCommandList::Serialise_OMSetDepthBounds(SerialiserType { if(GetWrapped(pCommandList)->GetReal1() == NULL) { - RDCERR("Can't replay ID3D12GraphicsCommandList1 command"); + SET_ERROR_RESULT(m_Cmd->m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires ID3D12GraphicsCommandList1 which isn't available"); return false; } if(m_pDevice->GetOpts2().DepthBoundsTestSupported == 0) { - RDCERR("Can't replay OMSetDepthBounds without device support"); + if(Min <= 0.0f && Max >= 1.0f) + { + RDCWARN( + "Depth bounds is not supported, but skipping no-op " + "OMSetDepthBounds(Min=%F, Max=%f)", + Min, Max); + return true; + } + + SET_ERROR_RESULT(m_Cmd->m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires depth bounds support which isn't available"); return false; } @@ -346,7 +359,8 @@ bool WrappedID3D12GraphicsCommandList::Serialise_SetSamplePositions( { if(GetWrapped(pCommandList)->GetReal1() == NULL) { - RDCERR("Can't replay ID3D12GraphicsCommandList1 command"); + SET_ERROR_RESULT(m_Cmd->m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires ID3D12GraphicsCommandList1 which isn't available"); return false; } @@ -362,9 +376,9 @@ bool WrappedID3D12GraphicsCommandList::Serialise_SetSamplePositions( return true; } - RDCERR( - "Can't replay SetSamplePositions with " - "D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER_NOT_SUPPORTED"); + SET_ERROR_RESULT( + m_Cmd->m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires programmable sample position support which isn't available"); return false; } @@ -446,7 +460,8 @@ bool WrappedID3D12GraphicsCommandList::Serialise_ResolveSubresourceRegion( { if(GetWrapped(pCommandList)->GetReal1() == NULL) { - RDCERR("Can't replay ID3D12GraphicsCommandList1 command"); + SET_ERROR_RESULT(m_Cmd->m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires ID3D12GraphicsCommandList1 which isn't available"); return false; } @@ -542,7 +557,8 @@ bool WrappedID3D12GraphicsCommandList::Serialise_SetViewInstanceMask(SerialiserT { if(GetWrapped(pCommandList)->GetReal1() == NULL) { - RDCERR("Can't replay ID3D12GraphicsCommandList1 command"); + SET_ERROR_RESULT(m_Cmd->m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires ID3D12GraphicsCommandList1 which isn't available"); return false; } @@ -554,7 +570,8 @@ bool WrappedID3D12GraphicsCommandList::Serialise_SetViewInstanceMask(SerialiserT return true; } - RDCERR("Can't replay SetViewInstanceMask with D3D12_VIEW_INSTANCING_TIER_NOT_SUPPORTED"); + SET_ERROR_RESULT(m_Cmd->m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires view instancing support which isn't available"); return false; } diff --git a/renderdoc/driver/d3d12/d3d12_command_list2_wrap.cpp b/renderdoc/driver/d3d12/d3d12_command_list2_wrap.cpp index f0498e032..d66a3d27d 100644 --- a/renderdoc/driver/d3d12/d3d12_command_list2_wrap.cpp +++ b/renderdoc/driver/d3d12/d3d12_command_list2_wrap.cpp @@ -41,7 +41,8 @@ bool WrappedID3D12GraphicsCommandList::Serialise_WriteBufferImmediate( { if(GetWrapped(pCommandList)->GetReal2() == NULL) { - RDCERR("Can't replay ID3D12GraphicsCommandList2 command"); + SET_ERROR_RESULT(m_Cmd->m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires ID3D12GraphicsCommandList2 which isn't available"); return false; } diff --git a/renderdoc/driver/d3d12/d3d12_command_list4_wrap.cpp b/renderdoc/driver/d3d12/d3d12_command_list4_wrap.cpp index f1d26cd4d..001feac9b 100644 --- a/renderdoc/driver/d3d12/d3d12_command_list4_wrap.cpp +++ b/renderdoc/driver/d3d12/d3d12_command_list4_wrap.cpp @@ -201,7 +201,8 @@ bool WrappedID3D12GraphicsCommandList::Serialise_BeginRenderPass( { if(GetWrapped(pCommandList)->GetReal4() == NULL) { - RDCERR("Can't replay ID3D12GraphicsCommandList4 command"); + SET_ERROR_RESULT(m_Cmd->m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires ID3D12GraphicsCommandList4 which isn't available"); return false; } @@ -476,7 +477,8 @@ bool WrappedID3D12GraphicsCommandList::Serialise_EndRenderPass(SerialiserType &s { if(GetWrapped(pCommandList)->GetReal4() == NULL) { - RDCERR("Can't replay ID3D12GraphicsCommandList4 command"); + SET_ERROR_RESULT(m_Cmd->m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires ID3D12GraphicsCommandList4 which isn't available"); return false; } diff --git a/renderdoc/driver/d3d12/d3d12_command_list5_wrap.cpp b/renderdoc/driver/d3d12/d3d12_command_list5_wrap.cpp index 065127bbc..3235f6221 100644 --- a/renderdoc/driver/d3d12/d3d12_command_list5_wrap.cpp +++ b/renderdoc/driver/d3d12/d3d12_command_list5_wrap.cpp @@ -40,7 +40,8 @@ bool WrappedID3D12GraphicsCommandList::Serialise_RSSetShadingRate( { if(GetWrapped(pCommandList)->GetReal5() == NULL) { - RDCERR("Can't replay ID3D12GraphicsCommandList5 command"); + SET_ERROR_RESULT(m_Cmd->m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires ID3D12GraphicsCommandList5 which isn't available"); return false; } @@ -59,9 +60,8 @@ bool WrappedID3D12GraphicsCommandList::Serialise_RSSetShadingRate( return true; } - RDCERR( - "Can't replay WrappedID3D12GraphicsCommandList::RSSetShadingRate with " - "D3D12_VARIABLE_SHADING_RATE_TIER_NOT_SUPPORTED"); + SET_ERROR_RESULT(m_Cmd->m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires variable rate shading support which isn't available"); return false; } @@ -141,7 +141,8 @@ bool WrappedID3D12GraphicsCommandList::Serialise_RSSetShadingRateImage(Serialise { if(GetWrapped(pCommandList)->GetReal5() == NULL) { - RDCERR("Can't replay ID3D12GraphicsCommandList5 command"); + SET_ERROR_RESULT(m_Cmd->m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires ID3D12GraphicsCommandList5 which isn't available"); return false; } @@ -154,9 +155,8 @@ bool WrappedID3D12GraphicsCommandList::Serialise_RSSetShadingRateImage(Serialise return true; } - RDCERR( - "Can't replay WrappedID3D12GraphicsCommandList::RSSetShadingRate with " - "D3D12_VARIABLE_SHADING_RATE_TIER_NOT_SUPPORTED"); + SET_ERROR_RESULT(m_Cmd->m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires variable rate shading support which isn't available"); return false; } diff --git a/renderdoc/driver/d3d12/d3d12_command_list_wrap.cpp b/renderdoc/driver/d3d12/d3d12_command_list_wrap.cpp index eadd9e525..76bceb8e1 100644 --- a/renderdoc/driver/d3d12/d3d12_command_list_wrap.cpp +++ b/renderdoc/driver/d3d12/d3d12_command_list_wrap.cpp @@ -265,7 +265,8 @@ bool WrappedID3D12GraphicsCommandList::Serialise_Reset(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, hr: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_Cmd->m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating command list, HRESULT: %s", ToStr(hr).c_str()); return false; } diff --git a/renderdoc/driver/d3d12/d3d12_command_queue.h b/renderdoc/driver/d3d12/d3d12_command_queue.h index c184c4c08..43bd3ecbe 100644 --- a/renderdoc/driver/d3d12/d3d12_command_queue.h +++ b/renderdoc/driver/d3d12/d3d12_command_queue.h @@ -149,8 +149,6 @@ class WrappedID3D12CommandQueue : public ID3D12CommandQueue, bool m_MarkedActive = false; - ReplayStatus m_FailedReplayStatus = ReplayStatus::APIReplayFailed; - WrappedID3D12DebugCommandQueue m_WrappedDebug; WrappedID3D12CompatibilityQueue m_WrappedCompat; @@ -204,8 +202,7 @@ public: return m_SparseBindResources.find(id) != m_SparseBindResources.end(); } - ReplayStatus ReplayLog(CaptureState readType, uint32_t startEventID, uint32_t endEventID, - bool partial); + RDResult ReplayLog(CaptureState readType, uint32_t startEventID, uint32_t endEventID, bool partial); void SetFrameReader(StreamReader *reader) { m_FrameReader = reader; } D3D12CommandData *GetCommandData() { return &m_Cmd; } const rdcarray &GetUsage(ResourceId id) { return m_Cmd.m_ResourceUses[id]; } diff --git a/renderdoc/driver/d3d12/d3d12_commands.cpp b/renderdoc/driver/d3d12/d3d12_commands.cpp index 216ebff34..ee42afb9d 100644 --- a/renderdoc/driver/d3d12/d3d12_commands.cpp +++ b/renderdoc/driver/d3d12/d3d12_commands.cpp @@ -905,15 +905,15 @@ bool WrappedID3D12CommandQueue::ProcessChunk(ReadSerialiser &ser, D3D12Chunk chu return ret; } -ReplayStatus WrappedID3D12CommandQueue::ReplayLog(CaptureState readType, uint32_t startEventID, - uint32_t endEventID, bool partial) +RDResult WrappedID3D12CommandQueue::ReplayLog(CaptureState readType, uint32_t startEventID, + uint32_t endEventID, bool partial) { m_State = readType; if(!m_FrameReader) { - RDCERR("Can't replay context capture without frame reader"); - return ReplayStatus::InternalError; + RETURN_ERROR_RESULT(ResultCode::InvalidParameter, + "Can't replay context capture without frame reader"); } m_FrameReader->SetOffset(0); @@ -1012,7 +1012,7 @@ ReplayStatus WrappedID3D12CommandQueue::ReplayLog(CaptureState readType, uint32_ D3D12Chunk context = ser.ReadChunk(); if(ser.GetReader()->IsErrored()) - return ReplayStatus::APIDataCorrupted; + return RDResult(ResultCode::APIDataCorrupted, ser.GetError().message); m_Cmd.m_ChunkMetadata = ser.ChunkMetadata(); @@ -1023,15 +1023,52 @@ ReplayStatus WrappedID3D12CommandQueue::ReplayLog(CaptureState readType, uint32_ ser.EndChunk(); if(ser.GetReader()->IsErrored()) - return ReplayStatus::APIDataCorrupted; + return RDResult(ResultCode::APIDataCorrupted, ser.GetError().message); // if there wasn't a serialisation error, but the chunk didn't succeed, then it's an API replay // failure. if(!success) - return m_FailedReplayStatus; + { + rdcstr extra; - if(m_pDevice->HasFatalError()) - return m_pDevice->FatalErrorCheck(); + if(m_pDevice->GetInfoQueue()) + { + extra += "\n"; + + for(UINT64 i = 0; + i < m_pDevice->GetInfoQueue()->GetNumStoredMessagesAllowedByRetrievalFilter(); i++) + { + SIZE_T len = 0; + m_pDevice->GetInfoQueue()->GetMessage(i, NULL, &len); + + char *msgbuf = new char[len]; + D3D12_MESSAGE *message = (D3D12_MESSAGE *)msgbuf; + + m_pDevice->GetInfoQueue()->GetMessage(i, message, &len); + + extra += "\n"; + extra += message->pDescription; + + delete[] msgbuf; + } + } + else + { + extra += + "\n\nMore debugging information may be available by enabling API validation on " + "replay"; + } + + if(m_pDevice->HasFatalError()) + { + RDResult result = m_pDevice->FatalErrorCheck(); + result.message = rdcstr(result.message) + extra; + return result; + } + + m_Cmd.m_FailedReplayResult.message = rdcstr(m_Cmd.m_FailedReplayResult.message) + extra; + return m_Cmd.m_FailedReplayResult; + } RenderDoc::Inst().SetProgress( LoadProgress::FrameEventsRead, @@ -1078,7 +1115,7 @@ ReplayStatus WrappedID3D12CommandQueue::ReplayLog(CaptureState readType, uint32_ m_Cmd.m_RerecordCmds.clear(); m_Cmd.m_RerecordCmdList.clear(); - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } WrappedID3D12GraphicsCommandList::WrappedID3D12GraphicsCommandList(ID3D12GraphicsCommandList *real, diff --git a/renderdoc/driver/d3d12/d3d12_commands.h b/renderdoc/driver/d3d12/d3d12_commands.h index 2c4a3bb31..4bc6d16b1 100644 --- a/renderdoc/driver/d3d12/d3d12_commands.h +++ b/renderdoc/driver/d3d12/d3d12_commands.h @@ -235,6 +235,8 @@ struct D3D12CommandData ResourceId m_LastCmdListID; + RDResult m_FailedReplayResult = ResultCode::APIReplayFailed; + std::map m_CrackedAllocators; rdcarray m_IndirectBuffers; diff --git a/renderdoc/driver/d3d12/d3d12_device.cpp b/renderdoc/driver/d3d12/d3d12_device.cpp index 944305c3f..c416d7cb0 100644 --- a/renderdoc/driver/d3d12/d3d12_device.cpp +++ b/renderdoc/driver/d3d12/d3d12_device.cpp @@ -3019,8 +3019,8 @@ void WrappedID3D12Device::CheckHRESULT(HRESULT hr) if(hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET || hr == DXGI_ERROR_DEVICE_HUNG || hr == DXGI_ERROR_DRIVER_INTERNAL_ERROR) { - RDCLOG("Logging device lost fatal error for %s", ToStr(hr).c_str()); - m_FatalError = ReplayStatus::ReplayDeviceLost; + SET_ERROR_RESULT(m_FatalError, ResultCode::ReplayDeviceLost, + "Logging device lost fatal error for %s", ToStr(hr).c_str()); } else if(hr == E_OUTOFMEMORY) { @@ -3031,7 +3031,7 @@ void WrappedID3D12Device::CheckHRESULT(HRESULT hr) else { RDCLOG("Logging out of memory fatal error for %s", ToStr(hr).c_str()); - m_FatalError = ReplayStatus::ReplayOutOfMemory; + m_FatalError = ResultCode::ReplayOutOfMemory; } } else @@ -3172,8 +3172,8 @@ bool WrappedID3D12Device::Serialise_SetShaderExtUAV(SerialiserType &ser, GPUVend { if(!m_ReplayNVAPI) { - RDCERR("This capture uses nvapi extensions but it failed to initialise."); - m_FailedReplayStatus = ReplayStatus::APIUnsupported; + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIUnsupported, + "This capture uses nvapi extensions but it failed to initialise."); return false; } m_ReplayNVAPI->SetShaderExtUAV(space, reg, true); @@ -3185,9 +3185,9 @@ bool WrappedID3D12Device::Serialise_SetShaderExtUAV(SerialiserType &ser, GPUVend } else { - RDCERR("This capture uses unexpected %s extensions which are not supported.", - ToStr(vendor).c_str()); - m_FailedReplayStatus = ReplayStatus::APIUnsupported; + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIUnsupported, + "This capture uses %s extensions which are not supported.", + ToStr(vendor).c_str()); return false; } } @@ -3935,12 +3935,12 @@ void WrappedID3D12Device::AddResourceCurChunk(ResourceId id) AddResourceCurChunk(GetReplay()->GetResourceDesc(id)); } -ReplayStatus WrappedID3D12Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) +RDResult WrappedID3D12Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture); if(sectionIdx < 0) - return ReplayStatus::FileCorrupted; + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, "File does not contain captured API data"); StreamReader *reader = rdc->ReadSection(sectionIdx); @@ -3958,8 +3958,9 @@ ReplayStatus WrappedID3D12Device::ReadLogInitialisation(RDCFile *rdc, bool store if(reader->IsErrored()) { + RDResult result = reader->GetError(); delete reader; - return ReplayStatus::FileIOFailed; + return result; } ReadSerialiser ser(reader, Ownership::Stream); @@ -4004,19 +4005,64 @@ ReplayStatus WrappedID3D12Device::ReadLogInitialisation(RDCFile *rdc, bool store chunkIdx++; if(reader->IsErrored()) - return ReplayStatus::APIDataCorrupted; + return RDResult(ResultCode::APIDataCorrupted, ser.GetError().message); + + UINT64 startMessage = 0; + + // store how many messages existed before this chunk. If it fails we'll only print messages + // related to/generated by it + if(m_pInfoQueue) + startMessage = m_pInfoQueue->GetNumStoredMessagesAllowedByRetrievalFilter(); bool success = ProcessChunk(ser, context); ser.EndChunk(); if(reader->IsErrored()) - return ReplayStatus::APIDataCorrupted; + return RDResult(ResultCode::APIDataCorrupted, ser.GetError().message); // if there wasn't a serialisation error, but the chunk didn't succeed, then it's an API replay // failure. if(!success) - return HasFatalError() ? m_FatalError : m_FailedReplayStatus; + { + rdcstr extra; + + if(m_pInfoQueue) + { + extra += "\n"; + + for(UINT64 i = startMessage; + i < m_pInfoQueue->GetNumStoredMessagesAllowedByRetrievalFilter(); i++) + { + SIZE_T len = 0; + m_pInfoQueue->GetMessage(i, NULL, &len); + + char *msgbuf = new char[len]; + D3D12_MESSAGE *message = (D3D12_MESSAGE *)msgbuf; + + m_pInfoQueue->GetMessage(i, message, &len); + + extra += "\n"; + extra += message->pDescription; + + delete[] msgbuf; + } + } + else + { + extra += + "\n\nMore debugging information may be available by enabling API validation on replay"; + } + + if(HasFatalError()) + { + m_FatalError.message = rdcstr(m_FatalError.message) + extra; + return m_FatalError; + } + + m_FailedReplayResult.message = rdcstr(m_FailedReplayResult.message) + extra; + return m_FailedReplayResult; + } uint64_t offsetEnd = reader->GetOffset(); @@ -4052,10 +4098,10 @@ ReplayStatus WrappedID3D12Device::ReadLogInitialisation(RDCFile *rdc, bool store savedDebugMessages.swap(m_DebugMessages); } - ReplayStatus status = m_Queue->ReplayLog(m_State, 0, 0, false); + RDResult result = m_Queue->ReplayLog(m_State, 0, 0, false); - if(status != ReplayStatus::Succeeded) - return status; + if(result != ResultCode::Succeeded) + return result; } chunkInfos[context].total += timer.GetMilliseconds(); @@ -4118,13 +4164,14 @@ ReplayStatus WrappedID3D12Device::ReadLogInitialisation(RDCFile *rdc, bool store RDCDEBUG("Allocating %llu persistant bytes of memory for the log.", GetReplay()->WriteFrameRecord().frameInfo.persistentSize); - if(m_FatalError != ReplayStatus::Succeeded) + if(m_FatalError != ResultCode::Succeeded) return m_FatalError; if(m_pDevice && m_pDevice->GetDeviceRemovedReason() != S_OK) - return ReplayStatus::ReplayDeviceLost; + RETURN_ERROR_RESULT(ResultCode::ReplayDeviceLost, "Device lost during load: %s", + ToStr(m_pDevice->GetDeviceRemovedReason()).c_str()); - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } void WrappedID3D12Device::ReplayLog(uint32_t startEventID, uint32_t endEventID, @@ -4229,18 +4276,18 @@ void WrappedID3D12Device::ReplayLog(uint32_t startEventID, uint32_t endEventID, cmd.m_RenderState.ApplyState(this, list); } - ReplayStatus status = ReplayStatus::Succeeded; + RDResult result = ResultCode::Succeeded; if(replayType == eReplay_Full) - status = m_Queue->ReplayLog(m_State, startEventID, endEventID, partial); + result = m_Queue->ReplayLog(m_State, startEventID, endEventID, partial); else if(replayType == eReplay_WithoutDraw) - status = m_Queue->ReplayLog(m_State, startEventID, RDCMAX(1U, endEventID) - 1, partial); + result = m_Queue->ReplayLog(m_State, startEventID, RDCMAX(1U, endEventID) - 1, partial); else if(replayType == eReplay_OnlyDraw) - status = m_Queue->ReplayLog(m_State, endEventID, endEventID, partial); + result = m_Queue->ReplayLog(m_State, endEventID, endEventID, partial); else RDCFATAL("Unexpected replay type"); - RDCASSERTEQUAL(status, ReplayStatus::Succeeded); + RDCASSERTEQUAL(result.code, ResultCode::Succeeded); if(cmd.m_OutsideCmdList != NULL) { diff --git a/renderdoc/driver/d3d12/d3d12_device.h b/renderdoc/driver/d3d12/d3d12_device.h index 6b442c732..b948ea19c 100644 --- a/renderdoc/driver/d3d12/d3d12_device.h +++ b/renderdoc/driver/d3d12/d3d12_device.h @@ -668,7 +668,7 @@ private: rdcarray m_DebugMessages; int m_OOMHandler = 0; - ReplayStatus m_FatalError = ReplayStatus::Succeeded; + RDResult m_FatalError = ResultCode::Succeeded; uint64_t m_TimeBase = 0; double m_TimeFrequency = 1.0f; @@ -679,7 +679,7 @@ private: rdcarray m_CapturedFrames; rdcarray m_Actions; - ReplayStatus m_FailedReplayStatus = ReplayStatus::APIReplayFailed; + RDResult m_FailedReplayResult = ResultCode::APIReplayFailed; bool m_AppControlledCapture = false; @@ -810,9 +810,9 @@ public: m_OOMHandler--; } void CheckHRESULT(HRESULT hr); - void ReportFatalError(ReplayStatus error) { m_FatalError = error; } - ReplayStatus FatalErrorCheck() { return m_FatalError; } - bool HasFatalError() { return m_FatalError != ReplayStatus::Succeeded; } + void ReportFatalError(RDResult error) { m_FatalError = error; } + RDResult FatalErrorCheck() { return m_FatalError; } + bool HasFatalError() { return m_FatalError != ResultCode::Succeeded; } ResourceDescription &GetResourceDesc(ResourceId id); void AddResource(ResourceId id, ResourceType type, const char *defaultNamePrefix); void DerivedResource(ResourceId parent, ResourceId child); @@ -846,6 +846,7 @@ public: D3D12Replay *GetReplay() { return m_Replay; } WrappedID3D12CommandQueue *GetQueue() { return m_Queue; } ID3D12CommandAllocator *GetAlloc() { return m_Alloc; } + ID3D12InfoQueue *GetInfoQueue() { return m_pInfoQueue; } void ApplyBarriers(rdcarray &barriers); void GetDynamicDescriptorReferences(rdcarray &refs) @@ -930,7 +931,7 @@ public: bool Serialise_DynamicDescriptorCopies(SerialiserType &ser, const rdcarray &DescriptorCopies); - ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); + RDResult ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); void ReplayLog(uint32_t startEventID, uint32_t endEventID, ReplayLogType replayType); void SetStructuredExport(uint64_t sectionVersion) diff --git a/renderdoc/driver/d3d12/d3d12_device_wrap.cpp b/renderdoc/driver/d3d12/d3d12_device_wrap.cpp index 66ecf5155..89864e89d 100644 --- a/renderdoc/driver/d3d12/d3d12_device_wrap.cpp +++ b/renderdoc/driver/d3d12/d3d12_device_wrap.cpp @@ -57,7 +57,8 @@ bool WrappedID3D12Device::Serialise_CreateCommandQueue(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating command queue, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -188,7 +189,8 @@ bool WrappedID3D12Device::Serialise_CreateCommandAllocator(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating command allocator, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -288,7 +290,8 @@ bool WrappedID3D12Device::Serialise_CreateCommandList(SerialiserType &ser, UINT if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating command list, HRESULT: %s", ToStr(hr).c_str()); return false; } else if(list) @@ -456,7 +459,8 @@ bool WrappedID3D12Device::Serialise_CreateGraphicsPipelineState( if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating graphics pipeline, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -743,7 +747,8 @@ bool WrappedID3D12Device::Serialise_CreateComputePipelineState( if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating compute pipeline, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -914,7 +919,8 @@ bool WrappedID3D12Device::Serialise_CreateDescriptorHeap( if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating descriptor heap, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -1006,7 +1012,8 @@ bool WrappedID3D12Device::Serialise_CreateRootSignature(SerialiserType &ser, UIN if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating root signature, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -1484,7 +1491,8 @@ bool WrappedID3D12Device::Serialise_CreateCommittedResource( if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating committed resource, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -1676,7 +1684,8 @@ bool WrappedID3D12Device::Serialise_CreateHeap(SerialiserType &ser, const D3D12_ if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating heap, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -1800,7 +1809,8 @@ bool WrappedID3D12Device::Serialise_CreatePlacedResource( if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating placed resource, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -2001,7 +2011,8 @@ bool WrappedID3D12Device::Serialise_CreateReservedResource( if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating reserved resource, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -2217,7 +2228,8 @@ bool WrappedID3D12Device::Serialise_CreateFence(SerialiserType &ser, UINT64 Init if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating fence, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -2308,7 +2320,8 @@ bool WrappedID3D12Device::Serialise_CreateQueryHeap(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating query heap, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -2393,7 +2406,8 @@ bool WrappedID3D12Device::Serialise_CreateCommandSignature(SerialiserType &ser, if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating command signature, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -2761,7 +2775,8 @@ bool WrappedID3D12Device::Serialise_OpenSharedHandle(SerialiserType &ser, HANDLE hr = m_pDevice->CreateFence(initialValue, flags, __uuidof(ID3D12Fence), (void **)&ret); if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating shared fence, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -2819,7 +2834,8 @@ bool WrappedID3D12Device::Serialise_OpenSharedHandle(SerialiserType &ser, HANDLE __uuidof(ID3D12Resource), (void **)&ret); if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating shared committed resource, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -2897,7 +2913,8 @@ bool WrappedID3D12Device::Serialise_OpenSharedHandle(SerialiserType &ser, HANDLE hr = m_pDevice->CreateHeap(&desc, __uuidof(ID3D12Heap), (void **)&ret); if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating shared heap, HRESULT: %s", ToStr(hr).c_str()); return false; } else diff --git a/renderdoc/driver/d3d12/d3d12_device_wrap2.cpp b/renderdoc/driver/d3d12/d3d12_device_wrap2.cpp index f0b0abfa2..9db6a0c1f 100644 --- a/renderdoc/driver/d3d12/d3d12_device_wrap2.cpp +++ b/renderdoc/driver/d3d12/d3d12_device_wrap2.cpp @@ -67,13 +67,20 @@ bool WrappedID3D12Device::Serialise_CreatePipelineState(SerialiserType &ser, } if(m_pDevice2) + { hr = m_pDevice2->CreatePipelineState(unwrappedDesc.AsDescStream(), guid, (void **)&ret); + } else - RDCERR("Replaying a without D3D12.2 available"); + { + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires ID3D12Device2 which isn't available"); + return false; + } if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating pipeline state, HRESULT: %s", ToStr(hr).c_str()); return false; } else diff --git a/renderdoc/driver/d3d12/d3d12_device_wrap4.cpp b/renderdoc/driver/d3d12/d3d12_device_wrap4.cpp index bbb657ee3..b5faac61c 100644 --- a/renderdoc/driver/d3d12/d3d12_device_wrap4.cpp +++ b/renderdoc/driver/d3d12/d3d12_device_wrap4.cpp @@ -55,14 +55,21 @@ bool WrappedID3D12Device::Serialise_CreateCommandList1(SerialiserType &ser, UINT ID3D12GraphicsCommandList *list = NULL; HRESULT hr = E_NOINTERFACE; if(m_pDevice4) + { hr = CreateCommandList1(nodeMask, type, flags, __uuidof(ID3D12GraphicsCommandList), (void **)&list); + } else - RDCERR("Replaying a without D3D12.4 available"); + { + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires ID3D12Device4 which isn't available"); + return false; + } if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating command list, HRESULT: %s", ToStr(hr).c_str()); return false; } else if(list) @@ -265,14 +272,21 @@ bool WrappedID3D12Device::Serialise_CreateCommittedResource1( ID3D12Resource *ret = NULL; HRESULT hr = E_NOINTERFACE; if(m_pDevice4) + { hr = m_pDevice4->CreateCommittedResource1(&props, HeapFlags, &desc, InitialResourceState, pOptimizedClearValue, NULL, guid, (void **)&ret); + } else - RDCERR("Replaying a without D3D12.4 available"); + { + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires ID3D12Device2 which isn't available"); + return false; + } if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating committed resource, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -459,7 +473,8 @@ bool WrappedID3D12Device::Serialise_CreateHeap1(SerialiserType &ser, const D3D12 if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating heap, HRESULT: %s", ToStr(hr).c_str()); return false; } else diff --git a/renderdoc/driver/d3d12/d3d12_device_wrap8.cpp b/renderdoc/driver/d3d12/d3d12_device_wrap8.cpp index 4c7d3679d..d1e8a4ee6 100644 --- a/renderdoc/driver/d3d12/d3d12_device_wrap8.cpp +++ b/renderdoc/driver/d3d12/d3d12_device_wrap8.cpp @@ -93,14 +93,21 @@ bool WrappedID3D12Device::Serialise_CreateCommittedResource2( ID3D12Resource *ret = NULL; HRESULT hr = E_NOINTERFACE; if(m_pDevice8) + { hr = m_pDevice8->CreateCommittedResource2(&props, HeapFlags, &desc, InitialResourceState, pOptimizedClearValue, NULL, guid, (void **)&ret); + } else - RDCERR("Replaying a without D3D12.8 available"); + { + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires ID3D12Device8 which isn't available"); + return false; + } if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating committed resource, HRESULT: %s", ToStr(hr).c_str()); return false; } else @@ -310,14 +317,22 @@ bool WrappedID3D12Device::Serialise_CreatePlacedResource1( ID3D12Resource *ret = NULL; HRESULT hr = E_NOINTERFACE; if(m_pDevice8) + { hr = m_pDevice8->CreatePlacedResource1(Unwrap(pHeap), HeapOffset, &Descriptor, InitialState, pOptimizedClearValue, guid, (void **)&ret); + } else - RDCERR("Replaying a without D3D12.8 available"); + { + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires ID3D12Device8 which isn't available"); + return false; + } if(FAILED(hr)) { RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating placed resource, HRESULT: %s", ToStr(hr).c_str()); return false; } else diff --git a/renderdoc/driver/d3d12/d3d12_device_wrap9.cpp b/renderdoc/driver/d3d12/d3d12_device_wrap9.cpp index 924855ac0..944d9d373 100644 --- a/renderdoc/driver/d3d12/d3d12_device_wrap9.cpp +++ b/renderdoc/driver/d3d12/d3d12_device_wrap9.cpp @@ -78,13 +78,20 @@ bool WrappedID3D12Device::Serialise_CreateCommandQueue1(SerialiserType &ser, ID3D12CommandQueue *ret = NULL; HRESULT hr = E_NOINTERFACE; if(m_pDevice9) + { hr = m_pDevice9->CreateCommandQueue1(&Descriptor, creator, guid, (void **)&ret); + } else - RDCERR("Replaying a without D3D12.9 available"); + { + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Capture requires ID3D12Device9 which isn't available"); + return false; + } if(FAILED(hr)) { - RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating command queue, HRESULT: %s", ToStr(hr).c_str()); return false; } else diff --git a/renderdoc/driver/d3d12/d3d12_replay.cpp b/renderdoc/driver/d3d12/d3d12_replay.cpp index 83d232bd3..880701c5e 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.cpp +++ b/renderdoc/driver/d3d12/d3d12_replay.cpp @@ -115,7 +115,7 @@ void D3D12Replay::Initialise(IDXGIFactory1 *factory) } } -ReplayStatus D3D12Replay::FatalErrorCheck() +RDResult D3D12Replay::FatalErrorCheck() { return m_pDevice->FatalErrorCheck(); } @@ -210,7 +210,7 @@ void D3D12Replay::DestroyResources() SAFE_DELETE(m_pAMDCounters); } -ReplayStatus D3D12Replay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) +RDResult D3D12Replay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { return m_pDevice->ReadLogInitialisation(rdc, storeStructuredBuffers); } @@ -4210,7 +4210,7 @@ void D3D12Replay::SetProxyBufferData(ResourceId bufid, byte *data, size_t dataSi #pragma endregion -ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver) +RDResult D3D12_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver) { RDCDEBUG("Creating a D3D12 replay device"); @@ -4236,8 +4236,8 @@ ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I if(!dxilconv) { - RDCERR("Found d3d12.dll in plugin path, but couldn't load dxilconv7.dll"); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, + "Found d3d12.dll in plugin path, but couldn't load dxilconv7.dll"); } } else @@ -4254,10 +4254,9 @@ ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I else { if(rdc) - RDCERR("Failed to load d3d12.dll"); + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Failed to load d3d12.dll"); else - RDCWARN("Failed to load d3d12.dll"); - return ReplayStatus::APIInitFailed; + RETURN_WARNING_RESULT(ResultCode::APIInitFailed, "Failed to load d3d12.dll"); } } } @@ -4270,14 +4269,12 @@ ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I HMODULE dxgilib = LoadLibraryA("dxgi.dll"); if(dxgilib == NULL) { - RDCERR("Failed to load dxgi.dll"); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Failed to load dxgi.dll"); } if(GetD3DCompiler() == NULL) { - RDCERR("Failed to load d3dcompiler_??.dll"); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Failed to load d3dcompiler_??.dll"); } D3D12InitParams initParams; @@ -4292,14 +4289,16 @@ ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture); if(sectionIdx < 0) - return ReplayStatus::InternalError; + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, "File does not contain captured API data"); ver = rdc->GetSectionProperties(sectionIdx).version; if(!D3D12InitParams::IsSupportedVersion(ver)) { - RDCERR("Incompatible D3D12 serialise version %llu", ver); - return ReplayStatus::APIIncompatibleVersion; + RETURN_ERROR_RESULT(ResultCode::APIIncompatibleVersion, + "D3D12 capture is incompatible version %llu, newest supported by this " + "build of RenderDoc is %llu", + ver, D3D12InitParams::CurrentVersion); } StreamReader *reader = rdc->ReadSection(sectionIdx); @@ -4312,16 +4311,15 @@ ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I if(chunk != SystemChunk::DriverInit) { - RDCERR("Expected to get a DriverInit chunk, instead got %u", chunk); - return ReplayStatus::FileCorrupted; + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, + "Expected to get a DriverInit chunk, instead got %u", chunk); } SERIALISE_ELEMENT(initParams); if(ser.IsErrored()) { - RDCERR("Failed reading driver init params."); - return ReplayStatus::FileIOFailed; + return ser.GetError(); } if(initParams.AdapterDesc.DeviceId != 0) @@ -4390,7 +4388,8 @@ ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I HRESULT hr = createFunc(__uuidof(IDXGIFactory1), (void **)&factory); if(FAILED(hr)) - RDCERR("Couldn't create DXGI factory! HRESULT: %s", ToStr(hr).c_str()); + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Couldn't create DXGI factory! HRESULT: %s", + ToStr(hr).c_str()); IDXGIAdapter *adapter = NULL; @@ -4407,8 +4406,9 @@ ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I if(!nvapiDev) { - RDCERR("Capture requires nvapi to replay, but it's not available or can't be initialised"); - return ReplayStatus::APIHardwareUnsupported; + RETURN_ERROR_RESULT( + ResultCode::APIHardwareUnsupported, + "Capture requires nvapi to replay, but it's not available or can't be initialised"); } } else if(initParams.VendorExtensions == GPUVendor::AMD) @@ -4417,8 +4417,9 @@ ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I if(!agsDev) { - RDCERR("Capture requires ags to replay, but it's not available or can't be initialised"); - return ReplayStatus::APIHardwareUnsupported; + RETURN_ERROR_RESULT( + ResultCode::APIHardwareUnsupported, + "Capture requires ags to replay, but it's not available or can't be initialised"); } createDevice = [agsDev](IUnknown *pAdapter, D3D_FEATURE_LEVEL MinimumFeatureLevel, REFIID riid, @@ -4428,11 +4429,11 @@ ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I } else if(initParams.VendorExtensions != GPUVendor::Unknown) { - RDCERR( + RETURN_ERROR_RESULT( + ResultCode::APIInitFailed, "Capture requires vendor extensions by %s to replay, but no support for that is " "available.", ToStr(initParams.VendorExtensions).c_str()); - return ReplayStatus::APIInitFailed; } bool debugLayerEnabled = false; @@ -4480,11 +4481,10 @@ ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I if(FAILED(hr)) { - RDCERR("Couldn't create a d3d12 device."); - SAFE_DELETE(rgp); - return ReplayStatus::APIHardwareUnsupported; + RETURN_ERROR_RESULT(ResultCode::APIHardwareUnsupported, "Couldn't create a d3d12 device: %s", + ToStr(hr).c_str()); } if(nvapiDev) @@ -4492,14 +4492,14 @@ ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I BOOL ok = nvapiDev->SetReal(dev); if(!ok) { - RDCERR( - "This capture needs nvapi extensions to replay, but device selected for replay can't " - "support nvapi extensions"); SAFE_RELEASE(dev); SAFE_RELEASE(nvapiDev); SAFE_RELEASE(factory); SAFE_DELETE(rgp); - return ReplayStatus::APIHardwareUnsupported; + RETURN_ERROR_RESULT( + ResultCode::APIHardwareUnsupported, + "This capture needs nvapi extensions to replay, but device selected for replay can't " + "support nvapi extensions"); } } @@ -4507,14 +4507,14 @@ ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I { if(!agsDev->ExtensionsSupported()) { - RDCERR( - "This capture needs AGS extensions to replay, but device selected for replay can't " - "support AGS extensions"); SAFE_RELEASE(dev); SAFE_RELEASE(nvapiDev); SAFE_RELEASE(factory); SAFE_DELETE(rgp); - return ReplayStatus::APIHardwareUnsupported; + RETURN_ERROR_RESULT( + ResultCode::APIHardwareUnsupported, + "This capture needs AGS extensions to replay, but device selected for replay can't " + "support nvapi extensions"); } } @@ -4532,25 +4532,27 @@ ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, I replay->Initialise(factory); *driver = (IReplayDriver *)replay; - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } static DriverRegistration D3D12DriverRegistration(RDCDriver::D3D12, &D3D12_CreateReplayDevice); -void D3D12_ProcessStructured(RDCFile *rdc, SDFile &output) +RDResult D3D12_ProcessStructured(RDCFile *rdc, SDFile &output) { WrappedID3D12Device device(NULL, D3D12InitParams(), false); int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture); if(sectionIdx < 0) - return; + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, "File does not contain captured API data"); device.SetStructuredExport(rdc->GetSectionProperties(sectionIdx).version); - ReplayStatus status = device.ReadLogInitialisation(rdc, true); + RDResult result = device.ReadLogInitialisation(rdc, true); - if(status == ReplayStatus::Succeeded) + if(result == ResultCode::Succeeded) device.GetStructuredFile()->Swap(output); + + return result; } static StructuredProcessRegistration D3D12ProcessRegistration(RDCDriver::D3D12, diff --git a/renderdoc/driver/d3d12/d3d12_replay.h b/renderdoc/driver/d3d12/d3d12_replay.h index 8a405a83e..eed6e27aa 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.h +++ b/renderdoc/driver/d3d12/d3d12_replay.h @@ -100,7 +100,7 @@ public: void Initialise(IDXGIFactory1 *factory); void Shutdown(); - ReplayStatus FatalErrorCheck(); + RDResult FatalErrorCheck(); IReplayDriver *MakeDummyDriver(); void CreateResources(); @@ -139,7 +139,7 @@ public: void FreeTargetResource(ResourceId id); void FreeCustomShader(ResourceId id); - ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool readStructuredBuffers); + RDResult ReadLogInitialisation(RDCFile *rdc, bool readStructuredBuffers); void ReplayLog(uint32_t endEventID, ReplayLogType replayType); SDFile *GetStructuredFile(); diff --git a/renderdoc/driver/d3d8/d3d8_replay.cpp b/renderdoc/driver/d3d8/d3d8_replay.cpp index 897f90e8a..d7659059f 100644 --- a/renderdoc/driver/d3d8/d3d8_replay.cpp +++ b/renderdoc/driver/d3d8/d3d8_replay.cpp @@ -29,10 +29,10 @@ // button. #if 0 -ReplayStatus D3D8_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) +ResultDetails D3D8_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) { RDCERR("D3D8 captures are not currently supported"); - return ReplayStatus::APIUnsupported; + return ResultCode::APIUnsupported; } static DriverRegistration D3D8DriverRegistration(RDCDriver::D3D8, &D3D8_CreateReplayDevice); diff --git a/renderdoc/driver/d3d9/d3d9_replay.cpp b/renderdoc/driver/d3d9/d3d9_replay.cpp index 292c9b8cc..b4ae30426 100644 --- a/renderdoc/driver/d3d9/d3d9_replay.cpp +++ b/renderdoc/driver/d3d9/d3d9_replay.cpp @@ -29,10 +29,10 @@ // button. #if 0 -ReplayStatus D3D9_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) +ResultDetails D3D9_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) { RDCERR("D3D9 captures are not currently supported"); - return ReplayStatus::APIUnsupported; + return ResultCode::APIUnsupported; } static DriverRegistration D3D9DriverRegistration(RDCDriver::D3D9, &D3D9_CreateReplayDevice); diff --git a/renderdoc/driver/gl/cgl_platform.cpp b/renderdoc/driver/gl/cgl_platform.cpp index 20f2362f8..51340cdc0 100644 --- a/renderdoc/driver/gl/cgl_platform.cpp +++ b/renderdoc/driver/gl/cgl_platform.cpp @@ -213,14 +213,17 @@ class CGLPlatform : public GLPlatform return ret; } - ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api, bool debug) + RDResult InitialiseAPI(GLWindowingData &replayContext, RDCDriver api, bool debug) { RDCASSERT(api == RDCDriver::OpenGL); NSGL_init(); replayContext.nsgl_ctx = NSGL_createContext(NULL, NULL); - return ReplayStatus::Succeeded; + if(replayContext.nsgl_ctx != NULL) + return ResultCode::Succeeded; + + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Failed to create replay OpenGL context"); } void DrawQuads(float width, float height, const rdcarray &vertices) diff --git a/renderdoc/driver/gl/egl_platform.cpp b/renderdoc/driver/gl/egl_platform.cpp index b05735b3d..af69b01c4 100644 --- a/renderdoc/driver/gl/egl_platform.cpp +++ b/renderdoc/driver/gl/egl_platform.cpp @@ -371,7 +371,7 @@ class EGLPlatform : public GLPlatform } bool PopulateForReplay() { return EGL.PopulateForReplay(); } - ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api, bool debug) + RDResult InitialiseAPI(GLWindowingData &replayContext, RDCDriver api, bool debug) { Display *xlibDisplay = RenderDoc::Inst().GetGlobalEnvironment().xlibDisplay; wl_display *waylandDisplay = RenderDoc::Inst().GetGlobalEnvironment().waylandDisplay; @@ -395,8 +395,7 @@ class EGLPlatform : public GLPlatform EGLDisplay eglDisplay = EGL.GetDisplay(display); if(!eglDisplay) { - RDCERR("Couldn't open default EGL display"); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Couldn't open default EGL display"); } int major, minor; @@ -406,13 +405,13 @@ class EGLPlatform : public GLPlatform if(!replayContext.ctx) { - RDCERR("Couldn't create OpenGL ES 3.x replay context - required for replay"); DeleteReplayContext(replayContext); RDCEraseEl(replayContext); - return ReplayStatus::APIHardwareUnsupported; + RETURN_ERROR_RESULT(ResultCode::APIHardwareUnsupported, + "Couldn't create OpenGL ES 3.x replay context - required for replay"); } - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } void *GetReplayFunction(const char *funcname) diff --git a/renderdoc/driver/gl/gl_common.cpp b/renderdoc/driver/gl/gl_common.cpp index a52e6f63a..0cd1ac66a 100644 --- a/renderdoc/driver/gl/gl_common.cpp +++ b/renderdoc/driver/gl/gl_common.cpp @@ -49,13 +49,12 @@ bool CheckConstParam(bool t) return t; } -bool CheckReplayContext() +RDResult CheckReplayContext() { -#define REQUIRE_FUNC(func) \ - if(!GL.func) \ - { \ - RDCERR("Missing core function " STRINGIZE(func)); \ - return false; \ +#define REQUIRE_FUNC(func) \ + if(!GL.func) \ + { \ + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Missing core function " STRINGIZE(func)); \ } REQUIRE_FUNC(glGetString); @@ -88,10 +87,10 @@ bool CheckReplayContext() if(!extensionString.empty()) RDCLOG("%s", extensionString.c_str()); - return true; + return ResultCode::Succeeded; } -bool ValidateFunctionPointers() +RDResult ValidateFunctionPointers() { PFNGLGETSTRINGPROC *ptrs = (PFNGLGETSTRINGPROC *)&GL; size_t num = sizeof(GL) / sizeof(PFNGLGETSTRINGPROC); @@ -114,16 +113,16 @@ bool ValidateFunctionPointers() // process. // Other functions that are only called to deserialise are checked for presence separately - bool ret = true; + RDResult result; #define CHECK_PRESENT(func) \ if(!GL.func) \ { \ - RDCERR( \ + SET_ERROR_RESULT( \ + result, ResultCode::APIHardwareUnsupported, \ "Missing function %s, required for replay. RenderDoc requires a 3.2 context, " \ "and a handful of extensions, see the Documentation.", \ STRINGIZE(func)); \ - ret = false; \ } // these functions should all be present as part of a 3.2 context plus the extensions we require, @@ -332,7 +331,7 @@ bool ValidateFunctionPointers() // only called when such a call is serialised from the logfile, and // so they are checked for validity separately. - return ret; + return result; } static void CheckExtFromString(const char *ext) diff --git a/renderdoc/driver/gl/gl_common.h b/renderdoc/driver/gl/gl_common.h index 8e7c9f577..bd9f5fd66 100644 --- a/renderdoc/driver/gl/gl_common.h +++ b/renderdoc/driver/gl/gl_common.h @@ -324,7 +324,7 @@ struct GLPlatform virtual bool CanCreateGLContext() = 0; virtual bool CanCreateGLESContext() = 0; virtual bool PopulateForReplay() = 0; - virtual ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api, bool debug) = 0; + virtual RDResult InitialiseAPI(GLWindowingData &replayContext, RDCDriver api, bool debug) = 0; virtual void *GetReplayFunction(const char *funcname) = 0; }; @@ -349,9 +349,9 @@ class GLDummyPlatform : public GLPlatform virtual bool CanCreateGLContext() { return true; } virtual bool CanCreateGLESContext() { return true; } virtual bool PopulateForReplay() { return true; } - virtual ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api, bool debug) + virtual RDResult InitialiseAPI(GLWindowingData &replayContext, RDCDriver api, bool debug) { - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } }; @@ -464,12 +464,12 @@ T CheckConstParam(T t); // on GL we don't have an easy way of checking which functions/extensions were used or which // functions/extensions on replay could suffice. So we check at the last minute on replay and bail // out if it's not present -#define CheckReplayFunctionPresent(func) \ - if(GL.func == NULL) \ - { \ - RDCERR("Function " #func " not available on replay."); \ - m_FailedReplayStatus = ReplayStatus::APIHardwareUnsupported; \ - return false; \ +#define CheckReplayFunctionPresent(func) \ + if(GL.func == NULL) \ + { \ + RDCERR("Function " #func " not available on replay."); \ + m_FailedReplayResult = ResultCode::APIHardwareUnsupported; \ + return false; \ } // no longer in glcorearb.h or glext.h @@ -888,8 +888,8 @@ GLuint CreateShaderProgram(GLuint vs, GLuint fs, GLuint gs = 0); GLuint CreateCShaderProgram(const rdcstr &cs); // verify that we got a replay context that we can work with -bool CheckReplayContext(); -bool ValidateFunctionPointers(); +RDResult CheckReplayContext(); +RDResult ValidateFunctionPointers(); #include "core/core.h" #include "serialise/serialiser.h" diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index 91459eef0..4ca0d662b 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -3319,12 +3319,12 @@ void WrappedOpenGL::AddResourceInitChunk(GLResource res) } } -ReplayStatus WrappedOpenGL::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) +RDResult WrappedOpenGL::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture); if(sectionIdx < 0) - return ReplayStatus::FileCorrupted; + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, "File does not contain captured API data"); StreamReader *reader = rdc->ReadSection(sectionIdx); @@ -3342,8 +3342,9 @@ ReplayStatus WrappedOpenGL::ReadLogInitialisation(RDCFile *rdc, bool storeStruct if(reader->IsErrored()) { + RDResult result = reader->GetError(); delete reader; - return ReplayStatus::FileIOFailed; + return result; } ReadSerialiser ser(reader, Ownership::Stream); @@ -3386,19 +3387,19 @@ ReplayStatus WrappedOpenGL::ReadLogInitialisation(RDCFile *rdc, bool storeStruct chunkIdx++; if(reader->IsErrored()) - return ReplayStatus::APIDataCorrupted; + return RDResult(ResultCode::APIDataCorrupted, ser.GetError().message); bool success = ProcessChunk(ser, context); ser.EndChunk(); if(reader->IsErrored()) - return ReplayStatus::APIDataCorrupted; + return RDResult(ResultCode::APIDataCorrupted, ser.GetError().message); // if there wasn't a serialisation error, but the chunk didn't succeed, then it's an API replay // failure. if(!success) - return m_FailedReplayStatus; + return m_FailedReplayResult; uint64_t offsetEnd = reader->GetOffset(); @@ -3425,9 +3426,9 @@ ReplayStatus WrappedOpenGL::ReadLogInitialisation(RDCFile *rdc, bool storeStruct // contents savedDebugMessages.swap(m_DebugMessages); - ReplayStatus status = ContextReplayLog(m_State, 0, 0, false); + RDResult status = ContextReplayLog(m_State, 0, 0, false); - if(status != ReplayStatus::Succeeded) + if(status != ResultCode::Succeeded) return status; } @@ -3480,7 +3481,7 @@ ReplayStatus WrappedOpenGL::ReadLogInitialisation(RDCFile *rdc, bool storeStruct RDCDEBUG("Allocating %llu persistant bytes of memory for the log.", GetReplay()->WriteFrameRecord().frameInfo.persistentSize); - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } bool WrappedOpenGL::ProcessChunk(ReadSerialiser &ser, GLChunk chunk) @@ -5103,8 +5104,8 @@ bool WrappedOpenGL::ProcessChunk(ReadSerialiser &ser, GLChunk chunk) return false; } -ReplayStatus WrappedOpenGL::ContextReplayLog(CaptureState readType, uint32_t startEventID, - uint32_t endEventID, bool partial) +RDResult WrappedOpenGL::ContextReplayLog(CaptureState readType, uint32_t startEventID, + uint32_t endEventID, bool partial) { m_FrameReader->SetOffset(0); @@ -5205,7 +5206,7 @@ ReplayStatus WrappedOpenGL::ContextReplayLog(CaptureState readType, uint32_t sta GLChunk chunktype = ser.ReadChunk(); if(ser.GetReader()->IsErrored()) - return ReplayStatus::APIDataCorrupted; + return RDResult(ResultCode::APIDataCorrupted, ser.GetError().message); m_ChunkMetadata = ser.ChunkMetadata(); @@ -5214,12 +5215,12 @@ ReplayStatus WrappedOpenGL::ContextReplayLog(CaptureState readType, uint32_t sta ser.EndChunk(); if(ser.GetReader()->IsErrored()) - return ReplayStatus::APIDataCorrupted; + return RDResult(ResultCode::APIDataCorrupted, ser.GetError().message); // if there wasn't a serialisation error, but the chunk didn't succeed, then it's an API replay // failure. if(!success) - return m_FailedReplayStatus; + return m_FailedReplayResult; RenderDoc::Inst().SetProgress( LoadProgress::FrameEventsRead, @@ -5291,7 +5292,7 @@ ReplayStatus WrappedOpenGL::ContextReplayLog(CaptureState readType, uint32_t sta } } - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } bool WrappedOpenGL::ContextProcessChunk(ReadSerialiser &ser, GLChunk chunk) @@ -5733,7 +5734,7 @@ void WrappedOpenGL::ReplayLog(uint32_t startEventID, uint32_t endEventID, Replay m_ReplayEventCount = 0; - ReplayStatus status = ReplayStatus::Succeeded; + RDResult status = ResultCode::Succeeded; if(replayType == eReplay_Full) status = ContextReplayLog(m_State, startEventID, endEventID, partial); @@ -5744,7 +5745,7 @@ void WrappedOpenGL::ReplayLog(uint32_t startEventID, uint32_t endEventID, Replay else RDCFATAL("Unexpected replay type"); - RDCASSERTEQUAL(status, ReplayStatus::Succeeded); + RDCASSERTEQUAL(status.code, ResultCode::Succeeded); // make sure to end any unbalanced replay events if we stopped in the middle of a frame for(int i = 0; m_ReplayMarkers && i < m_ReplayEventCount; i++) diff --git a/renderdoc/driver/gl/gl_driver.h b/renderdoc/driver/gl/gl_driver.h index ccd1db9f8..630263eb2 100644 --- a/renderdoc/driver/gl/gl_driver.h +++ b/renderdoc/driver/gl/gl_driver.h @@ -111,12 +111,12 @@ private: GLPlatform &m_Platform; - ReplayStatus m_FatalError = ReplayStatus::Succeeded; + RDResult m_FatalError = ResultCode::Succeeded; rdcarray m_DebugMessages; template void Serialise_DebugMessages(SerialiserType &ser); rdcarray GetDebugMessages(); - ReplayStatus FatalErrorCheck() { return m_FatalError; } + RDResult FatalErrorCheck() { return m_FatalError; } rdcstr m_DebugMsgContext; bool m_SuppressDebugMessages; @@ -296,7 +296,7 @@ private: uint32_t m_LastEventID; GLChunk m_LastChunk; - ReplayStatus m_FailedReplayStatus = ReplayStatus::APIReplayFailed; + RDResult m_FailedReplayResult = ResultCode::APIReplayFailed; ActionDescription m_ParentAction; @@ -338,8 +338,8 @@ private: uint32_t m_InitChunkIndex = 0; bool ProcessChunk(ReadSerialiser &ser, GLChunk chunk); - ReplayStatus ContextReplayLog(CaptureState readType, uint32_t startEventID, uint32_t endEventID, - bool partial); + RDResult ContextReplayLog(CaptureState readType, uint32_t startEventID, uint32_t endEventID, + bool partial); bool ContextProcessChunk(ReadSerialiser &ser, GLChunk chunk); void AddUsage(const ActionDescription &a); void AddAction(const ActionDescription &a); @@ -665,7 +665,7 @@ public: // replay interface void Initialise(GLInitParams ¶ms, uint64_t sectionVersion, const ReplayOptions &opts); void ReplayLog(uint32_t startEventID, uint32_t endEventID, ReplayLogType replayType); - ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); + RDResult ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); GLuint GetFakeVAO0() { return m_Global_VAO0; } GLuint GetCurrentDefaultFBO() { return m_CurrentDefaultFBO; } diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index 082daaab0..7a9731edd 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -87,7 +87,7 @@ void GLReplay::Shutdown() delete m_pDriver; } -ReplayStatus GLReplay::FatalErrorCheck() +RDResult GLReplay::FatalErrorCheck() { return m_pDriver->FatalErrorCheck(); } @@ -107,7 +107,7 @@ IReplayDriver *GLReplay::MakeDummyDriver() return dummy; } -ReplayStatus GLReplay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) +RDResult GLReplay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { MakeCurrentReplayContext(&m_ReplayCtx); return m_pDriver->ReadLogInitialisation(rdc, storeStructuredBuffers); @@ -3795,8 +3795,8 @@ void GLReplay::CloseReplayContext() m_pDriver->UnregisterReplayContext(m_ReplayCtx); } -ReplayStatus CreateReplayDevice(RDCDriver rdcdriver, RDCFile *rdc, const ReplayOptions &opts, - GLPlatform &platform, IReplayDriver **&driver) +RDResult CreateReplayDevice(RDCDriver rdcdriver, RDCFile *rdc, const ReplayOptions &opts, + GLPlatform &platform, IReplayDriver **&driver) { GLInitParams initParams; uint64_t ver = GLInitParams::CurrentVersion; @@ -3808,14 +3808,16 @@ ReplayStatus CreateReplayDevice(RDCDriver rdcdriver, RDCFile *rdc, const ReplayO int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture); if(sectionIdx < 0) - return ReplayStatus::InternalError; + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, "File does not contain captured API data"); ver = rdc->GetSectionProperties(sectionIdx).version; if(!GLInitParams::IsSupportedVersion(ver)) { - RDCERR("Incompatible OpenGL serialise version %llu", ver); - return ReplayStatus::APIIncompatibleVersion; + RETURN_ERROR_RESULT( + ResultCode::APIIncompatibleVersion, + "Incompatible OpenGL serialise version %llu, newest version supported is %llu", ver, + GLInitParams::CurrentVersion); } StreamReader *reader = rdc->ReadSection(sectionIdx); @@ -3828,17 +3830,14 @@ ReplayStatus CreateReplayDevice(RDCDriver rdcdriver, RDCFile *rdc, const ReplayO if(chunk != SystemChunk::DriverInit) { - RDCERR("Expected to get a DriverInit chunk, instead got %u", chunk); - return ReplayStatus::FileCorrupted; + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, + "Expected to get a DriverInit chunk, instead got %u", chunk); } SERIALISE_ELEMENT(initParams); if(ser.IsErrored()) - { - RDCERR("Failed reading driver init params."); - return ReplayStatus::FileIOFailed; - } + return ser.GetError(); if(!initParams.renderer.empty()) RDCLOG("Capture was created on %s / %s", initParams.renderer.c_str(), @@ -3847,18 +3846,17 @@ ReplayStatus CreateReplayDevice(RDCDriver rdcdriver, RDCFile *rdc, const ReplayO GLWindowingData data = {}; - ReplayStatus status = platform.InitialiseAPI(data, rdcdriver, opts.apiValidation); + RDResult status = platform.InitialiseAPI(data, rdcdriver, opts.apiValidation); // any errors will be already printed, just pass the error up - if(status != ReplayStatus::Succeeded) + if(status != ResultCode::Succeeded) return status; bool current = platform.MakeContextCurrent(data); if(!current) { - RDCERR("Couldn't active the created GL ES context"); platform.DeleteReplayContext(data); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Couldn't activate the created replay context"); } // we use the platform's function which tries GL's GetProcAddress first, then falls back to @@ -3871,19 +3869,18 @@ ReplayStatus CreateReplayDevice(RDCDriver rdcdriver, RDCFile *rdc, const ReplayO GL.EmulateUnsupportedFunctions(); GL.EmulateRequiredExtensions(); - bool extensionsValidated = CheckReplayContext(); - - if(!extensionsValidated) + RDResult extensionsValidated = CheckReplayContext(); + if(extensionsValidated != ResultCode::Succeeded) { platform.DeleteReplayContext(data); - return ReplayStatus::APIInitFailed; + return extensionsValidated; } - bool functionsValidated = ValidateFunctionPointers(); - if(!functionsValidated) + RDResult functionsValidated = ValidateFunctionPointers(); + if(functionsValidated != ResultCode::Succeeded) { platform.DeleteReplayContext(data); - return ReplayStatus::APIHardwareUnsupported; + return functionsValidated; } WrappedOpenGL *gldriver = new WrappedOpenGL(platform); @@ -3901,16 +3898,16 @@ ReplayStatus CreateReplayDevice(RDCDriver rdcdriver, RDCFile *rdc, const ReplayO { delete gldriver; platform.DeleteReplayContext(data); - return ReplayStatus::APIHardwareUnsupported; + RETURN_ERROR_RESULT(ResultCode::APIHardwareUnsupported, "Failed to create analysis context"); } gldriver->Initialise(initParams, ver, opts); *driver = (IReplayDriver *)replay; - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } -void GL_ProcessStructured(RDCFile *rdc, SDFile &output) +RDResult GL_ProcessStructured(RDCFile *rdc, SDFile &output) { GLDummyPlatform dummy; WrappedOpenGL device(dummy); @@ -3918,13 +3915,15 @@ void GL_ProcessStructured(RDCFile *rdc, SDFile &output) int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture); if(sectionIdx < 0) - return; + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, "File does not contain captured API data"); device.SetStructuredExport(rdc->GetSectionProperties(sectionIdx).version); - ReplayStatus status = device.ReadLogInitialisation(rdc, true); + RDResult status = device.ReadLogInitialisation(rdc, true); - if(status == ReplayStatus::Succeeded) + if(status == ResultCode::Succeeded) device.GetStructuredFile()->Swap(output); + + return status; } static StructuredProcessRegistration GLProcessRegistration(RDCDriver::OpenGL, &GL_ProcessStructured); @@ -3951,7 +3950,7 @@ rdcarray GetReplayVersions(RDCDriver api) #if defined(RENDERDOC_SUPPORT_GLES) -ReplayStatus GLES_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver) +RDResult GLES_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver) { RDCLOG("Creating an OpenGL ES replay device"); @@ -3963,8 +3962,8 @@ ReplayStatus GLES_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IR if(!load_ok) { - RDCERR("Couldn't find required EGL function addresses"); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, + "Couldn't find required EGL function addresses"); } RDCLOG("Initialising GLES replay via libEGL"); @@ -3981,23 +3980,21 @@ ReplayStatus GLES_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IR if(!load_ok) { - RDCERR("Couldn't find required GLX function addresses"); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, + "Couldn't find required GL function addresses"); } return CreateReplayDevice(rdc ? rdc->GetDriver() : RDCDriver::OpenGLES, rdc, opts, GetGLPlatform(), driver); } - RDCERR( - "libEGL not available, and GL cannot initialise or doesn't support " - "EXT_create_context_es2_profile"); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, + "libEGL not available, and GL cannot initialise or doesn't support " + "EXT_create_context_es2_profile"); #else // no GL support, no fallback apart from EGL - RDCERR("libEGL is not available"); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "libEGL is not available"); #endif } @@ -4007,7 +4004,7 @@ static DriverRegistration GLESDriverRegistration(RDCDriver::OpenGLES, &GLES_Crea #if defined(RENDERDOC_SUPPORT_GL) -ReplayStatus GL_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver) +RDResult GL_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver) { GLPlatform *gl_platform = &GetGLPlatform(); @@ -4017,8 +4014,8 @@ ReplayStatus GL_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IRep RDCLOG("Forcing EGL device creation for wayland"); gl_platform = &GetEGLPlatform(); #else - RDCERR("EGL support must be enabled at build time when using Wayland"); - return ReplayStatus::InternalError; + RETURN_ERROR_RESULT(ResultCode::InternalError, + "EGL support must be enabled at build time when using Wayland"); #endif } @@ -4035,8 +4032,8 @@ ReplayStatus GL_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IRep if(!can_create_gl_context) { - RDCERR("Platform doesn't support GL contexts"); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, + "Current platform doesn't support OpenGL contexts"); } RDCDEBUG("Creating an OpenGL replay device"); @@ -4045,9 +4042,9 @@ ReplayStatus GL_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IRep if(!load_ok) { - RDCERR("Couldn't find required platform %s function addresses", - gl_platform == &GetGLPlatform() ? "GL" : "EGL"); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, + "Couldn't find required platform %s function addresses", + gl_platform == &GetGLPlatform() ? "GL" : "EGL"); } return CreateReplayDevice(rdc ? rdc->GetDriver() : RDCDriver::OpenGL, rdc, opts, *gl_platform, diff --git a/renderdoc/driver/gl/gl_replay.h b/renderdoc/driver/gl/gl_replay.h index 689c8b6dd..50fdcbea2 100644 --- a/renderdoc/driver/gl/gl_replay.h +++ b/renderdoc/driver/gl/gl_replay.h @@ -115,7 +115,7 @@ public: bool IsRemoteProxy() { return m_Proxy; } void Shutdown(); - ReplayStatus FatalErrorCheck(); + RDResult FatalErrorCheck(); IReplayDriver *MakeDummyDriver(); DriverInformation GetDriverInfo() { return m_DriverInfo; } @@ -151,7 +151,7 @@ public: void SavePipelineState(uint32_t eventId); void FreeTargetResource(ResourceId id); - ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); + RDResult ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); void ReplayLog(uint32_t endEventID, ReplayLogType replayType); SDFile *GetStructuredFile(); diff --git a/renderdoc/driver/gl/glx_platform.cpp b/renderdoc/driver/gl/glx_platform.cpp index 20917eba8..dc2fbb51d 100644 --- a/renderdoc/driver/gl/glx_platform.cpp +++ b/renderdoc/driver/gl/glx_platform.cpp @@ -333,7 +333,7 @@ class GLXPlatform : public GLPlatform } bool PopulateForReplay() { return GLX.PopulateForReplay(); } - ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api, bool debug) + RDResult InitialiseAPI(GLWindowingData &replayContext, RDCDriver api, bool debug) { // force debug in development builds #if ENABLED(RDOC_DEVEL) @@ -361,8 +361,7 @@ class GLXPlatform : public GLPlatform if(dpy == NULL) { - RDCERR("Couldn't open default X display"); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Couldn't open default X display"); } // don't need to care about the fb config as we won't be using the default framebuffer @@ -373,8 +372,7 @@ class GLXPlatform : public GLPlatform if(fbcfg == NULL) { - RDCERR("Couldn't choose default framebuffer config"); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Couldn't choose default framebuffer config"); } GLXContext ctx = NULL; @@ -402,8 +400,9 @@ class GLXPlatform : public GLPlatform if(ctx == NULL || X11ErrorSeen) { XFree(fbcfg); - RDCERR("Couldn't create 3.2 context - RenderDoc requires OpenGL 3.2 availability"); - return ReplayStatus::APIHardwareUnsupported; + RETURN_ERROR_RESULT( + ResultCode::APIHardwareUnsupported, + "Couldn't create 3.2 context - RenderDoc requires OpenGL 3.2 availability"); } GLCoreVersion = major * 10 + minor; @@ -421,8 +420,8 @@ class GLXPlatform : public GLPlatform { GLX.glXDestroyPbuffer(dpy, pbuffer); GLX.glXDestroyContext(dpy, ctx); - RDCERR("Couldn't make pbuffer & context current"); - return ReplayStatus::APIInitFailed; + return ResultCode::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Couldn't make pbuffer & context current"); } PFNGLGETSTRINGPROC GetString = (PFNGLGETSTRINGPROC)GetReplayFunction("glGetString"); @@ -448,7 +447,7 @@ class GLXPlatform : public GLPlatform pbuffers.insert(pbuffer); - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } void DrawQuads(float width, float height, const rdcarray &vertices) diff --git a/renderdoc/driver/gl/wgl_platform.cpp b/renderdoc/driver/gl/wgl_platform.cpp index f266a429b..c4bd7f584 100644 --- a/renderdoc/driver/gl/wgl_platform.cpp +++ b/renderdoc/driver/gl/wgl_platform.cpp @@ -365,7 +365,7 @@ class WGLPlatform : public GLPlatform } bool PopulateForReplay() { return WGL.PopulateForReplay(); } - ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api, bool debug) + RDResult InitialiseAPI(GLWindowingData &replayContext, RDCDriver api, bool debug) { // force debug in development builds #if ENABLED(RDOC_DEVEL) @@ -377,7 +377,7 @@ class WGLPlatform : public GLPlatform bool success = RegisterClass(); if(!success) - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Couldn't register window class"); HWND w = NULL; HDC dc = NULL; @@ -386,7 +386,8 @@ class WGLPlatform : public GLPlatform success = CreateTrampolineContext(w, dc, rc); if(!success) - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, + "Couldn't create unextended trampoline context"); if(!WGL.wglCreateContextAttribsARB || !WGL.wglGetPixelFormatAttribivARB) { @@ -394,8 +395,8 @@ class WGLPlatform : public GLPlatform WGL.wglDeleteContext(rc); ReleaseDC(w, dc); DestroyWindow(w); - RDCERR("RenderDoc requires WGL_ARB_create_context and WGL_ARB_pixel_format"); - return ReplayStatus::APIHardwareUnsupported; + RETURN_ERROR_RESULT(ResultCode::APIHardwareUnsupported, + "RenderDoc requires WGL_ARB_create_context and WGL_ARB_pixel_format"); } WGL.wglMakeCurrent(NULL, NULL); @@ -424,19 +425,17 @@ class WGLPlatform : public GLPlatform int pf = ChoosePixelFormat(dc, &pfd); if(pf == 0) { - RDCERR("Couldn't choose pixel format"); ReleaseDC(w, dc); DestroyWindow(w); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Couldn't choose pixel format"); } BOOL res = SetPixelFormat(dc, pf, &pfd); if(res == FALSE) { - RDCERR("Couldn't set pixel format"); ReleaseDC(w, dc); DestroyWindow(w); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Couldn't set pixel format"); } int attribs[64] = {0}; @@ -470,10 +469,11 @@ class WGLPlatform : public GLPlatform if(rc == NULL) { - RDCERR("Couldn't create at least 3.2 context - RenderDoc requires OpenGL 3.2 availability"); ReleaseDC(w, dc); DestroyWindow(w); - return ReplayStatus::APIHardwareUnsupported; + RETURN_ERROR_RESULT( + ResultCode::APIHardwareUnsupported, + "Couldn't create at least 3.2 context - RenderDoc requires OpenGL 3.2 availability"); } GLCoreVersion = major * 10 + minor; @@ -481,19 +481,18 @@ class WGLPlatform : public GLPlatform res = WGL.wglMakeCurrent(dc, rc); if(res == FALSE) { - RDCERR("Couldn't make 3.2 RC current"); WGL.wglMakeCurrent(NULL, NULL); WGL.wglDeleteContext(rc); ReleaseDC(w, dc); DestroyWindow(w); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Couldn't make modern OpenGL context current"); } replayContext.DC = dc; replayContext.ctx = rc; replayContext.wnd = w; - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } bool CreateTrampolineContext(HWND &w, HDC &dc, HGLRC &rc) diff --git a/renderdoc/driver/gl/wrappers/gl_emulated.cpp b/renderdoc/driver/gl/wrappers/gl_emulated.cpp index 818b3c9dc..21817b830 100644 --- a/renderdoc/driver/gl/wrappers/gl_emulated.cpp +++ b/renderdoc/driver/gl/wrappers/gl_emulated.cpp @@ -4135,7 +4135,7 @@ void MakeOfflineShaderReflection(ShaderStage stage, const rdcstr &source, const void MakeOnlineShaderReflection(ShaderStage stage, const rdcstr &source, const rdcstr &entryPoint, ShaderReflection &refl, ShaderBindpointMapping &mapping) { - ReplayStatus status = ReplayStatus::UnknownError; + RDResult status = ResultCode::APIUnsupported; IReplayDriver *driver = NULL; RDCASSERT(entryPoint == "main"); @@ -4145,7 +4145,7 @@ void MakeOnlineShaderReflection(ShaderStage stage, const rdcstr &source, const r if(replays.find(RDCDriver::OpenGL) != replays.end()) status = RenderDoc::Inst().CreateProxyReplayDriver(RDCDriver::OpenGL, &driver); - if(status != ReplayStatus::Succeeded) + if(status != ResultCode::Succeeded) { RDCERR("No GL support locally, couldn't create proxy GL driver for reflection"); return; diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 209ebcd01..718cde0b9 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -284,7 +284,8 @@ VkCommandBuffer WrappedVulkan::GetNextCmd() else { ret = VK_NULL_HANDLE; - m_FailedReplayStatus = ReplayStatus::APIInitFailed; + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIInitFailed, + "Failed to create command buffer: %s", ToStr(vkr).c_str()); } } @@ -2506,14 +2507,14 @@ void WrappedVulkan::AddResourceCurChunk(ResourceId id) AddResourceCurChunk(GetReplay()->GetResourceDesc(id)); } -ReplayStatus WrappedVulkan::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) +RDResult WrappedVulkan::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture); GetResourceManager()->SetState(m_State); if(sectionIdx < 0) - return ReplayStatus::FileCorrupted; + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, "File does not contain captured API data"); StreamReader *reader = rdc->ReadSection(sectionIdx); @@ -2531,8 +2532,9 @@ ReplayStatus WrappedVulkan::ReadLogInitialisation(RDCFile *rdc, bool storeStruct if(reader->IsErrored()) { + RDResult result = reader->GetError(); delete reader; - return ReplayStatus::FileIOFailed; + return result; } ReadSerialiser ser(reader, Ownership::Stream); @@ -2581,9 +2583,13 @@ ReplayStatus WrappedVulkan::ReadLogInitialisation(RDCFile *rdc, bool storeStruct if(reader->IsErrored()) { SAFE_DELETE(sink); - return ReplayStatus::APIDataCorrupted; + return RDResult(ResultCode::APIDataCorrupted, ser.GetError().message); } + size_t firstMessage = 0; + if(sink) + firstMessage = sink->msgs.size(); + bool success = ProcessChunk(ser, context); ser.EndChunk(); @@ -2591,15 +2597,34 @@ ReplayStatus WrappedVulkan::ReadLogInitialisation(RDCFile *rdc, bool storeStruct if(reader->IsErrored()) { SAFE_DELETE(sink); - return ReplayStatus::APIDataCorrupted; + return RDResult(ResultCode::APIDataCorrupted, ser.GetError().message); } // if there wasn't a serialisation error, but the chunk didn't succeed, then it's an API replay // failure. if(!success) { + rdcstr extra; + + if(sink) + { + extra += "\n"; + + for(size_t i = firstMessage; i < sink->msgs.size(); i++) + { + extra += "\n"; + extra += sink->msgs[i].description; + } + } + else + { + extra += + "\n\nMore debugging information may be available by enabling API validation on replay"; + } + SAFE_DELETE(sink); - return m_FailedReplayStatus; + m_FailedReplayResult.message = rdcstr(m_FailedReplayResult.message) + extra; + return m_FailedReplayResult; } uint64_t offsetEnd = reader->GetOffset(); @@ -2641,9 +2666,9 @@ ReplayStatus WrappedVulkan::ReadLogInitialisation(RDCFile *rdc, bool storeStruct for(auto it = m_CreationInfo.m_Memory.begin(); it != m_CreationInfo.m_Memory.end(); ++it) it->second.SimplifyBindings(); - ReplayStatus status = ContextReplayLog(m_State, 0, 0, false); + RDResult status = ContextReplayLog(m_State, 0, 0, false); - if(status != ReplayStatus::Succeeded) + if(status != ResultCode::Succeeded) { SAFE_DELETE(sink); return status; @@ -2711,11 +2736,11 @@ ReplayStatus WrappedVulkan::ReadLogInitialisation(RDCFile *rdc, bool storeStruct FreeAllMemory(MemoryScope::IndirectReadback); - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } -ReplayStatus WrappedVulkan::ContextReplayLog(CaptureState readType, uint32_t startEventID, - uint32_t endEventID, bool partial) +RDResult WrappedVulkan::ContextReplayLog(CaptureState readType, uint32_t startEventID, + uint32_t endEventID, bool partial) { m_FrameReader->SetOffset(0); @@ -2828,7 +2853,7 @@ ReplayStatus WrappedVulkan::ContextReplayLog(CaptureState readType, uint32_t sta VulkanChunk chunktype = ser.ReadChunk(); if(ser.GetReader()->IsErrored()) - return ReplayStatus::APIDataCorrupted; + return RDResult(ResultCode::APIDataCorrupted, ser.GetError().message); m_ChunkMetadata = ser.ChunkMetadata(); @@ -2839,12 +2864,35 @@ ReplayStatus WrappedVulkan::ContextReplayLog(CaptureState readType, uint32_t sta ser.EndChunk(); if(ser.GetReader()->IsErrored()) - return ReplayStatus::APIDataCorrupted; + return RDResult(ResultCode::APIDataCorrupted, ser.GetError().message); // if there wasn't a serialisation error, but the chunk didn't succeed, then it's an API replay // failure. if(!success) - return m_FailedReplayStatus; + { + rdcstr extra; + + ScopedDebugMessageSink *sink = GetDebugMessageSink(); + + if(sink) + { + extra += "\n"; + + for(size_t i = 0; i < sink->msgs.size(); i++) + { + extra += "\n"; + extra += sink->msgs[i].description; + } + } + else + { + extra += + "\n\nMore debugging information may be available by enabling API validation on replay"; + } + + m_FailedReplayResult.message = rdcstr(m_FailedReplayResult.message) + extra; + return m_FailedReplayResult; + } RenderDoc::Inst().SetProgress( LoadProgress::FrameEventsRead, @@ -2939,7 +2987,7 @@ ReplayStatus WrappedVulkan::ContextReplayLog(CaptureState readType, uint32_t sta m_RerecordCmds.clear(); m_RerecordCmdList.clear(); - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } void WrappedVulkan::ApplyInitialContents() @@ -3803,7 +3851,7 @@ void WrappedVulkan::ReplayLog(uint32_t startEventID, uint32_t endEventID, Replay m_RenderState.dynamicRendering.flags = dynamicFlags; } - ReplayStatus status = ReplayStatus::Succeeded; + RDResult status = ResultCode::Succeeded; if(replayType == eReplay_Full) status = ContextReplayLog(m_State, startEventID, endEventID, partial); @@ -3814,7 +3862,7 @@ void WrappedVulkan::ReplayLog(uint32_t startEventID, uint32_t endEventID, Replay else RDCFATAL("Unexpected replay type"); - RDCASSERTEQUAL(status, ReplayStatus::Succeeded); + RDCASSERTEQUAL(status.code, ResultCode::Succeeded); if(m_OutsideCmdBuffer != VK_NULL_HANDLE) { @@ -4054,8 +4102,9 @@ void WrappedVulkan::CheckErrorVkResult(VkResult vkr) if(vkr == VK_ERROR_INITIALIZATION_FAILED || vkr == VK_ERROR_DEVICE_LOST || vkr == VK_ERROR_UNKNOWN) { - RDCLOG("Logging device lost fatal error for %s", ToStr(vkr).c_str()); - m_FailedReplayStatus = m_FatalError = ReplayStatus::ReplayDeviceLost; + SET_ERROR_RESULT(m_FatalError, ResultCode::ReplayDeviceLost, + "Logging device lost fatal error for %s", ToStr(vkr).c_str()); + m_FailedReplayResult = m_FatalError; } else if(vkr == VK_ERROR_OUT_OF_HOST_MEMORY || vkr == VK_ERROR_OUT_OF_DEVICE_MEMORY) { @@ -4065,8 +4114,9 @@ void WrappedVulkan::CheckErrorVkResult(VkResult vkr) } else { - RDCLOG("Logging out of memory fatal error for %s", ToStr(vkr).c_str()); - m_FailedReplayStatus = m_FatalError = ReplayStatus::ReplayOutOfMemory; + SET_ERROR_RESULT(m_FatalError, ResultCode::ReplayOutOfMemory, + "Logging out of memory fatal error for %s", ToStr(vkr).c_str()); + m_FailedReplayResult = m_FatalError; } } else diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index 5ab6501ac..83055940a 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -296,7 +296,7 @@ private: void AddDebugMessage(DebugMessage msg); int m_OOMHandler = 0; - ReplayStatus m_FatalError = ReplayStatus::Succeeded; + RDResult m_FatalError = ResultCode::Succeeded; CaptureState m_State; bool m_AppControlledCapture = false; @@ -947,7 +947,7 @@ private: std::set m_SparseBindResources; - ReplayStatus m_FailedReplayStatus = ReplayStatus::APIReplayFailed; + RDResult m_FailedReplayResult = ResultCode::APIReplayFailed; VulkanActionTreeNode m_ParentAction; @@ -979,8 +979,8 @@ private: } bool ProcessChunk(ReadSerialiser &ser, VulkanChunk chunk); - ReplayStatus ContextReplayLog(CaptureState readType, uint32_t startEventID, uint32_t endEventID, - bool partial); + RDResult ContextReplayLog(CaptureState readType, uint32_t startEventID, uint32_t endEventID, + bool partial); bool ContextProcessChunk(ReadSerialiser &ser, VulkanChunk chunk); void AddAction(const ActionDescription &a); void AddEvent(); @@ -1059,7 +1059,7 @@ public: void AddDebugMessage(MessageCategory c, MessageSeverity sv, MessageSource src, rdcstr d); - ReplayStatus Initialise(VkInitParams ¶ms, uint64_t sectionVersion, const ReplayOptions &opts); + RDResult Initialise(VkInitParams ¶ms, uint64_t sectionVersion, const ReplayOptions &opts); uint64_t GetLogVersion() { return m_SectionVersion; } void SetStructuredExport(uint64_t sectionVersion) { @@ -1069,7 +1069,7 @@ public: void Shutdown(); void ReplayLog(uint32_t startEventID, uint32_t endEventID, ReplayLogType replayType); void ReplayDraw(VkCommandBuffer cmd, const ActionDescription &action); - ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); + RDResult ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); SDFile *GetStructuredFile() { return m_StructuredFile; } SDFile *DetachStructuredFile() @@ -1146,8 +1146,8 @@ public: else m_OOMHandler--; } - ReplayStatus FatalErrorCheck() { return m_FatalError; } - bool HasFatalError() { return m_FatalError != ReplayStatus::Succeeded; } + RDResult FatalErrorCheck() { return m_FatalError; } + bool HasFatalError() { return m_FatalError != ResultCode::Succeeded; } inline void CheckVkResult(VkResult vkr) { if(vkr == VK_SUCCESS) diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index f8d9794b3..9f446c8c7 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -84,7 +84,7 @@ void VulkanReplay::Shutdown() delete m_pDriver; } -ReplayStatus VulkanReplay::FatalErrorCheck() +RDResult VulkanReplay::FatalErrorCheck() { return m_pDriver->FatalErrorCheck(); } @@ -219,7 +219,7 @@ APIProperties VulkanReplay::GetAPIProperties() return ret; } -ReplayStatus VulkanReplay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) +RDResult VulkanReplay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { return m_pDriver->ReadLogInitialisation(rdc, storeStructuredBuffers); } @@ -4528,7 +4528,7 @@ void VulkanReplay::SetProxyBufferData(ResourceId bufid, byte *data, size_t dataS VULKANNOTIMP("SetProxyTextureData"); } -ReplayStatus Vulkan_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver) +RDResult Vulkan_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver) { RDCDEBUG("Creating a VulkanReplay replay device"); @@ -4570,8 +4570,7 @@ ReplayStatus Vulkan_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, if(module == NULL) { - RDCERR("Failed to load vulkan library"); - return ReplayStatus::APIInitFailed; + RETURN_ERROR_RESULT(ResultCode::APIInitFailed, "Failed to load vulkan library"); } VkInitParams initParams; @@ -4585,14 +4584,16 @@ ReplayStatus Vulkan_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture); if(sectionIdx < 0) - return ReplayStatus::InternalError; + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, "File does not contain captured API data"); ver = rdc->GetSectionProperties(sectionIdx).version; if(!VkInitParams::IsSupportedVersion(ver)) { - RDCERR("Incompatible Vulkan serialise version %llu", ver); - return ReplayStatus::APIIncompatibleVersion; + RETURN_ERROR_RESULT(ResultCode::APIIncompatibleVersion, + "Vulkan capture is incompatible version %llu, newest supported by this " + "build of RenderDoc is %llu", + ver, VkInitParams::CurrentVersion); } StreamReader *reader = rdc->ReadSection(sectionIdx); @@ -4605,16 +4606,15 @@ ReplayStatus Vulkan_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, if(chunk != SystemChunk::DriverInit) { - RDCERR("Expected to get a DriverInit chunk, instead got %u", chunk); - return ReplayStatus::FileCorrupted; + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, + "Expected to get a DriverInit chunk, instead got %u", chunk); } SERIALISE_ELEMENT(initParams); if(ser.IsErrored()) { - RDCERR("Failed reading driver init params."); - return ReplayStatus::FileIOFailed; + return ser.GetError(); } } @@ -4637,9 +4637,9 @@ ReplayStatus Vulkan_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, VulkanReplay *replay = vk->GetReplay(); replay->SetProxy(isProxy); - ReplayStatus status = vk->Initialise(initParams, ver, opts); + RDResult status = vk->Initialise(initParams, ver, opts); - if(status != ReplayStatus::Succeeded) + if(status != ResultCode::Succeeded) { SAFE_DELETE(rgp); @@ -4654,7 +4654,7 @@ ReplayStatus Vulkan_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, replay->GetInitialDriverVersion(); - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } struct VulkanDriverRegistration @@ -4669,20 +4669,22 @@ struct VulkanDriverRegistration static VulkanDriverRegistration VkDriverRegistration; -void Vulkan_ProcessStructured(RDCFile *rdc, SDFile &output) +RDResult Vulkan_ProcessStructured(RDCFile *rdc, SDFile &output) { WrappedVulkan vulkan; int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture); if(sectionIdx < 0) - return; + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, "File does not contain captured API data"); vulkan.SetStructuredExport(rdc->GetSectionProperties(sectionIdx).version); - ReplayStatus status = vulkan.ReadLogInitialisation(rdc, true); + RDResult status = vulkan.ReadLogInitialisation(rdc, true); - if(status == ReplayStatus::Succeeded) + if(status == ResultCode::Succeeded) vulkan.GetStructuredFile()->Swap(output); + + return status; } static StructuredProcessRegistration VulkanProcessRegistration(RDCDriver::Vulkan, diff --git a/renderdoc/driver/vulkan/vk_replay.h b/renderdoc/driver/vulkan/vk_replay.h index 124229d2f..a7c423d8a 100644 --- a/renderdoc/driver/vulkan/vk_replay.h +++ b/renderdoc/driver/vulkan/vk_replay.h @@ -268,7 +268,7 @@ public: bool IsRemoteProxy() { return m_Proxy; } void Shutdown(); - ReplayStatus FatalErrorCheck(); + RDResult FatalErrorCheck(); IReplayDriver *MakeDummyDriver(); void CreateResources(); @@ -308,7 +308,7 @@ public: void SavePipelineState(uint32_t eventId); void FreeTargetResource(ResourceId id); - ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); + RDResult ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); void ReplayLog(uint32_t endEventID, ReplayLogType replayType); SDFile *GetStructuredFile(); diff --git a/renderdoc/driver/vulkan/vk_serialise.cpp b/renderdoc/driver/vulkan/vk_serialise.cpp index a95f8cd9d..a69a7b339 100644 --- a/renderdoc/driver/vulkan/vk_serialise.cpp +++ b/renderdoc/driver/vulkan/vk_serialise.cpp @@ -1601,13 +1601,15 @@ static void SerialiseNext(SerialiserType &ser, VkStructureType &sType, const voi // if we encounter an unsupported struct on read we *cannot* continue since we don't know the // members that got serialised after it -#define PNEXT_UNSUPPORTED(StructType) \ - case StructType: \ - { \ - RDCERR("No support for " #StructType " is available in this build"); \ - pNext = NULL; \ - ser.SetErrored(); \ - return; \ +#define PNEXT_UNSUPPORTED(StructType) \ + case StructType: \ + { \ + RDResult res; \ + SET_ERROR_RESULT(res, ResultCode::APIUnsupported, \ + "No support for " #StructType " is available in this build"); \ + pNext = NULL; \ + ser.SetError(res); \ + return; \ } // if we come across a struct we should process, then serialise a pointer to it. diff --git a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp index f4a741963..bea5c708b 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp @@ -710,7 +710,8 @@ bool WrappedVulkan::Serialise_vkCreateCommandPool(SerialiserType &ser, VkDevice if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating command pool, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -819,7 +820,8 @@ bool WrappedVulkan::Serialise_vkAllocateCommandBuffers(SerialiserType &ser, VkDe if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed allocating command buffer, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -1120,7 +1122,8 @@ bool WrappedVulkan::Serialise_vkBeginCommandBuffer(SerialiserType &ser, VkComman if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed beginning command buffer, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -1194,7 +1197,9 @@ bool WrappedVulkan::Serialise_vkBeginCommandBuffer(SerialiserType &ser, VkComman if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed allocating re-recording command buffer, VkResult: %s", + ToStr(ret).c_str()); return false; } else diff --git a/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp index f67b60ea3..72d613c1c 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp @@ -201,7 +201,8 @@ bool WrappedVulkan::Serialise_vkCreateDescriptorPool(SerialiserType &ser, VkDevi if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating descriptor pool, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -297,7 +298,8 @@ bool WrappedVulkan::Serialise_vkCreateDescriptorSetLayout( if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating descriptor layout, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -455,8 +457,10 @@ bool WrappedVulkan::Serialise_vkAllocateDescriptorSets(SerialiserType &ser, VkDe if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, even after trying overflow, VkResult: %s", - ToStr(ret).c_str()); + SET_ERROR_RESULT( + m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed allocating descriptor sets, even after trying to overflow pool, VkResult: %s", + ToStr(ret).c_str()); return false; } } @@ -1450,7 +1454,9 @@ bool WrappedVulkan::Serialise_vkCreateDescriptorUpdateTemplate( if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating descriptor update template, VkResult: %s", + ToStr(ret).c_str()); return false; } else diff --git a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp index 90c618a76..89afa5a18 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp @@ -202,8 +202,8 @@ static void StripUnwantedExtensions(rdcarray &Extensions) }); } -ReplayStatus WrappedVulkan::Initialise(VkInitParams ¶ms, uint64_t sectionVersion, - const ReplayOptions &opts) +RDResult WrappedVulkan::Initialise(VkInitParams ¶ms, uint64_t sectionVersion, + const ReplayOptions &opts) { m_InitParams = params; m_SectionVersion = sectionVersion; @@ -331,8 +331,9 @@ ReplayStatus WrappedVulkan::Initialise(VkInitParams ¶ms, uint64_t sectionVer { if(supportedExtensions.find(params.Extensions[i]) == supportedExtensions.end()) { - RDCERR("Capture requires extension '%s' which is not supported", params.Extensions[i].c_str()); - return ReplayStatus::APIHardwareUnsupported; + RETURN_ERROR_RESULT(ResultCode::APIHardwareUnsupported, + "Capture requires extension '%s' which is not supported", + params.Extensions[i].c_str()); } } @@ -451,8 +452,8 @@ ReplayStatus WrappedVulkan::Initialise(VkInitParams ¶ms, uint64_t sectionVer if(ret != VK_SUCCESS) { - RDCLOG("Instance creation returned %s", ToStr(ret).c_str()); - return ReplayStatus::APIHardwareUnsupported; + RETURN_ERROR_RESULT(ResultCode::APIHardwareUnsupported, "Vulkan instance creation returned %s", + ToStr(ret).c_str()); } RDCASSERTEQUAL(ret, VK_SUCCESS); @@ -516,7 +517,10 @@ ReplayStatus WrappedVulkan::Initialise(VkInitParams ¶ms, uint64_t sectionVer CheckVkResult(vkr); if(count == 0) - return ReplayStatus::APIHardwareUnsupported; + { + RETURN_ERROR_RESULT(ResultCode::APIHardwareUnsupported, + "No physical devices exist in this vulkan instance"); + } m_ReplayPhysicalDevices.resize(count); m_ReplayPhysicalDevicesUsed.resize(count); @@ -534,7 +538,7 @@ ReplayStatus WrappedVulkan::Initialise(VkInitParams ¶ms, uint64_t sectionVer LoadLibraryA("nvoglv64.dll"); #endif - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } VkResult WrappedVulkan::vkCreateInstance(const VkInstanceCreateInfo *pCreateInfo, @@ -1549,7 +1553,9 @@ bool WrappedVulkan::SelectGraphicsComputeQueue(const rdcarrayGetPhysicalDeviceFeatures(Unwrap(physicalDevice), &availFeatures); -#define CHECK_PHYS_FEATURE(feature) \ - if(enabledFeatures.feature && !availFeatures.feature) \ - { \ - m_FailedReplayStatus = ReplayStatus::APIHardwareUnsupported; \ - RDCERR("Capture requires physical device feature '" #feature "' which is not supported"); \ - return false; \ +#define CHECK_PHYS_FEATURE(feature) \ + if(enabledFeatures.feature && !availFeatures.feature) \ + { \ + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, \ + "Capture requires physical device feature '" #feature \ + "' which is not supported"); \ + return false; \ } CHECK_PHYS_FEATURE(robustBufferAccess); @@ -2166,14 +2176,14 @@ bool WrappedVulkan::Serialise_vkCreateDevice(SerialiserType &ser, VkPhysicalDevi #define END_PHYS_EXT_CHECK() } -#define CHECK_PHYS_EXT_FEATURE(feature) \ - if(ext->feature && !avail.feature) \ - { \ - m_FailedReplayStatus = ReplayStatus::APIHardwareUnsupported; \ - RDCERR("Capture requires physical device feature '" #feature \ - "' in struct '%s' which is not supported", \ - structName); \ - return false; \ +#define CHECK_PHYS_EXT_FEATURE(feature) \ + if(ext->feature && !avail.feature) \ + { \ + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, \ + "Capture requires physical device feature '" #feature \ + "' in struct '%s' which is not supported", \ + structName); \ + return false; \ } VkPhysicalDeviceDescriptorIndexingFeatures descIndexingFeatures = {}; @@ -2258,8 +2268,8 @@ bool WrappedVulkan::Serialise_vkCreateDevice(SerialiserType &ser, VkPhysicalDevi if(ext->bufferDeviceAddress && !avail.bufferDeviceAddressCaptureReplay) { - m_FailedReplayStatus = ReplayStatus::APIHardwareUnsupported; - RDCERR( + SET_ERROR_RESULT( + m_FailedReplayResult, ResultCode::APIHardwareUnsupported, "Capture requires bufferDeviceAddress support, which is available, but " "bufferDeviceAddressCaptureReplay support is not available which is required to " "replay"); @@ -2464,8 +2474,8 @@ bool WrappedVulkan::Serialise_vkCreateDevice(SerialiserType &ser, VkPhysicalDevi if(ext->bufferDeviceAddress && !avail.bufferDeviceAddressCaptureReplay) { - m_FailedReplayStatus = ReplayStatus::APIHardwareUnsupported; - RDCERR( + SET_ERROR_RESULT( + m_FailedReplayResult, ResultCode::APIHardwareUnsupported, "Capture requires bufferDeviceAddress support, which is available, but " "bufferDeviceAddressCaptureReplay support is not available which is required to " "replay"); @@ -2483,8 +2493,8 @@ bool WrappedVulkan::Serialise_vkCreateDevice(SerialiserType &ser, VkPhysicalDevi if(ext->bufferDeviceAddress && !avail.bufferDeviceAddressCaptureReplay) { - m_FailedReplayStatus = ReplayStatus::APIHardwareUnsupported; - RDCERR( + SET_ERROR_RESULT( + m_FailedReplayResult, ResultCode::APIHardwareUnsupported, "Capture requires bufferDeviceAddress support, which is available, but " "bufferDeviceAddressCaptureReplay support is not available which is required to " "replay"); @@ -3392,7 +3402,8 @@ bool WrappedVulkan::Serialise_vkCreateDevice(SerialiserType &ser, VkPhysicalDevi if(vkr != VK_SUCCESS) { - RDCERR("Failed to create logical device: %s", ToStr(vkr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Error creating logical device, VkResult: %s", ToStr(vkr).c_str()); return false; } diff --git a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp index 38ec65074..841149af3 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp @@ -582,7 +582,8 @@ bool WrappedVulkan::Serialise_vkCreateSampler(SerialiserType &ser, VkDevice devi if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Error creating sampler, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -748,7 +749,8 @@ bool WrappedVulkan::Serialise_vkCreateFramebuffer(SerialiserType &ser, VkDevice if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Error creating framebuffer, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -1041,7 +1043,8 @@ bool WrappedVulkan::Serialise_vkCreateRenderPass(SerialiserType &ser, VkDevice d if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Error creating render pass, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -1293,7 +1296,8 @@ bool WrappedVulkan::Serialise_vkCreateRenderPass2(SerialiserType &ser, VkDevice if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Error creating render pass, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -1492,7 +1496,8 @@ bool WrappedVulkan::Serialise_vkCreateQueryPool(SerialiserType &ser, VkDevice de if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Error creating query pool, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -1668,7 +1673,8 @@ bool WrappedVulkan::Serialise_vkCreateSamplerYcbcrConversion( if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Error creating YCbCr sampler, VkResult: %s", ToStr(ret).c_str()); return false; } else diff --git a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp index d37ba21e2..094e2ce47 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp @@ -216,7 +216,8 @@ bool WrappedVulkan::CheckMemoryRequirements(const char *resourceName, ResourceId if(external) { - RDCERR( + SET_ERROR_RESULT( + m_FailedReplayResult, ResultCode::APIHardwareUnsupported, "Trying to bind %s to memory %s which is type %u, " "but only these types are allowed: %s\n" "This resource was created with external memory bindings, which is not represented in " @@ -224,44 +225,43 @@ bool WrappedVulkan::CheckMemoryRequirements(const char *resourceName, ResourceId "Some drivers do not allow externally-imported resources to be bound to non-external " "memory, meaning this cannot be replayed.", resourceName, ToStr(memOrigId).c_str(), memInfo.memoryTypeIndex, bitsString.c_str()); - m_FailedReplayStatus = ReplayStatus::APIHardwareUnsupported; return false; } - RDCERR( + SET_ERROR_RESULT( + m_FailedReplayResult, ResultCode::APIHardwareUnsupported, "Trying to bind %s to memory %s which is type %u, " "but only these types are allowed: %s\n" "This is most likely caused by incompatible hardware or drivers between capture and " "replay, causing a change in memory requirements.", resourceName, ToStr(memOrigId).c_str(), memInfo.memoryTypeIndex, bitsString.c_str()); - m_FailedReplayStatus = ReplayStatus::APIHardwareUnsupported; return false; } // verify offset alignment if((memoryOffset % mrq.alignment) != 0) { - RDCERR( + SET_ERROR_RESULT( + m_FailedReplayResult, ResultCode::APIHardwareUnsupported, "Trying to bind %s to memory %s which is type %u, " "but offset 0x%llx doesn't satisfy alignment 0x%llx.\n" "This is most likely caused by incompatible hardware or drivers between capture and " "replay, causing a change in memory requirements.", resourceName, ToStr(memOrigId).c_str(), memInfo.memoryTypeIndex, memoryOffset, mrq.alignment); - m_FailedReplayStatus = ReplayStatus::APIHardwareUnsupported; return false; } // verify size if(mrq.size > memInfo.allocSize - memoryOffset) { - RDCERR( + SET_ERROR_RESULT( + m_FailedReplayResult, ResultCode::APIHardwareUnsupported, "Trying to bind %s to memory %s which is type %u, " "but at offset 0x%llx the reported size of 0x%llx won't fit the 0x%llx bytes of memory.\n" "This is most likely caused by incompatible hardware or drivers between capture and " "replay, causing a change in memory requirements.", resourceName, ToStr(memOrigId).c_str(), memInfo.memoryTypeIndex, memoryOffset, mrq.size, memInfo.allocSize); - m_FailedReplayStatus = ReplayStatus::APIHardwareUnsupported; return false; } @@ -293,7 +293,8 @@ bool WrappedVulkan::Serialise_vkAllocateMemory(SerialiserType &ser, VkDevice dev if(patched.memoryTypeIndex >= m_PhysicalDeviceData.memProps.memoryTypeCount) { - RDCERR( + SET_ERROR_RESULT( + m_FailedReplayResult, ResultCode::APIHardwareUnsupported, "Tried to allocate memory from index %u, but on replay we only have %u memory types.\n" "This is most likely caused by incompatible hardware or drivers between capture and " "replay, causing a change in memory requirements.", @@ -305,7 +306,8 @@ bool WrappedVulkan::Serialise_vkAllocateMemory(SerialiserType &ser, VkDevice dev if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed allocating memory, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -857,12 +859,14 @@ bool WrappedVulkan::Serialise_vkUnmapMemory(SerialiserType &ser, VkDevice device (void **)&MapData); if(vkr != VK_SUCCESS) { - RDCERR("Error mapping memory on replay: %s", ToStr(vkr).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Error mapping memory on replay, VkResult: %s", ToStr(vkr).c_str()); return false; } if(!MapData) { - RDCERR("Manually reporting failed memory map"); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Error mapping memory on replay"); CheckVkResult(VK_ERROR_MEMORY_MAP_FAILED); return false; } @@ -1072,7 +1076,8 @@ bool WrappedVulkan::Serialise_vkFlushMappedMemoryRanges(SerialiserType &ser, VkD RDCERR("Error mapping memory on replay: %s", ToStr(ret).c_str()); if(!MappedData) { - RDCERR("Manually reporting failed memory map"); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Error mapping memory on replay"); CheckVkResult(VK_ERROR_MEMORY_MAP_FAILED); return false; } @@ -1594,7 +1599,8 @@ bool WrappedVulkan::Serialise_vkCreateBuffer(SerialiserType &ser, VkDevice devic if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Error creating buffer, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -1844,7 +1850,8 @@ bool WrappedVulkan::Serialise_vkCreateBufferView(SerialiserType &ser, VkDevice d if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Error creating buffer view, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -2089,7 +2096,8 @@ bool WrappedVulkan::Serialise_vkCreateImage(SerialiserType &ser, VkDevice device if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Error creating image, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -2572,7 +2580,8 @@ bool WrappedVulkan::Serialise_vkCreateImageView(SerialiserType &ser, VkDevice de if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Error creating image view, VkResult: %s", ToStr(ret).c_str()); return false; } else diff --git a/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp index d3d35a335..869020d3c 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp @@ -127,7 +127,8 @@ bool WrappedVulkan::Serialise_vkCreatePipelineLayout(SerialiserType &ser, VkDevi if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating pipeline layout, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -252,7 +253,8 @@ bool WrappedVulkan::Serialise_vkCreateShaderModule(SerialiserType &ser, VkDevice if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating shader module, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -350,7 +352,8 @@ bool WrappedVulkan::Serialise_vkCreatePipelineCache(SerialiserType &ser, VkDevic if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating pipeline cache, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -462,7 +465,8 @@ bool WrappedVulkan::Serialise_vkCreateGraphicsPipelines( if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating graphics pipeline, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -759,7 +763,8 @@ bool WrappedVulkan::Serialise_vkCreateComputePipelines(SerialiserType &ser, VkDe if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating compute pipeline, VkResult: %s", ToStr(ret).c_str()); return false; } else diff --git a/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp index 3ff26e113..54a8d11c0 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp @@ -122,7 +122,8 @@ bool WrappedVulkan::Serialise_vkCreateFence(SerialiserType &ser, VkDevice device if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating fence, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -349,7 +350,8 @@ bool WrappedVulkan::Serialise_vkCreateEvent(SerialiserType &ser, VkDevice device if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating event, VkResult: %s", ToStr(ret).c_str()); return false; } else @@ -555,7 +557,8 @@ bool WrappedVulkan::Serialise_vkCreateSemaphore(SerialiserType &ser, VkDevice de if(ret != VK_SUCCESS) { - RDCERR("Failed on resource serialise-creation, VkResult: %s", ToStr(ret).c_str()); + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating semaphore, VkResult: %s", ToStr(ret).c_str()); return false; } else diff --git a/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp index bdcbba8ce..463e29e2b 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp @@ -396,6 +396,14 @@ bool WrappedVulkan::Serialise_vkCreateSwapchainKHR(SerialiserType &ser, VkDevice VkResult vkr = ObjDisp(device)->CreateImage(Unwrap(device), &imInfo, NULL, &im); CheckVkResult(vkr); + if(vkr != VK_SUCCESS) + { + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed creating fake backbuffer textures, VkResult: %s", + ToStr(vkr).c_str()); + return false; + } + ResourceId liveId = GetResourceManager()->WrapResource(Unwrap(device), im); VkMemoryRequirements mrq = {0}; @@ -411,7 +419,12 @@ bool WrappedVulkan::Serialise_vkCreateSwapchainKHR(SerialiserType &ser, VkDevice CheckVkResult(vkr); if(vkr != VK_SUCCESS) + { + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed allocating fake backbuffer texture memory, VkResult: %s", + ToStr(vkr).c_str()); return false; + } ResourceId memid = GetResourceManager()->WrapResource(Unwrap(device), mem); // register as a live-only resource, so it is cleaned up properly diff --git a/renderdoc/os/os_specific.cpp b/renderdoc/os/os_specific.cpp index afdb50df4..00b041e73 100644 --- a/renderdoc/os/os_specific.cpp +++ b/renderdoc/os/os_specific.cpp @@ -85,6 +85,31 @@ rdcstr Fmt(const char *format, ...) return ret; } +rdcstr Fmt(rdcliteral format, ...) +{ + // optimisation - if there are no format specifiers hence no arguments, preserve and return the + // literal + if(strchr(format.c_str(), '%') == NULL) + return format; + + va_list args; + va_start(args, format); + + va_list args2; + va_copy(args2, args); + + int size = ::utf8printv(NULL, 0, format.c_str(), args2); + + rdcstr ret; + ret.resize(size); + ::utf8printv(ret.data(), size + 1, format.c_str(), args); + + va_end(args); + va_end(args2); + + return ret; +} + rdcstr FmtArgs(const char *format, Args &args) { int size = ::utf8printf_custom(NULL, 0, format, args); diff --git a/renderdoc/os/os_specific.h b/renderdoc/os/os_specific.h index 24c36f591..b255ad007 100644 --- a/renderdoc/os/os_specific.h +++ b/renderdoc/os/os_specific.h @@ -40,12 +40,12 @@ #include "api/replay/rdcpair.h" #include "api/replay/rdcstr.h" #include "common/globalconfig.h" +#include "common/result.h" struct CaptureOptions; struct EnvironmentModification; struct PathEntry; enum class WindowingSystem : uint32_t; -enum class ReplayStatus : uint32_t; typedef std::function RENDERDOC_ProgressCallback; namespace Process @@ -59,14 +59,15 @@ rdcstr GetEnvVariable(const rdcstr &name); uint64_t GetMemoryUsage(); bool CanGlobalHook(); -bool StartGlobalHook(const rdcstr &pathmatch, const rdcstr &capturefile, const CaptureOptions &opts); +RDResult StartGlobalHook(const rdcstr &pathmatch, const rdcstr &capturefile, + const CaptureOptions &opts); bool IsGlobalHookActive(); void StopGlobalHook(); -rdcpair InjectIntoProcess(uint32_t pid, - const rdcarray &env, - const rdcstr &capturefile, - const CaptureOptions &opts, bool waitForExit); +rdcpair InjectIntoProcess(uint32_t pid, + const rdcarray &env, + const rdcstr &capturefile, const CaptureOptions &opts, + bool waitForExit); struct ProcessResult { rdcstr strStdout, strStderror; @@ -76,10 +77,11 @@ uint32_t LaunchProcess(const rdcstr &app, const rdcstr &workingDir, const rdcstr bool internal, ProcessResult *result = NULL); uint32_t LaunchScript(const rdcstr &script, const rdcstr &workingDir, const rdcstr &args, bool internal, ProcessResult *result = NULL); -rdcpair LaunchAndInjectIntoProcess( - const rdcstr &app, const rdcstr &workingDir, const rdcstr &cmdLine, - const rdcarray &env, const rdcstr &capturefile, - const CaptureOptions &opts, bool waitForExit); +rdcpair LaunchAndInjectIntoProcess(const rdcstr &app, const rdcstr &workingDir, + const rdcstr &cmdLine, + const rdcarray &env, + const rdcstr &capturefile, + const CaptureOptions &opts, bool waitForExit); bool IsModuleLoaded(const rdcstr &module); void *LoadModule(const rdcstr &module); void *GetFunctionAddress(void *module, const rdcstr &function); @@ -173,6 +175,7 @@ public: bool Connected() const; + RDResult GetError() const { return m_Error; } uint32_t GetTimeout() const { return timeoutMS; } void SetTimeout(uint32_t milliseconds) { timeoutMS = milliseconds; } Socket *AcceptClient(uint32_t timeoutMilliseconds); @@ -188,6 +191,7 @@ public: private: ptrdiff_t socket; uint32_t timeoutMS; + RDResult m_Error; }; Socket *CreateServerSocket(const rdcstr &addr, uint16_t port, int queuesize); diff --git a/renderdoc/os/posix/posix_network.cpp b/renderdoc/os/posix/posix_network.cpp index 64bcfc57d..f4c78eb8d 100644 --- a/renderdoc/os/posix/posix_network.cpp +++ b/renderdoc/os/posix/posix_network.cpp @@ -126,7 +126,8 @@ Socket *Socket::AcceptClient(uint32_t timeoutMilliseconds) if(err != EWOULDBLOCK && err != EAGAIN && err != EINTR) { - RDCWARN("accept: %s", errno_string(err).c_str()); + SET_WARNING_RESULT(m_Error, ResultCode::NetworkIOFailed, "accept failed: %s", + errno_string(err).c_str()); Shutdown(); } @@ -180,13 +181,15 @@ bool Socket::SendDataBlocking(const void *buf, uint32_t length) } else if(err == EWOULDBLOCK || err == EAGAIN) { - RDCWARN("Timeout of %f seconds exceeded in send", float(timeoutMS) / 1000.0f); + SET_WARNING_RESULT(m_Error, ResultCode::NetworkIOFailed, + "Timeout of %f seconds exceeded in send", float(timeoutMS) / 1000.0f); Shutdown(); return false; } else { - RDCWARN("send: %s", errno_string(err).c_str()); + SET_WARNING_RESULT(m_Error, ResultCode::NetworkIOFailed, "send failed: %s", + errno_string(err).c_str()); Shutdown(); return false; } @@ -229,7 +232,8 @@ bool Socket::IsRecvDataWaiting() } else { - RDCWARN("recv: %s", errno_string(err).c_str()); + SET_WARNING_RESULT(m_Error, ResultCode::NetworkIOFailed, "recv peek failed: %s", + errno_string(err).c_str()); Shutdown(); return false; } @@ -261,7 +265,8 @@ bool Socket::RecvDataNonBlocking(void *buf, uint32_t &length) } else { - RDCWARN("recv: %s", errno_string(err).c_str()); + SET_WARNING_RESULT(m_Error, ResultCode::NetworkIOFailed, "recv non blocking failed: %s", + errno_string(err).c_str()); Shutdown(); return false; } @@ -312,13 +317,15 @@ bool Socket::RecvDataBlocking(void *buf, uint32_t length) } else if(err == EWOULDBLOCK || err == EAGAIN) { - RDCWARN("Timeout of %f seconds exceeded in recv", float(timeoutMS) / 1000.0f); + SET_WARNING_RESULT(m_Error, ResultCode::NetworkIOFailed, + "Timeout of %f seconds exceeded in recv", float(timeoutMS) / 1000.0f); Shutdown(); return false; } else { - RDCWARN("recv: %s", errno_string(err).c_str()); + SET_WARNING_RESULT(m_Error, ResultCode::NetworkIOFailed, "recv blocking failed: %s", + errno_string(err).c_str()); Shutdown(); return false; } diff --git a/renderdoc/os/posix/posix_process.cpp b/renderdoc/os/posix/posix_process.cpp index 34da0e7b1..99e628f82 100644 --- a/renderdoc/os/posix/posix_process.cpp +++ b/renderdoc/os/posix/posix_process.cpp @@ -36,6 +36,7 @@ //#include "api/app/renderdoc_app.h" #include "api/replay/capture_options.h" #include "api/replay/control_types.h" +#include "common/formatting.h" #include "common/threading.h" #include "core/core.h" #include "os/os_specific.h" @@ -643,12 +644,16 @@ static pid_t RunProcess(rdcstr appName, rdcstr workDir, const rdcstr &cmdLine, c return childPid; } -rdcpair Process::InjectIntoProcess( - uint32_t pid, const rdcarray &env, const rdcstr &logfile, - const CaptureOptions &opts, bool waitForExit) +rdcpair Process::InjectIntoProcess(uint32_t pid, + const rdcarray &env, + const rdcstr &logfile, + const CaptureOptions &opts, bool waitForExit) { RDCUNIMPLEMENTED("Injecting into already running processes on linux"); - return {ReplayStatus::InjectionFailed, 0}; + return { + RDResult(ResultCode::InjectionFailed, + "Injecting into already running processes is not supported on non-Windows systems"), + 0}; } uint32_t Process::LaunchProcess(const rdcstr &app, const rdcstr &workingDir, const rdcstr &cmdLine, @@ -876,15 +881,16 @@ void ResetHookingEnvVars() unsetenv("RENDERDOC_ORIGPRELOAD"); } -rdcpair Process::LaunchAndInjectIntoProcess( +rdcpair Process::LaunchAndInjectIntoProcess( const rdcstr &app, const rdcstr &workingDir, const rdcstr &cmdLine, const rdcarray &envList, const rdcstr &capturefile, const CaptureOptions &opts, bool waitForExit) { if(app.empty()) { - RDCERR("Invalid empty 'app'"); - return {ReplayStatus::InternalError, 0}; + RDResult result; + SET_ERROR_RESULT(result, ResultCode::InvalidParameter, "Invalid empty path to launch."); + return {result, 0}; } // turn environment string to a UTF-8 map @@ -970,14 +976,23 @@ rdcpair Process::LaunchAndInjectIntoProcess( } CleanupStringArray(envp); - return {ret == 0 ? ReplayStatus::InjectionFailed : ReplayStatus::Succeeded, (uint32_t)ret}; + RDResult result; + if(ret == 0) + { + SET_ERROR_RESULT(result, ResultCode::InvalidParameter, + "Couldn't connect to target program. Check that it didn't crash or exit " + "during early initialisation, e.g. due to an incorrectly configured working " + "directory."); + } + return {result, (uint32_t)ret}; } -bool Process::StartGlobalHook(const rdcstr &pathmatch, const rdcstr &logfile, - const CaptureOptions &opts) +RDResult Process::StartGlobalHook(const rdcstr &pathmatch, const rdcstr &logfile, + const CaptureOptions &opts) { RDCUNIMPLEMENTED("Global hooking of all processes on linux"); - return false; + return RDResult(ResultCode::InvalidParameter, + "Global hooking is not supported on non-Windows systems"); } bool Process::CanGlobalHook() diff --git a/renderdoc/os/win32/sys_win32_hooks.cpp b/renderdoc/os/win32/sys_win32_hooks.cpp index addb0345a..91418f6be 100644 --- a/renderdoc/os/win32/sys_win32_hooks.cpp +++ b/renderdoc/os/win32/sys_win32_hooks.cpp @@ -298,11 +298,11 @@ private: RDCDEBUG("Intercepting %s", entryPoint); // inherit logfile and capture options - rdcpair res = Process::InjectIntoProcess( + rdcpair res = Process::InjectIntoProcess( lpProcessInformation->dwProcessId, {}, RenderDoc::Inst().GetCaptureFileTemplate(), RenderDoc::Inst().GetCaptureOptions(), false); - if(res.first == ReplayStatus::Succeeded) + if(res.first == ResultCode::Succeeded) RenderDoc::Inst().AddChildProcess((uint32_t)lpProcessInformation->dwProcessId, res.second); } diff --git a/renderdoc/os/win32/win32_network.cpp b/renderdoc/os/win32/win32_network.cpp index 39aef21b3..93c066af5 100644 --- a/renderdoc/os/win32/win32_network.cpp +++ b/renderdoc/os/win32/win32_network.cpp @@ -166,7 +166,8 @@ Socket *Socket::AcceptClient(uint32_t timeoutMilliseconds) if(err != WSAEWOULDBLOCK) { - RDCWARN("accept: %s", wsaerr_string(err).c_str()); + SET_WARNING_RESULT(m_Error, ResultCode::NetworkIOFailed, "accept failed: %s", + wsaerr_string(err).c_str()); Shutdown(); } @@ -212,13 +213,14 @@ bool Socket::SendDataBlocking(const void *buf, uint32_t length) if(err == WSAEWOULDBLOCK || err == WSAETIMEDOUT) { - RDCWARN("Timeout in send"); + SET_WARNING_RESULT(m_Error, ResultCode::NetworkIOFailed, "Timeout in send"); Shutdown(); return false; } else { - RDCWARN("send: %s", wsaerr_string(err).c_str()); + SET_WARNING_RESULT(m_Error, ResultCode::NetworkIOFailed, "send failed: %s", + wsaerr_string(err).c_str()); Shutdown(); return false; } @@ -258,7 +260,8 @@ bool Socket::IsRecvDataWaiting() } else { - RDCWARN("recv: %s", wsaerr_string(err).c_str()); + SET_WARNING_RESULT(m_Error, ResultCode::NetworkIOFailed, "recv peek failed: %s", + wsaerr_string(err).c_str()); Shutdown(); return false; } @@ -290,7 +293,8 @@ bool Socket::RecvDataNonBlocking(void *buf, uint32_t &length) } else { - RDCWARN("recv: %s", wsaerr_string(err).c_str()); + SET_WARNING_RESULT(m_Error, ResultCode::NetworkIOFailed, "recv non blocking failed: %s", + wsaerr_string(err).c_str()); Shutdown(); return false; } @@ -333,13 +337,14 @@ bool Socket::RecvDataBlocking(void *buf, uint32_t length) if(err == WSAEWOULDBLOCK || err == WSAETIMEDOUT) { - RDCWARN("Timeout in recv"); + SET_WARNING_RESULT(m_Error, ResultCode::NetworkIOFailed, "Timeout in recv"); Shutdown(); return false; } else { - RDCWARN("recv: %s", wsaerr_string(err).c_str()); + SET_WARNING_RESULT(m_Error, ResultCode::NetworkIOFailed, "recv blocking failed: %s", + wsaerr_string(err).c_str()); Shutdown(); return false; } diff --git a/renderdoc/os/win32/win32_process.cpp b/renderdoc/os/win32/win32_process.cpp index d19933a62..672733647 100644 --- a/renderdoc/os/win32/win32_process.cpp +++ b/renderdoc/os/win32/win32_process.cpp @@ -571,9 +571,10 @@ static PROCESS_INFORMATION RunProcess(const rdcstr &app, const rdcstr &workingDi return pi; } -rdcpair Process::InjectIntoProcess( - uint32_t pid, const rdcarray &env, const rdcstr &capturefile, - const CaptureOptions &opts, bool waitForExit) +rdcpair Process::InjectIntoProcess(uint32_t pid, + const rdcarray &env, + const rdcstr &capturefile, + const CaptureOptions &opts, bool waitForExit) { rdcwstr wcapturefile = StringFormat::UTF82Wide(capturefile); @@ -631,9 +632,11 @@ rdcpair Process::InjectIntoProcess( if(!success) { DWORD err = GetLastError(); - RDCERR("Couldn't determine bitness of process, err: %08x", err); + RDResult result; + SET_ERROR_RESULT(result, ResultCode::IncompatibleProcess, + "Couldn't determine bitness of process, err: %08x", err); CloseHandle(hProcess); - return {ReplayStatus::IncompatibleProcess, 0}; + return {result, 0}; } bool capalt = false; @@ -651,8 +654,11 @@ rdcpair Process::InjectIntoProcess( if(!success) { DWORD err = GetLastError(); - RDCERR("Couldn't determine bitness of self, err: %08x", err); - return {ReplayStatus::InternalError, 0}; + RDResult result; + SET_ERROR_RESULT(result, ResultCode::IncompatibleProcess, + "Couldn't determine bitness of self, err: %08x", err); + CloseHandle(hProcess); + return {result, 0}; } // we know we're 32-bit, so if the target process is not wow64 @@ -701,10 +707,13 @@ rdcpair Process::InjectIntoProcess( if(!capalt) { RDCDEBUG("Running from %ls", renderdocPathLower); - RDCERR("Can't capture x64 process with x86 renderdoc"); CloseHandle(hProcess); - return {ReplayStatus::IncompatibleProcess, 0}; + RDResult result; + SET_ERROR_RESULT(result, ResultCode::IncompatibleProcess, + "Can't capture 64-bit program with 32-bit build of RenderDoc. Please run a " + "64-bit build of RenderDoc"); + return {result, 0}; } } #else @@ -914,10 +923,18 @@ rdcpair Process::InjectIntoProcess( if(!retValue) { - RDCERR( - "Can't spawn alternate bitness renderdoccmd - have you built 32-bit and 64-bit?\n" - "You need to build the matching bitness for the programs you want to capture."); - return {ReplayStatus::InternalError, 0}; + RDResult result; +#if RENDERDOC_OFFICIAL_BUILD + SET_ERROR_RESULT(result, ResultCode::InternalError, + "Can't run 32-bit renderdoccmd to capture 32-bit program."); +#else + SET_ERROR_RESULT( + result, ResultCode::InternalError, + "Can't run 32-bit renderdoccmd to capture 32-bit program." + "If this is a locally built RenderDoc you must build both 32-bit and 64-bit versions."); +#endif + CloseHandle(hProcess); + return {result, 0}; } ResumeThread(pi.hThread); @@ -934,23 +951,40 @@ rdcpair Process::InjectIntoProcess( CloseHandle(hProcess); if(exitCode == 0) - return {ReplayStatus::UnknownError, 0}; - if(exitCode < RenderDoc_FirstTargetControlPort) - return {(ReplayStatus)exitCode, 0}; + { + RDResult result; + SET_ERROR_RESULT(result, ResultCode::UnknownError, + "Encountered error while launching target 32-bit program."); + return {result, 0}; + } - return {ReplayStatus::Succeeded, (uint32_t)exitCode}; + if(exitCode < RenderDoc_FirstTargetControlPort) + { + ResultCode code = (ResultCode)exitCode; + + RDResult result; + SET_ERROR_RESULT(result, code, "32-bit renderdoccmd returned '%s'", ToStr(code).c_str()); + return {code, 0}; + } + + return {ResultCode::Succeeded, (uint32_t)exitCode}; } InjectDLL(hProcess, renderdocPath); + const char *rdoc_dll = STRINGIZE(RDOC_DLL_FILE); + uintptr_t loc = FindRemoteDLL(pid, STRINGIZE(RDOC_DLL_FILE) ".dll"); - rdcpair result = {ReplayStatus::Succeeded, 0}; + rdcpair result = {ResultCode::Succeeded, 0}; if(loc == 0) { - RDCERR("Can't locate " STRINGIZE(RDOC_DLL_FILE) ".dll in remote PID %d", pid); - result.first = ReplayStatus::InjectionFailed; + SET_ERROR_RESULT( + result.first, ResultCode::InjectionFailed, + "Failed to inject %s.dll into process. Check that the process did not crash or exit " + "early in initialisation, e.g. if the working directory is incorrectly set.", + rdoc_dll); } else { @@ -1085,7 +1119,7 @@ uint32_t Process::LaunchScript(const rdcstr &script, const rdcstr &workingDir, return LaunchProcess("cmd.exe", workingDir, args, internal, result); } -rdcpair Process::LaunchAndInjectIntoProcess( +rdcpair Process::LaunchAndInjectIntoProcess( const rdcstr &app, const rdcstr &workingDir, const rdcstr &cmdLine, const rdcarray &env, const rdcstr &capturefile, const CaptureOptions &opts, bool waitForExit) @@ -1095,24 +1129,30 @@ rdcpair Process::LaunchAndInjectIntoProcess( if(func == NULL) { - RDCERR("Can't find required export function in " STRINGIZE( - RDOC_DLL_FILE) ".dll - corrupted/missing file?"); - return {ReplayStatus::InternalError, 0}; + const char *rdoc_dll = STRINGIZE(RDOC_DLL_FILE); + RDResult result; + SET_ERROR_RESULT(result, ResultCode::InternalError, + "Can't find required export function in %s.dll - corrupted/missing file?", + rdoc_dll); + return {result, 0}; } PROCESS_INFORMATION pi = RunProcess(app, workingDir, cmdLine, env, false, NULL, NULL); if(pi.dwProcessId == 0) - return {ReplayStatus::InjectionFailed, 0}; + { + RDResult result; + SET_ERROR_RESULT(result, ResultCode::InjectionFailed, "Failed to launch process"); + return {result, 0}; + } - rdcpair ret = - InjectIntoProcess(pi.dwProcessId, {}, capturefile, opts, false); + rdcpair ret = InjectIntoProcess(pi.dwProcessId, {}, capturefile, opts, false); CloseHandle(pi.hProcess); ResumeThread(pi.hThread); ResumeThread(pi.hThread); - if(ret.second == 0 || ret.first != ReplayStatus::Succeeded) + if(ret.second == 0 || ret.first != ResultCode::Succeeded) { CloseHandle(pi.hThread); return ret; @@ -1395,11 +1435,14 @@ static void GlobalHookThread() } } -bool Process::StartGlobalHook(const rdcstr &pathmatch, const rdcstr &capturefile, - const CaptureOptions &opts) +RDResult Process::StartGlobalHook(const rdcstr &pathmatch, const rdcstr &capturefile, + const CaptureOptions &opts) { if(pathmatch.empty()) - return false; + { + RETURN_ERROR_RESULT(ResultCode::InvalidParameter, + "Invalid global hook parameter, empty path to match"); + } rdcstr renderdocPath; FileIO::GetLibraryFilename(renderdocPath); @@ -1461,7 +1504,7 @@ bool Process::StartGlobalHook(const rdcstr &pathmatch, const rdcstr &capturefile // we bail out immediately bool success = BackupAndChangeRegistry(hookdata, shimpathWow32, shimpathNative); if(!success) - return false; + return RDResult(ResultCode::InternalError, "Registry change failed"); PROCESS_INFORMATION pi = {0}; STARTUPINFO si = {0}; @@ -1492,6 +1535,8 @@ bool Process::StartGlobalHook(const rdcstr &pathmatch, const rdcstr &capturefile // this is the end of the pipe that the child will inherit and use as stdin HANDLE childEnd = NULL; + DWORD err; + // create a pipe with the writing end for us, and the reading end as the child process's stdin { SECURITY_ATTRIBUTES pipeSec; @@ -1504,9 +1549,10 @@ bool Process::StartGlobalHook(const rdcstr &pathmatch, const rdcstr &capturefile if(!res) { - RDCERR("Could not create 32-bit stdin pipe"); + err = GetLastError(); RestoreRegistry(hookdata); - return false; + RETURN_ERROR_RESULT(ResultCode::InternalError, "Could not create 32-bit stdin pipe (err %u)", + err); } // we don't want the child process to inherit our end @@ -1514,9 +1560,10 @@ bool Process::StartGlobalHook(const rdcstr &pathmatch, const rdcstr &capturefile if(!res) { - RDCERR("Could not make 32-bit stdin pipe inheritable"); + err = GetLastError(); RestoreRegistry(hookdata); - return false; + RETURN_ERROR_RESULT(ResultCode::InternalError, + "Could not make 32-bit stdin pipe inheritable (err %u)", err); } si.hStdInput = childEnd; @@ -1526,15 +1573,17 @@ bool Process::StartGlobalHook(const rdcstr &pathmatch, const rdcstr &capturefile BOOL retValue = CreateProcessW(NULL, ¶msAlloc[0], &pSec, &tSec, true, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi); + err = GetLastError(); + // we don't need this end anymore, the child has it CloseHandle(childEnd); if(retValue == FALSE) { - RDCERR("Can't launch 64-bit renderdoccmd from '%s'", cmdpathNative.c_str()); CloseHandle(hookdata.dataNative.pipe); RestoreRegistry(hookdata); - return false; + RETURN_ERROR_RESULT(ResultCode::InternalError, "Can't launch renderdoccmd from '%s' (err %u)", + cmdpathNative.c_str(), err); } CloseHandle(pi.hThread); @@ -1562,18 +1611,20 @@ bool Process::StartGlobalHook(const rdcstr &pathmatch, const rdcstr &capturefile if(!res) { - RDCERR("Could not create 64-bit stdin pipe"); + err = GetLastError(); RestoreRegistry(hookdata); - return false; + RETURN_ERROR_RESULT(ResultCode::InternalError, "Could not create 64-bit stdin pipe (err %u)", + err); } res = SetHandleInformation(hookdata.dataWow32.pipe, HANDLE_FLAG_INHERIT, 0); if(!res) { - RDCERR("Could not make 64-bit stdin pipe inheritable"); + err = GetLastError(); RestoreRegistry(hookdata); - return false; + RETURN_ERROR_RESULT(ResultCode::InternalError, + "Could not make 64-bit stdin pipe inheritable (err %u)", err); } si.hStdInput = childEnd; @@ -1582,16 +1633,18 @@ bool Process::StartGlobalHook(const rdcstr &pathmatch, const rdcstr &capturefile retValue = CreateProcessW(NULL, ¶msAlloc[0], &pSec, &tSec, true, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi); + err = GetLastError(); + // we don't need this end anymore CloseHandle(childEnd); if(retValue == FALSE) { - RDCERR("Can't launch 32-bit renderdoccmd from '%s'", cmdpathWow32.c_str()); CloseHandle(hookdata.dataNative.pipe); CloseHandle(hookdata.dataWow32.pipe); RestoreRegistry(hookdata); - return false; + RETURN_ERROR_RESULT(ResultCode::InternalError, "Can't launch renderdoccmd from '%s' (err %u)", + cmdpathWow32.c_str(), err); } CloseHandle(pi.hThread); @@ -1604,7 +1657,7 @@ bool Process::StartGlobalHook(const rdcstr &pathmatch, const rdcstr &capturefile globalHook->pipeThread = Threading::CreateThread(&GlobalHookThread); - return true; + return RDResult(); } bool Process::IsGlobalHookActive() diff --git a/renderdoc/os/win32/win32_shellext.cpp b/renderdoc/os/win32/win32_shellext.cpp index 5ce69ad28..651c744f4 100644 --- a/renderdoc/os/win32/win32_shellext.cpp +++ b/renderdoc/os/win32/win32_shellext.cpp @@ -138,9 +138,9 @@ struct RDCThumbnailProvider : public IThumbnailProvider, IInitializeWithStream } StreamReader reader(captureHeader.data(), (ULONG)size); - m_ddsData = load_dds_from_file(&reader); + RDResult res = load_dds_from_file(&reader, m_ddsData); - if(m_ddsData.subresources.empty()) + if(res != ResultCode::Succeeded) { return E_INVALIDARG; } diff --git a/renderdoc/renderdoc.vcxproj b/renderdoc/renderdoc.vcxproj index 2d19506cb..2ca40af87 100644 --- a/renderdoc/renderdoc.vcxproj +++ b/renderdoc/renderdoc.vcxproj @@ -201,6 +201,7 @@ + diff --git a/renderdoc/renderdoc.vcxproj.filters b/renderdoc/renderdoc.vcxproj.filters index a2f6c4ac3..61ebca09b 100644 --- a/renderdoc/renderdoc.vcxproj.filters +++ b/renderdoc/renderdoc.vcxproj.filters @@ -576,6 +576,9 @@ Resources\hlsl + + Common + diff --git a/renderdoc/replay/app_api.cpp b/renderdoc/replay/app_api.cpp index 42293368a..339043071 100644 --- a/renderdoc/replay/app_api.cpp +++ b/renderdoc/replay/app_api.cpp @@ -127,9 +127,9 @@ static void SetCaptureFileComments(const char *filePath, const char *comments) RDCFile rdc; rdc.Open(path); - if(rdc.ErrorCode() != ContainerError::NoError) + if(rdc.Error() != ResultCode::Succeeded) { - RDCERR("Error opening '%s' to add capture comments", path.c_str()); + RDCERR("Error adding capture file comments: %s", ResultDetails(rdc.Error()).Message().c_str()); return; } diff --git a/renderdoc/replay/capture_file.cpp b/renderdoc/replay/capture_file.cpp index 517d6dda4..6e2c67296 100644 --- a/renderdoc/replay/capture_file.cpp +++ b/renderdoc/replay/capture_file.cpp @@ -103,27 +103,26 @@ public: CaptureFile(); virtual ~CaptureFile(); - ReplayStatus OpenFile(const rdcstr &filename, const rdcstr &filetype, - RENDERDOC_ProgressCallback progress); - ReplayStatus OpenBuffer(const bytebuf &buffer, const rdcstr &filetype, - RENDERDOC_ProgressCallback progress); - bool CopyFileTo(const rdcstr &filename); - rdcstr ErrorString() { return m_ErrorString; } + ResultDetails OpenFile(const rdcstr &filename, const rdcstr &filetype, + RENDERDOC_ProgressCallback progress); + ResultDetails OpenBuffer(const bytebuf &buffer, const rdcstr &filetype, + RENDERDOC_ProgressCallback progress); + ResultDetails CopyFileTo(const rdcstr &filename); void Shutdown() { delete this; } ReplaySupport LocalReplaySupport() { return m_Support; } rdcstr DriverName() { return m_DriverName; } rdcstr RecordedMachineIdent() { return m_Ident; } uint64_t TimestampBase() { return m_RDC ? m_RDC->GetTimestampBase() : 0; } double TimestampFrequency() { return m_RDC ? m_RDC->GetTimestampFrequency() : 1.0; } - rdcpair OpenCapture(const ReplayOptions &opts, - RENDERDOC_ProgressCallback progress); + rdcpair OpenCapture(const ReplayOptions &opts, + RENDERDOC_ProgressCallback progress); void SetMetadata(const rdcstr &driverName, uint64_t machineIdent, FileType thumbType, uint32_t thumbWidth, uint32_t thumbHeight, const bytebuf &thumbData, uint64_t timeBase, double timeFreq); - ReplayStatus Convert(const rdcstr &filename, const rdcstr &filetype, const SDFile *file, - RENDERDOC_ProgressCallback progress); + ResultDetails Convert(const rdcstr &filename, const rdcstr &filetype, const SDFile *file, + RENDERDOC_ProgressCallback progress); rdcarray GetCaptureFileFormats() { @@ -163,23 +162,23 @@ public: int32_t FindSectionByType(SectionType type); SectionProperties GetSectionProperties(int32_t index); bytebuf GetSectionContents(int32_t index); - bool WriteSection(const SectionProperties &props, const bytebuf &contents); + ResultDetails WriteSection(const SectionProperties &props, const bytebuf &contents); bool HasCallstacks(); - bool InitResolver(bool interactive, RENDERDOC_ProgressCallback progress); + ResultDetails InitResolver(bool interactive, RENDERDOC_ProgressCallback progress); rdcarray GetResolve(const rdcarray &callstack); private: - ReplayStatus Init(); + ResultDetails Init(); - void InitStructuredData(RENDERDOC_ProgressCallback progress = RENDERDOC_ProgressCallback()); + RDResult InitStructuredData(RENDERDOC_ProgressCallback progress = RENDERDOC_ProgressCallback()); RDCFile *m_RDC = NULL; Callstack::StackResolver *m_Resolver = NULL; SDFile m_StructuredData; - rdcstr m_DriverName, m_Ident, m_ErrorString; + rdcstr m_DriverName, m_Ident; ReplaySupport m_Support = ReplaySupport::Unsupported; }; @@ -193,14 +192,14 @@ CaptureFile::~CaptureFile() SAFE_DELETE(m_Resolver); } -ReplayStatus CaptureFile::OpenFile(const rdcstr &filename, const rdcstr &filetype, - RENDERDOC_ProgressCallback progress) +ResultDetails CaptureFile::OpenFile(const rdcstr &filename, const rdcstr &filetype, + RENDERDOC_ProgressCallback progress) { CaptureImporter importer = RenderDoc::Inst().GetCaptureImporter(filetype); if(importer) { - ReplayStatus ret; + ResultDetails ret; { StreamReader reader(FileIO::fopen(filename, FileIO::ReadBinary)); @@ -209,9 +208,8 @@ ReplayStatus CaptureFile::OpenFile(const rdcstr &filename, const rdcstr &filetyp ret = importer(filename, reader, m_RDC, m_StructuredData, progress); } - if(ret != ReplayStatus::Succeeded) + if(ret.code != ResultCode::Succeeded) { - m_ErrorString = StringFormat::Fmt("Importer '%s' failed to import file.", filetype.c_str()); SAFE_DELETE(m_RDC); return ret; } @@ -235,14 +233,14 @@ ReplayStatus CaptureFile::OpenFile(const rdcstr &filename, const rdcstr &filetyp return Init(); } -ReplayStatus CaptureFile::OpenBuffer(const bytebuf &buffer, const rdcstr &filetype, - RENDERDOC_ProgressCallback progress) +ResultDetails CaptureFile::OpenBuffer(const bytebuf &buffer, const rdcstr &filetype, + RENDERDOC_ProgressCallback progress) { CaptureImporter importer = RenderDoc::Inst().GetCaptureImporter(filetype); if(importer) { - ReplayStatus ret; + RDResult ret; { StreamReader reader(buffer); @@ -251,9 +249,8 @@ ReplayStatus CaptureFile::OpenBuffer(const bytebuf &buffer, const rdcstr &filety ret = importer(rdcstr(), reader, m_RDC, m_StructuredData, progress); } - if(ret != ReplayStatus::Succeeded) + if(ret != ResultCode::Succeeded) { - m_ErrorString = StringFormat::Fmt("Importer '%s' failed to import file.", filetype.c_str()); SAFE_DELETE(m_RDC); return ret; } @@ -277,85 +274,95 @@ ReplayStatus CaptureFile::OpenBuffer(const bytebuf &buffer, const rdcstr &filety return Init(); } -bool CaptureFile::CopyFileTo(const rdcstr &filename) +ResultDetails CaptureFile::CopyFileTo(const rdcstr &filename) { if(m_RDC) return m_RDC->CopyFileTo(filename); - return false; + return RDResult(ResultCode::InternalError, "RDC file unexpectedly NULL"); } -ReplayStatus CaptureFile::Init() +ResultDetails CaptureFile::Init() { if(!m_RDC) - return ReplayStatus::InternalError; + return RDResult(ResultCode::InternalError, "RDC file unexpectedly NULL"); - m_ErrorString = m_RDC->ErrorString(); + RDResult rdcRes = m_RDC->Error(); - switch(m_RDC->ErrorCode()) + if(rdcRes != ResultCode::Succeeded) + return rdcRes; + + RDCDriver driverType = m_RDC->GetDriver(); + m_DriverName = m_RDC->GetDriverName(); + + uint64_t fileMachineIdent = m_RDC->GetMachineIdent(); + + m_Support = RenderDoc::Inst().HasReplayDriver(driverType) ? ReplaySupport::Supported + : ReplaySupport::Unsupported; + + if(fileMachineIdent != 0) { - case ContainerError::FileNotFound: return ReplayStatus::FileNotFound; - case ContainerError::FileIO: return ReplayStatus::FileIOFailed; - case ContainerError::Corrupt: return ReplayStatus::FileCorrupted; - case ContainerError::UnsupportedVersion: return ReplayStatus::FileIncompatibleVersion; - case ContainerError::NoError: + uint64_t machineIdent = OSUtility::GetMachineIdent(); + + m_Ident = OSUtility::MakeMachineIdentString(fileMachineIdent); + + if((machineIdent & OSUtility::MachineIdent_OS_Mask) != + (fileMachineIdent & OSUtility::MachineIdent_OS_Mask)) + m_Support = ReplaySupport::SuggestRemote; + } + + // can't open files without a capture in them (except images, which are special) + if(driverType != RDCDriver::Image && m_RDC->SectionIndex(SectionType::FrameCapture) == -1) + m_Support = ReplaySupport::Unsupported; + + return RDResult(); +} + +RDResult CaptureFile::InitStructuredData(RENDERDOC_ProgressCallback progress) +{ + if(m_StructuredData.chunks.empty()) + { + if(m_RDC && m_RDC->SectionIndex(SectionType::FrameCapture) >= 0) { - RDCDriver driverType = m_RDC->GetDriver(); - m_DriverName = m_RDC->GetDriverName(); + StructuredProcessor proc = RenderDoc::Inst().GetStructuredProcessor(m_RDC->GetDriver()); - uint64_t fileMachineIdent = m_RDC->GetMachineIdent(); + RenderDoc::Inst().SetProgressCallback(progress); - m_Support = RenderDoc::Inst().HasReplayDriver(driverType) ? ReplaySupport::Supported - : ReplaySupport::Unsupported; + RDResult result; - if(fileMachineIdent != 0) - { - uint64_t machineIdent = OSUtility::GetMachineIdent(); + if(proc) + result = proc(m_RDC, m_StructuredData); + else + SET_ERROR_RESULT(result, ResultCode::APIUnsupported, + "Can't get structured data for driver %s", m_RDC->GetDriverName().c_str()); - m_Ident = OSUtility::MakeMachineIdentString(fileMachineIdent); + RenderDoc::Inst().SetProgressCallback(RENDERDOC_ProgressCallback()); - if((machineIdent & OSUtility::MachineIdent_OS_Mask) != - (fileMachineIdent & OSUtility::MachineIdent_OS_Mask)) - m_Support = ReplaySupport::SuggestRemote; - } - - // can't open files without a capture in them (except images, which are special) - if(driverType != RDCDriver::Image && m_RDC->SectionIndex(SectionType::FrameCapture) == -1) - m_Support = ReplaySupport::Unsupported; - - return ReplayStatus::Succeeded; + return result; } + + RETURN_ERROR_RESULT(ResultCode::InvalidParameter, + "Can't initialise structured data for capture with no API data"); } - // all container errors should be handled and returned above - return ReplayStatus::InternalError; + return RDResult(); } -void CaptureFile::InitStructuredData(RENDERDOC_ProgressCallback progress /*= RENDERDOC_ProgressCallback()*/) +rdcpair CaptureFile::OpenCapture(const ReplayOptions &opts, + RENDERDOC_ProgressCallback progress) { - if(m_StructuredData.chunks.empty() && m_RDC && m_RDC->SectionIndex(SectionType::FrameCapture) >= 0) - { - StructuredProcessor proc = RenderDoc::Inst().GetStructuredProcessor(m_RDC->GetDriver()); + ResultDetails ret; + ReplayController *render = NULL; - RenderDoc::Inst().SetProgressCallback(progress); + if(!m_RDC) + ret = RDResult(ResultCode::InternalError, "RDC file unexpectedly NULL"); - if(proc) - proc(m_RDC, m_StructuredData); - else - RDCERR("Can't get structured data for driver %s", m_RDC->GetDriverName().c_str()); + ret = m_RDC->Error(); - RenderDoc::Inst().SetProgressCallback(RENDERDOC_ProgressCallback()); - } -} + if(!ret.OK()) + return {ret, render}; -rdcpair CaptureFile::OpenCapture(const ReplayOptions &opts, - RENDERDOC_ProgressCallback progress) -{ - if(!m_RDC || m_RDC->ErrorCode() != ContainerError::NoError) - return rdcpair(ReplayStatus::InternalError, NULL); - - ReplayController *render = new ReplayController(); - ReplayStatus ret; + render = new ReplayController(); LogReplayOptions(opts); @@ -365,13 +372,13 @@ rdcpair CaptureFile::OpenCapture(const Replay RenderDoc::Inst().SetProgressCallback(RENDERDOC_ProgressCallback()); - if(ret != ReplayStatus::Succeeded) + if(!ret.OK()) { render->Shutdown(); render = NULL; } - return rdcpair(ret, render); + return {ret, render}; } void CaptureFile::SetMetadata(const rdcstr &driverName, uint64_t machineIdent, FileType thumbType, @@ -405,13 +412,13 @@ void CaptureFile::SetMetadata(const rdcstr &driverName, uint64_t machineIdent, F m_RDC->SetData(driver, driverName, machineIdent, thumb, timeBase, timeFreq); } -ReplayStatus CaptureFile::Convert(const rdcstr &filename, const rdcstr &filetype, - const SDFile *file, RENDERDOC_ProgressCallback progress) +ResultDetails CaptureFile::Convert(const rdcstr &filename, const rdcstr &filetype, + const SDFile *file, RENDERDOC_ProgressCallback progress) { if(!m_RDC) { - RDCERR("Data missing for creation of file, set metadata first."); - return ReplayStatus::FileCorrupted; + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, + "Data missing for creation of file, set metadata first."); } // make sure progress is valid so we don't have to check it everywhere @@ -433,7 +440,10 @@ ReplayStatus CaptureFile::Convert(const rdcstr &filename, const rdcstr &filetype } else { - InitStructuredData(fetchProgress); + RDResult result = InitStructuredData(fetchProgress); + + if(result != ResultCode::Succeeded) + return result; return exporter(filename, *m_RDC, GetStructuredData(), exportProgress); } @@ -449,30 +459,24 @@ ReplayStatus CaptureFile::Convert(const rdcstr &filename, const rdcstr &filetype output.Create(filename); - if(output.ErrorCode() != ContainerError::NoError) - { - switch(output.ErrorCode()) - { - case ContainerError::FileNotFound: return ReplayStatus::FileNotFound; - case ContainerError::FileIO: return ReplayStatus::FileIOFailed; - default: break; - } - return ReplayStatus::InternalError; - } - - bool success = true; + if(output.Error() != ResultCode::Succeeded) + return output.Error(); // when we don't have a frame capture section, write it from the structured data. int frameCaptureIndex = m_RDC->SectionIndex(SectionType::FrameCapture); if(frameCaptureIndex == -1) { + RDResult result; if(file == NULL) { - InitStructuredData(fetchProgress); + result = InitStructuredData(fetchProgress); file = &m_StructuredData; } + if(result != ResultCode::Succeeded) + return result; + SectionProperties frameCapture; frameCapture.flags = SectionFlags::ZstdCompressed; frameCapture.type = SectionType::FrameCapture; @@ -487,9 +491,12 @@ ReplayStatus CaptureFile::Convert(const rdcstr &filename, const rdcstr &filetype writer->Finish(); - success = success && !writer->IsErrored(); + RDResult ret = writer->GetError(); delete writer; + + if(ret != ResultCode::Succeeded) + return ret; } else { @@ -504,14 +511,16 @@ ReplayStatus CaptureFile::Convert(const rdcstr &filename, const rdcstr &filetype writer->Finish(); - success = success && !writer->IsErrored() && !reader->IsErrored(); + RDResult ret = writer->GetError(); + if(ret == ResultCode::Succeeded) + ret = reader->GetError(); delete reader; delete writer; - } - if(!success) - return ReplayStatus::FileIOFailed; + if(ret != ResultCode::Succeeded) + return ret; + } // write all other sections for(int i = 0; i < m_RDC->NumSections(); i++) @@ -528,16 +537,17 @@ ReplayStatus CaptureFile::Convert(const rdcstr &filename, const rdcstr &filetype writer->Finish(); - success = success && !writer->IsErrored() && !reader->IsErrored(); + RDResult ret = writer->GetError(); + if(ret == ResultCode::Succeeded) + ret = reader->GetError(); delete reader; delete writer; - if(!success) - return ReplayStatus::FileIOFailed; + return ret; } - return ReplayStatus::Succeeded; + return RDResult(); } Thumbnail CaptureFile::GetThumbnail(FileType type, uint32_t maxsize) @@ -732,11 +742,23 @@ bytebuf CaptureFile::GetSectionContents(int32_t index) return ret; } -bool CaptureFile::WriteSection(const SectionProperties &props, const bytebuf &contents) +ResultDetails CaptureFile::WriteSection(const SectionProperties &props, const bytebuf &contents) { + if(!m_RDC) + { + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, + "Data missing for creation of file, set metadata first."); + } + + RDResult rdcRes = m_RDC->Error(); + + if(rdcRes != ResultCode::Succeeded) + return rdcRes; + StreamWriter *writer = m_RDC->WriteSection(props); - if(!writer) - return false; + rdcRes = m_RDC->Error(); + if(!writer || rdcRes != ResultCode::Succeeded) + return rdcRes; writer->Write(contents.data(), contents.size()); @@ -744,7 +766,7 @@ bool CaptureFile::WriteSection(const SectionProperties &props, const bytebuf &co delete writer; - return true; + return RDResult(); } bool CaptureFile::HasCallstacks() @@ -752,12 +774,18 @@ bool CaptureFile::HasCallstacks() return m_RDC && m_RDC->SectionIndex(SectionType::ResolveDatabase) >= 0; } -bool CaptureFile::InitResolver(bool interactive, RENDERDOC_ProgressCallback progress) +ResultDetails CaptureFile::InitResolver(bool interactive, RENDERDOC_ProgressCallback progress) { + if(!m_RDC) + { + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, + "Data missing for creation of file, set metadata first."); + } + if(!HasCallstacks()) { - RDCERR("Capture has no callstacks - can't initialise resolver."); - return false; + RETURN_ERROR_RESULT(ResultCode::DataNotAvailable, + "Capture has no callstacks - can't initialise resolver."); } if(progress) @@ -766,7 +794,10 @@ bool CaptureFile::InitResolver(bool interactive, RENDERDOC_ProgressCallback prog int idx = m_RDC->SectionIndex(SectionType::ResolveDatabase); if(idx < 0) - return false; + { + RETURN_ERROR_RESULT(ResultCode::DataNotAvailable, + "Capture has no callstacks - can't initialise resolver."); + } StreamReader *reader = m_RDC->ReadSection(idx); @@ -778,8 +809,7 @@ bool CaptureFile::InitResolver(bool interactive, RENDERDOC_ProgressCallback prog if(!success) { - RDCERR("Failed to read resolve database."); - return false; + RETURN_ERROR_RESULT(ResultCode::FileIOFailed, "Failed to read resolve database."); } if(progress) @@ -789,11 +819,12 @@ bool CaptureFile::InitResolver(bool interactive, RENDERDOC_ProgressCallback prog if(!m_Resolver) { - RDCERR("Couldn't create callstack resolver - capture possibly from another platform."); - return false; + RETURN_ERROR_RESULT( + ResultCode::APIUnsupported, + "Couldn't create callstack resolver - capture possibly from another platform."); } - return true; + return RDResult(); } rdcarray CaptureFile::GetResolve(const rdcarray &callstack) diff --git a/renderdoc/replay/dummy_driver.cpp b/renderdoc/replay/dummy_driver.cpp index b2c5b138c..871eb0bab 100644 --- a/renderdoc/replay/dummy_driver.cpp +++ b/renderdoc/replay/dummy_driver.cpp @@ -148,9 +148,9 @@ FrameRecord DummyDriver::GetFrameRecord() return m_FrameRecord; } -ReplayStatus DummyDriver::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) +RDResult DummyDriver::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { - return ReplayStatus::APIReplayFailed; + return ResultCode::APIReplayFailed; } void DummyDriver::ReplayLog(uint32_t endEventID, ReplayLogType replayType) @@ -314,9 +314,9 @@ bool DummyDriver::IsRemoteProxy() return m_Proxy; } -ReplayStatus DummyDriver::FatalErrorCheck() +RDResult DummyDriver::FatalErrorCheck() { - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } IReplayDriver *DummyDriver::MakeDummyDriver() diff --git a/renderdoc/replay/dummy_driver.h b/renderdoc/replay/dummy_driver.h index 2d3e7a547..5bb523999 100644 --- a/renderdoc/replay/dummy_driver.h +++ b/renderdoc/replay/dummy_driver.h @@ -63,7 +63,7 @@ public: FrameRecord GetFrameRecord(); - ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); + RDResult ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); void ReplayLog(uint32_t endEventID, ReplayLogType replayType); SDFile *GetStructuredFile(); @@ -124,7 +124,7 @@ public: // IReplayDriver bool IsRemoteProxy(); - ReplayStatus FatalErrorCheck(); + RDResult FatalErrorCheck(); IReplayDriver *MakeDummyDriver(); rdcarray GetSupportedWindowSystems(); diff --git a/renderdoc/replay/entry_points.cpp b/renderdoc/replay/entry_points.cpp index f8f9a443a..097d32978 100644 --- a/renderdoc/replay/entry_points.cpp +++ b/renderdoc/replay/entry_points.cpp @@ -28,6 +28,7 @@ #include "api/replay/version.h" #include "common/common.h" #include "common/formatting.h" +#include "common/threading.h" #include "core/core.h" #include "maths/camera.h" #include "maths/formatpacking.h" @@ -36,6 +37,27 @@ #include "strings/string_utils.h" #include "superluminal/superluminal.h" +Threading::CriticalSection detailStringLock; +rdcarray detailStrings; + +RDResult::operator ResultDetails() const +{ + RDCCOMPILE_ASSERT(ResultCode(0) == ResultCode::Succeeded, + "ResultCode 0 value should be succeeded"); + + ResultDetails ret; + ret.code = code; + ret.internal_msg = NULL; + if(!message.empty()) + { + SCOPED_LOCK(detailStringLock); + ret.internal_msg = new rdcstr(ToStr(code) + ": " + message); + detailStrings.push_back(ret.internal_msg); + } + + return ret; +} + // these entry points are for the replay/analysis side - not for the application. extern "C" RENDERDOC_API uint32_t RENDERDOC_CC RENDERDOC_NumVerticesPerPrimitive(Topology topology) @@ -287,6 +309,13 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_InitialiseReplay(GlobalEnvi extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_ShutdownReplay() { + { + SCOPED_LOCK(detailStringLock); + for(const rdcstr *msg : detailStrings) + delete msg; + detailStrings.clear(); + } + RenderDoc::Inst().ShutdownReplay(); } @@ -335,11 +364,11 @@ RENDERDOC_ExecuteAndInject(const rdcstr &app, const rdcstr &workingDir, const rd const rdcarray &env, const rdcstr &capturefile, const CaptureOptions &opts, bool waitForExit) { - rdcpair status = Process::LaunchAndInjectIntoProcess( + rdcpair status = Process::LaunchAndInjectIntoProcess( app, workingDir, cmdLine, env, capturefile, opts, waitForExit != 0); ExecuteResult ret; - ret.status = status.first; + ret.result = status.first; ret.ident = status.second; return ret; } @@ -349,9 +378,8 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_GetDefaultCaptureOptions(Ca *opts = CaptureOptions(); } -extern "C" RENDERDOC_API bool RENDERDOC_CC RENDERDOC_StartGlobalHook(const rdcstr &pathmatch, - const rdcstr &capturefile, - const CaptureOptions &opts) +extern "C" RENDERDOC_API ResultDetails RENDERDOC_CC RENDERDOC_StartGlobalHook( + const rdcstr &pathmatch, const rdcstr &capturefile, const CaptureOptions &opts) { return Process::StartGlobalHook(pathmatch, capturefile, opts); } @@ -375,11 +403,11 @@ extern "C" RENDERDOC_API ExecuteResult RENDERDOC_CC RENDERDOC_InjectIntoProcess(uint32_t pid, const rdcarray &env, const rdcstr &capturefile, const CaptureOptions &opts, bool waitForExit) { - rdcpair status = + rdcpair status = Process::InjectIntoProcess(pid, env, capturefile, opts, waitForExit != 0); ExecuteResult ret; - ret.status = status.first; + ret.result = status.first; ret.ident = status.second; return ret; } diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index c9dbf8339..6647653e3 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -53,15 +53,6 @@ class undersized #define SIZE_CHECK(expected) #endif -template -void DoSerialise(SerialiserType &ser, ExecuteResult &el) -{ - SERIALISE_MEMBER(status); - SERIALISE_MEMBER(ident); - - SIZE_CHECK(8); -} - template void DoSerialise(SerialiserType &ser, PathEntry &el) { @@ -2354,7 +2345,6 @@ void DoSerialise(SerialiserType &ser, VKPipe::State &el) #pragma endregion Vulkan pipeline state -INSTANTIATE_SERIALISE_TYPE(ExecuteResult) INSTANTIATE_SERIALISE_TYPE(PathEntry) INSTANTIATE_SERIALISE_TYPE(SectionProperties) INSTANTIATE_SERIALISE_TYPE(EnvironmentModification) diff --git a/renderdoc/replay/replay_controller.cpp b/renderdoc/replay/replay_controller.cpp index 10d33dd65..9506b8e03 100644 --- a/renderdoc/replay/replay_controller.cpp +++ b/renderdoc/replay/replay_controller.cpp @@ -553,7 +553,7 @@ bytebuf ReplayController::GetTextureData(ResourceId tex, const Subresource &sub) return ret; } -bool ReplayController::SaveTexture(const TextureSave &saveData, const rdcstr &path) +ResultDetails ReplayController::SaveTexture(const TextureSave &saveData, const rdcstr &path) { CHECK_REPLAY_THREAD(); RENDERDOC_PROFILEFUNCTION(); @@ -563,14 +563,13 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const rdcstr &pa if(liveid == ResourceId()) { - RDCERR("Couldn't get Live ID for %s getting texture data", ToStr(sd.resourceId).c_str()); - return false; + RETURN_ERROR_RESULT(ResultCode::InvalidParameter, + "Couldn't get Live ID for %s getting texture data", + ToStr(sd.resourceId).c_str()); } TextureDescription td = m_pDevice->GetTexture(liveid); - bool success = false; - // clamp sample/mip/slice indices if(td.msSamp == 1) { @@ -833,8 +832,10 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const rdcstr &pa case ResourceFormatType::YUV12: case ResourceFormatType::YUV16: case ResourceFormatType::R4G4: - RDCERR("Unsupported file format %u", td.format.type); - return false; + { + RETURN_ERROR_RESULT(ResultCode::ImageUnsupported, "Unsupported file format %s", + ToStr(td.format.type).c_str()); + } default: bytesPerPixel = td.format.compCount * td.format.compByteWidth; } @@ -868,12 +869,12 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const rdcstr &pa if(data.empty()) { - RDCERR("Couldn't get bytes for mip %u, slice %u", mip, slice); - for(size_t i = 0; i < subdata.size(); i++) delete[] subdata[i]; - return false; + RETURN_ERROR_RESULT(ResultCode::DataNotAvailable, + "Couldn't readback bytes for mip %u, slice %u, sample %u", sub.mip, + sub.slice, sub.sample); } if(td.depth == 1) @@ -1170,10 +1171,12 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const rdcstr &pa FILE *f = FileIO::fopen(path, FileIO::WriteBinary); + RDResult res; + if(!f) { - success = false; - RDCERR("Couldn't write to path %s, error: %s", path.c_str(), FileIO::ErrorString().c_str()); + RETURN_ERROR_RESULT(ResultCode::FileIOFailed, "Couldn't write to path %s, error: %s", + path.c_str(), FileIO::ErrorString().c_str()); } else { @@ -1198,34 +1201,31 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const rdcstr &pa if(singleSlice) ddsData.depth = ddsData.slices = 1; - success = write_dds_to_file(f, ddsData); + res = write_dds_to_file(f, ddsData); } else if(sd.destType == FileType::BMP) { int ret = stbi_write_bmp_to_func(fileWriteFunc, (void *)f, td.width, td.height, numComps, subdata[0]); - success = (ret != 0); - if(!success) - RDCERR("stbi_write_bmp_to_func failed: %d", ret); + if(ret == 0) + SET_ERROR_RESULT(res, ResultCode::InternalError, "Failed to write BMP image"); } else if(sd.destType == FileType::PNG) { int ret = stbi_write_png_to_func(fileWriteFunc, (void *)f, td.width, td.height, numComps, subdata[0], rowPitch); - success = (ret != 0); - if(!success) - RDCERR("stbi_write_png_to_func failed: %d", ret); + if(ret == 0) + SET_ERROR_RESULT(res, ResultCode::InternalError, "Failed to write PNG image"); } else if(sd.destType == FileType::TGA) { int ret = stbi_write_tga_to_func(fileWriteFunc, (void *)f, td.width, td.height, numComps, subdata[0]); - success = (ret != 0); - if(!success) - RDCERR("stbi_write_tga_to_func failed: %d", ret); + if(ret == 0) + SET_ERROR_RESULT(res, ResultCode::InternalError, "Failed to write TGA image"); } else if(sd.destType == FileType::JPG) { @@ -1239,14 +1239,13 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const rdcstr &pa char *jpgdst = new char[len]; - success = jpge::compress_image_to_jpeg_file_in_memory(jpgdst, len, td.width, td.height, - numComps, subdata[0], p); - - if(!success) - RDCERR("jpge::compress_image_to_jpeg_file_in_memory failed"); + bool success = jpge::compress_image_to_jpeg_file_in_memory(jpgdst, len, td.width, td.height, + numComps, subdata[0], p); if(success) fwrite(jpgdst, 1, len, f); + else + SET_ERROR_RESULT(res, ResultCode::InternalError, "Failed to write JPG image"); delete[] jpgdst; } @@ -1338,10 +1337,9 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const rdcstr &pa if(sd.destType == FileType::HDR) { int ret = stbi_write_hdr_to_func(fileWriteFunc, (void *)f, td.width, td.height, 4, fldata); - success = (ret != 0); - if(!success) - RDCERR("stbi_write_hdr_to_func failed: %d", ret); + if(ret == 0) + SET_ERROR_RESULT(res, ResultCode::InternalError, "Failed to write HDR image"); } else if(sd.destType == FileType::EXR) { @@ -1384,11 +1382,10 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const rdcstr &pa size_t ret = SaveEXRImageToMemory(&exrImage, &exrHeader, &mem, &err); - success = (ret > 0); - if(success) + if(ret > 0) FileIO::fwrite(mem, 1, ret, f); else - RDCERR("Error saving EXR file %d: '%s'", ret, err); + SET_ERROR_RESULT(res, ResultCode::InternalError, "Failed to write EXR image: %s", err); free(mem); } @@ -1412,7 +1409,7 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const rdcstr &pa for(size_t i = 0; i < subdata.size(); i++) delete[] subdata[i]; - return success; + return res; } rdcarray ReplayController::PixelHistory(ResourceId target, uint32_t x, uint32_t y, @@ -1909,14 +1906,15 @@ void ReplayController::Shutdown() bool ReplayController::FatalErrorCheck() { // we've already processed the fatal error if we have a status - if(m_FatalError != ReplayStatus::Succeeded) + if(m_FatalError != ResultCode::Succeeded) return false; m_FatalError = m_pDevice->FatalErrorCheck(); - if(m_FatalError != ReplayStatus::Succeeded) + if(m_FatalError != ResultCode::Succeeded) { - RDCLOG("Fatal error detected: %s", ToStr(m_FatalError).c_str()); + RDCLOG("Fatal error detected: %s (%s)", ToStr(m_FatalError.code).c_str(), + m_FatalError.message.c_str()); IReplayDriver *old = m_pDevice; @@ -2102,26 +2100,26 @@ void ReplayController::RemoveReplacement(ResourceId id) m_Outputs[i]->Display(); } -ReplayStatus ReplayController::CreateDevice(RDCFile *rdc, const ReplayOptions &opts) +RDResult ReplayController::CreateDevice(RDCFile *rdc, const ReplayOptions &opts) { CHECK_REPLAY_THREAD(); RENDERDOC_PROFILEFUNCTION(); IReplayDriver *driver = NULL; - ReplayStatus status = RenderDoc::Inst().CreateReplayDriver(rdc, opts, &driver); + RDResult result = RenderDoc::Inst().CreateReplayDriver(rdc, opts, &driver); - if(driver && status == ReplayStatus::Succeeded) + if(driver && result == ResultCode::Succeeded) { RDCLOG("Created replay driver."); return PostCreateInit(driver, rdc); } RDCERR("Couldn't create a replay device."); - return status; + return result; } -ReplayStatus ReplayController::SetDevice(IReplayDriver *device) +RDResult ReplayController::SetDevice(IReplayDriver *device) { CHECK_REPLAY_THREAD(); @@ -2132,10 +2130,10 @@ ReplayStatus ReplayController::SetDevice(IReplayDriver *device) } RDCERR("Given invalid replay driver."); - return ReplayStatus::InternalError; + return ResultCode::InternalError; } -ReplayStatus ReplayController::PostCreateInit(IReplayDriver *device, RDCFile *rdc) +RDResult ReplayController::PostCreateInit(IReplayDriver *device, RDCFile *rdc) { CHECK_REPLAY_THREAD(); @@ -2147,9 +2145,9 @@ ReplayStatus ReplayController::PostCreateInit(IReplayDriver *device, RDCFile *rd GCNISA::CacheSupport(m_APIProps.pipelineType); - ReplayStatus status = m_pDevice->ReadLogInitialisation(rdc, false); + RDResult result = m_pDevice->ReadLogInitialisation(rdc, false); FatalErrorCheck(); - if(m_FatalError != ReplayStatus::Succeeded) + if(m_FatalError != ResultCode::Succeeded) return m_FatalError; m_pDevice->SetPipelineStates(&m_D3D11PipelineState, &m_D3D12PipelineState, &m_GLPipelineState, @@ -2157,8 +2155,8 @@ ReplayStatus ReplayController::PostCreateInit(IReplayDriver *device, RDCFile *rd GCNISA::GetTargets(m_APIProps.pipelineType, m_GCNTargets); - if(status != ReplayStatus::Succeeded) - return status; + if(result != ResultCode::Succeeded) + return result; m_Buffers = m_pDevice->GetBuffers(); FatalErrorCheck(); @@ -2170,11 +2168,11 @@ ReplayStatus ReplayController::PostCreateInit(IReplayDriver *device, RDCFile *rd m_FrameRecord = m_pDevice->GetFrameRecord(); FatalErrorCheck(); - if(m_FatalError != ReplayStatus::Succeeded) + if(m_FatalError != ResultCode::Succeeded) return m_FatalError; if(m_FrameRecord.actionList.empty()) - return ReplayStatus::APIReplayFailed; + return ResultCode::APIReplayFailed; m_Actions.clear(); SetupActionPointers(m_Actions, m_FrameRecord.actionList); diff --git a/renderdoc/replay/replay_controller.h b/renderdoc/replay/replay_controller.h index 3bf3a75d9..7da2f8cb5 100644 --- a/renderdoc/replay/replay_controller.h +++ b/renderdoc/replay/replay_controller.h @@ -46,13 +46,13 @@ public: rdcpair GetDimensions(); void ClearThumbnails(); - bool AddThumbnail(WindowingData window, ResourceId texID, const Subresource &sub, - CompType typeCast); + ResultDetails AddThumbnail(WindowingData window, ResourceId texID, const Subresource &sub, + CompType typeCast); void Display(); ReplayOutputType GetType() { return m_Type; } - bool SetPixelContext(WindowingData window); + ResultDetails SetPixelContext(WindowingData window); void SetPixelContextLocation(uint32_t x, uint32_t y); void DisablePixelContext(); @@ -130,8 +130,8 @@ public: APIProperties GetAPIProperties(); - ReplayStatus CreateDevice(RDCFile *rdc, const ReplayOptions &opts); - ReplayStatus SetDevice(IReplayDriver *device); + RDResult CreateDevice(RDCFile *rdc, const ReplayOptions &opts); + RDResult SetDevice(IReplayDriver *device); void FileChanged(); @@ -175,7 +175,14 @@ public: const rdcarray &GetBuffers(); const rdcarray &GetResources(); rdcarray GetDebugMessages(); - ReplayStatus GetFatalErrorStatus() { return m_FatalError; } + ResultDetails GetFatalErrorStatus() + { + // don't reconvert this on return every time, or we would cache many strings if this function is + // repeatedly called (which it is intended to be) + if(m_FatalError.code != m_FatalErrorResult.code) + m_FatalErrorResult = m_FatalError; + return m_FatalErrorResult; + } rdcarray GetShaderEntryPoints(ResourceId shader); const ShaderReflection *GetShader(ResourceId pipeline, ResourceId shader, ShaderEntryPoint entry); @@ -201,7 +208,7 @@ public: bytebuf GetBufferData(ResourceId buff, uint64_t offset, uint64_t len); bytebuf GetTextureData(ResourceId buff, const Subresource &sub); - bool SaveTexture(const TextureSave &saveData, const rdcstr &path); + ResultDetails SaveTexture(const TextureSave &saveData, const rdcstr &path); rdcarray GetCBufferVariableContents(ResourceId pipeline, ResourceId shader, ShaderStage stage, const rdcstr &entryPoint, @@ -224,7 +231,7 @@ public: private: virtual ~ReplayController(); - ReplayStatus PostCreateInit(IReplayDriver *device, RDCFile *rdc); + RDResult PostCreateInit(IReplayDriver *device, RDCFile *rdc); void FetchPipelineState(uint32_t eventId); @@ -244,7 +251,8 @@ private: int32_t m_ReplayLoopCancel = 0; int32_t m_ReplayLoopFinished = 0; - ReplayStatus m_FatalError = ReplayStatus::Succeeded; + RDResult m_FatalError = ResultCode::Succeeded; + ResultDetails m_FatalErrorResult = {ResultCode::Succeeded}; uint32_t m_EventID; diff --git a/renderdoc/replay/replay_driver.h b/renderdoc/replay/replay_driver.h index b887f87df..08b457d38 100644 --- a/renderdoc/replay/replay_driver.h +++ b/renderdoc/replay/replay_driver.h @@ -160,7 +160,7 @@ public: virtual FrameRecord GetFrameRecord() = 0; - virtual ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) = 0; + virtual RDResult ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) = 0; virtual void ReplayLog(uint32_t endEventID, ReplayLogType replayType) = 0; virtual SDFile *GetStructuredFile() = 0; @@ -212,7 +212,7 @@ public: virtual bool IsRenderOutput(ResourceId id) = 0; virtual void FileChanged() = 0; - virtual ReplayStatus FatalErrorCheck() = 0; + virtual RDResult FatalErrorCheck() = 0; virtual bool NeedRemapForFetch(const ResourceFormat &format) = 0; diff --git a/renderdoc/replay/replay_output.cpp b/renderdoc/replay/replay_output.cpp index de22ac807..e58061c4a 100644 --- a/renderdoc/replay/replay_output.cpp +++ b/renderdoc/replay/replay_output.cpp @@ -24,6 +24,7 @@ ******************************************************************************/ #include "common/common.h" +#include "common/formatting.h" #include "maths/formatpacking.h" #include "maths/matrix.h" #include "strings/string_utils.h" @@ -343,7 +344,7 @@ void ReplayOutput::ClearThumbnails() m_Thumbnails.clear(); } -bool ReplayOutput::SetPixelContext(WindowingData window) +ResultDetails ReplayOutput::SetPixelContext(WindowingData window) { CHECK_REPLAY_THREAD(); @@ -353,13 +354,16 @@ bool ReplayOutput::SetPixelContext(WindowingData window) m_pController->FatalErrorCheck(); - RDCASSERT(m_PixelContext.outputID > 0); + if(m_PixelContext.outputID == 0) + { + RETURN_ERROR_RESULT(ResultCode::InternalError, "Window creation failed"); + } - return m_PixelContext.outputID != 0; + return RDResult(); } -bool ReplayOutput::AddThumbnail(WindowingData window, ResourceId texID, const Subresource &sub, - CompType typeCast) +ResultDetails ReplayOutput::AddThumbnail(WindowingData window, ResourceId texID, + const Subresource &sub, CompType typeCast) { CHECK_REPLAY_THREAD(); @@ -389,7 +393,7 @@ bool ReplayOutput::AddThumbnail(WindowingData window, ResourceId texID, const Su m_Thumbnails[i].typeCast = typeCast; m_Thumbnails[i].dirty = true; - return true; + return RDResult(); } } @@ -403,11 +407,14 @@ bool ReplayOutput::AddThumbnail(WindowingData window, ResourceId texID, const Su m_pController->FatalErrorCheck(); - RDCASSERT(p.outputID > 0); - m_Thumbnails.push_back(p); - return true; + if(p.outputID == 0) + { + RETURN_ERROR_RESULT(ResultCode::InternalError, "Window creation failed"); + } + + return RDResult(); } rdcpair ReplayOutput::PickVertex(uint32_t x, uint32_t y) diff --git a/renderdoc/serialise/codecs/chrome_json_codec.cpp b/renderdoc/serialise/codecs/chrome_json_codec.cpp index ce802e95a..d45249f81 100644 --- a/renderdoc/serialise/codecs/chrome_json_codec.cpp +++ b/renderdoc/serialise/codecs/chrome_json_codec.cpp @@ -28,13 +28,14 @@ #include "common/formatting.h" #include "serialise/rdcfile.h" -ReplayStatus exportChrome(const rdcstr &filename, const RDCFile &rdc, const SDFile &structData, - RENDERDOC_ProgressCallback progress) +RDResult exportChrome(const rdcstr &filename, const RDCFile &rdc, const SDFile &structData, + RENDERDOC_ProgressCallback progress) { FILE *f = FileIO::fopen(filename, FileIO::WriteText); if(!f) - return ReplayStatus::FileIOFailed; + RETURN_ERROR_RESULT(ResultCode::FileIOFailed, "Failed to open '%s' for write: %s", + filename.c_str(), FileIO::ErrorString().c_str()); rdcstr str; @@ -91,7 +92,7 @@ ReplayStatus exportChrome(const rdcstr &filename, const RDCFile &rdc, const SDFi FileIO::fclose(f); - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } static ConversionRegistration XMLConversionRegistration( diff --git a/renderdoc/serialise/codecs/xml_codec.cpp b/renderdoc/serialise/codecs/xml_codec.cpp index 863160319..8f3952b66 100644 --- a/renderdoc/serialise/codecs/xml_codec.cpp +++ b/renderdoc/serialise/codecs/xml_codec.cpp @@ -284,9 +284,8 @@ static void Obj2XML(pugi::xml_node &parent, SDObject &child) } } -static ReplayStatus Structured2XML(const rdcstr &filename, const RDCFile &file, uint64_t version, - const StructuredChunkList &chunks, - RENDERDOC_ProgressCallback progress) +static RDResult Structured2XML(const rdcstr &filename, const RDCFile &file, uint64_t version, + const StructuredChunkList &chunks, RENDERDOC_ProgressCallback progress) { pugi::xml_document doc; @@ -472,7 +471,7 @@ static ReplayStatus Structured2XML(const rdcstr &filename, const RDCFile &file, xml_file_writer writer(filename); doc.save(writer); - return writer.stream.IsErrored() ? ReplayStatus::FileIOFailed : ReplayStatus::Succeeded; + return writer.stream.GetError(); } static SDObject *XML2Obj(pugi::xml_node &obj) @@ -577,11 +576,10 @@ static SDObject *XML2Obj(pugi::xml_node &obj) return ret; } -static ReplayStatus XML2Structured(const rdcstr &xml, const ThumbTypeAndData &thumb, - const ThumbTypeAndData &extThumb, const bytebuf &logfile, - const StructuredBufferList &buffers, RDCFile *rdc, - uint64_t &version, StructuredChunkList &chunks, - RENDERDOC_ProgressCallback progress) +static RDResult XML2Structured(const rdcstr &xml, const ThumbTypeAndData &thumb, + const ThumbTypeAndData &extThumb, const bytebuf &logfile, + const StructuredBufferList &buffers, RDCFile *rdc, uint64_t &version, + StructuredChunkList &chunks, RENDERDOC_ProgressCallback progress) { pugi::xml_document doc; doc.load_string(xml.c_str()); @@ -589,28 +587,22 @@ static ReplayStatus XML2Structured(const rdcstr &xml, const ThumbTypeAndData &th pugi::xml_node root = doc.child("rdc"); if(!root) - { - RDCERR("Malformed document, expected rdc node"); - return ReplayStatus::FileCorrupted; - } + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, + "Malformed xml document, couldn't get root node"); pugi::xml_node xHeader = root.first_child(); if(strcmp(xHeader.name(), "header") != 0) - { - RDCERR("Malformed document, expected header node"); - return ReplayStatus::FileCorrupted; - } + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, + "Malformed xml document, expected
node got <%s>", xHeader.name()); // process the header and push meta-data into RDC { pugi::xml_node xDriver = xHeader.first_child(); if(strcmp(xDriver.name(), "driver") != 0) - { - RDCERR("Malformed document, expected driver node"); - return ReplayStatus::FileCorrupted; - } + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, + "Malformed xml document, expected node got <%s>", xDriver.name()); RDCDriver driver = (RDCDriver)xDriver.attribute("id").as_uint(); rdcstr driverName = xDriver.text().as_string(); @@ -622,10 +614,9 @@ static ReplayStatus XML2Structured(const rdcstr &xml, const ThumbTypeAndData &th pugi::xml_node xThumbnail = xIdent.next_sibling(); if(strcmp(xThumbnail.name(), "thumbnail") != 0) - { - RDCERR("Malformed document, expected driver node"); - return ReplayStatus::FileCorrupted; - } + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, + "Malformed xml document, expected node got <%s>", + xThumbnail.name()); pugi::xml_node xTimebase = xThumbnail.next_sibling(); @@ -636,10 +627,9 @@ static ReplayStatus XML2Structured(const rdcstr &xml, const ThumbTypeAndData &th if(xTimebase) { if(strcmp(xTimebase.name(), "timebase") != 0) - { - RDCERR("Malformed document, expected driver node"); - return ReplayStatus::FileCorrupted; - } + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, + "Malformed xml document, expected node got <%s>", + xTimebase.name()); timeBase = xTimebase.attribute("base").as_ullong(); timeFreq = xTimebase.attribute("frequency").as_double(); @@ -781,16 +771,12 @@ static ReplayStatus XML2Structured(const rdcstr &xml, const ThumbTypeAndData &th pugi::xml_node xChunks = xSection; if(strcmp(xSection.name(), "chunks") != 0) - { - RDCERR("Malformed document, expected chunks node"); - return ReplayStatus::FileCorrupted; - } + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, + "Malformed xml document, expected node, got <%s>", xSection.name()); if(!xChunks.attribute("version")) - { - RDCERR("Malformed document, expected version attribute"); - return ReplayStatus::FileCorrupted; - } + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, + "Malformed xml document, expected version attribute"); version = xChunks.attribute("version").as_ullong(); @@ -800,7 +786,9 @@ static ReplayStatus XML2Structured(const rdcstr &xml, const ThumbTypeAndData &th for(pugi::xml_node xChunk = xChunks.first_child(); xChunk; xChunk = xChunk.next_sibling()) { if(strcmp(xChunk.name(), "chunk") != 0) - return ReplayStatus::FileCorrupted; + RETURN_ERROR_RESULT(ResultCode::FileCorrupted, + "Malformed xml document, expected child under , got <%s>", + xChunk.name()); SDChunk *chunk = new SDChunk(rdcstr(xChunk.attribute("name").as_string())); @@ -851,12 +839,11 @@ static ReplayStatus XML2Structured(const rdcstr &xml, const ThumbTypeAndData &th chunkIdx++; } - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } -static ReplayStatus Buffers2ZIP(const rdcstr &filename, const RDCFile &file, - const StructuredBufferList &buffers, - RENDERDOC_ProgressCallback progress) +static RDResult Buffers2ZIP(const rdcstr &filename, const RDCFile &file, + const StructuredBufferList &buffers, RENDERDOC_ProgressCallback progress) { rdcstr zipFile = strip_extension(filename); @@ -867,8 +854,8 @@ static ReplayStatus Buffers2ZIP(const rdcstr &filename, const RDCFile &file, if(!b) { - RDCERR("Failed to open .zip file '%s'", zipFile.c_str()); - return ReplayStatus::FileIOFailed; + RETURN_ERROR_RESULT(ResultCode::FileIOFailed, "Failed to open .zip file '%s': %s", + zipFile.c_str(), mz_zip_get_error_string(zip.m_last_error)); } for(size_t i = 0; i < buffers.size(); i++) @@ -948,19 +935,19 @@ static ReplayStatus Buffers2ZIP(const rdcstr &filename, const RDCFile &file, mz_zip_writer_finalize_archive(&zip); mz_zip_writer_end(&zip); - return ReplayStatus::Succeeded; + return ResultCode::Succeeded; } -static bool ZIP2Buffers(const rdcstr &filename, ThumbTypeAndData &thumb, ThumbTypeAndData &extThumb, - bytebuf &logfile, StructuredBufferList &buffers, - RENDERDOC_ProgressCallback progress) +static RDResult ZIP2Buffers(const rdcstr &filename, ThumbTypeAndData &thumb, + ThumbTypeAndData &extThumb, bytebuf &logfile, + StructuredBufferList &buffers, RENDERDOC_ProgressCallback progress) { rdcstr zipFile = strip_extension(filename); if(!FileIO::exists(zipFile)) { - RDCERR("Expected to file zip for %s at %s", filename.c_str(), zipFile.c_str()); - return false; + RETURN_ERROR_RESULT(ResultCode::FileIOFailed, "Expected to find zip for %s at %s", + filename.c_str(), zipFile.c_str()); } mz_zip_archive zip; @@ -1028,22 +1015,19 @@ static bool ZIP2Buffers(const rdcstr &filename, ThumbTypeAndData &thumb, ThumbTy mz_zip_reader_end(&zip); - return true; + return ResultCode::Succeeded; } -ReplayStatus importXMLZ(const rdcstr &filename, StreamReader &reader, RDCFile *rdc, - SDFile &structData, RENDERDOC_ProgressCallback progress) +RDResult importXMLZ(const rdcstr &filename, StreamReader &reader, RDCFile *rdc, SDFile &structData, + RENDERDOC_ProgressCallback progress) { ThumbTypeAndData thumb, extThumb; bytebuf logfile; if(!filename.empty()) { - bool success = ZIP2Buffers(filename, thumb, extThumb, logfile, structData.buffers, progress); - if(!success) - { - RDCERR("Couldn't load zip to go with %s", filename.c_str()); - return ReplayStatus::FileCorrupted; - } + RDResult res = ZIP2Buffers(filename, thumb, extThumb, logfile, structData.buffers, progress); + if(res != ResultCode::Succeeded) + return res; } rdcstr buf; @@ -1054,19 +1038,19 @@ ReplayStatus importXMLZ(const rdcstr &filename, StreamReader &reader, RDCFile *r structData.version, structData.chunks, progress); } -ReplayStatus exportXMLZ(const rdcstr &filename, const RDCFile &rdc, const SDFile &structData, - RENDERDOC_ProgressCallback progress) +RDResult exportXMLZ(const rdcstr &filename, const RDCFile &rdc, const SDFile &structData, + RENDERDOC_ProgressCallback progress) { - ReplayStatus ret = Buffers2ZIP(filename, rdc, structData.buffers, progress); + RDResult ret = Buffers2ZIP(filename, rdc, structData.buffers, progress); - if(ret != ReplayStatus::Succeeded) + if(ret != ResultCode::Succeeded) return ret; return Structured2XML(filename, rdc, structData.version, structData.chunks, progress); } -ReplayStatus exportXMLOnly(const rdcstr &filename, const RDCFile &rdc, const SDFile &structData, - RENDERDOC_ProgressCallback progress) +RDResult exportXMLOnly(const rdcstr &filename, const RDCFile &rdc, const SDFile &structData, + RENDERDOC_ProgressCallback progress) { return Structured2XML(filename, rdc, structData.version, structData.chunks, progress); } diff --git a/renderdoc/serialise/lz4io.cpp b/renderdoc/serialise/lz4io.cpp index 3eca00f98..dca8e971e 100644 --- a/renderdoc/serialise/lz4io.cpp +++ b/renderdoc/serialise/lz4io.cpp @@ -131,18 +131,22 @@ bool LZ4Compressor::FlushPage0() if(compSize < 0) { - RDCERR("Error compressing: %i", compSize); FreeAlignedBuffer(m_Page[0]); FreeAlignedBuffer(m_Page[1]); FreeAlignedBuffer(m_CompressBuffer); m_Page[0] = m_Page[1] = m_CompressBuffer = NULL; + SET_ERROR_RESULT(m_Error, ResultCode::CompressionFailed, "LZ4 compression failed: %i", compSize); return false; } bool success = true; success &= m_Write->Write(compSize); + if(!success) + m_Error = m_Write->GetError(); success &= m_Write->Write(m_CompressBuffer, compSize); + if(!success) + m_Error = m_Write->GetError(); // swap pages std::swap(m_Page[0], m_Page[1]); @@ -184,7 +188,12 @@ bool LZ4Decompressor::Recompress(Compressor *comp) { success &= FillPage0(); if(success) + { success &= comp->Write(m_Page[0], m_PageLength); + + if(!success) + m_Error = comp->GetError(); + } } success &= comp->Finish(); @@ -263,22 +272,31 @@ bool LZ4Decompressor::FillPage0() success &= m_Read->Read(compSize); if(!success || compSize < 0 || compSize > (int)LZ4_COMPRESSBOUND(lz4BlockSize)) { - RDCERR("Error reading size: %i", compSize); FreeAlignedBuffer(m_Page[0]); FreeAlignedBuffer(m_Page[1]); FreeAlignedBuffer(m_CompressBuffer); m_Page[0] = m_Page[1] = m_CompressBuffer = NULL; - return false; + + if(success) + { + m_Error = m_Read->GetError(); + } + else + { + SET_ERROR_RESULT(m_Error, ResultCode::CompressionFailed, + "LZ4 decompression encountered invalid compressed block size: %i", compSize); + } } + success &= m_Read->Read(m_CompressBuffer, compSize); if(!success) { - RDCERR("Error reading block: %i", compSize); FreeAlignedBuffer(m_Page[0]); FreeAlignedBuffer(m_Page[1]); FreeAlignedBuffer(m_CompressBuffer); m_Page[0] = m_Page[1] = m_CompressBuffer = NULL; + m_Error = m_Read->GetError(); return false; } @@ -287,11 +305,14 @@ bool LZ4Decompressor::FillPage0() if(decompSize < 0) { - RDCERR("Error decompressing: %i", decompSize); FreeAlignedBuffer(m_Page[0]); FreeAlignedBuffer(m_Page[1]); FreeAlignedBuffer(m_CompressBuffer); m_Page[0] = m_Page[1] = m_CompressBuffer = NULL; + + SET_ERROR_RESULT(m_Error, ResultCode::CompressionFailed, + "LZ4 decompression failed on block: %i", decompSize); + return false; } diff --git a/renderdoc/serialise/rdcfile.cpp b/renderdoc/serialise/rdcfile.cpp index 9d887e0f9..cca8dfe3b 100644 --- a/renderdoc/serialise/rdcfile.cpp +++ b/renderdoc/serialise/rdcfile.cpp @@ -231,19 +231,6 @@ struct BinarySectionHeader }; }; -#define SETERROR(error, ...) \ - { \ - m_ErrorString = StringFormat::Fmt(__VA_ARGS__); \ - RDCERR("%s", m_ErrorString.c_str()); \ - m_Error = error; \ - } - -#define RETURNERROR(error, ...) \ - { \ - SETERROR(error, __VA_ARGS__); \ - return; \ - } - RDCFile::~RDCFile() { if(m_File) @@ -256,7 +243,8 @@ void RDCFile::Open(const rdcstr &path) // empty path. if(path.empty()) { - RETURNERROR(ContainerError::FileNotFound, "Invalid file path specified"); + SET_ERROR_RESULT(m_Error, ResultCode::FileNotFound, "Invalid file path specified"); + return; } RDCLOG("Opening RDCFile %s", path.c_str()); @@ -270,8 +258,9 @@ void RDCFile::Open(const rdcstr &path) if(!m_File) { - RETURNERROR(ContainerError::FileNotFound, "Can't open capture file '%s' for read - errno %d", - path.c_str(), errno); + SET_ERROR_RESULT(m_Error, ResultCode::FileNotFound, + "Can't open capture file '%s' for read - errno %d", path.c_str(), errno); + return; } // try to identify if this is an image @@ -332,13 +321,16 @@ void RDCFile::Init(StreamReader &reader) if(reader.IsErrored()) { - RETURNERROR(ContainerError::FileIO, "I/O error reading magic number"); + SET_ERROR_RESULT(m_Error, ResultCode::FileIOFailed, "I/O error reading magic number"); + return; } if(header.magic != MAGIC_HEADER) { - RETURNERROR(ContainerError::Corrupt, "Invalid capture file. Expected magic %08x, got %08x.", - MAGIC_HEADER, (uint32_t)header.magic); + SET_ERROR_RESULT(m_Error, ResultCode::FileCorrupted, + "Invalid capture file. Expected magic %08x, got %08x.", MAGIC_HEADER, + (uint32_t)header.magic); + return; } m_SerVer = header.version; @@ -353,11 +345,12 @@ void RDCFile::Init(StreamReader &reader) memcpy(header.progVersion, "v0.x", sizeof("v0.x")); } - RETURNERROR( - ContainerError::UnsupportedVersion, + SET_ERROR_RESULT( + m_Error, ResultCode::FileIncompatibleVersion, "Capture file from wrong version. This program (v%s) uses logfile version %u, this file is " "logfile version %u captured on %s.", MAJOR_MINOR_VERSION_STRING, SERIALISE_VERSION, header.version, header.progVersion); + return; } BinaryThumbnail thumb; @@ -365,13 +358,16 @@ void RDCFile::Init(StreamReader &reader) if(reader.IsErrored()) { - RETURNERROR(ContainerError::FileIO, "I/O error reading thumbnail header"); + SET_ERROR_RESULT(m_Error, ResultCode::FileIOFailed, "I/O error reading thumbnail header"); + return; } // check the thumbnail size is sensible if(thumb.length > 10 * 1024 * 1024) { - RETURNERROR(ContainerError::Corrupt, "Thumbnail byte length invalid: %u", thumb.length); + SET_ERROR_RESULT(m_Error, ResultCode::FileCorrupted, "Thumbnail byte length invalid: %u", + thumb.length); + return; } bytebuf thumbData; @@ -380,7 +376,8 @@ void RDCFile::Init(StreamReader &reader) if(reader.IsErrored()) { - RETURNERROR(ContainerError::FileIO, "I/O error reading thumbnail data"); + SET_ERROR_RESULT(m_Error, ResultCode::FileIOFailed, "I/O error reading thumbnail data"); + return; } CaptureMetaData meta; @@ -388,13 +385,16 @@ void RDCFile::Init(StreamReader &reader) if(reader.IsErrored()) { - RETURNERROR(ContainerError::FileIO, "I/O error reading capture metadata"); + SET_ERROR_RESULT(m_Error, ResultCode::FileIOFailed, "I/O error reading capture metadata"); + return; } if(meta.driverNameLength == 0) { - RETURNERROR(ContainerError::Corrupt, - "Driver name length is invalid, must be at least 1 to contain NULL terminator"); + SET_ERROR_RESULT( + m_Error, ResultCode::FileCorrupted, + "Driver name length is invalid, must be at least 1 to contain NULL terminator"); + return; } rdcstr driverName; @@ -404,7 +404,8 @@ void RDCFile::Init(StreamReader &reader) if(reader.IsErrored()) { - RETURNERROR(ContainerError::FileIO, "I/O error reading driver name"); + SET_ERROR_RESULT(m_Error, ResultCode::FileIOFailed, "I/O error reading driver name"); + return; } // this initialises to a default 'no conversion' timebase, with base of 0 and frequency of 1.0 @@ -417,7 +418,8 @@ void RDCFile::Init(StreamReader &reader) if(reader.IsErrored()) { - RETURNERROR(ContainerError::FileIO, "I/O error reading capture timebase"); + SET_ERROR_RESULT(m_Error, ResultCode::FileIOFailed, "I/O error reading capture timebase"); + return; } } @@ -436,7 +438,8 @@ void RDCFile::Init(StreamReader &reader) if(reader.GetOffset() > header.headerLength) { - RETURNERROR(ContainerError::FileIO, "I/O error seeking to end of header"); + SET_ERROR_RESULT(m_Error, ResultCode::FileIOFailed, "I/O error seeking to end of header"); + return; } reader.SkipBytes(header.headerLength - (uint32_t)reader.GetOffset()); @@ -460,10 +463,17 @@ void RDCFile::Init(StreamReader &reader) char c = 0; reader.Read(c); if(reader.IsErrored()) - RETURNERROR(ContainerError::Corrupt, "Invalid ASCII data section '%hhx'", c); + { + SET_ERROR_RESULT(m_Error, ResultCode::FileCorrupted, "Invalid ASCII data section '%hhx'", c); + return; + } if(reader.AtEnd()) - RETURNERROR(ContainerError::Corrupt, "Invalid truncated ASCII data section"); + { + SET_ERROR_RESULT(m_Error, ResultCode::FileCorrupted, + "Invalid truncated ASCII data section"); + return; + } uint64_t length = 0; @@ -481,7 +491,11 @@ void RDCFile::Init(StreamReader &reader) } if(reader.IsErrored() || reader.AtEnd()) - RETURNERROR(ContainerError::Corrupt, "Invalid truncated ASCII data section"); + { + SET_ERROR_RESULT(m_Error, ResultCode::FileCorrupted, + "Invalid truncated ASCII data section"); + return; + } uint32_t type = 0; @@ -499,7 +513,11 @@ void RDCFile::Init(StreamReader &reader) } if(reader.IsErrored() || reader.AtEnd()) - RETURNERROR(ContainerError::Corrupt, "Invalid truncated ASCII data section"); + { + SET_ERROR_RESULT(m_Error, ResultCode::FileCorrupted, + "Invalid truncated ASCII data section"); + return; + } uint64_t version = 0; @@ -517,7 +535,11 @@ void RDCFile::Init(StreamReader &reader) } if(reader.IsErrored() || reader.AtEnd()) - RETURNERROR(ContainerError::Corrupt, "Invalid truncated ASCII data section"); + { + SET_ERROR_RESULT(m_Error, ResultCode::FileCorrupted, + "Invalid truncated ASCII data section"); + return; + } rdcstr name; @@ -534,7 +556,11 @@ void RDCFile::Init(StreamReader &reader) } if(reader.IsErrored() || reader.AtEnd()) - RETURNERROR(ContainerError::Corrupt, "Invalid truncated ASCII data section"); + { + SET_ERROR_RESULT(m_Error, ResultCode::FileCorrupted, + "Invalid truncated ASCII data section"); + return; + } SectionProperties props; props.flags = SectionFlags::ASCIIStored; @@ -552,8 +578,11 @@ void RDCFile::Init(StreamReader &reader) reader.SkipBytes(loc.diskLength); if(reader.IsErrored()) - RETURNERROR(ContainerError::Corrupt, "Error seeking past ASCII section '%s' data", - name.c_str()); + { + SET_ERROR_RESULT(m_Error, ResultCode::FileCorrupted, + "Error seeking past ASCII section '%s' data", name.c_str()); + return; + } m_Sections.push_back(props); m_SectionLocations.push_back(loc); @@ -564,7 +593,10 @@ void RDCFile::Init(StreamReader &reader) reader.Read(reading, offsetof(BinarySectionHeader, name) - 1); if(reader.IsErrored()) - RETURNERROR(ContainerError::Corrupt, "Error reading binary section header"); + { + SET_ERROR_RESULT(m_Error, ResultCode::FileCorrupted, "Error reading binary section header"); + return; + } SectionProperties props; props.flags = sectionHeader.sectionFlags; @@ -575,8 +607,9 @@ void RDCFile::Init(StreamReader &reader) if(sectionHeader.sectionNameLength == 0 || sectionHeader.sectionNameLength > 2 * 1024) { - RETURNERROR(ContainerError::Corrupt, "Invalid section name length %u", - sectionHeader.sectionNameLength); + SET_ERROR_RESULT(m_Error, ResultCode::FileCorrupted, "Invalid section name length %u", + sectionHeader.sectionNameLength); + return; } props.name.resize(sectionHeader.sectionNameLength - 1); @@ -584,12 +617,18 @@ void RDCFile::Init(StreamReader &reader) reader.Read(&props.name[0], sectionHeader.sectionNameLength - 1); if(reader.IsErrored()) - RETURNERROR(ContainerError::Corrupt, "Error reading binary section header"); + { + SET_ERROR_RESULT(m_Error, ResultCode::FileCorrupted, "Error reading binary section header"); + return; + } reader.SkipBytes(1); if(reader.IsErrored()) - RETURNERROR(ContainerError::Corrupt, "Error reading binary section header"); + { + SET_ERROR_RESULT(m_Error, ResultCode::FileCorrupted, "Error reading binary section header"); + return; + } SectionLocation loc; loc.headerOffset = headerOffset; @@ -602,18 +641,25 @@ void RDCFile::Init(StreamReader &reader) reader.SkipBytes(loc.diskLength); if(reader.IsErrored()) - RETURNERROR(ContainerError::Corrupt, "Error seeking past binary section '%s' data", - props.name.c_str()); + { + SET_ERROR_RESULT(m_Error, ResultCode::FileCorrupted, + "Error seeking past binary section '%s' data", props.name.c_str()); + return; + } } else { - RETURNERROR(ContainerError::Corrupt, "Unrecognised section type '%hhx'", sectionHeader.isASCII); + SET_ERROR_RESULT(m_Error, ResultCode::FileCorrupted, "Unrecognised section type '%hhx'", + sectionHeader.isASCII); + return; } } if(SectionIndex(SectionType::FrameCapture) == -1) { - RETURNERROR(ContainerError::Corrupt, "Capture file doesn't have a frame capture"); + SET_ERROR_RESULT(m_Error, ResultCode::FileCorrupted, + "Capture file doesn't have a frame capture"); + return; } int index = SectionIndex(SectionType::ExtendedThumbnail); @@ -641,27 +687,34 @@ void RDCFile::Init(StreamReader &reader) } } -bool RDCFile::CopyFileTo(const rdcstr &filename) +RDResult RDCFile::CopyFileTo(const rdcstr &filename) { if(!m_File) - return false; + RETURN_ERROR_RESULT(ResultCode::FileIOFailed, "Capture file '%s' is not currently open", + m_Filename.c_str(), errno); // remember our position and close the file uint64_t prevPos = FileIO::ftell64(m_File); FileIO::fclose(m_File); - // try to move to the new location - bool success = FileIO::Copy(m_Filename, filename, true); + RDResult ret; // if it succeeded, update our filename - if(success) + if(FileIO::Copy(m_Filename, filename, true)) + { m_Filename = filename; + } + else + { + SET_ERROR_RESULT(ret, ResultCode::FileIOFailed, "Couldn't copy to file '%s': %s", + filename.c_str(), FileIO::ErrorString().c_str()); + } // re-open the file (either the new one, or the old one if it failed) and re-seek m_File = FileIO::fopen(m_Filename, FileIO::ReadBinary); FileIO::fseek64(m_File, prevPos, SEEK_SET); - return success; + return ret; } void RDCFile::SetData(RDCDriver driver, const rdcstr &driverName, uint64_t machineIdent, @@ -687,8 +740,9 @@ void RDCFile::Create(const rdcstr &filename) if(!m_File) { - RETURNERROR(ContainerError::FileIO, "Can't open capture file '%s' for write, errno %d", - filename.c_str(), errno); + SET_ERROR_RESULT(m_Error, ResultCode::FileIOFailed, + "Can't open capture file '%s' for write, errno %d", filename.c_str(), errno); + return; } RDCDEBUG("Opened capture file for write"); @@ -774,7 +828,8 @@ void RDCFile::Create(const rdcstr &filename) if(writer.IsErrored()) { - RETURNERROR(ContainerError::FileIO, "Error writing file header"); + SET_ERROR_RESULT(m_Error, ResultCode::FileIOFailed, "Error writing file header"); + return; } } @@ -784,8 +839,9 @@ void RDCFile::Create(const rdcstr &filename) if(!m_File) { - RETURNERROR(ContainerError::FileIO, "Can't open capture file '%s' as read-only, errno %d", - filename.c_str(), errno); + SET_ERROR_RESULT(m_Error, ResultCode::FileIOFailed, + "Can't open capture file '%s' as read-only, errno %d", filename.c_str(), errno); + return; } FileIO::fseek64(m_File, 0, SEEK_END); @@ -822,16 +878,18 @@ int RDCFile::SectionIndex(const rdcstr &name) const StreamReader *RDCFile::ReadSection(int index) const { - if(m_Error != ContainerError::NoError) - return new StreamReader(StreamReader::InvalidStream); + if(m_Error != ResultCode::Succeeded) + return new StreamReader(StreamReader::InvalidStream, m_Error); if(m_File == NULL) { if(index < (int)m_MemorySections.size()) return new StreamReader(m_MemorySections[index]); - RDCERR("Section %d is not available in memory.", index); - return new StreamReader(StreamReader::InvalidStream); + RDResult res; + SET_ERROR_RESULT(res, ResultCode::InvalidParameter, + "Section %d is not available in this capture file.", index); + return new StreamReader(StreamReader::InvalidStream, res); } const SectionProperties &props = m_Sections[index]; @@ -861,7 +919,7 @@ StreamReader *RDCFile::ReadSection(int index) const StreamWriter *RDCFile::WriteSection(const SectionProperties &props) { - if(m_Error != ContainerError::NoError) + if(m_Error != ResultCode::Succeeded) return new StreamWriter(StreamWriter::InvalidStream); RDCASSERT((size_t)props.type < (size_t)SectionType::Count); @@ -1156,7 +1214,8 @@ StreamWriter *RDCFile::WriteSection(const SectionProperties &props) if(numWritten != offsetof(BinarySectionHeader, name) + name.size() + 1) { - SETERROR(ContainerError::FileIO, "Error seeking to end of file, errno %d", errno); + SET_ERROR_RESULT(m_Error, ResultCode::FileIOFailed, "Error seeking to end of file, errno %d", + errno); return new StreamWriter(StreamWriter::InvalidStream); } @@ -1220,7 +1279,9 @@ StreamWriter *RDCFile::WriteSection(const SectionProperties &props) if(bytesWritten != 2 * sizeof(uint64_t)) { - RETURNERROR(ContainerError::FileIO, "Error applying fixup to section header, errno %d", errno); + SET_ERROR_RESULT(m_Error, ResultCode::FileIOFailed, + "Error applying fixup to section header: %s", FileIO::ErrorString().c_str()); + return; } FileIO::fflush(m_File); diff --git a/renderdoc/serialise/rdcfile.h b/renderdoc/serialise/rdcfile.h index 908f1e8dd..ca7e787af 100644 --- a/renderdoc/serialise/rdcfile.h +++ b/renderdoc/serialise/rdcfile.h @@ -27,15 +27,6 @@ #include "core/core.h" #include "streamio.h" -enum class ContainerError -{ - NoError = 0, - FileNotFound, - FileIO, - Corrupt, - UnsupportedVersion, -}; - extern const char *SectionTypeNames[]; struct RDCThumb @@ -74,7 +65,7 @@ public: void Open(const rdcstr &filename); void Open(const bytebuf &buffer); - bool CopyFileTo(const rdcstr &filename); + RDResult CopyFileTo(const rdcstr &filename); // Sets the parameters of an RDCFile in memory. void SetData(RDCDriver driver, const rdcstr &driverName, uint64_t machineIdent, @@ -83,8 +74,7 @@ public: // creates a new file with current properties, file will be overwritten if it already exists void Create(const rdcstr &filename); - ContainerError ErrorCode() const { return m_Error; } - rdcstr ErrorString() const { return m_ErrorString; } + const RDResult &Error() const { return m_Error; } RDCDriver GetDriver() const { return m_Driver; } const rdcstr &GetDriverName() const { return m_DriverName; } uint64_t GetMachineIdent() const { return m_MachineIdent; } @@ -120,8 +110,7 @@ private: double m_TimeFrequency = 1.0; RDCThumb m_Thumb; - ContainerError m_Error = ContainerError::NoError; - rdcstr m_ErrorString; + RDResult m_Error; struct SectionLocation { diff --git a/renderdoc/serialise/serialiser.h b/renderdoc/serialise/serialiser.h index 53a9f3016..22565fb68 100644 --- a/renderdoc/serialise/serialiser.h +++ b/renderdoc/serialise/serialiser.h @@ -25,8 +25,10 @@ #pragma once #include +#include "api/replay/replay_enums.h" #include "api/replay/structured_data.h" #include "common/formatting.h" +#include "common/result.h" #include "streamio.h" // function to deallocate anything from a serialise. Default impl @@ -116,7 +118,11 @@ public: Serialiser(const Serialiser &other) = delete; bool IsErrored() { return IsReading() ? m_Read->IsErrored() : m_Write->IsErrored(); } - void SetErrored() { IsReading() ? m_Read->SetErrored() : m_Write->SetErrored(); } + RDResult GetError() { return IsReading() ? m_Read->GetError() : m_Write->GetError(); } + void SetError(RDResult result) + { + IsReading() ? m_Read->SetError(result) : m_Write->SetError(result); + } StreamWriter *GetWriter() { return m_Write; } StreamReader *GetReader() { return m_Read; } uint32_t GetChunkMetadataRecording() { return m_ChunkFlags; } @@ -1430,15 +1436,18 @@ private: if(count > size) { - RDCERR("Reading invalid array or byte buffer - %llu larger than total stream size %llu.", - count, size); + RDResult result; + SET_ERROR_RESULT( + result, ResultCode::FileCorrupted, + "Reading invalid array or byte buffer - %llu larger than total stream size %llu.", count, + size); // if we owned the previous stream, delete it if(m_Ownership == Ownership::Stream) delete m_Read; // replace our stream with an invalid one so all subsequent reads fail - m_Read = new StreamReader(StreamReader::InvalidStream); + m_Read = new StreamReader(StreamReader::InvalidStream, result); m_Ownership = Ownership::Stream; // set the count to 0 diff --git a/renderdoc/serialise/streamio.cpp b/renderdoc/serialise/streamio.cpp index 636ef7708..5669c2c01 100644 --- a/renderdoc/serialise/streamio.cpp +++ b/renderdoc/serialise/streamio.cpp @@ -62,7 +62,7 @@ StreamReader::StreamReader(const bytebuf &buffer) m_Ownership = Ownership::Nothing; } -StreamReader::StreamReader(StreamInvalidType) +StreamReader::StreamReader(StreamInvalidType, RDResult res) { m_InputSize = 0; @@ -71,7 +71,10 @@ StreamReader::StreamReader(StreamInvalidType) m_Ownership = Ownership::Nothing; - m_HasError = true; + m_Error = res; + if(m_Error == ResultCode::Succeeded) + SET_ERROR_RESULT(m_Error, ResultCode::InvalidParameter, + "Invalid stream created with no error code"); } StreamReader::StreamReader(StreamDummyType) @@ -367,17 +370,28 @@ bool StreamReader::ReadFromExternal(void *buffer, uint64_t length) if(m_Decompressor) { success = m_Decompressor->Read(buffer, length); + + if(!success) + m_Error = m_Decompressor->GetError(); } else if(m_File) { uint64_t numRead = FileIO::fread(buffer, 1, (size_t)length, m_File); success = (numRead == length); + + if(!success) + SET_ERROR_RESULT(m_Error, ResultCode::FileIOFailed, "Error reading from file: %s", + FileIO::ErrorString().c_str()); } else if(m_Sock) { if(!m_Sock->Connected()) { success = false; + m_Error = m_Sock->GetError(); + if(m_Error == ResultCode::Succeeded) + SET_ERROR_RESULT(m_Error, ResultCode::NetworkIOFailed, + "Socket unexpectedly disconnected during reading"); } else { @@ -399,6 +413,14 @@ bool StreamReader::ReadFromExternal(void *buffer, uint64_t length) if(success) m_InputSize += bufSize; } + + if(!success) + { + m_Error = m_Sock->GetError(); + if(m_Error == ResultCode::Succeeded) + SET_WARNING_RESULT(m_Error, ResultCode::NetworkIOFailed, + "Socket unexpectedly disconnected during reading"); + } } } else @@ -409,13 +431,6 @@ bool StreamReader::ReadFromExternal(void *buffer, uint64_t length) if(!success) { - if(m_File) - RDCERR("Error reading from file, errno %d", errno); - else if(m_Sock) - RDCWARN("Error reading from socket"); - - m_HasError = true; - // move to error state FreeAlignedBuffer(m_BufferBase); @@ -454,14 +469,17 @@ StreamWriter::StreamWriter(uint64_t initialBufSize) m_Ownership = Ownership::Nothing; } -StreamWriter::StreamWriter(StreamInvalidType) +StreamWriter::StreamWriter(StreamInvalidType, RDResult res) { m_BufferBase = m_BufferHead = m_BufferEnd = NULL; m_Ownership = Ownership::Nothing; m_InMemory = false; - m_HasError = true; + m_Error = res; + if(m_Error == ResultCode::Succeeded) + SET_ERROR_RESULT(m_Error, ResultCode::InvalidParameter, + "Invalid stream created with no error code"); } StreamWriter::StreamWriter(Network::Socket *sock, Ownership own) @@ -520,10 +538,7 @@ bool StreamWriter::SendSocketData(const void *data, uint64_t numBytes) { bool success = FlushSocketData(); if(!success) - { - HandleError(); return false; - } } // if it's larger than our buffer (even after flushing) just write directly @@ -532,7 +547,11 @@ bool StreamWriter::SendSocketData(const void *data, uint64_t numBytes) bool success = m_Sock->SendDataBlocking(data, (uint32_t)numBytes); if(!success) { - HandleError(); + RDResult res = m_Sock->GetError(); + if(res == ResultCode::Succeeded) + SET_ERROR_RESULT(res, ResultCode::NetworkIOFailed, + "Socket unexpectedly disconnected during sending"); + HandleError(res); return false; } } @@ -552,7 +571,11 @@ bool StreamWriter::FlushSocketData() bool success = m_Sock->SendDataBlocking(m_BufferBase, uint32_t(m_BufferHead - m_BufferBase)); if(!success) { - HandleError(); + RDResult res = m_Sock->GetError(); + if(res == ResultCode::Succeeded) + SET_ERROR_RESULT(res, ResultCode::NetworkIOFailed, + "Socket unexpectedly disconnected during sending"); + HandleError(res); return false; } @@ -562,14 +585,10 @@ bool StreamWriter::FlushSocketData() return true; } -void StreamWriter::HandleError() +void StreamWriter::HandleError(RDResult result) { - if(m_File) - RDCERR("Error writing to file, errno %d", errno); - else if(m_Sock) - RDCWARN("Error writing to socket"); - - m_HasError = true; + if(m_Error == ResultCode::Succeeded) + m_Error = result; FreeAlignedBuffer(m_BufferBase); diff --git a/renderdoc/serialise/streamio.h b/renderdoc/serialise/streamio.h index 452feb92f..ec7b53a8a 100644 --- a/renderdoc/serialise/streamio.h +++ b/renderdoc/serialise/streamio.h @@ -26,7 +26,9 @@ #include #include +#include "api/replay/replay_enums.h" #include "common/common.h" +#include "common/formatting.h" #include "os/os_specific.h" enum class Ownership @@ -45,12 +47,14 @@ class Compressor public: Compressor(StreamWriter *write, Ownership own) : m_Write(write), m_Ownership(own) {} virtual ~Compressor(); + RDResult GetError() { return m_Error; } virtual bool Write(const void *data, uint64_t numBytes) = 0; virtual bool Finish() = 0; protected: StreamWriter *m_Write; Ownership m_Ownership; + RDResult m_Error; }; class Decompressor @@ -58,12 +62,14 @@ class Decompressor public: Decompressor(StreamReader *read, Ownership own) : m_Read(read), m_Ownership(own) {} virtual ~Decompressor(); + RDResult GetError() { return m_Error; } virtual bool Recompress(Compressor *comp) = 0; virtual bool Read(void *data, uint64_t numBytes) = 0; protected: StreamReader *m_Read; Ownership m_Ownership; + RDResult m_Error; }; class StreamReader @@ -78,7 +84,7 @@ public: DummyStream }; - StreamReader(StreamInvalidType); + StreamReader(StreamInvalidType, RDResult res); StreamReader(StreamDummyType); StreamReader(const byte *buffer, uint64_t bufferSize); StreamReader(const bytebuf &buffer); @@ -91,8 +97,13 @@ public: ~StreamReader(); - bool IsErrored() { return m_HasError; } - void SetErrored() { m_HasError = true; } + bool IsErrored() { return m_Error != ResultCode::Succeeded; } + RDResult GetError() { return m_Error; } + void SetError(RDResult res) + { + if(m_Error == ResultCode::Succeeded && res != ResultCode::Succeeded) + m_Error = res; + } void SetOffset(uint64_t offs); inline uint64_t GetOffset() { return m_BufferHead - m_BufferBase + m_ReadOffset; } @@ -130,7 +141,7 @@ public: if(numBytes == 0 || m_Dummy) return true; - if(!m_BufferBase || m_HasError) + if(!m_BufferBase || IsErrored()) { // read 0s if we're in an error state if(data) @@ -142,11 +153,10 @@ public: // if we're reading past the end, error, read nothing (no partial reads) and return if(m_Sock == NULL && GetOffset() + numBytes > GetSize()) { - RDCERR("Reading off the end of the stream"); m_BufferHead = m_BufferBase + m_BufferSize; if(data) memset(data, 0, (size_t)numBytes); - m_HasError = true; + SET_ERROR_RESULT(m_Error, ResultCode::FileIOFailed, "Reading off the end of data stream"); return false; } @@ -267,8 +277,9 @@ private: // the offset in the file/decompressor that corresponds to the start of m_BufferBase uint64_t m_ReadOffset = 0; - // flag indicating if an error has been encountered and the stream is now invalid - bool m_HasError = false; + // result indicating if an error has been encountered and the stream is now invalid, with details + // of what happened + RDResult m_Error; // flag indicating this reader is a dummy and doesn't read anything or clear inputs. Used with a // structured serialiser to 'read' pre-existing data. @@ -290,14 +301,19 @@ public: InvalidStream }; - StreamWriter(StreamInvalidType); + StreamWriter(StreamInvalidType, RDResult res); StreamWriter(uint64_t initialBufSize); StreamWriter(FILE *file, Ownership own); StreamWriter(Network::Socket *file, Ownership own); StreamWriter(Compressor *compressor, Ownership own); - bool IsErrored() { return m_HasError; } - void SetErrored() { m_HasError = true; } + bool IsErrored() { return m_Error != ResultCode::Succeeded; } + RDResult GetError() { return m_Error; } + void SetError(RDResult res) + { + if(m_Error == ResultCode::Succeeded && res != ResultCode::Succeeded) + m_Error = res; + } static const int DefaultScratchSize = 32 * 1024; ~StreamWriter(); @@ -370,7 +386,10 @@ public: uint64_t written = (uint64_t)FileIO::fwrite(data, 1, (size_t)numBytes, m_File); if(written != numBytes) { - HandleError(); + RDResult result; + SET_ERROR_RESULT(result, ResultCode::FileIOFailed, "Writing to file failed: %s", + FileIO::ErrorString().c_str()); + HandleError(result); return false; } @@ -435,7 +454,10 @@ public: return ret; } - RDCERR("Can't seek a file/socket/compressor stream writer"); + RDResult result; + SET_ERROR_RESULT(result, ResultCode::InternalError, + "Can't seek a file/socket/compressor stream writer"); + HandleError(result); return false; } @@ -443,11 +465,27 @@ public: bool Flush() { if(m_Compressor) + { return true; + } else if(m_File) - return FileIO::fflush(m_File); + { + bool success = FileIO::fflush(m_File); + + if(success) + return true; + + RDResult result; + SET_ERROR_RESULT(result, ResultCode::FileIOFailed, "File flushing failed: %s", + FileIO::ErrorString().c_str()); + HandleError(result); + + return false; + } else if(m_Sock) + { return FlushSocketData(); + } return true; } @@ -455,11 +493,27 @@ public: bool Finish() { if(m_Compressor) + { return m_Compressor->Finish(); + } else if(m_File) - return FileIO::fflush(m_File); + { + bool success = FileIO::fflush(m_File); + + if(success) + return true; + + RDResult result; + SET_ERROR_RESULT(result, ResultCode::FileIOFailed, "File flushing failed: %s", + FileIO::ErrorString().c_str()); + HandleError(result); + + return false; + } else if(m_Sock) + { return true; + } return true; } @@ -491,7 +545,7 @@ private: } } - void HandleError(); + void HandleError(RDResult result); bool SendSocketData(const void *data, uint64_t numBytes); bool FlushSocketData(); @@ -523,8 +577,9 @@ private: // true if we're not writing to file/compressor, used to optimise checks in Write bool m_InMemory = true; - // flag indicating if an error has been encountered and the stream is now invalid - bool m_HasError = false; + // result indicating if an error has been encountered and the stream is now invalid, with details + // of what happened + RDResult m_Error; // do we own the file/compressor? are we responsible for // cleaning it up? diff --git a/renderdoc/serialise/zstdio.cpp b/renderdoc/serialise/zstdio.cpp index f5a8a7c7f..ec33cae10 100644 --- a/renderdoc/serialise/zstdio.cpp +++ b/renderdoc/serialise/zstdio.cpp @@ -149,10 +149,11 @@ bool ZSTDCompressor::CompressZSTDFrame(ZSTD_inBuffer &in, ZSTD_outBuffer &out) if(ZSTD_isError(err)) { - RDCERR("Error compressing: %s", ZSTD_getErrorName(err)); FreeAlignedBuffer(m_Page); FreeAlignedBuffer(m_CompressBuffer); m_Page = m_CompressBuffer = NULL; + SET_ERROR_RESULT(m_Error, ResultCode::CompressionFailed, "ZSTD compression failed: %s", + ZSTD_getErrorName(err)); return false; } @@ -167,9 +168,11 @@ bool ZSTDCompressor::CompressZSTDFrame(ZSTD_inBuffer &in, ZSTD_outBuffer &out) if(ZSTD_isError(err) || (inpos == in.pos && outpos == out.pos)) { if(ZSTD_isError(err)) - RDCERR("Error compressing: %s", ZSTD_getErrorName(err)); + SET_ERROR_RESULT(m_Error, ResultCode::CompressionFailed, "ZSTD compression failed: %s", + ZSTD_getErrorName(err)); else - RDCERR("Error compressing, no progress made"); + SET_ERROR_RESULT(m_Error, ResultCode::CompressionFailed, + "ZSTD compression failed, no progress made"); FreeAlignedBuffer(m_Page); FreeAlignedBuffer(m_CompressBuffer); m_Page = m_CompressBuffer = NULL; @@ -182,9 +185,11 @@ bool ZSTDCompressor::CompressZSTDFrame(ZSTD_inBuffer &in, ZSTD_outBuffer &out) if(ZSTD_isError(err) || err != 0) { if(ZSTD_isError(err)) - RDCERR("Error compressing: %s", ZSTD_getErrorName(err)); + SET_ERROR_RESULT(m_Error, ResultCode::CompressionFailed, "ZSTD compression failed: %s", + ZSTD_getErrorName(err)); else - RDCERR("Error compressing, couldn't end stream"); + SET_ERROR_RESULT(m_Error, ResultCode::CompressionFailed, + "Error compressing, couldn't end stream"); FreeAlignedBuffer(m_Page); FreeAlignedBuffer(m_CompressBuffer); m_Page = m_CompressBuffer = NULL; @@ -220,7 +225,12 @@ bool ZSTDDecompressor::Recompress(Compressor *comp) { success &= FillPage(); if(success) + { success &= comp->Write(m_Page, m_PageLength); + + if(!success) + m_Error = comp->GetError(); + } } success &= comp->Finish(); @@ -297,6 +307,7 @@ bool ZSTDDecompressor::FillPage() if(!success) { + m_Error = m_Read->GetError(); FreeAlignedBuffer(m_Page); FreeAlignedBuffer(m_CompressBuffer); m_Page = m_CompressBuffer = NULL; @@ -307,7 +318,8 @@ bool ZSTDDecompressor::FillPage() if(ZSTD_isError(err)) { - RDCERR("Error decompressing: %s", ZSTD_getErrorName(err)); + SET_ERROR_RESULT(m_Error, ResultCode::CompressionFailed, "ZSTD decompression failed: %s", + ZSTD_getErrorName(err)); FreeAlignedBuffer(m_Page); FreeAlignedBuffer(m_CompressBuffer); m_Page = m_CompressBuffer = NULL; @@ -328,9 +340,11 @@ bool ZSTDDecompressor::FillPage() if(ZSTD_isError(err) || (inpos == in.pos && outpos == out.pos)) { if(ZSTD_isError(err)) - RDCERR("Error decompressing: %s", ZSTD_getErrorName(err)); + SET_ERROR_RESULT(m_Error, ResultCode::CompressionFailed, "ZSTD decompression failed: %s", + ZSTD_getErrorName(err)); else - RDCERR("Error decompressing, no progress made"); + SET_ERROR_RESULT(m_Error, ResultCode::CompressionFailed, + "ZSTD decompression failed, no progress made"); FreeAlignedBuffer(m_Page); FreeAlignedBuffer(m_CompressBuffer); m_Page = m_CompressBuffer = NULL; diff --git a/renderdoccmd/renderdoccmd.cpp b/renderdoccmd/renderdoccmd.cpp index 213ea6e50..45605ac2d 100644 --- a/renderdoccmd/renderdoccmd.cpp +++ b/renderdoccmd/renderdoccmd.cpp @@ -259,14 +259,14 @@ public: bytebuf buf; ICaptureFile *file = RENDERDOC_OpenCaptureFile(); - ReplayStatus st = file->OpenFile(conv(infile), "rdc", NULL); - if(st == ReplayStatus::Succeeded) + ResultDetails st = file->OpenFile(conv(infile), "rdc", NULL); + if(st.OK()) { buf = file->GetThumbnail(type, maxsize).data; } else { - std::cerr << "Couldn't open '" << infile << "': " << ToStr(st) << std::endl; + std::cerr << "Couldn't open '" << infile << "': " << st.Message() << std::endl; } file->Shutdown(); @@ -359,10 +359,10 @@ public: ExecuteResult res = RENDERDOC_ExecuteAndInject( conv(executable), conv(workingDir), conv(cmdLine), env, conv(logFile), opts, wait_for_exit); - if(res.status != ReplayStatus::Succeeded) + if(res.result.code != ResultCode::Succeeded) { - std::cerr << "Failed to create & inject: " << ToStr(res.status) << std::endl; - return (int)res.status; + std::cerr << "Failed to create & inject: " << res.result.Message() << std::endl; + return (int)res.result.code; } if(wait_for_exit) @@ -430,10 +430,10 @@ public: ExecuteResult res = RENDERDOC_InjectIntoProcess(PID, env, conv(captureFile), opts, wait_for_exit); - if(res.status != ReplayStatus::Succeeded) + if(res.result.code != ResultCode::Succeeded) { - std::cerr << "Failed to inject: " << ToStr(res.status) << std::endl; - return (int)res.status; + std::cerr << "Failed to inject: " << res.result.Message() << std::endl; + return (int)res.result.code; } if(wait_for_exit) @@ -580,12 +580,12 @@ public: std::cout << "Replaying '" << filename << "' on " << remote_host << "." << std::endl; IRemoteServer *remote = NULL; - ReplayStatus status = RENDERDOC_CreateRemoteServerConnection(conv(remote_host), &remote); + ResultDetails result = RENDERDOC_CreateRemoteServerConnection(conv(remote_host), &remote); - if(remote == NULL || status != ReplayStatus::Succeeded) + if(remote == NULL || result.code != ResultCode::Succeeded) { - std::cerr << "Error: " << ToStr(status) << " - Couldn't connect to " << remote_host << "." - << std::endl; + std::cerr << "Error: " << result.Message() << " - Couldn't connect to " << remote_host + << "." << std::endl; std::cerr << " Have you run renderdoccmd remoteserver on '" << remote_host << "'?" << std::endl; return 1; @@ -596,9 +596,9 @@ public: rdcstr remotePath = remote->CopyCaptureToRemote(conv(filename), NULL); IReplayController *renderer = NULL; - rdctie(status, renderer) = remote->OpenCapture(~0U, remotePath, ReplayOptions(), NULL); + rdctie(result, renderer) = remote->OpenCapture(~0U, remotePath, ReplayOptions(), NULL); - if(status == ReplayStatus::Succeeded) + if(result.OK()) { DisplayRendererPreview(renderer, width, height, loops); @@ -606,7 +606,8 @@ public: } else { - std::cerr << "Couldn't load and replay '" << filename << "': " << ToStr(status) << std::endl; + std::cerr << "Couldn't load and replay '" << filename << "': " << result.Message() + << std::endl; } remote->ShutdownConnection(); @@ -617,19 +618,21 @@ public: ICaptureFile *file = RENDERDOC_OpenCaptureFile(); - if(file->OpenFile(conv(filename), "rdc", NULL) != ReplayStatus::Succeeded) + ResultDetails res = file->OpenFile(conv(filename), "rdc", NULL); + + if(res.code != ResultCode::Succeeded) { - std::cerr << "Couldn't load '" << filename << "'." << std::endl; + std::cerr << "Couldn't load '" << filename << "': " << res.Message() << std::endl; return 1; } IReplayController *renderer = NULL; - ReplayStatus status = ReplayStatus::InternalError; - rdctie(status, renderer) = file->OpenCapture(ReplayOptions(), NULL); + ResultDetails result = {}; + rdctie(result, renderer) = file->OpenCapture(ReplayOptions(), NULL); file->Shutdown(); - if(status == ReplayStatus::Succeeded) + if(result.OK()) { DisplayRendererPreview(renderer, width, height, loops); @@ -637,7 +640,8 @@ public: } else { - std::cerr << "Couldn't load and replay '" << filename << "': " << ToStr(status) << std::endl; + std::cerr << "Couldn't load and replay '" << filename << "': " << result.Message() + << std::endl; return 1; } } @@ -804,21 +808,21 @@ public: ICaptureFile *file = RENDERDOC_OpenCaptureFile(); - ReplayStatus st = file->OpenFile(conv(infile), conv(infmt), NULL); + ResultDetails st = file->OpenFile(conv(infile), conv(infmt), NULL); - if(st != ReplayStatus::Succeeded) + if(st.code != ResultCode::Succeeded) { - std::cerr << "Couldn't load '" << infile << "' as '" << infmt << "': " << ToStr(st) + std::cerr << "Couldn't load '" << infile << "' as '" << infmt << "': " << st.Message() << std::endl; return 1; } st = file->Convert(conv(outfile), conv(outfmt), NULL, NULL); - if(st != ReplayStatus::Succeeded) + if(st.code != ResultCode::Succeeded) { std::cerr << "Couldn't convert '" << infile << "' to '" << outfile << "' as '" << outfmt - << "': " << ToStr(st) << std::endl; + << "': " << st.Message() << std::endl; return 1; } @@ -1014,10 +1018,10 @@ public: ExecuteResult result = RENDERDOC_InjectIntoProcess(pid, env, conv(capfile), cmdopts, false); - if(result.status == ReplayStatus::Succeeded) + if(result.result.OK()) return result.ident; - return (int)result.status; + return (int)result.result.code; } }; @@ -1109,12 +1113,12 @@ public: ICaptureFile *capfile = RENDERDOC_OpenCaptureFile(); - ReplayStatus status = capfile->OpenFile(conv(rdc), "", NULL); + ResultDetails result = capfile->OpenFile(conv(rdc), "", NULL); - if(status != ReplayStatus::Succeeded) + if(result.code != ResultCode::Succeeded) { capfile->Shutdown(); - std::cerr << "Couldn't load '" << rdc << "': " << ToStr(status) << std::endl; + std::cerr << "Couldn't load '" << rdc << "': " << result.Message() << std::endl; return 1; } diff --git a/util/test/rdtest/analyse.py b/util/test/rdtest/analyse.py index d910cf8f6..b513efcef 100644 --- a/util/test/rdtest/analyse.py +++ b/util/test/rdtest/analyse.py @@ -30,12 +30,12 @@ def open_capture(filename="", cap: rd.CaptureFile=None, opts: rd.ReplayOptions=N cap = rd.OpenCaptureFile() # Open a particular file - status = cap.OpenFile(filename, '', None) + result = cap.OpenFile(filename, '', None) # Make sure the file opened successfully - if status != rd.ReplayStatus.Succeeded: + if result != rd.ResultCode.Succeeded: cap.Shutdown() - raise RuntimeError("Couldn't open '{}': {}".format(filename, str(status))) + raise RuntimeError("Couldn't open '{}': {}".format(filename, str(result))) api = cap.DriverName() @@ -44,13 +44,13 @@ def open_capture(filename="", cap: rd.CaptureFile=None, opts: rd.ReplayOptions=N cap.Shutdown() raise RuntimeError("{} capture cannot be replayed".format(api)) - status, controller = cap.OpenCapture(opts, None) + result, controller = cap.OpenCapture(opts, None) if own_cap: cap.Shutdown() - if status != rd.ReplayStatus.Succeeded: - raise RuntimeError("Couldn't initialise replay for {}: {}".format(api, str(rd.ReplayStatus(status)))) + if result != rd.ResultCode.Succeeded: + raise RuntimeError("Couldn't initialise replay for {}: {}".format(api, str(result))) return controller diff --git a/util/test/rdtest/capture.py b/util/test/rdtest/capture.py index f125c8869..e2562c5cf 100644 --- a/util/test/rdtest/capture.py +++ b/util/test/rdtest/capture.py @@ -139,8 +139,8 @@ def run_executable(exe: str, cmdline: str, # Execute the test program res = rd.ExecuteAndInject(exe, workdir, cmdline, envmods, cappath, opts, wait_for_exit) - if res.status != rd.ReplayStatus.Succeeded: - raise RuntimeError("Couldn't launch program: {}".format(str(res.status))) + if res.result != rd.ResultCode.Succeeded: + raise RuntimeError("Couldn't launch program: {}".format(str(res.result))) return res.ident diff --git a/util/test/rdtest/shared/Texture_Zoo.py b/util/test/rdtest/shared/Texture_Zoo.py index 8a9cbbb2f..09b3f1a8f 100644 --- a/util/test/rdtest/shared/Texture_Zoo.py +++ b/util/test/rdtest/shared/Texture_Zoo.py @@ -619,17 +619,17 @@ class Texture_Zoo(): # Wait for it to start time.sleep(0.5) - ret: Tuple[rd.ReplayStatus, rd.RemoteServer] = rd.CreateRemoteServerConnection('localhost') - status, remote = ret + ret: Tuple[rd.ResultCode, rd.RemoteServer] = rd.CreateRemoteServerConnection('localhost') + result, remote = ret - if status != rd.ReplayStatus.Succeeded: + if result != rd.ResultCode.Succeeded: time.sleep(2) - ret: Tuple[rd.ReplayStatus, rd.RemoteServer] = rd.CreateRemoteServerConnection('localhost') - status, remote = ret + ret: Tuple[rd.ResultCode, rd.RemoteServer] = rd.CreateRemoteServerConnection('localhost') + result, remote = ret - if status != rd.ReplayStatus.Succeeded: - raise rdtest.TestFailureException("Couldn't connect to remote server: {}".format(str(status))) + if result != rd.ResultCode.Succeeded: + raise rdtest.TestFailureException("Couldn't connect to remote server: {}".format(str(result))) proxies = remote.LocalProxies() @@ -641,10 +641,10 @@ class Texture_Zoo(): continue try: - ret: Tuple[rd.ReplayStatus, rd.ReplayController] = remote.OpenCapture(proxies.index(api), - capture_filename, - rd.ReplayOptions(), None) - status, self.controller = ret + ret: Tuple[rd.ResultCode, rd.ReplayController] = remote.OpenCapture(proxies.index(api), + capture_filename, + rd.ReplayOptions(), None) + result, self.controller = ret # Now check with the proxy self.check_capture_with_controller(api) @@ -670,17 +670,17 @@ class Texture_Zoo(): continue cap = rd.OpenCaptureFile() - status = cap.OpenFile(file.path, 'rdc', None) + result = cap.OpenFile(file.path, 'rdc', None) - if status != rd.ReplayStatus.Succeeded: + if result != rd.ResultCode.Succeeded: rdtest.log.error("Couldn't open {}".format(file.name)) failed = True continue - ret: Tuple[rd.ReplayStatus, rd.ReplayController] = cap.OpenCapture(rd.ReplayOptions(), None) - status, self.controller = ret + ret: Tuple[rd.ResultCode, rd.ReplayController] = cap.OpenCapture(rd.ReplayOptions(), None) + result, self.controller = ret - if status != rd.ReplayStatus.Succeeded: + if result != rd.ResultCode.Succeeded: rdtest.log.error("Couldn't open {}".format(file.name)) failed = True continue diff --git a/util/test/rdtest/testcase.py b/util/test/rdtest/testcase.py index f8ca829a9..6de18838a 100644 --- a/util/test/rdtest/testcase.py +++ b/util/test/rdtest/testcase.py @@ -742,9 +742,9 @@ class TestCase: conv_path = util.get_tmp_path('conv.rdc') origrdc = rd.OpenCaptureFile() - status = origrdc.OpenFile(capture_filename, '', None) + result = origrdc.OpenFile(capture_filename, '', None) - self.check(status == rd.ReplayStatus.Succeeded, "Couldn't open '{}': {}".format(capture_filename, str(status))) + self.check(result == rd.ResultCode.Succeeded, "Couldn't open '{}': {}".format(capture_filename, str(result))) # Export to rdc, to recompress origrdc.Convert(recomp_path, '', None, None) @@ -754,9 +754,9 @@ class TestCase: # Load up the zip.xml file zipxml = rd.OpenCaptureFile() - status = zipxml.OpenFile(conv_zipxml_path, 'zip.xml', None) + result = zipxml.OpenFile(conv_zipxml_path, 'zip.xml', None) - self.check(status == rd.ReplayStatus.Succeeded, "Couldn't open '{}': {}".format(conv_zipxml_path, str(status))) + self.check(result == rd.ResultCode.Succeeded, "Couldn't open '{}': {}".format(conv_zipxml_path, str(result))) # Convert out to rdc zipxml.Convert(conv_path, '', None, None) diff --git a/util/test/tests/GL/GL_Buffer_Updates.py b/util/test/tests/GL/GL_Buffer_Updates.py index debfad954..b7c77f088 100644 --- a/util/test/tests/GL/GL_Buffer_Updates.py +++ b/util/test/tests/GL/GL_Buffer_Updates.py @@ -44,12 +44,12 @@ class GL_Buffer_Updates(rdtest.TestCase): cap = rd.OpenCaptureFile() # Open a particular file - status = cap.OpenFile(self.capture_filename, '', None) + result = cap.OpenFile(self.capture_filename, '', None) # Make sure the file opened successfully - if status != rd.ReplayStatus.Succeeded: + if result != rd.ResultCode.Succeeded: cap.Shutdown() - raise rdtest.TestFailureException("Couldn't open '{}': {}".format(self.capture_filename, str(status))) + raise rdtest.TestFailureException("Couldn't open '{}': {}".format(self.capture_filename, str(result))) thumb: rd.Thumbnail = cap.GetThumbnail(rd.FileType.PNG, 0) diff --git a/util/test/tests/GL/GL_Callstacks.py b/util/test/tests/GL/GL_Callstacks.py index e78a82b85..cde57bbeb 100644 --- a/util/test/tests/GL/GL_Callstacks.py +++ b/util/test/tests/GL/GL_Callstacks.py @@ -24,12 +24,12 @@ class GL_Callstacks(rdtest.TestCase): cap = rd.OpenCaptureFile() # Open a particular file - status = cap.OpenFile(self.capture_filename, '', None) + result = cap.OpenFile(self.capture_filename, '', None) # Make sure the file opened successfully - if status != rd.ReplayStatus.Succeeded: + if result != rd.ResultCode.Succeeded: cap.Shutdown() - raise rdtest.TestFailureException("Couldn't open capture for access: {}".format(self.capture_filename, str(status))) + raise rdtest.TestFailureException("Couldn't open capture for access: {}".format(self.capture_filename, str(result))) if not cap.HasCallstacks(): raise rdtest.TestFailureException("Capture does not report having callstacks")