278 lines
13 KiB
C++
278 lines
13 KiB
C++
#include "../render_2D/export.h"
|
|
#include <gtest/gtest.h>
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <limits>
|
|
namespace renderive {
|
|
namespace {
|
|
TEST(Renderive_Core2_Frame_Pipeline, ManualLifecycleSeparatesPrepareRefreshAndRender) {
|
|
Plot_Scene plot({.frame_mode = Plot_Frame_Mode::Manual});
|
|
plot.init();
|
|
plot.set_viewport_size({96, 54});
|
|
ASSERT_TRUE(plot.prepare_frame());
|
|
auto snapshot = plot.frame_status();
|
|
EXPECT_EQ(snapshot.mode, Plot_Frame_Mode::Manual);
|
|
EXPECT_EQ(snapshot.last_event, "prepared");
|
|
EXPECT_EQ(snapshot.limit_state, "not_applicable");
|
|
EXPECT_EQ(snapshot.produced_frame_count, 1u);
|
|
EXPECT_EQ(snapshot.consumed_frame_count, 0u);
|
|
EXPECT_EQ(snapshot.pending_frame_count, 1u);
|
|
EXPECT_EQ(snapshot.latest_sequence, 1u);
|
|
ASSERT_TRUE(plot.refresh_manual_frame());
|
|
snapshot = plot.frame_status();
|
|
EXPECT_EQ(snapshot.last_event, "refresh_succeeded");
|
|
EXPECT_EQ(snapshot.produced_frame_count, 1u);
|
|
EXPECT_EQ(snapshot.consumed_frame_count, 0u);
|
|
EXPECT_EQ(snapshot.pending_frame_count, 0u);
|
|
EXPECT_EQ(snapshot.latest_sequence, 1u);
|
|
ASSERT_TRUE(plot.render_prepared_frame());
|
|
snapshot = plot.frame_status();
|
|
EXPECT_EQ(snapshot.last_event, "rendered");
|
|
EXPECT_EQ(snapshot.produced_frame_count, 1u);
|
|
EXPECT_EQ(snapshot.consumed_frame_count, 1u);
|
|
EXPECT_EQ(snapshot.pending_frame_count, 0u);
|
|
EXPECT_EQ(snapshot.latest_sequence, 1u);
|
|
EXPECT_EQ(plot.diagnostics().refresh.frame_count, 1u);
|
|
}
|
|
TEST(Renderive_Core2_Frame_Pipeline, HighFrequencyLowLatencyLifecycleExposesKernelTiming) {
|
|
Plot_Scene plot;
|
|
plot.init();
|
|
plot.set_viewport_size({96, 54});
|
|
EXPECT_TRUE(plot.set_max_render_fps(1'000'000'000.0));
|
|
ASSERT_TRUE(plot.prepare_frame());
|
|
auto snapshot = plot.frame_status();
|
|
EXPECT_EQ(snapshot.last_event, "published");
|
|
EXPECT_EQ(snapshot.limit_state, "frequency_limited");
|
|
EXPECT_DOUBLE_EQ(snapshot.frequency_hz, 1'000'000'000.0);
|
|
EXPECT_EQ(snapshot.target_interval_ns, 1u);
|
|
EXPECT_EQ(snapshot.produced_frame_count, 1u);
|
|
EXPECT_EQ(snapshot.consumed_frame_count, 0u);
|
|
EXPECT_EQ(snapshot.pending_frame_count, 1u);
|
|
ASSERT_TRUE(plot.render_prepared_frame());
|
|
snapshot = plot.frame_status();
|
|
EXPECT_EQ(snapshot.last_event, "lifecycle_completed");
|
|
EXPECT_DOUBLE_EQ(snapshot.frequency_hz, 1'000'000'000.0);
|
|
EXPECT_EQ(snapshot.target_interval_ns, 1u);
|
|
EXPECT_EQ(snapshot.produced_frame_count, 1u);
|
|
EXPECT_EQ(snapshot.consumed_frame_count, 1u);
|
|
EXPECT_EQ(snapshot.pending_frame_count, 0u);
|
|
EXPECT_EQ(snapshot.bottleneck_duration_ns,
|
|
std::max(snapshot.paint_duration_ns, snapshot.render_duration_ns));
|
|
EXPECT_EQ(snapshot.next_refresh_interval_ns,
|
|
std::max(snapshot.target_interval_ns, snapshot.bottleneck_duration_ns));
|
|
EXPECT_GE(snapshot.end_to_end_ns, snapshot.paint_duration_ns);
|
|
EXPECT_GE(snapshot.end_to_end_ns, snapshot.render_duration_ns);
|
|
const auto refresh = plot.refresh_feedback_snapshot();
|
|
EXPECT_DOUBLE_EQ(refresh.frequency_hz, snapshot.frequency_hz);
|
|
EXPECT_EQ(refresh.frame_count, snapshot.consumed_frame_count);
|
|
EXPECT_EQ(refresh.next_refresh_interval_ns, snapshot.next_refresh_interval_ns);
|
|
}
|
|
TEST(Renderive_Core2_Frame_Pipeline, LowLatencyConsumerFeedbackFlowsThroughCore2IntoKernelScheduling) {
|
|
Plot_Scene plot;
|
|
plot.init();
|
|
plot.set_viewport_size({96, 54});
|
|
EXPECT_TRUE(plot.set_max_render_fps(100.0));
|
|
EXPECT_TRUE(plot.set_consumer_feedback({40'000'000}));
|
|
ASSERT_TRUE(plot.prepare_frame());
|
|
ASSERT_TRUE(plot.render_prepared_frame());
|
|
const auto snapshot = plot.frame_status();
|
|
EXPECT_TRUE(snapshot.frequency_limit_enabled);
|
|
EXPECT_TRUE(snapshot.consumer_feedback_enabled);
|
|
EXPECT_EQ(snapshot.limit_state, "consumer_limited");
|
|
EXPECT_EQ(snapshot.target_interval_ns, 10'000'000u);
|
|
EXPECT_EQ(snapshot.consumer_sample_interval_ns, 40'000'000u);
|
|
EXPECT_EQ(snapshot.consumer_smoothed_interval_ns, 40'000'000u);
|
|
EXPECT_EQ(snapshot.consumer_variation_ns, 0u);
|
|
EXPECT_EQ(snapshot.consumer_safety_interval_ns, 40'000'000u);
|
|
EXPECT_EQ(snapshot.consumer_interval_ns, 40'000'000u);
|
|
EXPECT_EQ(snapshot.next_refresh_interval_ns, 40'000'000u);
|
|
EXPECT_EQ(plot.refresh_feedback_snapshot().next_refresh_interval_ns, 40'000'000u);
|
|
}
|
|
TEST(Renderive_Core2_Frame_Pipeline, LowLatencyLimitsCanBeClearedIndependently) {
|
|
Plot_Scene plot;
|
|
plot.init();
|
|
plot.set_viewport_size({96, 54});
|
|
EXPECT_TRUE(plot.set_max_render_fps(100.0));
|
|
EXPECT_TRUE(plot.set_consumer_feedback({40'000'000}));
|
|
ASSERT_TRUE(plot.clear_consumer_feedback());
|
|
ASSERT_TRUE(plot.clear_max_render_fps());
|
|
ASSERT_TRUE(plot.prepare_frame());
|
|
ASSERT_TRUE(plot.render_prepared_frame());
|
|
const auto snapshot = plot.frame_status();
|
|
EXPECT_FALSE(snapshot.frequency_limit_enabled);
|
|
EXPECT_FALSE(snapshot.consumer_feedback_enabled);
|
|
EXPECT_EQ(snapshot.target_interval_ns, 0u);
|
|
EXPECT_EQ(snapshot.consumer_interval_ns, 0u);
|
|
EXPECT_EQ(snapshot.next_refresh_interval_ns, snapshot.bottleneck_duration_ns);
|
|
EXPECT_TRUE(snapshot.limit_state == "paint_limited" || snapshot.limit_state == "render_limited");
|
|
}
|
|
TEST(Renderive_Core2_Frame_Pipeline, LowLatencyReplacementRetiresOldFrameWithoutPhantomPendingFrame) {
|
|
Plot_Scene plot;
|
|
plot.init();
|
|
plot.set_viewport_size({64, 40});
|
|
ASSERT_TRUE(plot.prepare_frame());
|
|
ASSERT_TRUE(plot.prepare_frame());
|
|
auto snapshot = plot.frame_status();
|
|
EXPECT_EQ(snapshot.last_event, "published");
|
|
EXPECT_EQ(snapshot.produced_frame_count, 2u);
|
|
EXPECT_EQ(snapshot.dropped_frame_count, 1u);
|
|
EXPECT_EQ(snapshot.pending_frame_count, 1u);
|
|
EXPECT_EQ(snapshot.latest_sequence, 2u);
|
|
ASSERT_TRUE(plot.render_prepared_frame());
|
|
snapshot = plot.frame_status();
|
|
EXPECT_EQ(snapshot.last_event, "lifecycle_completed");
|
|
EXPECT_EQ(snapshot.produced_frame_count, 2u);
|
|
EXPECT_EQ(snapshot.consumed_frame_count, 1u);
|
|
EXPECT_EQ(snapshot.dropped_frame_count, 1u);
|
|
EXPECT_EQ(snapshot.pending_frame_count, 0u);
|
|
EXPECT_EQ(snapshot.latest_sequence, 2u);
|
|
}
|
|
TEST(Renderive_Core2_Frame_Pipeline, LowLatencyDiscardAndEmptyRenderRemainObservable) {
|
|
Plot_Scene plot;
|
|
plot.init();
|
|
plot.set_viewport_size({64, 40});
|
|
ASSERT_TRUE(plot.prepare_frame());
|
|
ASSERT_TRUE(plot.discard_pending_frame());
|
|
auto snapshot = plot.frame_status();
|
|
EXPECT_EQ(snapshot.last_event, "manually_discarded");
|
|
EXPECT_EQ(snapshot.produced_frame_count, 1u);
|
|
EXPECT_EQ(snapshot.consumed_frame_count, 0u);
|
|
EXPECT_EQ(snapshot.dropped_frame_count, 1u);
|
|
EXPECT_EQ(snapshot.pending_frame_count, 0u);
|
|
EXPECT_EQ(snapshot.latest_sequence, 1u);
|
|
const auto empty_render = plot.render_prepared_frame();
|
|
ASSERT_FALSE(empty_render);
|
|
EXPECT_EQ(empty_render.error(),
|
|
Plot_Render_Prepared_Frame_Error::renderer_unavailable);
|
|
snapshot = plot.frame_status();
|
|
EXPECT_EQ(snapshot.last_event, "swap_failed");
|
|
EXPECT_EQ(snapshot.failed_operation_count, 1u);
|
|
EXPECT_EQ(snapshot.pending_frame_count, 0u);
|
|
EXPECT_EQ(snapshot.latest_sequence, 1u);
|
|
}
|
|
TEST(Renderive_Core2_Frame_Pipeline, ManualDiscardAndFailedRefreshUpdateObserverState) {
|
|
Plot_Scene plot({.frame_mode = Plot_Frame_Mode::Manual});
|
|
plot.init();
|
|
plot.set_viewport_size({64, 40});
|
|
ASSERT_TRUE(plot.prepare_frame());
|
|
ASSERT_TRUE(plot.discard_pending_frame());
|
|
auto snapshot = plot.frame_status();
|
|
EXPECT_EQ(snapshot.last_event, "manually_discarded");
|
|
EXPECT_EQ(snapshot.produced_frame_count, 1u);
|
|
EXPECT_EQ(snapshot.dropped_frame_count, 1u);
|
|
EXPECT_EQ(snapshot.pending_frame_count, 0u);
|
|
EXPECT_EQ(snapshot.latest_sequence, 1u);
|
|
const auto last_paint_duration_ns = snapshot.paint_duration_ns;
|
|
const auto failed_refresh = plot.refresh_manual_frame();
|
|
ASSERT_FALSE(failed_refresh);
|
|
EXPECT_EQ(failed_refresh.error(),
|
|
Plot_Refresh_Manual_Frame_Error::no_pending_frame);
|
|
snapshot = plot.frame_status();
|
|
EXPECT_EQ(snapshot.last_event, "refresh_failed");
|
|
EXPECT_EQ(snapshot.failed_operation_count, 1u);
|
|
EXPECT_EQ(snapshot.pending_frame_count, 0u);
|
|
EXPECT_EQ(snapshot.latest_sequence, 1u);
|
|
EXPECT_EQ(snapshot.paint_duration_ns, last_paint_duration_ns);
|
|
}
|
|
TEST(Renderive_Core2_Frame_Pipeline, PlaybackPreservesQueueDepthAndEmptyAcquireFailure) {
|
|
Plot_Scene plot({.frame_mode = Plot_Frame_Mode::Playback});
|
|
plot.init();
|
|
plot.set_viewport_size({64, 40});
|
|
ASSERT_TRUE(plot.prepare_frame());
|
|
ASSERT_TRUE(plot.prepare_frame());
|
|
ASSERT_TRUE(plot.prepare_frame());
|
|
auto snapshot = plot.frame_status();
|
|
EXPECT_EQ(snapshot.produced_frame_count, 3u);
|
|
EXPECT_EQ(snapshot.pending_frame_count, 3u);
|
|
EXPECT_EQ(snapshot.latest_sequence, 3u);
|
|
ASSERT_TRUE(plot.render_prepared_frame());
|
|
ASSERT_TRUE(plot.render_prepared_frame());
|
|
ASSERT_TRUE(plot.render_prepared_frame());
|
|
snapshot = plot.frame_status();
|
|
EXPECT_EQ(snapshot.last_event, "rendered");
|
|
EXPECT_EQ(snapshot.consumed_frame_count, 3u);
|
|
EXPECT_EQ(snapshot.pending_frame_count, 0u);
|
|
EXPECT_EQ(snapshot.latest_sequence, 3u);
|
|
const auto last_paint_duration_ns = snapshot.paint_duration_ns;
|
|
const auto last_render_duration_ns = snapshot.render_duration_ns;
|
|
const auto last_queue_wait_ns = snapshot.queue_wait_ns;
|
|
EXPECT_FALSE(plot.render_prepared_frame());
|
|
snapshot = plot.frame_status();
|
|
EXPECT_EQ(snapshot.last_event, "queue_empty");
|
|
EXPECT_EQ(snapshot.failed_operation_count, 1u);
|
|
EXPECT_EQ(snapshot.pending_frame_count, 0u);
|
|
EXPECT_EQ(snapshot.latest_sequence, 3u);
|
|
EXPECT_EQ(snapshot.paint_duration_ns, last_paint_duration_ns);
|
|
EXPECT_EQ(snapshot.render_duration_ns, last_render_duration_ns);
|
|
EXPECT_EQ(snapshot.queue_wait_ns, last_queue_wait_ns);
|
|
}
|
|
TEST(Renderive_Core2_Frame_Pipeline, PlaybackFullCyclePreservesPreviouslyQueuedFrames) {
|
|
Plot_Scene plot({.frame_mode = Plot_Frame_Mode::Playback});
|
|
plot.init();
|
|
plot.set_viewport_size({96, 54});
|
|
ASSERT_TRUE(plot.prepare_frame());
|
|
ASSERT_TRUE(plot.prepare_frame());
|
|
ASSERT_TRUE(plot.prepare_frame());
|
|
auto snapshot = plot.frame_status();
|
|
ASSERT_EQ(snapshot.pending_frame_count, 3u);
|
|
ASSERT_EQ(snapshot.produced_frame_count, 3u);
|
|
ASSERT_EQ(plot.render_frame(true).value(), Plot_Render_Status::rendered);
|
|
snapshot = plot.frame_status();
|
|
EXPECT_EQ(snapshot.mode, Plot_Frame_Mode::Playback);
|
|
EXPECT_EQ(snapshot.last_event, "rendered");
|
|
EXPECT_EQ(snapshot.produced_frame_count, 4u);
|
|
EXPECT_EQ(snapshot.consumed_frame_count, 1u);
|
|
EXPECT_EQ(snapshot.pending_frame_count, 3u);
|
|
// The cycle enqueues sequence 4 but must consume the FIFO head (sequence 1).
|
|
EXPECT_EQ(snapshot.latest_sequence, 1u);
|
|
EXPECT_FALSE(plot.discard_pending_frame());
|
|
EXPECT_EQ(plot.frame_status().pending_frame_count, 3u);
|
|
}
|
|
TEST(Renderive_Core2_Frame_Pipeline, EveryModeHonorsActiveDirtyAndForceRenderGates) {
|
|
const auto verify = [](Plot_Frame_Mode mode) {
|
|
Plot_Scene scene({.frame_mode = mode});
|
|
scene.init();
|
|
scene.set_viewport_size({80, 45});
|
|
EXPECT_FALSE(scene.view_active());
|
|
EXPECT_NE(scene.render_frame(false).value(), Plot_Render_Status::rendered);
|
|
EXPECT_EQ(scene.frame_status().observation_count, 0u);
|
|
scene.activate_view();
|
|
ASSERT_EQ(scene.render_frame(false).value(), Plot_Render_Status::rendered);
|
|
const auto completed = scene.frame_status();
|
|
EXPECT_EQ(completed.mode, mode);
|
|
EXPECT_EQ(completed.consumed_frame_count, 1u);
|
|
EXPECT_NE(scene.render_frame(false).value(), Plot_Render_Status::rendered);
|
|
EXPECT_EQ(scene.frame_status().consumed_frame_count, 1u);
|
|
scene.deactivate_view();
|
|
scene.set_background_color(Color{8, 16, 24, 255});
|
|
EXPECT_NE(scene.render_frame(false).value(), Plot_Render_Status::rendered);
|
|
ASSERT_EQ(scene.render_frame(true).value(), Plot_Render_Status::rendered);
|
|
EXPECT_EQ(scene.frame_status().consumed_frame_count, 2u);
|
|
};
|
|
verify(Plot_Frame_Mode::Manual);
|
|
verify(Plot_Frame_Mode::Low_Latency);
|
|
verify(Plot_Frame_Mode::Playback);
|
|
}
|
|
TEST(Renderive_Core2_Frame_Pipeline, FrequencyConfigurationRejectsInvalidValuesAndWrongModes) {
|
|
Plot_Scene low_latency;
|
|
EXPECT_EQ(low_latency.set_max_render_fps(0.0).error(), Plot_Set_Max_Render_Fps_Error::invalid_fps);
|
|
EXPECT_EQ(low_latency.set_max_render_fps(-1.0).error(), Plot_Set_Max_Render_Fps_Error::invalid_fps);
|
|
EXPECT_EQ(low_latency.set_max_render_fps(std::numeric_limits<double>::quiet_NaN()).error(),
|
|
Plot_Set_Max_Render_Fps_Error::invalid_fps);
|
|
EXPECT_EQ(low_latency.set_max_render_fps(std::numeric_limits<double>::infinity()).error(),
|
|
Plot_Set_Max_Render_Fps_Error::invalid_fps);
|
|
EXPECT_TRUE(low_latency.set_max_render_fps(1'000.0));
|
|
EXPECT_DOUBLE_EQ(low_latency.max_render_fps(), 1'000.0);
|
|
Plot_Scene manual({.frame_mode = Plot_Frame_Mode::Manual});
|
|
Plot_Scene playback({.frame_mode = Plot_Frame_Mode::Playback});
|
|
EXPECT_EQ(manual.set_max_render_fps(60.0).error(), Plot_Set_Max_Render_Fps_Error::unsupported_frame_mode);
|
|
EXPECT_EQ(playback.set_max_render_fps(60.0).error(), Plot_Set_Max_Render_Fps_Error::unsupported_frame_mode);
|
|
const auto unsupported_refresh = playback.refresh_manual_frame();
|
|
ASSERT_FALSE(unsupported_refresh);
|
|
EXPECT_EQ(unsupported_refresh.error(),
|
|
Plot_Refresh_Manual_Frame_Error::unsupported_frame_mode);
|
|
EXPECT_FALSE(playback.discard_pending_frame());
|
|
}
|
|
} // namespace
|
|
} // namespace renderive
|