Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions src/core/zr_engine_present.inc
Original file line number Diff line number Diff line change
Expand Up @@ -464,14 +464,6 @@ zr_result_t engine_present(zr_engine_t* e) {
/* Enforced contract: the per-frame arena is reset exactly once per present. */
zr_arena_reset(&e->arena_frame);

if (e->cfg_runtime.wait_for_output_drain != 0u) {
const int32_t timeout_ms = zr_engine_output_wait_timeout_ms(&e->cfg_runtime);
zr_result_t rc = plat_wait_output_writable(e->plat, timeout_ms);
if (rc != ZR_OK) {
return rc;
}
}

size_t out_len = 0u;
zr_term_state_t final_ts;
zr_diff_stats_t stats;
Expand Down Expand Up @@ -502,6 +494,20 @@ zr_result_t engine_present(zr_engine_t* e) {
}
}

if (out_len != 0u && e->cfg_runtime.wait_for_output_drain != 0u) {
/*
Wait right before flush so CPU diff/render work overlaps terminal drain
from the prior frame.
*/
const int32_t timeout_ms = zr_engine_output_wait_timeout_ms(&e->cfg_runtime);
rc = plat_wait_output_writable(e->plat, timeout_ms);
if (rc != ZR_OK) {
/* Keep reuse conservative when present fails before prev/next commit. */
e->diff_prev_hashes_valid = 0u;
return rc;
}
}

const uint64_t write_start_us = zr_engine_now_us();
rc = zr_engine_present_write(e, out_len);
if (rc != ZR_OK) {
Expand Down
31 changes: 31 additions & 0 deletions tests/unit/test_engine_present_backpressure.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,37 @@ ZR_TEST_UNIT(engine_present_wait_for_output_drain_succeeds_when_writable) {
engine_destroy(e);
}

ZR_TEST_UNIT(engine_present_wait_for_output_drain_skips_wait_when_no_flush_needed) {
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);

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);
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);
}

ZR_TEST_UNIT(engine_create_wait_for_output_drain_unsupported_fails_early) {
mock_plat_reset();
mock_plat_set_size(10u, 4u);
Expand Down
Loading