- 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
218 lines
7.2 KiB
Python
218 lines
7.2 KiB
Python
"""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,
|
||
}
|