Repository navigation
hdr: gate the pre-HDR exposure restore workaround on FW version (DSO-18682) - #15508
Conversation
FW >= 5.17.4.11 restores the pre-HDR manual exposure/gain itself (DSO-18682); keep the host-side workaround only for older FW. Signed-off-by: Yogev Modlin <yogev.modlin@realsenseai.com>
Frame metadata is already restored by FW that has the DSO-18682 bug; only the queried UVC control value stays stuck at the last sub-preset exposure. Signed-off-by: Yogev Modlin <yogev.modlin@realsenseai.com>
sysrsbuild-gh-agentic
left a comment
There was a problem hiding this comment.
🤖 Auto-generated by rs-agentic bot
Code review of PR #15508 — see inline comments below.
Signed-off-by: Yogev Modlin <yogev.modlin@realsenseai.com>
Nir-Az
left a comment
There was a problem hiding this comment.
Reviewed the gate and the test change. The direction is right - keeping the workaround and gating it beats deleting it, and the queried-value assertion is the part that actually makes this test measure the FW.
One blocker inline in src/hdr-config.cpp: set_sequence_index(0.f) inside the workaround block is not only part of the exposure save/restore, it is also the option-routing reset. Gating it off changes exposure get/set behavior after HDR disable on fixed FW.
Validation gap: the HW table covers pytest-hdr-long.py only. The blocker lives in the sequence_id flow, so unit-tests/live/d400/pytest-hdr-sanity.py and unit-tests/live/hdr/* also want a run on 5.17.4.11 with the workaround gated off. Note that neither of those would fail on the blocker as written - hdr-sanity never re-reads exposure after HDR off, and function_scoped_device builds a fresh hdr_config per test, so nothing leaks between tests. Worth a manual check rather than trusting a green suite.
Leaving this as a comment, not request-changes - the PR is already marked do-not-merge until FW #222 lands.
🤖 Generated by AI
|
🤖 Auto-generated by rs-agentic bot The new push () is a merge from The two open items from the previous review round remain unaddressed:
|
…version, guard the queried check
|
Pushed Two things worth surfacing here rather than only in a thread: 1. The gate constant was wrong — now 2. Your blocker is fixed, and it also removes most of the validation gap you flagged. With What I did and did not run this round. Built clean ( 🤖 Generated by AI |
Hardware validation of
|
| Camera | FW | Gate resolves to | Result |
|---|---|---|---|
D455 013322250138 |
5.17.4.13 | workaround OFF | 32/32 passed (203s) |
D455 213622301050 |
5.17.3.25 | workaround ON | 32/32 passed (202s) |
The first row is a genuine 5.17.4.13 release image, not the hand-stamped build from the original table — so the corrected gate constant is exercised end-to-end (5.17.4.13 < 5.17.4.13 → false) rather than a forced flag. That closes the other open item from the description too.
The suites do not prove the blocker fix — you called this
You predicted neither suite would fail on the blocker as written, and that is what I see: 32/32 also passes on the pre-fix build. So the green runs above are a no-regression result, nothing more.
For direct evidence I drove the exact flow from your comment — sequence_id = 1/2, configure both slots, HDR on, HDR off — and read back the state you named. A/B across the two builds:
| librealsense build | FW 5.17.4.13 (gate off) | FW 5.17.3.25 (gate on) |
|---|---|---|
a3d75e88d (pre-fix) |
FAIL — sequence_id=2, exposure=1200, gain=20 |
PASS |
9b6fc755d (fixed) |
PASS — sequence_id=0, exposure=5000, gain=50 |
PASS |
Pre-fix on fixed FW, after HDR off, get_option(exposure)/get_option(gain) return 1200 / 20 — the values I had written into HDR slot 2, not the pre-HDR 5000 / 50. That is the cached-slot read you described, and it only happens on the FW-version side where the workaround gates off, which is the divergence the gate was not meant to introduce. With the hoist, both FW versions behave identically.
One part of your analysis I did not confirm
You also noted that set_option(exposure) reaches hdr_config::set_exposure and never gets to the camera. My probe cannot show that: it sets 8000 and reads back 8000 on every cell of the matrix, because the set and the get route to the same slot, so the readback is self-consistent whether or not it reached the sensor. Distinguishing that needs a streamed frame-metadata check. The routing/query half is confirmed; the write-never-lands half I am taking on your reading of hdr-config.cpp:513-514 rather than claiming I measured it.
Setup
Built both trees from source on the rig (BUILD_PYTHON_BINDINGS=ON, Release, GCC); pytest picked up the local pyrealsense2 — -I- Using pyrealsense2 from: ~/lrs-18682-hdr/build/Release/pyrealsense2.cpython-310-...so. Cameras returned to hdr_enabled=0, sequence_id=0, enable_auto_exposure=1 afterwards.
🤖 Generated by AI
Make
test_hdr_start_stop_recover_manual_exposure_and_gainmeasure the FW, not the SDKHost-side counterpart to FW PR # 222 (FW lost the pre-HDR manual exposure/gain restore on sub-preset exit in 5.14).
As @Nir-Az pointed out, the test cannot prove that FW fix today:
hdr_config::_use_workaroundis hardcodedtrue, so on HDR enable the SDK overwrites the manual exposure withPRE_ENABLE_HDR_EXPOSURE = 30000and on disable re-writes the saved value itself. The test measures that re-write.1. Gate the workaround on FW version— kept, not deleted
hdr_configctor takesbool use_workaround, threaded throughd400_depth_sensor::init_hdr_config(). Ind400-device.cpp, next to the existinghdr_firmware_version:A single
LOG_INFOstates which side performs the restore, so a CI log shows the mode actually in effect (it is per-device — a rig with mixed FW shows both).Only the exposure save/re-write is gated. The
set_sequence_index(0.f)option-routing reset is not — see §3.The constant is
5.17.4.13, resolved against the FW repo rather than predicted.v5.17.4.11c4086b04v5.17.4.12b9e9f94ev5.17.4.133654c669The version bot bumps
uvc_version.hahead of the work rather than once per merge, so.11and.12were both tagged before the fix landed.5.17.4.13is the first released FW that carries it. (An earlier revision of this PR had5.17.4.11, which would have gated the workaround off on real.11/.12cameras that have no FW fix.)2. Also assert the queried exposure/gain (
adaabe42)Gating alone is not sufficient — verified on hardware. With the workaround off on broken FW the test still passes, because broken FW does restore what reaches the sensor:
Only the queried (UVC control-DB) value stays at the last HDR sub-preset exposure — which is exactly the field the FW fix writes back. Since the test asserted only on frame metadata, it could not fail on this bug regardless of the workaround. (The original 2022
30000 == 5000signature was the SDK's own sentinel in frames in flight — a workaround artifact.)So at the existing
iteration_to_check_after_disablepoint the test now also checksget_option(exposure)/get_option(gain). The 5-frame check window is unchanged.3. Review round 2 (
9b6fc755d)set_sequence_index(0.f)hoisted out of bothif (_use_workaround)blocks. It is the option-routing reset, not part of the exposure workaround:disable()only sends the empty sub-preset and resets nothing, so without it an app that configured HDR throughsequence_id != 0keeps querying and setting the cached slot value after HDR is off. The routing flow is now identical for every FW version — today's behavior, unconditionally. On the disable path it had also been nested inside the_pre_hdr_exposurein-range check, so it was already being skipped when the saved exposure fell out of range; that is fixed too.5.17.4.13(§1).assert queried_values_checkedafter thetry/finallyfails the test if no post-disable frame ever carriedsequence_idmetadata. Switched tocheck.equalso failures print both values.use_workarounddefault dropped from thehdr_configctor — one construction site, and it already passes the flag explicitly.Hardware validation — rs-nuc01, D455 013322250138
development@4d3ecc025+ commitsc4586a5a/adaabe42. Every leg pinned with--device <serial>(--device D455is not deterministic on a 2×D455 rig — it silently picked the un-flashed camera once).pytest-hdr-long.pyThe "fixed FW" image is FW
185fdc5b— the content that shipped as 5.17.4.13 — hand-stamped inuvc_version.hat the time to what was then assumed to be the post-merge version. The FW behavior those rows prove is unaffected by the constant correction; the workaround was forced off explicitly, not via the gate.Build note: rosbag2 was off in this validation build (the rig has no route to github.com for the FastCDR fetch) — orthogonal to HDR.
Hardware validation of round 2 — rs-nuc01
Two D455s that sit on either side of the gate. Suites run as
python3 -m pytest -v --context nightly --device <serial> live/d400/pytest-hdr-sanity.py live/d400/pytest-hdr-long.py live/hdr/(covers
hdr-sanity,hdr-long,hdr-configurations,hdr-performance,hdr-preset).013322250138213622301050The first row exercises the corrected constant end-to-end rather than a forced flag.
The suites do not prove the routing fix — as @Nir-Az predicted, 32/32 also passes pre-fix. Direct
evidence comes from driving his flow (
sequence_id = 1/2, configure slots, HDR on, HDR off) andreading back the state after disable:
a3d75e88d(pre-fix)sequence_id=2,exposure=1200,gain=209b6fc755d(fixed)sequence_id=0,exposure=5000,gain=50Pre-fix on fixed FW the query returns HDR slot 2's values instead of the pre-HDR 5000/50, and only on
the FW-version side where the workaround gates off — the divergence the gate was not meant to
introduce. With the hoist both FW versions behave identically.
Not confirmed by measurement: that
set_option(exposure)never reaches the camera pre-fix. The probesets 8000 and reads back 8000 in every cell, because set and get route to the same slot; separating
that needs a streamed frame-metadata check.
FW PR 222 has merged. The gate constant is resolved and no longer merge-order-dependent.
Tracked on [RSDSO-18682]