Rewrite python docs & onboarding to be more opinionated
* The docs now explicitly tell people how to write UI scripts, then UI extensions, with as quick a start as possible, and go into detail in later documents. Use of the python module directly is also not featured prominently as this is a rare use-case for casual scripting needs.
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 669 B |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 84 KiB |
@@ -0,0 +1,119 @@
|
||||
Python FAQ
|
||||
==========
|
||||
|
||||
This page details some commonly asked questions about Python.
|
||||
|
||||
.. _python-crashes:
|
||||
|
||||
What can I do if I hit a crash while running my python script?
|
||||
--------------------------------------------------------------
|
||||
|
||||
RenderDoc's python bindings are generally quite thin wrappers over the C++ APIs, which means the benefit of low overhead and powerful exposed functionality. The drawback is that it is quite possible to cause crashes, corruption or misbehaviour by passing invalid data to the APIs.
|
||||
|
||||
Aside from type errors you could encounter crashes due to passing semantically invalid data such as a function that expects the :ref:`Resource ID <resourceids>` of a shader, but you pass the ID of a texture. Generally speaking you should not expect robust error-checking from the python API.
|
||||
|
||||
It is also important to bear in mind that python :ref:`lifetime management <lifetimes>` with refcounting may not match the C++ object lifetime. Holding onto python objects that refer to deleted C++ objects could cause crashes as well. Commonly this could happen when a capture is closed and cached information is cleaned up whether or not python still holds handles to it.
|
||||
|
||||
When you encounter a crash, generally this is your responsibility to debug in your script, unless this can be reproduced purely using the UI. If you are certain you have encountered a RenderDoc bug and your script is correct you can report this, but you would need strong and clear evidence that it is not a bug in your script.
|
||||
|
||||
Is there a more useful object preview in the REPL/print?
|
||||
--------------------------------------------------------
|
||||
|
||||
If you are browsing the API using the REPL you may return temporary objects at times and find that the preview for them is rather unhelpful:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
<Swig Object of type 'FooBar *' at 0x000001234ABCD000>
|
||||
|
||||
This is due to the way the RenderDoc python bindings are generated and with how python creates strings for arbitrary objects. To get a more useful preview of an object particularly if it is a struct with properties you can use :func:`~renderdoc.DumpObject`.
|
||||
|
||||
Why can't I see the new UI panel I created?
|
||||
-------------------------------------------
|
||||
|
||||
To avoid unnecessary UI churn and flickering, when you create a new UI panel such as a :class:`~qrenderdoc.BufferViewer` or :class:`~qrenderdoc.ShaderViewer` particularly ones that are not singleton and so will not already be open, the panel is created but not shown in the UI yet.
|
||||
|
||||
You need to call :meth:`~qrenderdoc.CaptureContext.AddDockWindow` to add the UI into the UI hierarchy somewhere.
|
||||
|
||||
Can I run python scripts from the command line?
|
||||
-----------------------------------------------
|
||||
|
||||
For some workflows it may be desirable to run scripts from the command line, the UI offers two ways to do
|
||||
|
||||
Passing ``--py path/to/script.py`` on the command line when running the RenderDoc UI runs the script early in initialisation before the UI has been created or shown and can be used for headless execution or processing. Unlike in most cases, calling ``sys.exit()`` in one of these scripts will cause the RenderDoc process to exit.
|
||||
|
||||
Passing ``--ui-py path/to/script.py`` on the command line will wait until the RenderDoc UI has been shown, then show the python scripting window and load & run the specified script file as a new tab.
|
||||
|
||||
What version compatibility guarantees does python API provide?
|
||||
--------------------------------------------------------------
|
||||
|
||||
Currently the python API is not considered locked, and so each version of RenderDoc may cause incompatible changes to the python API. As the python API is a wrapper, this will only happen when something is renamed or removed, or if the meaning of a member changes. New members in a struct or new methods in a class will not affect any existing python structs and this represents most of the change in the API.
|
||||
|
||||
The release notes for each RenderDoc version includes a section on any breaking python changes, with information on how to address your scripts. Generally it is recommended that you target a recent or latest version of RenderDoc and it is not expected that scripts will try to handle multiple RenderDoc versions.
|
||||
|
||||
Can I get more access to the UI for customisation?
|
||||
--------------------------------------------------
|
||||
|
||||
Currently the entire underlying :doc:`renderdoc <renderdoc/index>` module automatically exposes all functionality possible as the same API is both wrapped for python and used in C++ by the UI.
|
||||
|
||||
The same is not true for the :doc:`qrenderdoc <qrenderdoc/index>` module which exposes the UI windows and functionality. This interface is more conservatively written to avoid exposing huge amounts of unused functionality which may then have significant churn and API-breaking changes.
|
||||
|
||||
If you have something in the UI you would like to customise or interact with, this will be considered if you file a feature request. Generally as long as it would not be unreasonably difficult or constraining on the C++ implementation most things can be exposed if there is a use for them but this will be done by request more than proactively.
|
||||
|
||||
Can I get callbacks when captures are loaded/closed or events are selected?
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
Yes you can! RenderDoc offers a :doc:`in_depth/frame_viewers` API where you can register an object with a given interface, and it will receive callbacks for these cases. This will allow you to have something reactive or that updates when the user browses the frame.
|
||||
|
||||
Can I access these APIs in C++?
|
||||
-------------------------------
|
||||
|
||||
The short answer is yes, but it is not recommended.
|
||||
|
||||
The python API gets the advantage of being tolerant to ABI-breaking changes when a structure is reorganised or a member is added, as python scripts will only break when there is a source-breaking change. If you use these APIs in C++ you will be subject to all ABI changes.
|
||||
|
||||
For this reason using the APIs in C++ is possible but not documented or supported. You should consider very carefully if you are thinking about this whether it would be better to use the python interfaces.
|
||||
|
||||
Can I freely modify and use data I obtain from the APIs?
|
||||
--------------------------------------------------------
|
||||
|
||||
As mentioned above and :ref:`elsewhere <lifetimes>`, RenderDoc's python bindings are fairly directly linked to the underlying C++ structures. In many cases the C++ provides data that is a read-only reference - which has no direct equivalent in python.
|
||||
|
||||
In the majority of cases, lists and objects returned to python are instead copied - these are then owned by python and can be freely modified at will. There are some exceptions for cases where copying by value is prohibitive and so references are returned:
|
||||
|
||||
* For :class:`~renderdoc.ShaderReflection` objects these are stored as references.
|
||||
* When :class:`~renderdoc.ActionDescription` objects are obtained for a capture, these refer to previous/next neighbours and children by reference.
|
||||
* The :class:`~renderdoc.SDFile` for a capture stores all children as reference, including the :class:`~renderdoc.SDObject` and buffers.
|
||||
|
||||
You **should not** modify any of these objects, as this could lead to problems or even crashes as internal data is corrupted.
|
||||
|
||||
Can I use Python scripting together with Android?
|
||||
-------------------------------------------------
|
||||
|
||||
In theory RenderDoc's scripting works transparently regardless of where the replay is running, when used in the UI.
|
||||
|
||||
However Android is an unstable, unreliable, and often broken platform. As a result the use of python scripting with Android captures is not considered officially supported. It is possible that you can use scripting when running on Android but this should be taken with care.
|
||||
|
||||
.. _example_preamble:
|
||||
|
||||
Why do the examples have a preamble for ``pyrenderdoc``?
|
||||
--------------------------------------------------------
|
||||
|
||||
The :doc:`examples <examples/index>` all contain a preamble in their source which is used as hints for external IDEs about the pre-provided modules & global variable. Including this effectively does nothing when the script runs and could be omitted, but means autocomplete and type checking works correctly.
|
||||
|
||||
.. highlight:: python
|
||||
.. code:: python
|
||||
|
||||
# these imports are not strictly necessary, but are convenient
|
||||
import renderdoc
|
||||
import qrenderdoc
|
||||
|
||||
# this is here to give autocomplete when editing the example
|
||||
# in VS Code where it doesn't know about this global
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pyrenderdoc = qrenderdoc.CaptureContext()
|
||||
|
||||
When opening one of the example sources in an IDE like VS Code, it will have no way of knowing that the ``renderdoc`` and ``qrenderdoc`` modules are already imported when running any script in the RenderDoc UI. Importing these again costs very little and helps with autocomplete.
|
||||
|
||||
The ``pyrenderdoc`` global is also pre-provided in python script environments in the RenderDoc UI. To hint this we use a python feature where a single constant in the ``typing`` module called ``TYPE_CHECKING`` is only set to ``True`` when in a type checker like in an IDE, and is ``False`` when actually executing. This allows us to 'initialise' ``pyrenderdoc`` with the correct :class:`~qrenderdoc.CaptureContext` type. Note that this type can not be created from python so this statement would fail if actually executed.
|
||||
@@ -0,0 +1,27 @@
|
||||
if not pyrenderdoc.IsCaptureLoaded():
|
||||
filename = pyrenderdoc.Extensions().OpenFileName("Choose a capture", "", "*.rdc")
|
||||
|
||||
pyrenderdoc.LoadCapture(filename, renderdoc.ReplayOptions(), filename, False, True)
|
||||
|
||||
eid = pyrenderdoc.CurEvent()
|
||||
|
||||
name = pyrenderdoc.GetEventBrowser().GetEventName(eid)
|
||||
|
||||
print(f"Currently we are at EID {eid} named: '{name}'")
|
||||
if eid > 1:
|
||||
prevname = pyrenderdoc.GetEventBrowser().GetEventName(eid - 1)
|
||||
print(f" > the previous event {eid-1} is named: '{prevname}'")
|
||||
|
||||
pipe = pyrenderdoc.CurPipelineState()
|
||||
|
||||
outputs = pipe.GetOutputTargets()
|
||||
|
||||
for idx, out in enumerate(outputs):
|
||||
if out.resource != renderdoc.ResourceId.Null():
|
||||
name = pyrenderdoc.GetResourceName(out.resource)
|
||||
print(f"Output {idx} is: {name}")
|
||||
|
||||
depth = pipe.GetDepthTarget()
|
||||
|
||||
name = pyrenderdoc.GetResourceName(depth.resource)
|
||||
print(f"Depth is: {name}")
|
||||
@@ -0,0 +1,114 @@
|
||||
Tutorial: First Steps with Python
|
||||
=================================
|
||||
|
||||
We will begin by writing a very simple script to run directly in the UI. To start with open the python scripting panel from :guilabel:`Window` → :guilabel:`Python Scripting`.
|
||||
|
||||
Python Scripting panel
|
||||
----------------------
|
||||
|
||||
.. figure:: ../imgs/Screenshots/PythonShellBlank.png
|
||||
|
||||
The python shell when first opened
|
||||
|
||||
The main area is a script editor which allows you to write python code, load and save scripts, and run them.
|
||||
|
||||
On the left is a project explorer which shows recent files loaded, UI extensions (which will be detailed later) as well as a few pre-provided examples which can be loaded and run.
|
||||
|
||||
At the bottom is an interactive REPL (read-evaluate-print loop) which can run python one-liners interactively, as well as tabs that show text output & errors as well as help information.
|
||||
|
||||
Your first script
|
||||
-----------------
|
||||
|
||||
To begin with you should open any capture, and we will run a very simple script on it.
|
||||
|
||||
The example code below can either be copy-pasted or you can type it out to see the autocomplete information as it goes. This is also available as ``Tutorial: First Steps with Python`` in the :guilabel:`Examples` section of the project explorer.
|
||||
|
||||
.. highlight:: python
|
||||
.. literalinclude:: first_steps.py
|
||||
|
||||
You might get output like this in the output window when you click :guilabel:`Run`:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
Currently we are at EID 9408 named: 'vkCmdDrawIndexed(123, 2)'
|
||||
> the previous event 9407 is named: 'vkCmdBindDescriptorSets(1, { Descriptor Set 692529 })'
|
||||
Output 0 is: 2D Color Attachment 690491
|
||||
Output 1 is: 2D Color Attachment 690493
|
||||
Output 2 is: 2D Color Attachment 690496
|
||||
Output 3 is: 2D Color Attachment 690498
|
||||
Output 4 is: 2D Color Attachment 690500
|
||||
|
||||
Breaking it down
|
||||
----------------
|
||||
|
||||
When running scripts in RenderDoc's UI, there is a pre-filled global variable ``pyrenderdoc`` which is your entry point to the API access. This variable represents a :class:`~qrenderdoc.CaptureContext` that provides common data like the current event as well as handles to the available panels like the event browser.
|
||||
|
||||
First we can check that a capture is loaded, and if not prompt the user to give us one to open.
|
||||
|
||||
.. highlight:: python
|
||||
.. code:: python
|
||||
|
||||
if not pyrenderdoc.IsCaptureLoaded():
|
||||
filename = pyrenderdoc.Extensions().OpenFileName("Choose a capture", "", "*.rdc")
|
||||
|
||||
pyrenderdoc.LoadCapture(filename, renderdoc.ReplayOptions(), filename, False, True)
|
||||
|
||||
From ``pyrenderdoc`` we can query the :ref:`current event ID <currentevent>` as an integer. With that :ref:`event ID <eventids>` we can fetch the :class:`~qrenderdoc.EventBrowser` from :meth:`~qrenderdoc.CaptureContext.GetEventBrowser` and use it to look up the formatted name of both the current and previous event with :meth:`~qrenderdoc.EventBrowser.GetEventName`.
|
||||
|
||||
.. highlight:: python
|
||||
.. code:: python
|
||||
|
||||
eid = pyrenderdoc.CurEvent()
|
||||
|
||||
name = pyrenderdoc.GetEventBrowser().GetEventName(eid)
|
||||
|
||||
print(f"Currently we are at EID {eid} named: '{name}'")
|
||||
if eid > 1:
|
||||
prevname = pyrenderdoc.GetEventBrowser().GetEventName(eid-1)
|
||||
print(f" > the previous event {eid-1} is named: '{prevname}'")
|
||||
|
||||
After that we fetch the current :class:`~renderdoc.PipeState` from :meth:`~qrenderdoc.CaptureContext.CurPipelineState` which is an abstraction over a subset of the current pipeline state, which will work regardless of the API in the capture. This doesn't provide all possible states especially where there are differences between APIs but is convenient for simple uses.
|
||||
|
||||
From the pipeline state we get the list of color output targets with :meth:`~renderdoc.PipeState.GetOutputTargets`.
|
||||
|
||||
.. highlight:: python
|
||||
.. code:: python
|
||||
|
||||
pipe = pyrenderdoc.CurPipelineState()
|
||||
|
||||
outputs = pipe.GetOutputTargets()
|
||||
|
||||
The list of outputs may contain unbound resources as some APIs have a fixed set of outputs which may be sparsely populated. We print all of the bound resources and skip any that are unbound, printing the names and indices of the rest via lookup of their :ref:`Resource ID <resourceids>`. For those unfamiliar with python, ``enumerate()`` is a built-in function that returns pairs of ``index, element`` for each element in a list or sequence.
|
||||
|
||||
.. highlight:: python
|
||||
.. code:: python
|
||||
|
||||
for idx, out in enumerate(outputs):
|
||||
if out.resource != renderdoc.ResourceId.Null():
|
||||
name = pyrenderdoc.GetResourceName(out.resource)
|
||||
print(f"Output {idx} is: {name}")
|
||||
|
||||
The ``print()`` statements get sent to the output panel, which will show itself if currently hidden when you run the script. If you hit any python exceptions they will also print to this output panel.
|
||||
|
||||
We can also separately show the depth target, if bound, using the same mechanism:
|
||||
|
||||
.. highlight:: python
|
||||
.. code:: python
|
||||
|
||||
depth = pipe.GetDepthTarget()
|
||||
|
||||
name = pyrenderdoc.GetResourceName(depth.resource)
|
||||
print(f"Depth is: {name}")
|
||||
|
||||
.. note::
|
||||
|
||||
It is safe to call ``sys.exit()`` to abort a script running, this will not close RenderDoc itself!
|
||||
|
||||
This tutorial will not exhaustively cover everything available in the :class:`pyrenderdoc <qrenderdoc.CaptureContext>` context. You are encouraged to browse the documentation or explore through autocomplete to see what is available.
|
||||
|
||||
Next steps
|
||||
----------
|
||||
|
||||
This gives a *very* brief 5-minute intro to using python. You could now look at some more of the :doc:`examples/index` that give starting points for common workflows or pieces of analysis.
|
||||
|
||||
To move away from manually opening & running scripts next we will create a UI extension which is more convenient for day-to-day use.
|
||||
@@ -0,0 +1,91 @@
|
||||
Python IDE integration
|
||||
======================
|
||||
|
||||
RenderDoc supports integration with external editors, as RenderDoc is not as powerful or capable for python development as a dedicated IDE. With IDE integration it is also possible to set breakpoints and step-through debug python scripts as well which is not possible within RenderDoc.
|
||||
|
||||
For this tutorial we will use VS Code as it has the best integration with RenderDoc, but it is possible to use other tools such as PyCharm.
|
||||
|
||||
.. image:: ../imgs/python/VSCodeDebugging.png
|
||||
:target: ../_images/VSCodeDebugging.png
|
||||
|
||||
A screenshot of a VS Code debugging session, paused in the :doc:`tutorial UI extension <ui_extensions>`.
|
||||
|
||||
VS Code quick setup
|
||||
-------------------
|
||||
|
||||
If you want to use `VS Code <https://code.visualstudio.com/>`_ with RenderDoc, it will work out of the box for simple code editing.
|
||||
|
||||
To use it for debugging python code and with fully-featured autocomplete, here are the extra setup steps needed.
|
||||
|
||||
#. Install the `pylance <https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance>`_ and `debugpy <https://marketplace.visualstudio.com/items?itemName=ms-python.debugpy>`_ extensions if you don't have them already. These are automatically installed by the default `python <https://marketplace.visualstudio.com/items?itemName=ms-python.python>`_ meta-extension.
|
||||
#. Open the settings window (:kbd:`Ctrl-,`) to modify these settings:
|
||||
#. Add the RenderDoc stubs folder to :abbr:`Extensions → PyLance → Extra Paths (python.analysis.extraPaths)` (``@id:python.analysis.extraPaths``).
|
||||
|
||||
On Windows this is ``%APPDATA%\qrenderdoc\pystubs\latest`` and on linux it's ``~/.local/share/qrenderdoc/pystubs/latest``.
|
||||
#. Disable the :abbr:`Extensions → Python Debugger → Just my code (debugpy.debugJustMyCode)` (``@id:debugpy.debugJustMyCode``) setting.
|
||||
#. Enable the :abbr:`Features → Tasks → Allow Automatic Tasks (task.allowAutomaticTasks)` (``@id:task.allowAutomaticTasks``) setting (optional).
|
||||
#. When actively debugging, enable ``Breakpoints → User Uncaught Exceptions`` at the bottom of the ``Run and Debug`` sidebar.
|
||||
|
||||
If you just installed the debugging extensions, you will have to restart the RenderDoc UI for them to be found. After this you can use the :guilabel:`Debug` buttons in the python scripting panel or the extension manager to debug python code, with full autocomplete in VS Code. In the VS Code settings JSON this looks like so:
|
||||
|
||||
.. highlight:: json
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"python.analysis.extraPaths": [
|
||||
"C:\\users\\baldurk\\appdata\\roaming\\qrenderdoc\\pystubs\\latest"
|
||||
],
|
||||
"debugpy.debugJustMyCode": false,
|
||||
"task.allowAutomaticTasks": "on"
|
||||
}
|
||||
|
||||
.. _pystubs:
|
||||
|
||||
Python Stubs
|
||||
------------
|
||||
|
||||
Python modules written in C like RenderDoc's can't have type annotations that are key to providing good autocomplete in an IDE. The standard alternative is to provide python 'stub' files which are written in pure python and have no implementations, only signatures and other type annotations.
|
||||
|
||||
RenderDoc generates appropriate stubs within your application data directory with one per version as well as a rolling 'latest' version. On Windows this is ``%APPDATA%\qrenderdoc\pystubs`` and on linux it's ``~/.local/share/qrenderdoc/pystubs``.
|
||||
|
||||
Typically you can use the ``latest`` version without problems, but if you are targeting a specific version of RenderDoc you can use one of the versioned directories.
|
||||
|
||||
In VS Code to enable the use of stubs, add this folder to the ``python.analysis.extraPaths`` setting which can be found at ``Python → Analysis: Extra Paths`` in the VS Code UI. For other editors consult your editor's documentation for how to add extra stubs paths for typechecking and autocomplete.
|
||||
|
||||
After doing this any python scripts that import the ``renderdoc`` or ``qrenderdoc`` modules should have proper autocomplete.
|
||||
|
||||
Python Debugging
|
||||
----------------
|
||||
|
||||
RenderDoc supports integration with `debugpy library <https://github.com/microsoft/debugpy>`_, a common remote debugging toolkit that allows external debuggers or IDEs to connect and debug python code running within the RenderDoc UI.
|
||||
|
||||
If you have VS Code installed in the standard location, and have the `ms-python.debugpy <https://marketplace.visualstudio.com/items?itemName=ms-python.debugpy>`_ extension installed, RenderDoc will automatically load and initialise ``debugpy`` on first startup. It will also try to load ``debugpy`` from PyCharm's installation if VS Code can't be found, or you can specify a custom path to where ``debugpy`` should be loaded.
|
||||
|
||||
If you have just installed these extensions, you will need to restart RenderDoc to initialise it.
|
||||
|
||||
.. note::
|
||||
If building RenderDoc from source you will build against Python 3.6 which does not support debugging. Customise your local build to :ref:`use a newer version of Python <custom-py-ver>` or use one of the official releases which uses Python 3.8.
|
||||
|
||||
Once ``debugpy`` has been loaded, the debugger is listening on the default local port ``5678``. Within VS Code or your IDE you can configure what may be called a 'remote attach' or 'debug server attach' connecting to ``localhost`` on port ``5678``.
|
||||
|
||||
If RenderDoc has detected your installation of VS Code it also provides convenient ways to debug UI extensions and scripts. From the extension manager or in a python script you have written you can press the :guilabel:`Debug` button. This will automatically try to launch VS Code with the necessary environment to connect a debugger. If you enable the ``Allow Automatic Tasks`` option in VS Code's settings it will automatically connect to the debugger on startup, otherwise you will have to choose to start debugging in order to connect.
|
||||
|
||||
.. figure:: ../imgs/python/DebuggerAttached.png
|
||||
|
||||
The status bar showing that a python debugger is connected.
|
||||
|
||||
.. important::
|
||||
Due to a quirk of how the python integration works in RenderDoc, VS Code may consider scripts as not being contained properly within a project. It is strongly recommend that you **disable** the ``Just my code`` option under the ``debugpy`` extension.
|
||||
|
||||
You may also need to check the ``User Uncaught Exceptions`` setting under ``Breakpoints`` to properly trap exceptions that are thrown in the python code, as otherwise RenderDoc will catch them itself for display.
|
||||
|
||||
Next steps
|
||||
----------
|
||||
|
||||
You should now have a good setup for writing and debugging python code using RenderDoc's modules, as well as a starting point for writing scripts and UI extensions.
|
||||
|
||||
If you have a specific task in mind you can explore the API, you may want to consult the :doc:`examples/index` which show how to do several simple tasks and demonstrate the use of :class:`~renderdoc.ReplayController` which is the main entry point for the underlying API and most of the possible power that RenderDoc exposes.
|
||||
|
||||
If you are writing more complex scripts you may want to see :doc:`in_depth/index` which have more detailed explanations of particular topics or things to bear in mind.
|
||||
|
||||
There is also a :doc:`faq` which addresses some common issues or stumbling blocks.
|
||||
@@ -1,2 +1,23 @@
|
||||
Python API
|
||||
==========
|
||||
|
||||
RenderDoc exposes a significant amount of functionality to custom python scripting. This documentation will walk you through how to get started, showing examples of one-off scripts and permanent UI extensions.
|
||||
|
||||
Scripting this way can help automate or simplify common patterns you use, and personalise RenderDoc for your specific needs or wants.
|
||||
|
||||
RenderDoc's internal APIs are exposed directly to python, meaning any feature exposed in the UI will be accessible in some way to python as well. Many aspects of the UI itself are also accessible, and that can be extended in future by request.
|
||||
|
||||
It's assumed that you have a basic familiarity with python or can learn the syntax.
|
||||
|
||||
-----------------
|
||||
|
||||
.. toctree::
|
||||
first_steps
|
||||
ui_extensions
|
||||
ide_integration
|
||||
faq
|
||||
examples/index
|
||||
in_depth/index
|
||||
python_module
|
||||
renderdoc/index
|
||||
qrenderdoc/index
|
||||
@@ -0,0 +1,90 @@
|
||||
.. _pymodule:
|
||||
|
||||
Using Python Module Manually
|
||||
============================
|
||||
|
||||
This documentation has introduced python scripting from within the embedded python runtime in the RenderDoc UI. Demonstrating how it can be used to customise and extend the UI in personalised ways.
|
||||
|
||||
It is also possible to load the RenderDoc module from within a standalone python interpreter as a normal python module, though this does come with caveats and limitations. This allows the most amount of control over functionality and can integrate most easily with custom automation.
|
||||
|
||||
The only interface available in the python interpreter is the ``renderdoc`` module, for obvious reasons the ``qrenderdoc`` module that provides access to UI functionality is not available standalone outside of the UI itself.
|
||||
|
||||
.. warning::
|
||||
Using the python module directly is an advanced use case and is **not** necessary for writing scripts or extensions to customise the UI!
|
||||
|
||||
Building the module
|
||||
-------------------
|
||||
|
||||
RenderDoc by default does not ship with a python module that can be loaded into the ``python`` interpreter. This is because the python bindings are specific to a particular major-and-minor version of python, as well as the general difficulty with distributing binary python modules.
|
||||
|
||||
It is however possible to build the module as long as you know the exact python version you will be using, and this page details how to do that and the limitations of this setup.
|
||||
|
||||
The first step is to ensure that you have a local version of `RenderDoc's source <https://github.com/baldurk/renderdoc>`_ and can build it successfully. The necessary `dependencies <https://github.com/baldurk/renderdoc/blob/v1.x/docs/CONTRIBUTING/Dependencies.md>`_ and `instructions <https://github.com/baldurk/renderdoc/blob/v1.x/docs/CONTRIBUTING/Compiling.md>`_ are listed on github but e.g. on windows all that is needed is Visual Studio 2015+.
|
||||
|
||||
Once you have built RenderDoc, by default you will have a version of the python module already. Depending on your platform it will be generated in a different place - on Windows to prevent filename collisions it is in a ``pymodules`` subfolder under the relevant platform and build type, on linux it will be output to the ``lib`` folder as ``renderdoc.so``.
|
||||
|
||||
This module will be built against the default python interpreter - on linux this will depend on the version available in your system which may already be the one you want, on windows this will be the version of python bundled in the source code which is Python 3.6. The module can *only* be safely loaded in this version of python and no other. To use with a different version of python, you must rebuild RenderDoc against that version of python.
|
||||
|
||||
.. _custom-py-ver:
|
||||
|
||||
Targeting a different python version
|
||||
------------------------------------
|
||||
|
||||
Forcing the build to use a different python version varies depending on where you are building it.
|
||||
|
||||
Windows
|
||||
^^^^^^^
|
||||
|
||||
On windows the python configuration is set in the visual studio project. Each of the projects ``qrenderdoc``, ``pyrenderdoc_module`` and ``qrenderdoc_module`` have a tab in their properties labelled :guilabel:`Python Configuration` which has a single entry pointing to the location of a python interpreter.
|
||||
|
||||
Almost everything that RenderDoc needs is present in a normal python installation - the header files in ``Include/``, link library in ``libs/`` and python interpreter DLL. However on windows RenderDoc also expects the standard library to be available as a compiled zip bundle ``python3.xx.zip`` similar to the 'embeddable package' that is provided by python downloads, and default installations only have the standard library as loose files in ``Lib/``.
|
||||
|
||||
For convenience the RenderDoc source checkout has a python script which will compile this zip. Running ``util/make_python_lib_zip.py`` from the interpreter you wish to use will automatically compile its library from the ``Lib/`` folder into a ``python3.xx.zip`` next to ``python.exe``. If you wish to control which library folder to compile and where to put the zip, you can pass these as arguments to the ``make_python_lib_zip.py`` script, but note that the visual studio build will expect to find the library zip next to the DLL.
|
||||
|
||||
Once this zip has been created or otherwise obtained, rebuilding the visual studio solution should use the new python version for both the embedded runtime in the RenderDoc UI and build the python module against it. You can see it successful with a message like so::
|
||||
|
||||
Built against python from C:\Python314
|
||||
|
||||
If there is a problem with the specified location you may instead see a message indicating the problem, and RenderDoc will fall back to using the bundled python 3.6::
|
||||
|
||||
** Could not use python version C:\Python314 due to missing requirements.
|
||||
** Check for C:\Python314\include\Python.h, C:\Python314\pythonMAJMIN.zip, and either C:\Python314\pythonMAJMIN.lib or C:\Python314\libs\pythonMAJMIN.lib
|
||||
|
||||
Linux
|
||||
^^^^^
|
||||
|
||||
On Linux if you are using cmake version 3.12 or newer you can specify ``-DFORCE_PY_VERSION=3.xx`` on the command line to force the build to require a specific version. You may need to install a different package for development files, or use additional cmake configuration options to ensure the correct locations are found. Consult your distribution's documentation or the cmake documentation for ``FindPython3`` for more information.
|
||||
|
||||
If successful, the cmake configuration will print which python version is in use before build, and the module will be loadable in that version of python.
|
||||
|
||||
Loading the module
|
||||
------------------
|
||||
|
||||
You can use whichever python mechanism you prefer to ensure that the RenderDoc module is in python's search path. This could mean modifying ``sys.path`` or specifying the python path another way at startup. This will ensure the RenderDoc module can be found by python.
|
||||
|
||||
The module itself also has a dependency on RenderDoc's core library as itself it is only a thin wrapper. The core library must be available for loading when the python module is imported. This again will depend on your platform.
|
||||
|
||||
Windows
|
||||
^^^^^^^
|
||||
|
||||
On windows by default the module will expect RenderDoc's core library to be in ``PATH``, but on python 3.8 and above, there is an extra step necessary on windows only. You must call ``os.add_dll_directory`` with the path where ``renderdoc.dll`` can be found.
|
||||
|
||||
Provided ``renderdoc.dll`` is available in the ``PATH`` and if necessary ``os.add_dll_directory`` is called, the module should load correctly. If the DLL can't be found you will see an error like this::
|
||||
|
||||
ImportError: DLL load failed while importing renderdoc: The specified module could not be found.
|
||||
|
||||
Linux
|
||||
^^^^^
|
||||
|
||||
On linux the python module is built with a ``RUNPATH`` that will include its own location, meaning that by default nothing else is required since the RenderDoc library ``librenderdoc.so`` is built in the same directory. If this is moved or changed, you should use ``LD_LIBRARY_PATH`` as appropriate to ensure the library can still be loaded. If you do not you will see an error like this::
|
||||
|
||||
ImportError: librenderdoc.so: cannot open shared object file: No such file or directory
|
||||
|
||||
Using RenderDoc's API directly
|
||||
------------------------------
|
||||
|
||||
If you are using RenderDoc's API directly there are some things you will need to take care of which normally the UI would handle for you.
|
||||
|
||||
The RenderDoc replay API must be initialised using :func:`~renderdoc.InitialiseReplay` once before any other API function is called, and at the end of the process you must call :func:`~renderdoc.ShutdownReplay`. It is not valid to call any API function before initialisation or after shutdown, and you can't re-initialise after shutting down. These functions are called by the UI normally and so must only be used when writing scripts that use the module directly.
|
||||
|
||||
For a complex example of how to use the RenderDoc python module directly you can look at the automatic testing scripts in the `RenderDoc repository <https://github.com/baldurk/renderdoc/tree/v1.x/util/test>`_. These scripts perform automatic capture, replay and analysis for self-testing of RenderDoc and are written entirely in python.
|
||||
@@ -0,0 +1,78 @@
|
||||
import renderdoc as rd
|
||||
import qrenderdoc as qrd
|
||||
from typing import List
|
||||
|
||||
|
||||
def check_draw(best_size: int, action: rd.ActionDescription):
|
||||
if action.flags & rd.ActionFlags.Drawcall:
|
||||
size = action.numIndices * action.numInstances
|
||||
if size > best_size:
|
||||
return action.eventId, size
|
||||
return 0, 0
|
||||
|
||||
|
||||
def find_largest_draw(best_size: int, actions: List[rd.ActionDescription]):
|
||||
ret = 0
|
||||
for action in actions:
|
||||
result = check_draw(best_size, action)
|
||||
|
||||
if result[0] == 0:
|
||||
result = find_largest_draw(best_size, action.children)
|
||||
|
||||
if result[0] > 0:
|
||||
ret, best_size = result
|
||||
|
||||
return ret, best_size
|
||||
|
||||
|
||||
def open_window(pyrenderdoc: qrd.CaptureContext, data):
|
||||
mqt = pyrenderdoc.Extensions().GetMiniQtHelper()
|
||||
|
||||
top = mqt.CreateToplevelWidget("Scavenger Hunt")
|
||||
|
||||
group = mqt.CreateGroupBox(False)
|
||||
mqt.SetWidgetText(group, "Exciting scavenger hunt!")
|
||||
|
||||
label = mqt.CreateLabel()
|
||||
mqt.SetWidgetText(label, "Guess the biggest draw!")
|
||||
|
||||
eid, size = find_largest_draw(0, pyrenderdoc.CurRootActions())
|
||||
|
||||
def do_guess(pyrenderdoc, widget, text):
|
||||
print(f"Spoiler: largest draw is {eid}, it drew {size} indices")
|
||||
|
||||
if pyrenderdoc.CurEvent() == eid:
|
||||
msg = "You found it!"
|
||||
elif pyrenderdoc.CurEvent() < eid:
|
||||
msg = "The largest draw is later in the capture..."
|
||||
else:
|
||||
msg = "The largest draw is earlier in the capture..."
|
||||
|
||||
mqt.SetWidgetText(label, f"Guess the biggest draw!\n\n{msg}")
|
||||
|
||||
button = mqt.CreateButton(do_guess)
|
||||
mqt.SetWidgetText(button, "Guess")
|
||||
|
||||
if eid == 0:
|
||||
mqt.SetWidgetText(
|
||||
label,
|
||||
"You don't have a capture with drawcalls loaded :(.\n"
|
||||
"Re-open this window after opening a capture!",
|
||||
)
|
||||
mqt.SetWidgetEnabled(button, False)
|
||||
|
||||
mqt.AddWidget(top, group)
|
||||
mqt.AddWidget(group, label)
|
||||
mqt.AddWidget(group, button)
|
||||
|
||||
pyrenderdoc.AddDockWindow(
|
||||
top, qrd.DockReference.TopOf, pyrenderdoc.GetEventBrowser().Widget(), 0.2
|
||||
)
|
||||
|
||||
|
||||
def register(version, pyrenderdoc: qrd.CaptureContext):
|
||||
print(f"Tutorial extension registered in RenderDoc {version}")
|
||||
|
||||
pyrenderdoc.Extensions().RegisterPanelMenu(
|
||||
qrd.PanelMenu.EventBrowser, ["Tutorial", "Scavenger Hunt"], open_window
|
||||
)
|
||||
@@ -1,2 +1,87 @@
|
||||
Tutorial: UI extensions
|
||||
=======================
|
||||
|
||||
This document outlines how to get started writing a UI extension.
|
||||
|
||||
Creating an extension
|
||||
---------------------
|
||||
|
||||
RenderDoc UI extensions are python modules loaded from standard locations on disk, depending on your platform. On Windows it's :file:`%APPDATA%\\qrenderdoc\\extensions` and on linux it's ``~/.local/share/qrenderdoc/extensions``.
|
||||
|
||||
In any subdirectory under this path you can register an extension by creating a ``extension.json`` file with some metadata, and creating a python module starting with an ``__init__.py`` file.
|
||||
|
||||
.. figure:: ../imgs/Screenshots/ExtensionManager.png
|
||||
|
||||
Extension Manager: Configures installed extensions.
|
||||
|
||||
To streamline setup we will ask RenderDoc to create a new extension for us. Open the extension manager by opening the :guilabel:`Tools` menu and select :guilabel:`Manage Extensions`, then click the :guilabel:`Create New...` button and enter a package name such as ``tutorialext``. This will create the ``extension.json`` and ``__init__.py`` files in a new folder ``tutorialext`` for us.
|
||||
|
||||
For more information about the registration of python extensions see :doc:`../how/how_python_extension`
|
||||
|
||||
Loading the extension
|
||||
---------------------
|
||||
|
||||
To load the extension, select it in the list in the extension manager and click the :guilabel:`Load` button. Python modules can't be unloaded but they can be reloaded if changes are made to the files on disk, so the button changes to :guilabel:`Reload`.
|
||||
|
||||
When the extension is loaded a ``register()`` function is called, which at the moment for us just prints a message. This message can be viewed in the output panel of the :doc:`python scripting <../window/python_scripting>` window, which you can jump to by clicking :guilabel:`View Output`. You can filter the output with the drop-down above the output text.
|
||||
|
||||
.. note::
|
||||
The output this will not show any messages from before the window was opened - you can click :guilabel:`Reload` after opening the python scripting window to see some new messages.
|
||||
|
||||
To avoid bugs in extensions from making the UI unusable easily, python extensions are not loaded by default. Once you've loaded an extension if you wish to have it automatically load on startup, you can do so by enabling :guilabel:`Always Load`.
|
||||
|
||||
Editing your extension
|
||||
----------------------
|
||||
|
||||
At this point you will have an ``extension.json`` and ``__init__.py`` in the ``extensions/tutorialext`` folder in your application data directory. These can be edited in the program of your choice, but we will use the python scripting window which can browse and open extension files for edit.
|
||||
|
||||
In the python scripting panel project sidebar, expand :guilabel:`UI Extensions` and :guilabel:`tutorialext` to open these two files and see the default-provided contents.
|
||||
|
||||
.. figure:: ../imgs/Screenshots/PythonUIProjectEditor.png
|
||||
|
||||
Python Scripting: Editing the files for a new UI extension.
|
||||
|
||||
We will add a UI button and new panel to demonstrate how UI extensions can provide user-interactive features. This is available as ``Tutorial: UI extension`` in the :guilabel:`Examples` section of the project explorer, though note that you will have to copy the code into your ``__init__.py`` as the example does not run on its own.
|
||||
|
||||
.. highlight:: python
|
||||
.. literalinclude:: ui_extensions.py
|
||||
|
||||
If you edit the ``__init__.py`` you'll find that the RenderDoc status bar will notify you that as well as having one extension currently loaded the files have been changed on disk. Clicking the button in the status bar will reload the extension:
|
||||
|
||||
.. figure:: ../imgs/Screenshots/PythonExtensionModified.png
|
||||
|
||||
The RenderDoc status bar with a modified extension loaded
|
||||
|
||||
.. |plugin| image:: ../imgs/icons/plugin.png
|
||||
|
||||
After the extension has been reloaded, you can use the new extension menu item under the extension icon |plugin| in the event browser. The menu item will open a new panel with a scavenger hunt for the largest drawcall in your capture.
|
||||
|
||||
.. figure:: ../imgs/Screenshots/TutorialUIExtension.png
|
||||
|
||||
The new button and window added by the extension.
|
||||
|
||||
Breaking it down
|
||||
----------------
|
||||
|
||||
This example demonstrates how you can bridge the gap between python scripts and UI elements. It shows how to add a menu item to one of the main interfaces - the event browser - and how to create a new UI panel with custom interactivity.
|
||||
|
||||
When our extension is loaded (or reloaded) the ``register()`` function we define is called with two parameters, the version of RenderDoc as a string e.g. ``"1.23"`` and the :class:`~qrenderdoc.CaptureContext` which is also available as a global ``pyrenderdoc``.
|
||||
|
||||
From the :class:`~qrenderdoc.CaptureContext` at ``pyrenderdoc`` that we used in the previous example, we can get access to :class:`~qrenderdoc.ExtensionManager` which gives us the option to create a UI. First we register a menu item (:meth:`~qrenderdoc.ExtensionManager.RegisterPanelMenu`) in the event browser's toolbar, providing a list of submenus and a callback to call when it is pressed.
|
||||
|
||||
.. tip::
|
||||
|
||||
There are multiple places where you can add a new menu item, explore the available enum values and functions here to see what options there are!
|
||||
|
||||
When the ``open_window`` callback is called, we create a new window using the :doc:`UI helpers <in_depth/miniqt>` in :class:`~qrenderdoc.MiniQtHelper`. Note that most RenderDoc builds ship with fully integrated python Qt access via PySide, but the full Qt API is quite complex and not necessary for simple quick UIs.
|
||||
|
||||
For the UI we create a groupbox with a label and a button. When the button is pressed, it updates the label based on where the :ref:`current event <currentevent>` is relative to the largest drawcall found in the capture. This is all contained within a top-level widget (:meth:`~qrenderdoc.MiniQtHelper.CreateToplevelWidget`)
|
||||
|
||||
Finally we use :meth:`~qrenderdoc.CaptureContext.AddDockWindow` to add the top-level widget into RenderDoc's docking system. Any widget can be added as a new top-level docking panel, but it is recommended that you use an explicit top-level widget to be able to use its callback when it is closed.
|
||||
|
||||
Next steps
|
||||
----------
|
||||
|
||||
This shows how to expose user-visible tools to connect through to custom scripts which can be of varying complexity. At this point you hopefully have the starting point to begin exploring APIs available in the documentation or through autocomplete.
|
||||
|
||||
Up until now we have written everything within the RenderDoc UI to get started quickly. This is fine for writing small snippets of code, but we can also set up an external IDE for a better experience when writing larger or more complex extensions.
|
||||
@@ -1,5 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="/py">
|
||||
<file alias="tutorial/first_steps.py">../../docs/python_api/first_steps.py</file>
|
||||
<file alias="tutorial/ui_extensions.py">../../docs/python_api/ui_extensions.py</file>
|
||||
<file alias="examples/show_buffer.py">../../docs/python_api/examples/show_buffer.py</file>
|
||||
<file alias="examples/show_texture.py">../../docs/python_api/examples/show_texture.py</file>
|
||||
<file alias="examples/iter_actions.py">../../docs/python_api/examples/iter_actions.py</file>
|
||||
|
||||