Files
XCEngine/engine/tools/renderdoc_parser/tools/session_tools.py
ssdfasd fb01beb959 Add renderdoc_parser: direct-call Python interface for RenderDoc capture analysis
- Convert from MCP protocol layer to direct Python function calls
- 42 functions across 9 modules: session, event, pipeline, resource, data, shader, advanced, performance, diagnostic
- Requires Python 3.6 (renderdoc.pyd is compiled for Python 3.6)
- Fix renderdoc API calls: GetColorBlends, GetStencilFaces, GetViewport(i), GetScissor(i)
- Remove Python 3.10+ type annotations for Python 3.6 compatibility
- Add README.md with full API documentation
- Includes test.py for basic smoke testing
2026-03-23 18:46:20 +08:00

218 lines
7.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Session management tools: open_capture, close_capture, get_capture_info, get_frame_overview."""
import os
from ..session import get_session
from ..util import rd, make_error, flags_to_list
def _get_gpu_quirks(driver_name: str) -> list:
"""Return known GPU/API quirks based on the driver name string."""
quirks: list = []
name_upper = driver_name.upper()
if "ADRENO" in name_upper:
quirks.extend(
[
"Adreno: mediump float 实际精度可能低于 spec 要求,关键计算建议强制 highp",
"Adreno: R11G11B10_FLOAT 没有符号位,写入负值会被 clamp 到 0 或产生未定义行为",
"Adreno: 某些驱动版本 textureLod 在 fragment shader 中存在 bug",
]
)
elif "MALI" in name_upper:
quirks.extend(
[
"Mali: R11G11B10_FLOAT 存储负值行为未定义",
"Mali: 大量 discard 指令会导致 early-Z 失效,影响 tile-based 性能",
"Mali: 某些型号 mediump 向量运算累积精度问题",
]
)
elif "POWERVR" in name_upper or "IMAGINATION" in name_upper:
quirks.extend(
[
"PowerVR: Tile-based deferred rendering避免不必要的 framebuffer load/store",
"PowerVR: 避免大量纹理采样器绑定",
]
)
elif "APPLE" in name_upper:
quirks.extend(
[
"Apple GPU: High-efficiency TBDR注意 tile memory 带宽",
"Apple: float16 在 Metal 上性能优秀,建议用于后处理",
]
)
if "OPENGL ES" in name_upper or "GLES" in name_upper:
quirks.append("OpenGL ES: 注意精度限定符 (highp/mediump/lowp) 的正确使用")
quirks.append("OpenGL ES: 扩展依赖需检查 GL_EXTENSIONS 兼容性")
return quirks
def open_capture(filepath: str) -> dict:
"""Open a RenderDoc capture (.rdc) file for analysis.
Automatically closes any previously opened capture.
Returns capture overview: API type, action count, resource counts.
Args:
filepath: Absolute path to the .rdc capture file.
"""
filepath = os.path.normpath(filepath)
session = get_session()
return session.open(filepath)
def close_capture() -> dict:
"""Close the currently open capture file and free resources."""
session = get_session()
return session.close()
def get_capture_info() -> dict:
"""Get information about the currently open capture.
Returns API type, file path, action count, texture/buffer counts,
and known GPU quirks based on the detected driver/GPU.
"""
session = get_session()
err = session.require_open()
if err:
return err
controller = session.controller
textures = controller.GetTextures()
buffers = controller.GetBuffers()
driver_name = session.driver_name
rt_draw_counts = {}
for _eid, action in session.action_map.items():
if action.flags & rd.ActionFlags.Drawcall:
for o in action.outputs:
if int(o) != 0:
key = str(o)
rt_draw_counts[key] = rt_draw_counts.get(key, 0) + 1
resolution = "unknown"
main_color_format = "unknown"
if rt_draw_counts:
top_rid = max(rt_draw_counts, key=lambda k: rt_draw_counts[k])
tex_desc = session.get_texture_desc(top_rid)
if tex_desc is not None:
resolution = f"{tex_desc.width}x{tex_desc.height}"
main_color_format = str(tex_desc.format.Name())
return {
"filepath": session.filepath,
"api": driver_name,
"resolution": resolution,
"main_color_format": main_color_format,
"total_actions": len(session.action_map),
"textures": len(textures),
"buffers": len(buffers),
"current_event": session.current_event,
"known_gpu_quirks": _get_gpu_quirks(driver_name),
}
def get_frame_overview() -> dict:
"""Get a frame-level statistics overview of the current capture.
Returns action counts by type, texture/buffer memory totals,
main render targets with draw counts, and estimated resolution.
"""
session = get_session()
err = session.require_open()
if err:
return err
controller = session.controller
draw_calls = 0
clears = 0
dispatches = 0
for _eid, action in session.action_map.items():
if action.flags & rd.ActionFlags.Drawcall:
draw_calls += 1
if action.flags & rd.ActionFlags.Clear:
clears += 1
if action.flags & rd.ActionFlags.Dispatch:
dispatches += 1
textures = controller.GetTextures()
tex_total_bytes = 0
for tex in textures:
bpp = 4
fmt_name = str(tex.format.Name()).upper()
if "R16G16B16A16" in fmt_name:
bpp = 8
elif "R32G32B32A32" in fmt_name:
bpp = 16
elif "R8G8B8A8" in fmt_name or "B8G8R8A8" in fmt_name:
bpp = 4
elif "R16G16" in fmt_name:
bpp = 4
elif "R32" in fmt_name and "G32" not in fmt_name:
bpp = 4
elif "R16" in fmt_name and "G16" not in fmt_name:
bpp = 2
elif "R8" in fmt_name and "G8" not in fmt_name:
bpp = 1
elif "BC" in fmt_name or "ETC" in fmt_name or "ASTC" in fmt_name:
bpp = 1
elif "D24" in fmt_name or "D32" in fmt_name:
bpp = 4
elif "D16" in fmt_name:
bpp = 2
size = tex.width * tex.height * max(tex.depth, 1) * max(tex.arraysize, 1) * bpp
if tex.mips > 1:
size = int(size * 1.33)
tex_total_bytes += size
buffers = controller.GetBuffers()
buf_total_bytes = sum(buf.length for buf in buffers)
rt_draw_counts = {}
for _eid, action in sorted(session.action_map.items()):
if not (action.flags & rd.ActionFlags.Drawcall):
continue
for o in action.outputs:
if int(o) != 0:
key = str(o)
rt_draw_counts[key] = rt_draw_counts.get(key, 0) + 1
render_targets = []
for rid_str, count in sorted(rt_draw_counts.items(), key=lambda x: -x[1]):
rt_entry: dict = {"resource_id": rid_str, "draw_count": count}
tex_desc = session.get_texture_desc(rid_str)
if tex_desc is not None:
rt_entry["size"] = f"{tex_desc.width}x{tex_desc.height}"
rt_entry["format"] = str(tex_desc.format.Name())
render_targets.append(rt_entry)
resolution = "unknown"
if render_targets:
top_rt = render_targets[0]
if "size" in top_rt:
resolution = top_rt["size"]
return {
"filepath": session.filepath,
"api": session.driver_name,
"resolution": resolution,
"total_actions": len(session.action_map),
"draw_calls": draw_calls,
"clears": clears,
"dispatches": dispatches,
"textures": {
"count": len(textures),
"total_memory_mb": round(tex_total_bytes / (1024 * 1024), 2),
},
"buffers": {
"count": len(buffers),
"total_memory_mb": round(buf_total_bytes / (1024 * 1024), 2),
},
"render_targets": render_targets,
}