Files
renderdoc/docs/HowRenderDocWorks.aml
T
baldurk c38affcded Initial commit of existing code.
* All renderdoc code up to this point was written by me, history is available by request
2014-05-02 08:33:01 +01:00

105 lines
6.8 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<topic id="f351fea6-d0ac-426b-9631-feec2408a7e2" revisionNumber="1">
<developerConceptualDocument xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5" xmlns:xlink="http://www.w3.org/1999/xlink">
<introduction>
<para>RenderDoc works on very simple operating principles. This page outlines the basic
idea behind its functioning to give people a better idea of what's going on.</para>
</introduction>
<section address="Capture">
<title>Capturing Logs</title>
<content>
<para>Leaving aside the relatively uninteresting matter of injecting the RenderDoc DLL
and calling functions to configure it in the target process, we begin by looking at
how RenderDoc captures a logfile.</para>
<para>We will use D3D11 as an example of a driver for RenderDoc - the driver layer is
responsible both for faithfully capturing the logfile's API usage, as well as then
replaying and analysing it later. Essentially anything built on top of a driver layer
can be used agnostically of the API the application in question is using.</para>
<para>When the driver initialises it will hook into every entry point into the API
such that when application uses the API it passes through the driver wrapper.
In the case of D3D11 this is the D3D11CreateDevice and CreateDXGIFactory functions.</para>
<para>After this point all accesses to the API remain wrapped and the driver essentially
"man-in-the-middle"s between the application and the real API.</para>
<para>The driver initialises in an idle logging state. In this state it's up to the specific
implementation about what it serialises. As a general rule, creation and deletion type actions
are always serialised, and data-upload calls can sometimes be serialised. In some cases
the driver might choose to optimise out some of the data-upload calls and lazy initialise
the contents of some resources to save on idle overhead.</para>
<para>This serialised data is stored in-memory in a chunk-based representation.
Although it's up to the driver implementation it is generally refcounted such that
resources which end up becoming unbound and destroyed will have their memory overhead
deleted.</para>
<para>When the capture button is hit the driver will enter active logging upon the beginning
of the next frame. In this state every API call is serialised out in order and any initial
contents and states are saved.</para>
<para>Once the frame completes this frame capture is serialised to disk along with the in-memory
data for any resources that are referenced - by default resources which are not referenced are
not included in the log.</para>
</content>
</section>
<section address="Replay">
<title>Replaying &amp; Analysing Logs</title>
<content>
<para>The replay process is ostensibly simple, but as with the capturing the devil is in the
details.</para>
<para>When replaying, the initial section of the log (up to the beginning of the frame) is read
and executed verbatim. Each resource created is mapped to the live version and vice versa so
later parts of the log can obtain the replayed representation of the original resource.</para>
<para>RenderDoc then does an initial pass over the captured frame. This allows us to build up a
list of all the 'drawcall' events, analyse dependencies and check which resources are used
at each drawcall for read, write, and so on. An internal tree is built up similar to what you
see in the Event Browser &amp; API Viewer, as well as a linked list with the linear sequence
of drawcalls, since both representations are useful for iterating over the frame.</para>
<para>After this point most work is done in response to user actions. The basic building block
is replaying a partial frame. Most analysis tools are built out of either replaying up to the
current event, replaying up to the event - not including the current drawcall - and replaying
<legacyItalic>only</legacyItalic> the current drawcall.</para>
<para>Care is taken to minimise this as much as possible as this tends to be the slowest operation
given the overheads of serialisation and decoding the command stream.</para>
<para>When replaying from the beginning of a frame (and not a partial subset of the frame) the
initial states of all resources are applied, and the initial pipeline state is restored. Resources
which did not have a serialised initial state (e.g. gbuffer textures) have an initial state saved
before the first replay of the frame, and this is restored. That way you don't get effects 'leaking'
from later in a frame into an earlier point.</para>
<para>For example, let's assume the user has the 'depth test' overlay enabled, and selects a
new event. This is the order of events that occur for the Texture Viewer - other viewers follow
similar patterns, with a certain degree of sharing to reduce redundant replays:</para>
<list class="ordered">
<listItem>
<para>The log is replayed up to, but not including, the selected drawcall. After doing this
the current pipeline state and contents of all resources exactly match the state at the point
of this drawcall.</para>
</listItem>
<listItem>
<para>We then save a copy of the pristine depth buffer, save the current pipeline state, and
set the reversed depth test. Replacing the pixel shader with one that just writes red, we repeat
the drawcall to draw all the areas that fail the depth test.</para>
</listItem>
<listItem>
<para>Restoring the depth buffer and
repeating this with a pixel shader which writes green, we fill in the overlay. Both of these
renders happen to an off-screen buffer.</para>
</listItem>
<listItem>
<para>After restoring the pipeline state we finally replay the original drawcall to get the final
image.</para>
</listItem>
<listItem>
<para>When we want to re-paint the viewed texture (either regular painting, or if the user changed
a visualisation option which is just a constant buffer value) we bind the current render target as
a resource and render it to the texture viewer control, then render the overlay texture on top of
that.</para>
</listItem>
</list>
<para>It's also worth mentioning that there is a bit of special handling for deferred contexts.
When you select an event in a command list, RenderDoc will replay as normal up to just before the
Execute() call, then it will replay all of the commands in the command list up to the currently
selected event on the immediate context.</para>
</content>
</section>
</developerConceptualDocument>
</topic>