add renderdoc analysis script and sphere test asset

This commit is contained in:
2026-03-23 19:08:05 +08:00
parent 6a7be5c6fe
commit bf44438934
3 changed files with 258 additions and 0 deletions

View File

@@ -0,0 +1,255 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
RenderDoc Capture Analysis Script
Uses RenderDoc's Python API to extract comprehensive information from .rdc capture files.
Run with: python analyze_capture.py <capture.rdc> [output.json]
Requires:
- Python 3.6
- renderdoc.pyd (compiled for Python 3.6)
- renderdoc.dll and dependencies in the same directory or PATH
"""
import sys
import json
import os
RENDERDOC_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, RENDERDOC_DIR)
os.environ["PATH"] = RENDERDOC_DIR + os.pathsep + os.environ.get("PATH", "")
import renderdoc as rd
def safe_str(s):
if s is None:
return ""
try:
return str(s)
except:
return repr(s)
def extract_resource_info(resource):
return {
"resource_id": str(resource.resourceId),
"name": safe_str(resource.name),
"type": str(resource.type) if hasattr(resource, "type") else "Unknown",
"format": str(resource.format) if hasattr(resource, "format") else "Unknown",
"dimension": str(resource.dimension)
if hasattr(resource, "dimension")
else "Unknown",
"width": resource.width if hasattr(resource, "width") else 0,
"height": resource.height if hasattr(resource, "height") else 0,
"depth": resource.depth if hasattr(resource, "depth") else 0,
}
def extract_action_info(action, structured_file, include_children=True):
action_info = {
"event_id": action.eventId,
"name": safe_str(
action.GetName(structured_file)
if hasattr(action, "GetName")
else action.customName
),
"custom_name": safe_str(action.customName),
"flags": str(action.flags) if hasattr(action, "flags") else "NoFlags",
}
if hasattr(action, "numIndices") and action.numIndices > 0:
action_info["num_indices"] = action.numIndices
if hasattr(action, "numInstances") and action.numInstances > 0:
action_info["num_instances"] = action.numInstances
if hasattr(action, "vertexOffset"):
action_info["vertex_offset"] = action.vertexOffset
if hasattr(action, "indexOffset"):
action_info["index_offset"] = action.indexOffset
if hasattr(action, "baseVertex"):
action_info["base_vertex"] = action.baseVertex
if include_children and hasattr(action, "children") and action.children:
action_info["children"] = [
extract_action_info(child, structured_file, True)
for child in action.children
]
return action_info
def extract_shader_variables(controller, state):
shader_vars = []
shader_stages = [
(rd.ShaderStage.Vertex, "VS"),
(rd.ShaderStage.Pixel, "PS"),
(rd.ShaderStage.Compute, "CS"),
(rd.ShaderStage.Geometry, "GS"),
(rd.ShaderStage.Hull, "HS"),
(rd.ShaderStage.Domain, "DS"),
]
for stage, stage_name in shader_stages:
try:
shader = state.GetShader(stage)
if shader and hasattr(shader, "reflection") and shader.reflection:
cbuffers = shader.reflection.GetCBuffers()
for cbuf in cbuffers:
try:
variables = controller.GetCBufferVariableContents(
state, shader, cbuf.resourceId
)
if variables:
for var in variables:
var_info = {
"stage": stage_name,
"cbuffer": safe_str(cbuf.name),
"name": safe_str(var.name)
if hasattr(var, "name")
else "",
"type": str(var.type)
if hasattr(var, "type")
else "Unknown",
}
if hasattr(var, "value"):
var_info["value"] = str(var.value)[:500]
shader_vars.append(var_info)
except Exception as e:
pass
except:
pass
return shader_vars
def analyze_capture(filename):
if not os.path.exists(filename):
return {"error": f"File not found: {filename}"}
cap = rd.OpenCaptureFile()
result = cap.OpenFile(filename, "", None)
if result != rd.ResultCode.Succeeded:
return {"error": f"Couldn't open file: {result}"}
if not cap.LocalReplaySupport():
return {"error": "Capture cannot be replayed locally"}
result, controller = cap.OpenCapture(rd.ReplayOptions(), None)
if result != rd.ResultCode.Succeeded:
return {"error": f"Couldn't init replay: {result}"}
try:
structured_file = controller.GetStructuredFile()
capture_info = {
"filename": os.path.basename(filename),
"full_path": os.path.abspath(filename),
"file_size": os.path.getsize(filename),
"renderdoc_version": rd.GetVersionString(),
}
frame_info = {}
try:
frame_desc = controller.GetFrameInfo()
if frame_desc:
frame_info = {
"frame_number": frame_desc.frameNumber
if hasattr(frame_desc, "frameNumber")
else 0,
}
except:
pass
root_actions = controller.GetRootActions()
actions = []
draw_call_count = 0
def process_actions(action_list):
nonlocal draw_call_count
for action in action_list:
action_info = extract_action_info(action, structured_file, False)
if hasattr(action, "flags") and action.flags & rd.ActionFlags.Drawcall:
draw_call_count += 1
actions.append(action_info)
if root_actions:
process_actions(root_actions)
frame_info["draw_call_count"] = draw_call_count
resources = []
try:
resource_list = controller.GetResourceList()
if resource_list:
resources = [extract_resource_info(res) for res in resource_list]
except:
pass
state = controller.GetPipelineState()
shader_variables = extract_shader_variables(controller, state)
debug_messages = []
try:
messages = controller.GetDebugMessages()
if messages:
for msg in messages:
debug_messages.append(
{
"event_id": msg.eventId if hasattr(msg, "eventId") else 0,
"severity": str(msg.severity)
if hasattr(msg, "severity")
else "Unknown",
"category": str(msg.category)
if hasattr(msg, "category")
else "Unknown",
"message": safe_str(msg.description)
if hasattr(msg, "description")
else "",
}
)
except:
pass
result_data = {
"capture_info": capture_info,
"frame_info": frame_info,
"actions": actions,
"resources": resources,
"shader_variables": shader_variables,
"debug_messages": debug_messages,
}
finally:
controller.Shutdown()
cap.Shutdown()
rd.ShutdownReplay()
return result_data
def main():
if len(sys.argv) < 2:
print("Usage: python analyze_capture.py <capture.rdc> [output.json]")
sys.exit(1)
filename = sys.argv[1]
output_path = sys.argv[2] if len(sys.argv) > 2 else None
print(f"Analyzing: {filename}", file=sys.stderr)
result = analyze_capture(filename)
if output_path:
with open(output_path, "w", encoding="utf-8") as f:
json.dump(result, f, indent=2, ensure_ascii=False)
print(f"Saved to: {output_path}", file=sys.stderr)
else:
print(json.dumps(result, indent=2, ensure_ascii=False))
if __name__ == "__main__":
main()