Skip to content

Commit e5be4b9

Browse files
feat: add GPU performance counters and CBuffer content reading (5 new MCP tools)
Add two major analysis capabilities referenced from rdc-cli: - GPU performance counters: list_counters, fetch_counters, get_counter_summary - Constant buffer reading: list_cbuffers, get_cbuffer_contents These fill the biggest analytical gap — the AI can now read shader uniform values (transform matrices, material params) and GPU performance metrics (duration, bandwidth, ALU utilization) to diagnose rendering and performance issues. Total MCP tools: 54 → 59. New files: counters.h/cpp, cbuffer.h/cpp, counter_tools.cpp, cbuffer_tools.cpp CLI commands: `counters` (--list, --name), `cbuffer` (--stage, --index) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b852cae commit e5be4b9

16 files changed

Lines changed: 951 additions & 2 deletions

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ if(RENDERDOC_DIR)
5151
src/core/diff_pipeline.cpp
5252
src/core/diff_summary.cpp
5353
src/core/pass_analysis.cpp
54+
src/core/counters.cpp
55+
src/core/cbuffer.cpp
5456
)
5557
target_include_directories(renderdoc-core PUBLIC
5658
${CMAKE_CURRENT_SOURCE_DIR}/src
@@ -121,6 +123,8 @@ if(RENDERDOC_DIR)
121123
src/mcp/tools/assertion_tools.cpp
122124
src/mcp/tools/diff_tools.cpp
123125
src/mcp/tools/pass_tools.cpp
126+
src/mcp/tools/counter_tools.cpp
127+
src/mcp/tools/cbuffer_tools.cpp
124128
)
125129
target_link_libraries(renderdoc-mcp-lib PUBLIC renderdoc-mcp-proto renderdoc-core)
126130

src/cli/cli_parse.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,12 @@ Args parseArgs(int argc, char* argv[]) {
224224
a.threshold = parseDouble(argv[++i], "--threshold");
225225
} else if (tok == "--stage" && i + 1 < argc) {
226226
a.stageStr = argv[++i];
227+
} else if (tok == "--list") {
228+
a.listMode = true;
229+
} else if (tok == "--name" && i + 1 < argc) {
230+
a.counterFilter = argv[++i];
231+
} else if (tok == "--index" && i + 1 < argc) {
232+
a.cbufferIndex = parseUint32(argv[++i], "--index");
227233
} else {
228234
a.positional.push_back(tok);
229235
}

src/cli/cli_parse.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ struct Args {
4343
std::string diffOutput;
4444
double threshold = 0.0;
4545
std::string stageStr = "vs-out";
46+
// Phase 5: Counters + CBuffer
47+
bool listMode = false;
48+
std::string counterFilter;
49+
std::optional<uint32_t> cbufferIndex;
4650
};
4751

4852
std::optional<renderdoc::core::ShaderStage> parseStage(const std::string& s);

src/cli/main.cpp

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include "core/snapshot.h"
2323
#include "core/usage.h"
2424
#include "core/pass_analysis.h"
25+
#include "core/counters.h"
26+
#include "core/cbuffer.h"
2527

2628
#include <cstdlib>
2729
#include <fstream>
@@ -1029,6 +1031,110 @@ static void cmdUnusedTargets(Session& session) {
10291031
<< "}\n";
10301032
}
10311033

1034+
// ---------------------------------------------------------------------------
1035+
// counters / cbuffer
1036+
// ---------------------------------------------------------------------------
1037+
1038+
static void cmdCounters(Session& session, bool listMode, const std::string& nameFilter,
1039+
std::optional<uint32_t> eventId) {
1040+
if (listMode) {
1041+
auto counters = listCounters(session);
1042+
std::cout << "{\n \"counters\": [\n";
1043+
for (size_t i = 0; i < counters.size(); ++i) {
1044+
const auto& c = counters[i];
1045+
std::cout << " {\"id\": " << c.id
1046+
<< ", \"name\": \"" << c.name
1047+
<< "\", \"category\": \"" << c.category
1048+
<< "\", \"unit\": \"" << c.unit
1049+
<< "\", \"resultType\": \"" << c.resultType
1050+
<< "\"}" << (i + 1 < counters.size() ? "," : "") << "\n";
1051+
}
1052+
std::cout << " ],\n \"count\": " << counters.size() << "\n}\n";
1053+
return;
1054+
}
1055+
1056+
std::vector<std::string> names;
1057+
if (!nameFilter.empty()) names.push_back(nameFilter);
1058+
1059+
auto result = fetchCounters(session, names, eventId);
1060+
std::cout << "{\n \"rows\": [\n";
1061+
for (size_t i = 0; i < result.rows.size(); ++i) {
1062+
const auto& r = result.rows[i];
1063+
std::cout << " {\"eventId\": " << r.eventId
1064+
<< ", \"counter\": \"" << r.counterName
1065+
<< "\", \"value\": " << r.value
1066+
<< ", \"unit\": \"" << r.unit
1067+
<< "\"}" << (i + 1 < result.rows.size() ? "," : "") << "\n";
1068+
}
1069+
std::cout << " ],\n \"totalCounters\": " << result.totalCounters
1070+
<< ",\n \"totalEvents\": " << result.totalEvents << "\n}\n";
1071+
}
1072+
1073+
static void cmdCBuffer(Session& session, const std::string& stageStr,
1074+
std::optional<uint32_t> cbufferIndex,
1075+
std::optional<uint32_t> eventId) {
1076+
auto stageOpt = parseStage(stageStr);
1077+
if (!stageOpt) {
1078+
std::cerr << "error: invalid stage '" << stageStr << "' (use vs|hs|ds|gs|ps|cs)\n";
1079+
return;
1080+
}
1081+
ShaderStage stage = *stageOpt;
1082+
1083+
if (!cbufferIndex.has_value()) {
1084+
// List mode: show constant block metadata
1085+
auto buffers = listCBuffers(session, stage, eventId);
1086+
std::cout << "{\n \"stage\": \"" << stageName(stage) << "\",\n \"cbuffers\": [\n";
1087+
for (size_t i = 0; i < buffers.size(); ++i) {
1088+
const auto& cb = buffers[i];
1089+
std::cout << " {\"index\": " << cb.index
1090+
<< ", \"name\": \"" << cb.name
1091+
<< "\", \"bindSet\": " << cb.bindSet
1092+
<< ", \"bindSlot\": " << cb.bindSlot
1093+
<< ", \"byteSize\": " << cb.byteSize
1094+
<< ", \"variableCount\": " << cb.variableCount
1095+
<< "}" << (i + 1 < buffers.size() ? "," : "") << "\n";
1096+
}
1097+
std::cout << " ],\n \"count\": " << buffers.size() << "\n}\n";
1098+
return;
1099+
}
1100+
1101+
// Fetch contents for a specific constant block
1102+
auto contents = getCBufferContents(session, stage, *cbufferIndex, eventId);
1103+
std::cout << "{\n \"blockName\": \"" << contents.blockName
1104+
<< "\",\n \"stage\": \"" << stageName(contents.stage)
1105+
<< "\",\n \"bindSet\": " << contents.bindSet
1106+
<< ",\n \"bindSlot\": " << contents.bindSlot
1107+
<< ",\n \"byteSize\": " << contents.byteSize
1108+
<< ",\n \"variables\": [\n";
1109+
1110+
// Simple flat print for now (nested structs not deeply formatted)
1111+
for (size_t i = 0; i < contents.variables.size(); ++i) {
1112+
const auto& v = contents.variables[i];
1113+
std::cout << " {\"name\": \"" << v.name
1114+
<< "\", \"type\": \"" << v.typeName << "\"";
1115+
if (!v.floatValues.empty()) {
1116+
std::cout << ", \"values\": [";
1117+
for (size_t j = 0; j < v.floatValues.size(); ++j)
1118+
std::cout << v.floatValues[j] << (j + 1 < v.floatValues.size() ? ", " : "");
1119+
std::cout << "]";
1120+
} else if (!v.intValues.empty()) {
1121+
std::cout << ", \"values\": [";
1122+
for (size_t j = 0; j < v.intValues.size(); ++j)
1123+
std::cout << v.intValues[j] << (j + 1 < v.intValues.size() ? ", " : "");
1124+
std::cout << "]";
1125+
} else if (!v.uintValues.empty()) {
1126+
std::cout << ", \"values\": [";
1127+
for (size_t j = 0; j < v.uintValues.size(); ++j)
1128+
std::cout << v.uintValues[j] << (j + 1 < v.uintValues.size() ? ", " : "");
1129+
std::cout << "]";
1130+
} else if (!v.members.empty()) {
1131+
std::cout << ", \"memberCount\": " << v.members.size();
1132+
}
1133+
std::cout << "}" << (i + 1 < contents.variables.size() ? "," : "") << "\n";
1134+
}
1135+
std::cout << " ]\n}\n";
1136+
}
1137+
10321138
// ---------------------------------------------------------------------------
10331139
// main
10341140
// ---------------------------------------------------------------------------
@@ -1142,6 +1248,10 @@ int main(int argc, char* argv[]) {
11421248
cmdPassDeps(session);
11431249
} else if (cmd == "unused-targets") {
11441250
cmdUnusedTargets(session);
1251+
} else if (cmd == "counters") {
1252+
cmdCounters(session, args.listMode, args.counterFilter, args.eventId);
1253+
} else if (cmd == "cbuffer") {
1254+
cmdCBuffer(session, args.stageStr, args.cbufferIndex, args.eventId);
11451255
} else {
11461256
std::cerr << "error: unknown command '" << cmd << "'\n\n";
11471257
renderdoc::cli::printUsage(argv[0]);

0 commit comments

Comments
 (0)