Rename 'draw' or 'drawcall' to action

* There's not a good accepted terminology for this kind of event, and for
  historical reasons 'drawcall' has been the accepted term, even though
  that can be quite confusing when a dispatch or a copy is a 'drawcall'.
* This is particularly highlighted by the event browser filters where
  $draw() includes draws and dispatches, but $dispatch() only includes
  dispatches, it's hard to intuitively understand why $draw() matches all
  of these calls.
* As a result we've defined the term 'action' to cover these types of
  events in the same way that we defined 'event' in the first place to
  mean a single atomic API call.
This commit is contained in:
baldurk
2021-06-23 18:14:18 +01:00
parent e7785bd99e
commit 7149302680
283 changed files with 5374 additions and 5320 deletions
@@ -94,7 +94,7 @@ def getMeshInputs(controller, draw):
meshInput.numIndices = draw.numIndices
# If the draw doesn't use an index buffer, don't use it even if bound
if not (draw.flags & rd.DrawFlags.Indexed):
if not (draw.flags & rd.ActionFlags.Indexed):
meshInput.indexResourceId = rd.ResourceId.Null()
# The total offset is the attribute offset from the base of the vertex
@@ -223,7 +223,7 @@ def printMeshData(controller, meshData):
def sampleCode(controller):
# Find the biggest drawcall in the whole capture
draw = None
for d in controller.GetDrawcalls():
for d in controller.GetRootActions():
draw = biggestDraw(draw, d)
# Move to that draw
@@ -36,7 +36,7 @@ In the object we pass both the indices (which does not vary per attribute in our
meshInput.numIndices = draw.numIndices
# If the draw doesn't use an index buffer, don't use it even if bound
if not (draw.flags & rd.DrawFlags.Indexed):
if not (draw.flags & rd.ActionFlags.Indexed):
meshInput.indexResourceId = rd.ResourceId.Null()
# The total offset is the attribute offset from the base of the vertex
@@ -68,8 +68,8 @@ out = controller.CreateOutput(windata, rd.ReplayOutputType.Texture)
# Fetch the list of textures
textures = controller.GetTextures()
# Fetch the list of drawcalls
draws = controller.GetDrawcalls()
# Fetch the list of actions
actions = controller.GetRootActions()
# Function to look up the texture descriptor for a given resourceId
def getTexture(texid):
@@ -85,23 +85,23 @@ def paint():
out.Display()
window.after(33, paint)
# Start on the first drawcall
curdraw = draws[0]
# Start on the first action
curact = actions[0]
loopcount = 0
# The advance function will be called every 50ms, to move to the next draw
# The advance function will be called every 50ms, to move to the next action
def advance():
global out, window, curdraw, draws, loopcount
global out, window, curact, actions, loopcount
# Move to the current drawcall
controller.SetFrameEvent(curdraw.eventId, False)
# Move to the current action
controller.SetFrameEvent(curact.eventId, False)
# Initialise a default TextureDisplay object
disp = rd.TextureDisplay()
# Set the first colour output as the texture to display
disp.resourceId = curdraw.outputs[0]
disp.resourceId = curact.outputs[0]
if disp.resourceId != rd.ResourceId.Null():
# Get the details of this texture
@@ -117,13 +117,13 @@ def advance():
# Update the texture display
out.SetTextureDisplay(disp)
# Set the next drawcall
curdraw = curdraw.next
# Set the next action
curact = curact.next
# If we have no next draw, start again from the first
if curdraw is None:
# If we have no next action, start again from the first
if curact is None:
loopcount = loopcount + 1
curdraw = draws[0]
curact = actions[0]
# after 3 loops, quit
if loopcount == 3:
@@ -43,7 +43,7 @@ Once we have the :py:class:`~renderdoc.WindowingData`, we can create a :py:class
# Create a texture output on the window
out = controller.CreateOutput(windata, rd.ReplayOutputType.Texture)
In order to iterate over all drawcalls we need some global state first from :py:meth:`~renderdoc.ReplayController.GetTextures` and :py:meth:`~renderdoc.ReplayController.GetDrawcalls`, and we'll also define a helper function to fetch a particular texture by resourceId, so that we can easily look up the details for a texture.
In order to iterate over all actions we need some global state first from :py:meth:`~renderdoc.ReplayController.GetTextures` and :py:meth:`~renderdoc.ReplayController.GetRootActions`, and we'll also define a helper function to fetch a particular texture by resourceId, so that we can easily look up the details for a texture.
.. highlight:: python
.. code:: python
@@ -51,8 +51,8 @@ In order to iterate over all drawcalls we need some global state first from :py:
# Fetch the list of textures
textures = controller.GetTextures()
# Fetch the list of drawcalls
draws = controller.GetDrawcalls()
# Fetch the list of actions
actions = controller.GetRootActions()
# Function to look up the texture descriptor for a given resourceId
def getTexture(texid):
@@ -62,7 +62,7 @@ In order to iterate over all drawcalls we need some global state first from :py:
return tex
return None
We now define two callback functions - ``paint`` and ``advance``. ``paint`` will be called every 33ms, it will display the output with the latest state using :py:meth:`~renderdoc.ReplayOutput.Display`. ``advance`` changes the current state to reflect a new drawcall.
We now define two callback functions - ``paint`` and ``advance``. ``paint`` will be called every 33ms, it will display the output with the latest state using :py:meth:`~renderdoc.ReplayOutput.Display`. ``advance`` changes the current state to reflect a new action.
.. highlight:: python
.. code:: python
@@ -73,48 +73,60 @@ We now define two callback functions - ``paint`` and ``advance``. ``paint`` will
out.Display()
window.after(33, paint)
Within ``advance`` we do a few things. First we move the current event to the current drawcall's ``eventId``, using :py:meth:`~renderdoc.ReplayController.SetFrameEvent`. Then we set up the texture display configuration with :py:meth:`~renderdoc.ReplayOutput.SetTextureDisplay`, to point to the first colour output at that drawcall.
Within ``advance`` we do a few things. First we move the current event to the current action's ``eventId``, using :py:meth:`~renderdoc.ReplayController.SetFrameEvent`. Then we set up the texture display configuration with :py:meth:`~renderdoc.ReplayOutput.SetTextureDisplay`, to point to the first colour output at that action.
When we update to a new texture, we fetch its details using our earlier ``getTexture`` and calculate a scale that keeps the texture fully visible on screen.
Finally we move to the next drawcall in the list for the next time ``advance`` is called.
Finally we move to the next action in the list for the next time ``advance`` is called.
.. highlight:: python
.. code:: python
# Start on the first drawcall
curdraw = 0
# Start on the first action
curact = actions[0]
loopcount = 0
# The advance function will be called every 150ms, to move to the next draw
# The advance function will be called every 50ms, to move to the next action
def advance():
global out, window, curdraw
# Move to the current drawcall
controller.SetFrameEvent(draws[curdraw].eventId, False)
global out, window, curact, actions, loopcount
# Move to the current action
controller.SetFrameEvent(curact.eventId, False)
# Initialise a default TextureDisplay object
disp = rd.TextureDisplay()
# Set the first colour output as the texture to display
disp.resourceId = draws[curdraw].outputs[0]
# Get the details of this texture
texDetails = getTexture(disp.resourceId)
# Calculate the scale required in width and height
widthScale = window.winfo_width() / texDetails.width
heightScale = window.winfo_height() / texDetails.height
# Use the lower scale to fit the texture on the window
disp.scale = min(widthScale, heightScale)
# Update the texture display
out.SetTextureDisplay(disp)
# Set the next drawcall
curdraw = (curdraw + 1) % len(draws)
window.after(150, advance)
disp.resourceId = curact.outputs[0]
if disp.resourceId != rd.ResourceId.Null():
# Get the details of this texture
texDetails = getTexture(disp.resourceId)
# Calculate the scale required in width and height
widthScale = window.winfo_width() / texDetails.width
heightScale = window.winfo_height() / texDetails.height
# Use the lower scale to fit the texture on the window
disp.scale = min(widthScale, heightScale)
# Update the texture display
out.SetTextureDisplay(disp)
# Set the next action
curact = curact.next
# If we have no next action, start again from the first
if curact is None:
loopcount = loopcount + 1
curact = actions[0]
# after 3 loops, quit
if loopcount == 3:
window.quit()
else:
window.after(50, advance)
Once we have the callbacks defined, we call them once to initialise the display and set up the repeated callbacks, and start the tkinter main window loop.
@@ -7,23 +7,23 @@ if 'renderdoc' not in sys.modules and '_renderdoc' not in sys.modules:
# Alias renderdoc for legibility
rd = renderdoc
draws = {}
actions = {}
# Define a recursive function for iterating over draws
# Define a recursive function for iterating over actions
def iterDraw(d, indent = ''):
global draws
global actions
# save the drawcall by eventId
draws[d.eventId] = d
# save the action by eventId
actions[d.eventId] = d
# Iterate over the draw's children
for d in d.children:
iterDraw(d, indent + ' ')
def sampleCode(controller):
# Iterate over all of the root drawcalls, so we have names for each
# Iterate over all of the root actions, so we have names for each
# eventId
for d in controller.GetDrawcalls():
for d in controller.GetRootActions():
iterDraw(d)
# Enumerate the available counters
@@ -50,10 +50,10 @@ def sampleCode(controller):
# Look in the results for any draws with 0 samples written - this is an indication
# that if a lot of draws appear then culling could be better.
for r in results:
draw = draws[r.eventId]
draw = actions[r.eventId]
# Only care about draws, not about clears and other misc events
if not (draw.flags & rd.DrawFlags.Drawcall):
if not (draw.flags & rd.ActionFlags.Drawcall):
continue
if samplesPassedDesc.resultByteWidth == 4:
@@ -1,7 +1,7 @@
Fetch GPU Counter Data
======================
In this example we will gather GPU counter data over a capture and find any drawcalls that completely failed the depth/stencil test.
In this example we will gather GPU counter data over a capture and find any actions that completely failed the depth/stencil test.
The first thing we do is enumerate a list of counters that the implementation supports using :py:meth:`~renderdoc.ReplayController.EnumerateCounters`. A few of these counters values are statically known - see :py:class:`~renderdoc.GPUCounter`. If you know which counter you want ahead of time you can continue straight away by calling :py:meth:`~renderdoc.ReplayController.FetchCounters` with a list of counters to sample from, or :py:meth:`~renderdoc.ReplayController.DescribeCounter` to obtain a :py:class:`~renderdoc.CounterDescription` of the counter itself:
@@ -34,7 +34,7 @@ However we will also print all available counters. If the implementation support
print(" %s" % desc.description)
print(" Returns %d byte %s, representing %s" % (desc.resultByteWidth, desc.resultType, desc.unit))
Once we have the list of :py:class:`~renderdoc.CounterResult` from sampling the specified counters, each result returned is for one counter on one event. Since we only fetched one counter we can simply iterate over the results looking up the drawcall for each. For actual draws (excluding clears and markers etc) we use the counter description to determine the data payload size, and get the value out. Interpreting this can either happen based on the description, or in our case we know that this counter returns a simple value we can check:
Once we have the list of :py:class:`~renderdoc.CounterResult` from sampling the specified counters, each result returned is for one counter on one event. Since we only fetched one counter we can simply iterate over the results looking up the action for each. For actual draws (excluding clears and markers etc) we use the counter description to determine the data payload size, and get the value out. Interpreting this can either happen based on the description, or in our case we know that this counter returns a simple value we can check:
.. highlight:: python
.. code:: python
@@ -42,10 +42,10 @@ Once we have the list of :py:class:`~renderdoc.CounterResult` from sampling the
# Look in the results for any draws with 0 samples written - this is an indication
# that if a lot of draws appear then culling could be better.
for r in results:
draw = draws[r.eventId]
draw = actions[r.eventId]
# Only care about draws, not about clears and other misc events
if not (draw.flags & rd.DrawFlags.Drawcall):
if not (draw.flags & rd.ActionFlags.Drawcall):
continue
if samplesPassedDesc.resultByteWidth == 4:
+1 -1
View File
@@ -26,7 +26,7 @@ In order to help with this, the examples are organised such that most code is wr
cap.Shutdown()
.. toctree::
iter_draws
iter_actions
fetch_shader
fetch_counters
save_texture
@@ -7,56 +7,56 @@ if 'renderdoc' not in sys.modules and '_renderdoc' not in sys.modules:
# Alias renderdoc for legibility
rd = renderdoc
# Define a recursive function for iterating over draws
def iterDraw(d, indent = ''):
# Print this drawcall
# Define a recursive function for iterating over actions
def iterAction(d, indent = ''):
# Print this action
print('%s%d: %s' % (indent, d.eventId, d.name))
# Iterate over the draw's children
# Iterate over the action's children
for d in d.children:
iterDraw(d, indent + ' ')
iterAction(d, indent + ' ')
def sampleCode(controller):
# Iterate over all of the root drawcalls
for d in controller.GetDrawcalls():
iterDraw(d)
# Iterate over all of the root actions
for d in controller.GetRootActions():
iterAction(d)
# Start iterating from the first real draw as a child of markers
draw = controller.GetDrawcalls()[0]
# Start iterating from the first real action as a child of markers
action = controller.GetRootActions()[0]
while len(draw.children) > 0:
draw = draw.children[0]
while len(action.children) > 0:
action = action.children[0]
# Counter for which pass we're in
passnum = 0
# Counter for how many draws are in the pass
# Counter for how many actions are in the pass
passcontents = 0
# Whether we've started seeing draws in the pass - i.e. we're past any
# Whether we've started seeing actions in the pass - i.e. we're past any
# starting clear calls that may be batched together
inpass = False
print("Pass #0 starts with %d: %s" % (draw.eventId, draw.name))
print("Pass #0 starts with %d: %s" % (action.eventId, action.name))
while draw != None:
while action != None:
# When we encounter a clear
if draw.flags & rd.DrawFlags.Clear:
if action.flags & rd.ActionFlags.Clear:
if inpass:
print("Pass #%d contained %d draws" % (passnum, passcontents))
print("Pass #%d contained %d actions" % (passnum, passcontents))
passnum += 1
print("Pass #%d starts with %d: %s" % (passnum, draw.eventId, draw.name))
print("Pass #%d starts with %d: %s" % (passnum, action.eventId, action.name))
passcontents = 0
inpass = False
else:
passcontents += 1
inpass = True
# Advance to the next drawcall
draw = draw.next
if draw is None:
# Advance to the next action
action = action.next
if action is None:
break
if inpass:
print("Pass #%d contained %d draws" % (passnum, passcontents))
print("Pass #%d contained %d actions" % (passnum, passcontents))
def loadCapture(filename):
# Open a capture file handle
@@ -1,24 +1,24 @@
Iterate drawcall tree
=====================
Iterate Action tree
===================
In this example we will show how to iterate over drawcalls.
In this example we will show how to iterate over actions.
The drawcalls returned from :py:meth:`~renderdoc.ReplayController.GetDrawcalls` are draws or marker regions at the root level - with no parent marker region. There are multiple ways to iterate through the list of draws.
The actions returned from :py:meth:`~renderdoc.ReplayController.GetRootActions` are actions or marker regions at the root level - with no parent marker region. There are multiple ways to iterate through the list of actions.
The first way illustrated in this sample is to walk the tree using :py:attr:`~renderdoc.DrawcallDescription.children`, which contains the list of child draws at any point in the tree. There is also :py:attr:`~renderdoc.DrawcallDescription.parent` which points to the parent drawcall.
The first way illustrated in this sample is to walk the tree using :py:attr:`~renderdoc.ActionDescription.children`, which contains the list of child actions at any point in the tree. There is also :py:attr:`~renderdoc.ActionDescription.parent` which points to the parent action.
The second is to use :py:attr:`~renderdoc.DrawcallDescription.previous` and :py:attr:`~renderdoc.DrawcallDescription.next`, which point to the previous and next draw respectively in a linear fashion, regardless of nesting depth.
The second is to use :py:attr:`~renderdoc.ActionDescription.previous` and :py:attr:`~renderdoc.ActionDescription.next`, which point to the previous and next action respectively in a linear fashion, regardless of nesting depth.
In the example we use this iteration to determine the number of passes, using the drawcall flags to denote the start of each pass by a starting clear call.
In the example we use this iteration to determine the number of passes, using the action flags to denote the start of each pass by a starting clear call.
Example Source
--------------
.. only:: html and not htmlhelp
:download:`Download the example script <iter_draws.py>`.
:download:`Download the example script <iter_actions.py>`.
.. literalinclude:: iter_draws.py
.. literalinclude:: iter_actions.py
Sample output:
@@ -95,11 +95,11 @@ Sample output:
652: API Calls
655: End of Frame
Pass #0 starts with 2: ClearRenderTargetView(0.000000, 0.000000, 0.000000, 1.000000)
Pass #0 contained 24 draws
Pass #0 contained 24 actions
Pass #1 starts with 319: ClearDepthStencilView(D=1.000000, S=00)
Pass #1 contained 24 draws
Pass #1 contained 24 actions
Pass #2 starts with 558: ClearRenderTargetView(0.000000, 0.000000, 0.000000, 1.000000)
Pass #2 contained 3 draws
Pass #2 contained 3 actions
Pass #3 starts with 617: ClearRenderTargetView(0.000000, 0.000000, 0.000000, 1.000000)
Pass #3 contained 4 draws
Pass #3 contained 4 actions
@@ -24,7 +24,7 @@ def biggestDraw(prevBiggest, d):
def sampleCode(controller):
# Find the biggest drawcall in the whole capture
draw = None
for d in controller.GetDrawcalls():
for d in controller.GetRootActions():
draw = biggestDraw(draw, d)
# Move to that draw
@@ -1,11 +1,11 @@
Save a texture to disk
======================
In this example we will find a particular drawcall, and save the color output to disk as an image file.
In this example we will find a particular action, and save the color output to disk as an image file.
To begin with, so that we have an interesting drawcall selected we iterate over the list of draws finding the drawcall with the highest vertex count. For more on how to iterate through a capture's list of drawcalls, see :doc:`iter_draws`.
To begin with, so that we have an interesting action selected we iterate over the list of actions finding the draw with the highest vertex count. For more on how to iterate through a capture's list of actions, see :doc:`iter_actions`.
Once we have set the drawcall we want as the current event, we can configure the texture save operation. To do this we create a :py:class:`~renderdoc.TextureSave` object. The properties of the object determine how the texture will be mapped to an image file to be saved to disk.
Once we have set the draw we want as the current event, we can configure the texture save operation. To do this we create a :py:class:`~renderdoc.TextureSave` object. The properties of the object determine how the texture will be mapped to an image file to be saved to disk.
At minimum you need to select a file format, and we'll try a few - :py:attr:`~renderdoc.FileType.JPG`, :py:attr:`~renderdoc.FileType.HDR`, :py:attr:`~renderdoc.FileType.PNG`, and :py:attr:`~renderdoc.FileType.DDS`.