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,
|
|||
|
|
}
|