Files
renderdoc/docs/python_api/examples/advanced_buffers.py
T

145 lines
5.0 KiB
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()
if not pyrenderdoc.IsCaptureLoaded():
raise RuntimeError("Run example with capture open")
controller = pyrenderdoc.GetBlockingController()
# grab the current action and pick an appropriate stage for it
action = pyrenderdoc.CurAction()
if action is None:
raise RuntimeError("Run example with a draw action selected")
if action.flags & renderdoc.ActionFlags.MeshDispatch:
stage = renderdoc.ShaderStage.Mesh
elif action.flags & renderdoc.ActionFlags.Drawcall:
stage = renderdoc.ShaderStage.Vertex
else:
raise RuntimeError("Run example with a geometry draw selected")
pipe = pyrenderdoc.CurPipelineState()
refl = pipe.GetShaderReflection(stage)
# find the push data, if there is some - not all APIs have this concept
push_buffers = [
ic
for ic in enumerate(refl.constantBlocks)
if ic[1].bufferBacked == False and ic[1].compileConstants == False
]
# if we didn't get anything, sort by the smallest declared constant buffer
if len(push_buffers) == 0:
push_buffers = list(
sorted(enumerate(refl.constantBlocks), key=lambda ic: ic[1].byteSize)
)
if len(push_buffers) == 0:
raise RuntimeError(f"{stage} shader has no appropriate constant buffer")
# get the index in the constant block interface and the ConstantBlock itself
idx, cb = push_buffers[0]
# get the descriptor for this constant block
descriptor = pipe.GetConstantBlock(stage, idx, 0).descriptor
# use the controller helper to handle all the reflection lookup,
# type interpretation, and data fetching for us
vars = controller.GetCBufferVariableContents(
pipe.GetComputePipelineObject(),
pipe.GetShader(stage),
stage,
refl.entryPoint,
idx,
descriptor.resource,
descriptor.byteOffset,
descriptor.byteSize,
)
# iterate over all the root-level variables and print their type
print(f"== Variables in {cb.name} at {cb.fixedBindSetOrSpace},{cb.fixedBindNumber}")
for v in vars:
print(f"{v.name} is {str(v.type)}")
# for pointer variables, dive inside and see the data they're pointing to
if v.type == renderdoc.VarType.GPUPointer:
ptr = v.GetPointer()
if ptr.pointer == 0:
print(" NULL pointer")
else:
# we will need to interpret these by hand, so use the BufferInterpreter
# helper
interp = qrenderdoc.BufferInterpreter
# first look up which buffer this pointer is pointing to using the helper
# which will enumerate through all known buffers and find the first match
buf, offs = interp.LookupPointer(ptr.pointer)
name = pyrenderdoc.GetResourceName(buf)
print(f" Pointer to {name}+{offs}")
pointerConst = renderdoc.ShaderConstant()
# get the type as declared by the shader - ptr will have a type ID that
# indexes into the reflection structure containing a list of type
# descriptions
pointerConst.name = "Pointer"
pointerConst.type = interp.GetPointerValType(ptr)
print(
f" Declared type {pointerConst.type.name} with {len(pointerConst.type.members)} members"
)
# in some shading langauges, pointers may be declared as a basic type
# and may be re-interpreted inside the shader with a type that does not
# appear in reflection. To mimic this, we will parse a structure string
# ourselves and use that interpreted type.
parse_result = interp.Parse("""
#pack(C)
struct Reinterpret
{
float3 xyz;
float multiplier;
};
Reinterpret val;
""")
# in this example there should be no errors expected
if len(parse_result.errors) != 0:
raise RuntimeError("Got parsing errors!")
# use our re-interpreted type instead of the declared type
pointerConst = parse_result.structure
print(
f" Interpreting as type {pointerConst.type.name} with {len(pointerConst.type.members)} members"
)
# manually fetch some raw byte data through the pointer ourselves
data = controller.GetBufferData(buf, offs, 128)
# now interpret the raw bytes
ptr_vars = interp.GetShaderVariables(pointerConst, data, 3)
# print out each of the instances of the struct and its members
for i, pv in enumerate(ptr_vars):
print(f" [{i}] {pv.name}:")
for member in pv.members:
val = ", ".join(
[f"{x:.2}" for x in member.value.f32v[0 : member.columns]]
)
print(f" .{member.name}: {val}")
print("")