add renderdoc analysis script and sphere test asset
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -13,3 +13,6 @@ Release/
|
||||
CMakeCache.txt
|
||||
cmake_install.cmake
|
||||
Res/NanoVDB/
|
||||
*.rdc
|
||||
*.pyd
|
||||
*.dll
|
||||
|
||||
255
MVS/RenderDoc/analyze_capture.py
Normal file
255
MVS/RenderDoc/analyze_capture.py
Normal 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()
|
||||
BIN
tests/RHI/OpenGL/integration/sphere/sphere.ppm
Normal file
BIN
tests/RHI/OpenGL/integration/sphere/sphere.ppm
Normal file
Binary file not shown.
Reference in New Issue
Block a user