mirror of
https://github.com/HKUDS/CLI-Anything.git
synced 2026-08-30 17:34:27 +08:00
fix: modernize nsight graphics capture compatibility
This commit is contained in:
@@ -58,7 +58,7 @@ cli-anything-nsight-graphics ^
|
||||
|
||||
```bash
|
||||
cli-anything-nsight-graphics launch detached ^
|
||||
--activity "Frame Debugger" ^
|
||||
--activity "Graphics Capture" ^
|
||||
--exe "C:\VulkanSDK\1.3.290.0\Bin\vkcube.exe"
|
||||
```
|
||||
|
||||
@@ -66,7 +66,7 @@ cli-anything-nsight-graphics launch detached ^
|
||||
|
||||
```bash
|
||||
cli-anything-nsight-graphics launch attach ^
|
||||
--activity "Frame Debugger" ^
|
||||
--activity "Graphics Capture" ^
|
||||
--pid 12345
|
||||
```
|
||||
|
||||
@@ -168,20 +168,32 @@ When `gpu-trace capture --summarize` is used, the result also includes:
|
||||
| Variable | Purpose |
|
||||
|----------|---------|
|
||||
| `NSIGHT_GRAPHICS_PATH` | Override executable discovery |
|
||||
| `NSIGHT_GRAPHICS_TEST_EXE` | E2E target executable |
|
||||
| `NSIGHT_GRAPHICS_TEST_ARGS` | Optional E2E target arguments |
|
||||
| `NSIGHT_GRAPHICS_TEST_WORKDIR` | Optional E2E working directory |
|
||||
| `NSIGHT_GRAPHICS_TEST_EXE` | Shared fallback E2E target executable |
|
||||
| `NSIGHT_GRAPHICS_TEST_ARGS` | Shared fallback E2E target arguments |
|
||||
| `NSIGHT_GRAPHICS_TEST_WORKDIR` | Shared fallback E2E working directory |
|
||||
| `NSIGHT_GRAPHICS_FRAME_TEST_EXE` | Optional frame-capture-specific executable override |
|
||||
| `NSIGHT_GRAPHICS_FRAME_TEST_ARGS` | Optional frame-capture-specific args override |
|
||||
| `NSIGHT_GRAPHICS_FRAME_TEST_WORKDIR` | Optional frame-capture-specific workdir override |
|
||||
| `NSIGHT_GRAPHICS_GPU_TRACE_TEST_EXE` | Optional GPU Trace-specific executable override |
|
||||
| `NSIGHT_GRAPHICS_GPU_TRACE_TEST_ARGS` | Optional GPU Trace-specific args override |
|
||||
| `NSIGHT_GRAPHICS_GPU_TRACE_TEST_WORKDIR` | Optional GPU Trace-specific workdir override |
|
||||
| `NSIGHT_GRAPHICS_CPP_TEST_EXE` | Optional C++ Capture-specific executable override |
|
||||
| `NSIGHT_GRAPHICS_CPP_TEST_ARGS` | Optional C++ Capture-specific args override |
|
||||
| `NSIGHT_GRAPHICS_CPP_TEST_WORKDIR` | Optional C++ Capture-specific workdir override |
|
||||
|
||||
## E2E Test Prerequisites
|
||||
|
||||
The E2E suite assumes:
|
||||
|
||||
- Nsight Graphics is installed and discoverable
|
||||
- `NSIGHT_GRAPHICS_TEST_EXE` points to a graphics workload that Nsight can
|
||||
launch or capture
|
||||
- optional args/workdir are provided if the test target requires them
|
||||
- either `NSIGHT_GRAPHICS_TEST_EXE` or the activity-specific `NSIGHT_GRAPHICS_*_TEST_EXE`
|
||||
overrides point to graphics workloads that Nsight can launch for that activity
|
||||
- optional args/workdir are provided if the chosen test target requires them
|
||||
|
||||
Typical examples include `vkcube.exe`, game samples, or internal engine demos.
|
||||
Different activities may require different targets on newer Nsight builds, so
|
||||
the E2E suite accepts per-activity overrides instead of assuming one executable
|
||||
works for frame capture, GPU Trace, and C++ Capture.
|
||||
|
||||
## Multiple Installations
|
||||
|
||||
|
||||
@@ -7,6 +7,55 @@ from typing import Sequence
|
||||
from cli_anything.nsight_graphics.utils import nsight_graphics_backend as backend
|
||||
|
||||
|
||||
def _build_unified_frame_args(
|
||||
activity: str,
|
||||
*,
|
||||
wait_seconds: int | None,
|
||||
wait_frames: int | None,
|
||||
wait_hotkey: bool,
|
||||
export_frame_perf_metrics: bool,
|
||||
export_range_perf_metrics: bool,
|
||||
) -> list[str]:
|
||||
backend.ensure_exactly_one(
|
||||
"frame trigger",
|
||||
{
|
||||
"wait_seconds": wait_seconds is not None,
|
||||
"wait_frames": wait_frames is not None,
|
||||
"wait_hotkey": wait_hotkey,
|
||||
},
|
||||
)
|
||||
|
||||
normalized = activity.lower()
|
||||
if normalized == "graphics capture":
|
||||
if export_frame_perf_metrics or export_range_perf_metrics:
|
||||
raise RuntimeError(
|
||||
"Frame performance export flags are not supported by Graphics Capture mode on modern ngfx.exe builds."
|
||||
)
|
||||
|
||||
extra_args = ["--frame-count", "1"]
|
||||
if wait_seconds is not None:
|
||||
extra_args.extend(["--elapsed-time", str(wait_seconds)])
|
||||
elif wait_frames is not None:
|
||||
extra_args.extend(["--frame-index", str(wait_frames)])
|
||||
else:
|
||||
extra_args.append("--hotkey-capture")
|
||||
return extra_args
|
||||
|
||||
extra_args: list[str] = []
|
||||
if wait_seconds is not None:
|
||||
extra_args.extend(["--wait-seconds", str(wait_seconds)])
|
||||
elif wait_frames is not None:
|
||||
extra_args.extend(["--wait-frames", str(wait_frames)])
|
||||
else:
|
||||
extra_args.append("--wait-hotkey")
|
||||
|
||||
if export_frame_perf_metrics:
|
||||
extra_args.append("--export-frame-perf-metrics")
|
||||
if export_range_perf_metrics:
|
||||
extra_args.append("--export-range-perf-metrics")
|
||||
return extra_args
|
||||
|
||||
|
||||
def capture_frame(
|
||||
*,
|
||||
nsight_path: str | None,
|
||||
@@ -27,35 +76,23 @@ def capture_frame(
|
||||
"""Run a Frame Debugger capture."""
|
||||
report = backend.probe_installation(nsight_path=nsight_path)
|
||||
binaries = report["binaries"]
|
||||
artifact_roots = backend.activity_artifact_roots("Frame Debugger", output_dir)
|
||||
activity = backend.resolve_activity_name(report, "Frame Debugger")
|
||||
artifact_roots = backend.activity_artifact_roots(activity, output_dir)
|
||||
|
||||
if binaries.get("ngfx"):
|
||||
backend.require_launch_target(project=project, exe=exe)
|
||||
backend.ensure_exactly_one(
|
||||
"frame trigger",
|
||||
{
|
||||
"wait_seconds": wait_seconds is not None,
|
||||
"wait_frames": wait_frames is not None,
|
||||
"wait_hotkey": wait_hotkey,
|
||||
},
|
||||
extra_args = _build_unified_frame_args(
|
||||
activity,
|
||||
wait_seconds=wait_seconds,
|
||||
wait_frames=wait_frames,
|
||||
wait_hotkey=wait_hotkey,
|
||||
export_frame_perf_metrics=export_frame_perf_metrics,
|
||||
export_range_perf_metrics=export_range_perf_metrics,
|
||||
)
|
||||
|
||||
extra_args: list[str] = []
|
||||
if wait_seconds is not None:
|
||||
extra_args.extend(["--wait-seconds", str(wait_seconds)])
|
||||
elif wait_frames is not None:
|
||||
extra_args.extend(["--wait-frames", str(wait_frames)])
|
||||
else:
|
||||
extra_args.append("--wait-hotkey")
|
||||
|
||||
if export_frame_perf_metrics:
|
||||
extra_args.append("--export-frame-perf-metrics")
|
||||
if export_range_perf_metrics:
|
||||
extra_args.append("--export-range-perf-metrics")
|
||||
|
||||
command = backend.build_unified_command(
|
||||
binaries,
|
||||
activity="Frame Debugger",
|
||||
activity=activity,
|
||||
project=project,
|
||||
output_dir=output_dir,
|
||||
hostname=hostname,
|
||||
@@ -105,6 +142,6 @@ def capture_frame(
|
||||
else:
|
||||
raise RuntimeError(backend.INSTALL_INSTRUCTIONS)
|
||||
|
||||
result["activity"] = "Frame Debugger"
|
||||
result["activity"] = activity
|
||||
result["output_dir"] = output_dir or backend.default_output_dir()
|
||||
return result
|
||||
|
||||
@@ -23,12 +23,13 @@ def launch_detached(
|
||||
"""Launch a target under Nsight and exit immediately."""
|
||||
report = backend.probe_installation(nsight_path=nsight_path)
|
||||
binaries = report["binaries"]
|
||||
resolved_activity = backend.resolve_activity_name(report, activity)
|
||||
backend.require_binary(binaries, "ngfx")
|
||||
backend.require_launch_target(project=project, exe=exe)
|
||||
|
||||
command = backend.build_unified_command(
|
||||
binaries,
|
||||
activity=activity,
|
||||
activity=resolved_activity,
|
||||
project=project,
|
||||
output_dir=output_dir,
|
||||
hostname=hostname,
|
||||
@@ -41,7 +42,7 @@ def launch_detached(
|
||||
)
|
||||
result = backend.run_command(command, timeout=120)
|
||||
result["tool_mode"] = "unified"
|
||||
result["activity"] = activity
|
||||
result["activity"] = resolved_activity
|
||||
return result
|
||||
|
||||
|
||||
@@ -58,11 +59,12 @@ def attach(
|
||||
"""Attach an activity to a running PID."""
|
||||
report = backend.probe_installation(nsight_path=nsight_path)
|
||||
binaries = report["binaries"]
|
||||
resolved_activity = backend.resolve_activity_name(report, activity)
|
||||
backend.require_binary(binaries, "ngfx")
|
||||
|
||||
command = backend.build_unified_command(
|
||||
binaries,
|
||||
activity=activity,
|
||||
activity=resolved_activity,
|
||||
project=project,
|
||||
output_dir=output_dir,
|
||||
hostname=hostname,
|
||||
@@ -71,6 +73,6 @@ def attach(
|
||||
)
|
||||
result = backend.run_command(command, timeout=120)
|
||||
result["tool_mode"] = "unified"
|
||||
result["activity"] = activity
|
||||
result["activity"] = resolved_activity
|
||||
result["pid"] = pid
|
||||
return result
|
||||
|
||||
@@ -241,6 +241,17 @@ class TestHelpParsing:
|
||||
assert "--wait-frames" in result["activity_options"]["Frame Debugger"]
|
||||
assert "--metric-set-id" in result["activity_options"]["GPU Trace Profiler"]
|
||||
|
||||
def test_resolve_activity_name_maps_legacy_frame_debugger_to_graphics_capture(self):
|
||||
report = {
|
||||
"supported_activities": [
|
||||
"Graphics Capture",
|
||||
"Generate C++ Capture",
|
||||
"GPU Trace Profiler",
|
||||
]
|
||||
}
|
||||
assert backend.resolve_activity_name(report, "Frame Debugger") == "Graphics Capture"
|
||||
assert backend.resolve_activity_name(report, "Graphics Capture") == "Graphics Capture"
|
||||
|
||||
|
||||
class TestCommandBuilders:
|
||||
def test_build_unified_command_formats_args_and_env(self):
|
||||
@@ -277,6 +288,12 @@ class TestCommandBuilders:
|
||||
assert "--capture-countdown-timer" in command
|
||||
assert command[command.index("--capture-countdown-timer") + 1] == "3000"
|
||||
|
||||
@patch("cli_anything.nsight_graphics.utils.nsight_graphics_backend.subprocess.run")
|
||||
def test_run_command_suppresses_graphics_capture_suggestion_dialog(self, run_mock):
|
||||
run_mock.return_value = type("Result", (), {"returncode": 0, "stdout": "", "stderr": ""})()
|
||||
backend.run_command(["C:/Nsight/ngfx.exe", "--help"])
|
||||
assert run_mock.call_args.kwargs["env"]["NSIGHT_SUGGEST_GRAPHICS_CAPTURE"] == "0"
|
||||
|
||||
def test_diff_snapshots_reports_new_nonempty_files(self, tmp_path):
|
||||
before = backend.snapshot_files([str(tmp_path)])
|
||||
artifact = tmp_path / "capture.ngfx-capture"
|
||||
@@ -287,6 +304,12 @@ class TestCommandBuilders:
|
||||
assert diff[0]["path"].endswith("capture.ngfx-capture")
|
||||
assert diff[0]["size"] > 0
|
||||
|
||||
def test_activity_artifact_roots_keeps_default_graphics_capture_location(self):
|
||||
roots = backend.activity_artifact_roots("Graphics Capture", "D:/captures")
|
||||
assert str(Path("D:/captures").resolve()) in roots
|
||||
assert any(root.endswith("Documents\\NVIDIA Nsight Graphics") for root in roots)
|
||||
assert any(root.endswith("Documents\\NVIDIA Nsight Graphics\\GraphicsCaptures") for root in roots)
|
||||
|
||||
def test_gpu_trace_summary_from_export_dir(self, tmp_path):
|
||||
base = tmp_path / "BASE"
|
||||
base.mkdir()
|
||||
@@ -416,6 +439,45 @@ class TestCoreModules:
|
||||
assert result["activity"] == "Frame Debugger"
|
||||
assert result["artifacts"]
|
||||
|
||||
@patch("cli_anything.nsight_graphics.core.frame.backend.run_with_artifacts")
|
||||
@patch("cli_anything.nsight_graphics.core.frame.backend.build_unified_command")
|
||||
@patch("cli_anything.nsight_graphics.core.frame.backend.probe_installation")
|
||||
def test_frame_capture_maps_graphics_capture_options(self, probe_mock, build_mock, run_mock):
|
||||
probe_mock.return_value = {
|
||||
"binaries": {"ngfx": "C:/Nsight/ngfx.exe", "ngfx_capture": None, "ngfx_replay": None},
|
||||
"supported_activities": ["Graphics Capture", "GPU Trace Profiler"],
|
||||
}
|
||||
build_mock.return_value = ["C:/Nsight/ngfx.exe", "--activity", "Graphics Capture"]
|
||||
run_mock.return_value = {
|
||||
"ok": True,
|
||||
"returncode": 0,
|
||||
"stdout": "",
|
||||
"stderr": "",
|
||||
"command": "ngfx",
|
||||
"artifacts": [{"path": "D:/out/capture.ngfx-capture", "size": 10, "mtime_ns": 1}],
|
||||
}
|
||||
|
||||
result = frame.capture_frame(
|
||||
nsight_path=None,
|
||||
project=None,
|
||||
output_dir="D:/out",
|
||||
hostname=None,
|
||||
platform_name=None,
|
||||
exe="C:/demo.exe",
|
||||
working_dir=None,
|
||||
args=(),
|
||||
envs=(),
|
||||
wait_seconds=1,
|
||||
wait_frames=None,
|
||||
wait_hotkey=False,
|
||||
export_frame_perf_metrics=False,
|
||||
export_range_perf_metrics=False,
|
||||
)
|
||||
|
||||
assert build_mock.call_args.kwargs["activity"] == "Graphics Capture"
|
||||
assert build_mock.call_args.kwargs["extra_args"] == ["--frame-count", "1", "--elapsed-time", "1"]
|
||||
assert result["activity"] == "Graphics Capture"
|
||||
|
||||
@patch("cli_anything.nsight_graphics.core.frame.backend.probe_installation")
|
||||
def test_frame_capture_split_mode_rejects_perf_exports(self, probe_mock):
|
||||
probe_mock.return_value = {
|
||||
|
||||
@@ -15,11 +15,26 @@ import pytest
|
||||
from cli_anything.nsight_graphics.utils.nsight_graphics_backend import probe_installation
|
||||
|
||||
HARNESS_ROOT = str(Path(__file__).resolve().parents[3])
|
||||
TEST_EXE = os.environ.get("NSIGHT_GRAPHICS_TEST_EXE", "").strip()
|
||||
TEST_ARGS = os.environ.get("NSIGHT_GRAPHICS_TEST_ARGS", "").strip()
|
||||
TEST_WORKDIR = os.environ.get("NSIGHT_GRAPHICS_TEST_WORKDIR", "").strip()
|
||||
DEFAULT_TEST_EXE = os.environ.get("NSIGHT_GRAPHICS_TEST_EXE", "").strip()
|
||||
DEFAULT_TEST_ARGS = os.environ.get("NSIGHT_GRAPHICS_TEST_ARGS", "").strip()
|
||||
DEFAULT_TEST_WORKDIR = os.environ.get("NSIGHT_GRAPHICS_TEST_WORKDIR", "").strip()
|
||||
|
||||
FRAME_TEST_EXE = os.environ.get("NSIGHT_GRAPHICS_FRAME_TEST_EXE", "").strip() or DEFAULT_TEST_EXE
|
||||
FRAME_TEST_ARGS = os.environ.get("NSIGHT_GRAPHICS_FRAME_TEST_ARGS", "").strip() or DEFAULT_TEST_ARGS
|
||||
FRAME_TEST_WORKDIR = os.environ.get("NSIGHT_GRAPHICS_FRAME_TEST_WORKDIR", "").strip() or DEFAULT_TEST_WORKDIR
|
||||
|
||||
GPU_TRACE_TEST_EXE = os.environ.get("NSIGHT_GRAPHICS_GPU_TRACE_TEST_EXE", "").strip() or DEFAULT_TEST_EXE
|
||||
GPU_TRACE_TEST_ARGS = os.environ.get("NSIGHT_GRAPHICS_GPU_TRACE_TEST_ARGS", "").strip() or DEFAULT_TEST_ARGS
|
||||
GPU_TRACE_TEST_WORKDIR = os.environ.get("NSIGHT_GRAPHICS_GPU_TRACE_TEST_WORKDIR", "").strip() or DEFAULT_TEST_WORKDIR
|
||||
|
||||
CPP_TEST_EXE = os.environ.get("NSIGHT_GRAPHICS_CPP_TEST_EXE", "").strip() or DEFAULT_TEST_EXE
|
||||
CPP_TEST_ARGS = os.environ.get("NSIGHT_GRAPHICS_CPP_TEST_ARGS", "").strip() or DEFAULT_TEST_ARGS
|
||||
CPP_TEST_WORKDIR = os.environ.get("NSIGHT_GRAPHICS_CPP_TEST_WORKDIR", "").strip() or DEFAULT_TEST_WORKDIR
|
||||
|
||||
HAS_NSIGHT = bool(probe_installation().get("ok"))
|
||||
HAS_TEST_EXE = bool(TEST_EXE and os.path.isfile(TEST_EXE))
|
||||
HAS_FRAME_TEST_EXE = bool(FRAME_TEST_EXE and os.path.isfile(FRAME_TEST_EXE))
|
||||
HAS_GPU_TRACE_TEST_EXE = bool(GPU_TRACE_TEST_EXE and os.path.isfile(GPU_TRACE_TEST_EXE))
|
||||
HAS_CPP_TEST_EXE = bool(CPP_TEST_EXE and os.path.isfile(CPP_TEST_EXE))
|
||||
|
||||
|
||||
def _resolve_cli(name: str) -> list[str]:
|
||||
@@ -48,7 +63,18 @@ def _resolve_cli(name: str) -> list[str]:
|
||||
|
||||
CLI_BASE = _resolve_cli("cli-anything-nsight-graphics")
|
||||
skip_no_nsight = pytest.mark.skipif(not HAS_NSIGHT, reason="Nsight Graphics not installed")
|
||||
skip_no_target = pytest.mark.skipif(not HAS_TEST_EXE, reason="NSIGHT_GRAPHICS_TEST_EXE not set or missing")
|
||||
skip_no_frame_target = pytest.mark.skipif(
|
||||
not HAS_FRAME_TEST_EXE,
|
||||
reason="NSIGHT_GRAPHICS_FRAME_TEST_EXE or NSIGHT_GRAPHICS_TEST_EXE not set or missing",
|
||||
)
|
||||
skip_no_gpu_trace_target = pytest.mark.skipif(
|
||||
not HAS_GPU_TRACE_TEST_EXE,
|
||||
reason="NSIGHT_GRAPHICS_GPU_TRACE_TEST_EXE or NSIGHT_GRAPHICS_TEST_EXE not set or missing",
|
||||
)
|
||||
skip_no_cpp_target = pytest.mark.skipif(
|
||||
not HAS_CPP_TEST_EXE,
|
||||
reason="NSIGHT_GRAPHICS_CPP_TEST_EXE or NSIGHT_GRAPHICS_TEST_EXE not set or missing",
|
||||
)
|
||||
|
||||
|
||||
def _run_json(*args: str, timeout: int = 600) -> dict:
|
||||
@@ -65,13 +91,13 @@ def _run_json(*args: str, timeout: int = 600) -> dict:
|
||||
return json.loads(result.stdout)
|
||||
|
||||
|
||||
def _target_args() -> list[str]:
|
||||
"""Build repeated CLI args for the configured test executable."""
|
||||
args = ["--exe", TEST_EXE]
|
||||
if TEST_WORKDIR:
|
||||
args.extend(["--dir", TEST_WORKDIR])
|
||||
if TEST_ARGS:
|
||||
for entry in shlex.split(TEST_ARGS, posix=os.name != "nt"):
|
||||
def _target_args(exe_path: str, args_text: str, workdir: str) -> list[str]:
|
||||
"""Build repeated CLI args for a configured activity target."""
|
||||
args = ["--exe", exe_path]
|
||||
if workdir:
|
||||
args.extend(["--dir", workdir])
|
||||
if args_text:
|
||||
for entry in shlex.split(args_text, posix=os.name != "nt"):
|
||||
args.extend(["--arg", entry])
|
||||
return args
|
||||
|
||||
@@ -86,15 +112,15 @@ class TestDoctorE2E:
|
||||
|
||||
|
||||
@skip_no_nsight
|
||||
@skip_no_target
|
||||
class TestTargetedE2E:
|
||||
@skip_no_frame_target
|
||||
def test_frame_capture(self, tmp_path):
|
||||
data = _run_json(
|
||||
"--output-dir",
|
||||
str(tmp_path),
|
||||
"frame",
|
||||
"capture",
|
||||
*_target_args(),
|
||||
*_target_args(FRAME_TEST_EXE, FRAME_TEST_ARGS, FRAME_TEST_WORKDIR),
|
||||
"--wait-seconds",
|
||||
"1",
|
||||
)
|
||||
@@ -102,13 +128,14 @@ class TestTargetedE2E:
|
||||
assert data["artifacts"]
|
||||
assert any(Path(item["path"]).exists() and item["size"] > 0 for item in data["artifacts"])
|
||||
|
||||
@skip_no_gpu_trace_target
|
||||
def test_gpu_trace_capture(self, tmp_path):
|
||||
data = _run_json(
|
||||
"--output-dir",
|
||||
str(tmp_path),
|
||||
"gpu-trace",
|
||||
"capture",
|
||||
*_target_args(),
|
||||
*_target_args(GPU_TRACE_TEST_EXE, GPU_TRACE_TEST_ARGS, GPU_TRACE_TEST_WORKDIR),
|
||||
"--start-after-ms",
|
||||
"1000",
|
||||
"--limit-to-frames",
|
||||
@@ -119,13 +146,14 @@ class TestTargetedE2E:
|
||||
assert data["artifacts"]
|
||||
assert any(Path(item["path"]).exists() and item["size"] > 0 for item in data["artifacts"])
|
||||
|
||||
@skip_no_cpp_target
|
||||
def test_cpp_capture(self, tmp_path):
|
||||
data = _run_json(
|
||||
"--output-dir",
|
||||
str(tmp_path),
|
||||
"cpp",
|
||||
"capture",
|
||||
*_target_args(),
|
||||
*_target_args(CPP_TEST_EXE, CPP_TEST_ARGS, CPP_TEST_WORKDIR),
|
||||
"--wait-seconds",
|
||||
"1",
|
||||
)
|
||||
|
||||
+37
-7
@@ -29,6 +29,11 @@ INSTALL_INSTRUCTIONS = (
|
||||
" C:\\Program Files\\NVIDIA Corporation\\Nsight Graphics <version>\\host\\windows-desktop-nomad-x64"
|
||||
)
|
||||
|
||||
_ACTIVITY_ALIASES = {
|
||||
"frame debugger": ("Frame Debugger", "Graphics Capture"),
|
||||
"graphics capture": ("Graphics Capture", "Frame Debugger"),
|
||||
}
|
||||
|
||||
|
||||
def _command_string(args: Sequence[str]) -> str:
|
||||
"""Render a command for display."""
|
||||
@@ -515,6 +520,25 @@ def parse_option_help(text: str) -> list[str]:
|
||||
return _dedupe(options)
|
||||
|
||||
|
||||
def resolve_activity_name(report: dict[str, Any], requested: str) -> str:
|
||||
"""Map a requested activity name onto the current Nsight installation."""
|
||||
supported = report.get("supported_activities") or []
|
||||
if not supported:
|
||||
return requested
|
||||
|
||||
supported_lookup = {item.lower(): item for item in supported}
|
||||
direct = supported_lookup.get(requested.lower())
|
||||
if direct:
|
||||
return direct
|
||||
|
||||
aliases = _ACTIVITY_ALIASES.get(requested.lower(), (requested,))
|
||||
for alias in aliases:
|
||||
resolved = supported_lookup.get(alias.lower())
|
||||
if resolved:
|
||||
return resolved
|
||||
return requested
|
||||
|
||||
|
||||
def _combined_output(result: dict[str, Any]) -> str:
|
||||
"""Combine stdout and stderr for parsing."""
|
||||
stdout = result.get("stdout", "") or ""
|
||||
@@ -530,12 +554,15 @@ def run_command(
|
||||
) -> dict[str, Any]:
|
||||
"""Run a subprocess and normalize the result."""
|
||||
try:
|
||||
env = os.environ.copy()
|
||||
env.setdefault("NSIGHT_SUGGEST_GRAPHICS_CAPTURE", "0")
|
||||
proc = subprocess.run(
|
||||
list(args),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=cwd,
|
||||
timeout=timeout,
|
||||
env=env,
|
||||
)
|
||||
return {
|
||||
"ok": proc.returncode == 0,
|
||||
@@ -820,16 +847,19 @@ def default_output_dir() -> str:
|
||||
|
||||
def activity_artifact_roots(activity: str, output_dir: Optional[str]) -> list[str]:
|
||||
"""Return directories to scan for generated artifacts."""
|
||||
if output_dir:
|
||||
return [str(Path(output_dir).resolve())]
|
||||
|
||||
base = Path(default_output_dir())
|
||||
roots = [str(base)]
|
||||
roots: list[str] = []
|
||||
if output_dir:
|
||||
roots.append(str(Path(output_dir).resolve()))
|
||||
else:
|
||||
roots.append(str(base))
|
||||
|
||||
normalized = activity.lower()
|
||||
if normalized == "frame debugger":
|
||||
roots.insert(0, str(base / "GraphicsCaptures"))
|
||||
if normalized in {"frame debugger", "graphics capture"}:
|
||||
roots.extend([str(base), str(base / "GraphicsCaptures")])
|
||||
elif normalized == "gpu trace profiler":
|
||||
roots.append(str(base / "GPUTrace"))
|
||||
if not output_dir:
|
||||
roots.append(str(base / "GPUTrace"))
|
||||
elif normalized == "generate c++ capture":
|
||||
roots.append(str(base / "CppCaptures"))
|
||||
return _dedupe(roots)
|
||||
|
||||
Reference in New Issue
Block a user