mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-29 09:56:39 +00:00
145 lines
5.0 KiB
Python
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("")
|