Skip to content

Commit 5b4ad6b

Browse files
bkaradzicCopilot
andcommitted
Address bghgary PR review feedback.
- AppContext: take PlaygroundOptions by value (defaulted to {}); drop the nullable-pointer dance and the six nullptr-guard branches in the ctor. - Win32: rename g_options -> options to match the file's existing global naming convention; rename GetCommandLineArgumentsW -> GetCommandLineArguments since the W suffix collides with the Win32 wide-char API convention (the function returns UTF-8 narrow strings). - CommandLine: replace 13 repeated match/apply blocks with a static kFlags table whose rows describe long name, short name, kind, value label, help text, and an apply functor. PrintUsage walks the same table so flag definitions are no longer a second source of truth and adding a flag is a single-row edit. - --list: emit canonical TSV (index, title, referenceImage, exclusionReason) so the listing is machine-parseable. The exclusionReason column reflects config state (ignores --include-excluded). Update help text and instruction docs to match. - validation_native.js: drop the leading underscore on _opts and the five run-counters (_ranCount etc.); the prefix only carries meaning on _playgroundOptions, which is host-injected from C++. Remove the duplicate \if (breakOnFail) debugger;\ in the runner -- failTest() already breaks before invoking the done callback. - instructions: replace the hard-coded test-index=286 example with a --test "<title>" form so config.json reorderings don't silently break the doc; drop hard-coded rdc / renderdoc-py version numbers. - Revert renderdoc-gpu-debug.instructions.md to master: the diff was pure em-dash to '--' substitution and unrelated to debuggability. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 0bef49f commit 5b4ad6b

8 files changed

Lines changed: 298 additions & 264 deletions

File tree

.github/instructions/babylon-native-debugging.instructions.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,11 @@ Substring match, case-insensitive, against `test.title`.
165165

166166
### Run one test by index (when titles collide or you only have a survey row)
167167
```powershell
168-
.\Playground.exe --headless --once --test-index=286 `
168+
.\Playground.exe --headless --once --test-index=N `
169169
app:///Scripts/validation_native.js
170170
```
171+
Use `--list` to find the index for a title. Indices are not stable across
172+
config.json edits; always re-resolve when the test list changes.
171173

172174
### Run a quarantined test without editing config.json
173175
```powershell
@@ -183,8 +185,8 @@ debugging.
183185
```powershell
184186
.\Playground.exe --list
185187
```
186-
Prints TSV with index, title, exclusion reason. Pipe to `findstr` /
187-
`Select-String` for filtering.
188+
Prints TSV with index, title, referenceImage, exclusionReason. Pipe to
189+
`findstr` / `Select-String` for filtering.
188190

189191
### Pixel-diff investigation
190192
1. Run the failing test with `--headless --once --include-excluded`.
@@ -225,14 +227,14 @@ returns `true`, so the legacy throw-on-missing behavior is preserved.
225227
# Playground process before main, so bgfx::findModule adopts it. Version
226228
# always matches what `rdc open` accepts -- no PATH-ordering bugs.
227229
& "<renderdoc-py-dir>\renderdoccmd.exe" capture -w .\Playground.exe `
228-
--headless --once --test-index=286 --include-excluded --capture=5 `
230+
--headless --once --test "<test title or substring>" --include-excluded --capture=5 `
229231
app:///Scripts/validation_native.js
230232
# .rdc lives at <cwd>\temp\bgfx_frame5.rdc
231233
```
232234
Equivalent with the rdc-cli wrapper (inject-only, no auto-capture handshake):
233235
```powershell
234236
& rdc capture --trigger --wait-for-exit -- .\Playground.exe `
235-
--headless --once --test-index=286 --include-excluded --capture=5 `
237+
--headless --once --test "<test title or substring>" --include-excluded --capture=5 `
236238
app:///Scripts/validation_native.js
237239
```
238240
Verify `renderdoc.dll` is loaded:

.github/instructions/playground/playground-renderdoc-capture.instructions.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ Just delete `<cwd>/temp/` if you want to free disk.
122122
## What the rdc CLI **cannot** do (drop to renderdoc-py)
123123

124124
- Show the IA input element layout (no `pipeline EID ia` section in
125-
rdc 0.5.x with renderdoc-py 1.41).
125+
current rdc).
126126
- Identify which buffer slot/offset feeds which shader semantic.
127127
- Show *all* components of VS output (only first 4 are dumped to JSON).
128128
- Debug shader execution (`rdc debug vertex/pixel` may crash the daemon
@@ -162,14 +162,14 @@ data = ctrl.GetBufferData(post.vertexResourceId,
162162
# GetAction(eid) on this version.
163163
```
164164

165-
## Worked example -- test 286 "Instances with color buffer"
165+
## Worked example -- "Instances with color buffer"
166166

167167
Symptom: pixel `(13,13,15)` instead of expected `(255,0,0)`. Both cubes
168168
render black; reference shows red+green from per-instance color buffer.
169169

170170
```powershell
171171
& $rdcmd capture -w .\Playground.exe `
172-
--once --test-index=286 --include-excluded --capture=5 `
172+
--once --test "Instances with color buffer" --include-excluded --capture=5 `
173173
app:///Scripts/validation_native.js
174174
# -> "First pixel off at 286108: Value: (13, 13, 15) - Expected: (255, 0, 0)"
175175
# -> "Pixel difference: 53296 pixels."

.github/instructions/renderdoc/renderdoc-gpu-debug.instructions.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# RenderDoc GPU Debugging Instructions
22

3-
> Based on [rudybear/renderdoc-skill](https://github.com/rudybear/renderdoc-skill) -- adapted for GitHub Copilot.
3+
> Based on [rudybear/renderdoc-skill](https://github.com/rudybear/renderdoc-skill) adapted for GitHub Copilot.
44
55
## When to Use
66

@@ -303,12 +303,12 @@ rdc diff capture_a.rdc capture_b.rdc --framebuffer --diff-output ./captures/anal
303303

304304
For detailed step-by-step debugging workflows with expected output shapes, see `debugging-recipes.instructions.md`. Available recipes:
305305

306-
- **Object is Invisible** -- culling, depth, blend, vertex transform checks
307-
- **Colors Are Wrong** -- texture bindings, constants, blend state, pixel shader trace
308-
- **Shadows Are Broken** -- shadow map resolution, depth bias, light matrices, PCF
309-
- **Performance Is Bad** -- draw counts, resource sizes, overdraw, state changes
310-
- **What Changed Between Two Frames** -- capture diff workflow
311-
- **Debug This Pixel** -- pixel history, shader trace, variable inspection
306+
- **Object is Invisible** culling, depth, blend, vertex transform checks
307+
- **Colors Are Wrong** texture bindings, constants, blend state, pixel shader trace
308+
- **Shadows Are Broken** shadow map resolution, depth bias, light matrices, PCF
309+
- **Performance Is Bad** draw counts, resource sizes, overdraw, state changes
310+
- **What Changed Between Two Frames** capture diff workflow
311+
- **Debug This Pixel** pixel history, shader trace, variable inspection
312312

313313
## 11. Output Size Management
314314

Apps/Playground/Scripts/validation_native.js

Lines changed: 42 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
(function () {
22
let currentScene;
33
let config;
4-
const _opts = (typeof _playgroundOptions === "object" && _playgroundOptions) ? _playgroundOptions : {};
5-
const justOnce = !!_opts.runOnce;
6-
const saveResult = (typeof _opts.saveResults === "boolean") ? _opts.saveResults : true;
4+
const opts = (typeof _playgroundOptions === "object" && _playgroundOptions) ? _playgroundOptions : {};
5+
const justOnce = !!opts.runOnce;
6+
const saveResult = (typeof opts.saveResults === "boolean") ? opts.saveResults : true;
77
const testWidth = 600;
88
const testHeight = 400;
9-
const generateReferences = !!_opts.generateReferences;
10-
const breakOnFail = !!_opts.breakOnFail;
11-
const listTests = !!_opts.listTests;
12-
const includeExcluded = !!_opts.includeExcluded;
13-
const testFilters = Array.isArray(_opts.testFilters) ? _opts.testFilters.map(s => String(s).toLowerCase()) : [];
14-
const testIndices = Array.isArray(_opts.testIndices) ? _opts.testIndices.map(n => +n) : [];
9+
const generateReferences = !!opts.generateReferences;
10+
const breakOnFail = !!opts.breakOnFail;
11+
const listTests = !!opts.listTests;
12+
const includeExcluded = !!opts.includeExcluded;
13+
const testFilters = Array.isArray(opts.testFilters) ? opts.testFilters.map(s => String(s).toLowerCase()) : [];
14+
const testIndices = Array.isArray(opts.testIndices) ? opts.testIndices.map(n => +n) : [];
1515
// CLI --capture=N: 1-based frame index at which to call
1616
// TestUtils.captureNextFrame() for every executed test. The runner
1717
// extends each test's render budget so the .rdc finalizes.
18-
const cliCaptureFrame = (typeof _opts.captureFrame === "number" && _opts.captureFrame > 0) ? (_opts.captureFrame | 0) : 0;
18+
const cliCaptureFrame = (typeof opts.captureFrame === "number" && opts.captureFrame > 0) ? (opts.captureFrame | 0) : 0;
1919
// Frames after the trigger to let RenderDoc finalize the .rdc.
2020
const POST_CAPTURE_FRAMES = 5;
2121

@@ -46,16 +46,13 @@
4646
}
4747

4848
// Per-run counters surfaced as a final summary line on exit.
49-
let _ranCount = 0;
50-
let _passedCount = 0;
51-
let _failedCount = 0;
52-
let _skippedCount = 0;
53-
let _missingRefCount = 0;
49+
let ranCount = 0;
50+
let passedCount = 0;
51+
let failedCount = 0;
52+
let skippedCount = 0;
53+
let missingRefCount = 0;
5454

55-
function getSkipReason(t) {
56-
if (includeExcluded) {
57-
return null;
58-
}
55+
function getExclusionReason(t) {
5956
if (t.onlyVisual) {
6057
return "onlyVisual";
6158
}
@@ -68,12 +65,19 @@
6865
return null;
6966
}
7067

68+
function getSkipReason(t) {
69+
if (includeExcluded) {
70+
return null;
71+
}
72+
return getExclusionReason(t);
73+
}
74+
7175
function logRunSummary() {
72-
console.log("Run complete. ran=" + _ranCount +
73-
" passed=" + _passedCount +
74-
" failed=" + _failedCount +
75-
" missingRef=" + _missingRefCount +
76-
" skipped=" + _skippedCount);
76+
console.log("Run complete. ran=" + ranCount +
77+
" passed=" + passedCount +
78+
" failed=" + failedCount +
79+
" missingRef=" + missingRefCount +
80+
" skipped=" + skippedCount);
7781
}
7882

7983
const engine = new BABYLON.NativeEngine();
@@ -448,7 +452,7 @@
448452
if (!test.referenceImage) {
449453
console.error("MISSING_REFERENCE_IMAGE: Test '" + (test.title || "(unnamed)") +
450454
"' has no 'referenceImage' field in config.json - cannot run pixel comparison.");
451-
_missingRefCount++;
455+
missingRefCount++;
452456
failTest(done);
453457
return;
454458
}
@@ -457,7 +461,7 @@
457461
console.error("MISSING_REFERENCE_IMAGE: Test '" + (test.title || "(unnamed)") +
458462
"' reference image not found at app:///ReferenceImages/" +
459463
test.referenceImage + " - cannot run pixel comparison.");
460-
_missingRefCount++;
464+
missingRefCount++;
461465
failTest(done);
462466
return;
463467
}
@@ -515,9 +519,13 @@
515519
config = JSON.parse(xhr.responseText);
516520

517521
if (listTests) {
522+
// Canonical TSV: index<TAB>title<TAB>referenceImage<TAB>exclusionReason.
523+
// exclusionReason reflects config state (ignores --include-excluded)
524+
// so the listing is the same regardless of run flags.
518525
for (let i = 0; i < config.tests.length; ++i) {
519526
const t = config.tests[i];
520-
console.log("[" + i + "] " + (t.title || "") + " -> " + (t.referenceImage || ""));
527+
const reason = getExclusionReason(t) || "";
528+
console.log(i + "\t" + (t.title || "") + "\t" + (t.referenceImage || "") + "\t" + reason);
521529
}
522530
engine.dispose();
523531
TestUtils.exit(0);
@@ -544,7 +552,7 @@
544552
const reason = getSkipReason(t);
545553
if (reason !== null) {
546554
console.log("Skipping '" + (t.title || "(unnamed)") + "' -- " + reason);
547-
_skippedCount++;
555+
skippedCount++;
548556
i++;
549557
continue;
550558
}
@@ -553,25 +561,21 @@
553561
if (i >= config.tests.length) {
554562
logRunSummary();
555563
engine.dispose();
556-
TestUtils.exit(_failedCount > 0 ? -1 : 0);
564+
TestUtils.exit(failedCount > 0 ? -1 : 0);
557565
return;
558566
}
559567
const currentTitle = config.tests[i].title || "(unnamed)";
560568
runTest(i, function (status) {
561-
_ranCount++;
569+
ranCount++;
562570
if (!status) {
563-
_failedCount++;
564-
// Inner failures fire the debugger via failTest();
565-
// this catches the comparison-failed path.
566-
if (breakOnFail) {
567-
// eslint-disable-next-line no-debugger
568-
debugger;
569-
}
571+
failedCount++;
572+
// failTest() already triggered the debugger before
573+
// reaching this callback; no second `debugger` here.
570574
logRunSummary();
571575
TestUtils.exit(-1);
572576
return;
573577
}
574-
_passedCount++;
578+
passedCount++;
575579
i++;
576580
if (justOnce || i >= config.tests.length) {
577581
logRunSummary();

Apps/Playground/Shared/AppContext.cpp

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,15 @@ AppContext::AppContext(
5252
size_t height,
5353
DebugLogCallback debugLog,
5454
AdditionalInitCallback additionalInit,
55-
const PlaygroundOptions* playgroundOptions)
55+
PlaygroundOptions playgroundOptions)
5656
{
57-
Babylon::DebugTrace::EnableDebugTrace(playgroundOptions == nullptr || !playgroundOptions->DebugTrace.has_value() ? true : *playgroundOptions->DebugTrace);
57+
Babylon::DebugTrace::EnableDebugTrace(playgroundOptions.DebugTrace.value_or(true));
5858
Babylon::DebugTrace::SetTraceOutput(debugLog);
5959

6060
Babylon::PerfTrace::Level perfLevel{Babylon::PerfTrace::Level::Mark};
61-
if (playgroundOptions != nullptr && playgroundOptions->PerfTrace.has_value())
61+
if (playgroundOptions.PerfTrace.has_value())
6262
{
63-
const auto& v = *playgroundOptions->PerfTrace;
63+
const auto& v = *playgroundOptions.PerfTrace;
6464
if (v == "None" || v == "none")
6565
{
6666
perfLevel = Babylon::PerfTrace::Level::None;
@@ -118,38 +118,37 @@ AppContext::AppContext(
118118

119119
m_runtime.emplace(options);
120120

121-
m_runtime->Dispatch([this, window, debugLog, additionalInit = std::move(additionalInit), playgroundOptions](Napi::Env env) {
121+
m_runtime->Dispatch([this, window, debugLog, additionalInit = std::move(additionalInit), playgroundOptions = std::move(playgroundOptions)](Napi::Env env) {
122122
m_device->AddToJavaScript(env);
123123

124-
if (playgroundOptions != nullptr)
125124
{
126125
auto js = Napi::Object::New(env);
127-
js.Set("listTests", Napi::Boolean::New(env, playgroundOptions->ListTests));
128-
js.Set("headless", Napi::Boolean::New(env, playgroundOptions->Headless));
129-
js.Set("breakOnFail", Napi::Boolean::New(env, playgroundOptions->BreakOnFail));
130-
js.Set("generateReferences", Napi::Boolean::New(env, playgroundOptions->GenerateReferences));
131-
js.Set("runOnce", Napi::Boolean::New(env, playgroundOptions->RunOnce));
132-
js.Set("includeExcluded", Napi::Boolean::New(env, playgroundOptions->IncludeExcluded));
133-
if (playgroundOptions->SaveResults.has_value())
126+
js.Set("listTests", Napi::Boolean::New(env, playgroundOptions.ListTests));
127+
js.Set("headless", Napi::Boolean::New(env, playgroundOptions.Headless));
128+
js.Set("breakOnFail", Napi::Boolean::New(env, playgroundOptions.BreakOnFail));
129+
js.Set("generateReferences", Napi::Boolean::New(env, playgroundOptions.GenerateReferences));
130+
js.Set("runOnce", Napi::Boolean::New(env, playgroundOptions.RunOnce));
131+
js.Set("includeExcluded", Napi::Boolean::New(env, playgroundOptions.IncludeExcluded));
132+
if (playgroundOptions.SaveResults.has_value())
134133
{
135-
js.Set("saveResults", Napi::Boolean::New(env, *playgroundOptions->SaveResults));
134+
js.Set("saveResults", Napi::Boolean::New(env, *playgroundOptions.SaveResults));
136135
}
137-
if (playgroundOptions->CaptureFrame.has_value())
136+
if (playgroundOptions.CaptureFrame.has_value())
138137
{
139-
js.Set("captureFrame", Napi::Number::New(env, *playgroundOptions->CaptureFrame));
138+
js.Set("captureFrame", Napi::Number::New(env, *playgroundOptions.CaptureFrame));
140139
}
141140

142-
auto filters = Napi::Array::New(env, playgroundOptions->TestFilters.size());
143-
for (uint32_t i = 0; i < playgroundOptions->TestFilters.size(); ++i)
141+
auto filters = Napi::Array::New(env, playgroundOptions.TestFilters.size());
142+
for (uint32_t i = 0; i < playgroundOptions.TestFilters.size(); ++i)
144143
{
145-
filters[i] = Napi::String::New(env, playgroundOptions->TestFilters[i]);
144+
filters[i] = Napi::String::New(env, playgroundOptions.TestFilters[i]);
146145
}
147146
js.Set("testFilters", filters);
148147

149-
auto indices = Napi::Array::New(env, playgroundOptions->TestIndices.size());
150-
for (uint32_t i = 0; i < playgroundOptions->TestIndices.size(); ++i)
148+
auto indices = Napi::Array::New(env, playgroundOptions.TestIndices.size());
149+
for (uint32_t i = 0; i < playgroundOptions.TestIndices.size(); ++i)
151150
{
152-
indices[i] = Napi::Number::New(env, playgroundOptions->TestIndices[i]);
151+
indices[i] = Napi::Number::New(env, playgroundOptions.TestIndices[i]);
153152
}
154153
js.Set("testIndices", indices);
155154

Apps/Playground/Shared/AppContext.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class AppContext
2626
size_t height,
2727
DebugLogCallback debugLog,
2828
AdditionalInitCallback additionalInit = {},
29-
const PlaygroundOptions* playgroundOptions = nullptr);
29+
PlaygroundOptions playgroundOptions = {});
3030

3131
~AppContext();
3232

0 commit comments

Comments
 (0)