mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-05 01:20:42 +00:00
c38affcded
* All renderdoc code up to this point was written by me, history is available by request
105 lines
6.8 KiB
XML
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 & 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 & 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>
|