perf: overlap wait_for_output_drain with render work#105
Conversation
📝 WalkthroughWalkthroughThe output drain wait mechanism in engine_present was refactored to execute conditionally after diff/render operations instead of pre-emptively, gated by output length and runtime configuration. Error handling was adjusted to reset prev-hash validation state before returning on failure. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/unit/test_engine_present_backpressure.c (1)
85-114: Add explicitArrange/Act/Assertsection markers for test structure consistency.Please use the standardized section comments in this test body.
As per coding guidelines: Add section markers in long functions (/* --- Section Name --- */) and in tests (/* --- Arrange/Act/Assert --- */, /* --- Validate/Compute/Emit --- */).Proposed refactor
ZR_TEST_UNIT(engine_present_wait_for_output_drain_skips_wait_when_no_flush_needed) { + /* --- Arrange --- */ mock_plat_reset(); mock_plat_set_size(10u, 4u); mock_plat_set_output_writable(1u); zr_engine_config_t cfg = zr_engine_config_default(); cfg.limits.out_max_bytes_per_frame = 4096u; cfg.wait_for_output_drain = 1u; zr_engine_t* e = NULL; ZR_ASSERT_EQ_U32(engine_create(&e, &cfg), ZR_OK); ZR_ASSERT_TRUE(e != NULL); + /* --- Act --- */ ZR_ASSERT_EQ_U32(engine_submit_drawlist(e, zr_test_dl_fixture1, (int)zr_test_dl_fixture1_len), ZR_OK); ZR_ASSERT_EQ_U32(engine_present(e), ZR_OK); ZR_ASSERT_EQ_U32(mock_plat_wait_output_call_count(), 1u); /* No drawlist/event changes for the second present => no terminal flush bytes. wait_for_output_drain should not gate this no-op frame. */ mock_plat_clear_writes(); mock_plat_set_output_writable(0u); ZR_ASSERT_EQ_U32(engine_present(e), ZR_OK); + /* --- Assert --- */ ZR_ASSERT_EQ_U32(mock_plat_wait_output_call_count(), 1u); ZR_ASSERT_EQ_U32((uint32_t)mock_plat_bytes_written_total(), 0u); engine_destroy(e); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/unit/test_engine_present_backpressure.c` around lines 85 - 114, Add the standardized test section markers to the test function engine_present_wait_for_output_drain_skips_wait_when_no_flush_needed: insert a /* --- Arrange/Act/Assert --- */ comment before setup calls (mock_plat_reset.. cfg/engine_create), add a marker between the actions (engine_submit_drawlist/engine_present) and the no-op second-present setup to split Act from Assert, and optionally add /* --- Validate/Compute/Emit --- */ (or the project's preferred validate marker) before final assertions (mock_plat_wait_output_call_count... engine_destroy) so the test clearly shows Arrange, Act, and Assert sections around the calls to engine_submit_drawlist, engine_present, mock_plat_clear_writes, and subsequent assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@tests/unit/test_engine_present_backpressure.c`:
- Around line 85-114: Add the standardized test section markers to the test
function engine_present_wait_for_output_drain_skips_wait_when_no_flush_needed:
insert a /* --- Arrange/Act/Assert --- */ comment before setup calls
(mock_plat_reset.. cfg/engine_create), add a marker between the actions
(engine_submit_drawlist/engine_present) and the no-op second-present setup to
split Act from Assert, and optionally add /* --- Validate/Compute/Emit --- */
(or the project's preferred validate marker) before final assertions
(mock_plat_wait_output_call_count... engine_destroy) so the test clearly shows
Arrange, Act, and Assert sections around the calls to engine_submit_drawlist,
engine_present, mock_plat_clear_writes, and subsequent assertions.
Summary
wait_for_output_drainwait from the top ofengine_present()to just before flushout_len != 0so no-byte presents are not blocked by output-drain pacingWhy
The old flow always waited before doing diff/render work, serializing potential terminal drain latency with CPU work. Reordering lets diff/render run first and only waits at flush point, preserving draw semantics while improving overlap.
Tests
cmake --preset posix-clang-debug -DZIREAEL_WARNINGS_AS_ERRORS=ON -DZIREAEL_BUILD_EXAMPLES=OFFcmake --build --preset posix-clang-debugctest --test-dir out/build/posix-clang-debug --no-tests=ignore --output-on-failurecmake --preset posix-clang-asan-ubsan -DZIREAEL_WARNINGS_AS_ERRORS=ON -DZIREAEL_BUILD_EXAMPLES=OFFcmake --build --preset posix-clang-asan-ubsanctest --test-dir out/build/posix-clang-asan-ubsan --no-tests=ignore --output-on-failureAll tests passed (12/12 on both lanes).
Summary by CodeRabbit
Bug Fixes
Performance
Tests