结构优化

This commit is contained in:
2026-07-30 18:09:03 +08:00
parent 9271599723
commit 13ca0f3141
107 changed files with 3749 additions and 2424 deletions
+148 -47
View File
@@ -3,14 +3,16 @@
#include <QEventLoop>
#include <QThread>
#include <QTime>
#include <gtest/gtest.h>
#include <cmath>
#include <unordered_map>
#include "../Renderive/Axis/Axis.h"
#include "../Renderive/Axis/Frequency_Axis.h"
#include "../Renderive/Axis/Time_Axis.h"
#include "../Renderive/Axis/Time_Axis_p.h"
#include "../Renderive/architecture/Plot.h"
#include "../Renderive/architecture/Plot_p.h"
#include "../Renderive/plot/Latency_Eager_Plot.h"
#include "../Renderive/plot/Plot_p.h"
#include "../Renderive/architecture/Timeline_Stream.h"
#include "../Renderive/base/Memory.h"
#include "../Renderive/plottable/Audio_Frequency.h"
#include "../Renderive/plottable/Audio_Frequency_p.h"
@@ -44,19 +46,30 @@ bool wait_ready(const std::shared_ptr<renderive::Waterfall>& waterfall, const st
}
return false;
}
bool wait_idle(renderive::Plot& plot) {
bool wait_ready(const std::shared_ptr<renderive::Waterfall>& waterfall, const std::shared_ptr<renderive::Time_Axis>& time_axis) {
QElapsedTimer timer;
timer.start();
while (timer.elapsed() < 3000) {
QApplication::processEvents(QEventLoop::AllEvents, 10);
if (!plot.is_rendering() && !plot.d->active_render_tasks.load(std::memory_order_acquire))
if (waterfall->ok() && time_axis->ok())
return true;
QThread::msleep(1);
}
return false;
}
int stress_data_paths() {
renderive::Plot plot;
bool wait_idle(renderive::Latency_Eager_Plot& plot) {
QElapsedTimer timer;
timer.start();
while (timer.elapsed() < 3000) {
QApplication::processEvents(QEventLoop::AllEvents, 10);
if (!plot.is_rendering() && !renderive::Abs_Plot_Private_Access::active_render_tasks(plot))
return true;
QThread::msleep(1);
}
return false;
}
void stress_data_paths() {
renderive::Latency_Eager_Plot plot;
plot.init();
plot.resize(720, 420);
auto data_node = plot.create_renderable_node(plot.get_root_renderable(), "Data_Renderable");
@@ -71,8 +84,7 @@ int stress_data_paths() {
auto planisphere = renderive::Planisphere::Builder(data_node, i_axis, q_axis).set_i_range({-2.0, 2.0}).set_q_range({-2.0, 2.0}).set_continue_millisecond(500).build();
plot.show();
plot.start_render(240);
if (!wait_ready(waterfall, time_axis, audio, planisphere))
return 1;
ASSERT_TRUE(wait_ready(waterfall, time_axis, audio, planisphere));
const int frequency_sizes[] = {64, 512, 8192, 8193, 128};
const int time_sizes[] = {32, 96, 48, 64, 24};
const renderive::Waterfall_Update_Mode update_modes[] = {
@@ -96,7 +108,7 @@ int stress_data_paths() {
for (int frame = 0; frame < time_sizes[stage] * 2; ++frame) {
std::vector<double> frequency_data = make_frequency_data(frequency_sizes[stage], frame);
expected_time = QTime::fromMSecsSinceStartOfDay((stage * 10000 + frame * 4) % 86400000);
int tick = time_axis->give_data(expected_time);
int tick = time_axis->timeline_stream()->push_time(expected_time);
waterfall->give_data(tick, frequency_data);
expected_frequency[tick] = frequency_data.front();
expected_power = frequency_data[frame % frequency_data.size()];
@@ -109,48 +121,44 @@ int stress_data_paths() {
}
process_events(60);
plot.pause_render();
if (!wait_idle(plot))
return 2;
auto* waterfall_data = waterfall->d();
if (waterfall_data->ring_buffer.col_count != frequency_sizes[stage] || waterfall_data->ring_buffer.row_count != time_sizes[stage])
return 3;
if (waterfall_data->image.width() != frequency_sizes[stage] || waterfall_data->image.height() != time_sizes[stage])
return 4;
ASSERT_TRUE(wait_idle(plot));
auto* waterfall_data = renderive::Waterfall_Private_Access::data(waterfall.get());
ASSERT_EQ(waterfall_data->ring_buffer.col_count, frequency_sizes[stage]);
ASSERT_EQ(waterfall_data->ring_buffer.row_count, time_sizes[stage]);
ASSERT_EQ(waterfall_data->image.width(), frequency_sizes[stage]);
ASSERT_EQ(waterfall_data->image.height(), time_sizes[stage]);
int row_count = waterfall_data->ring_buffer.snapshot();
int previous_tick{};
for (int row = 0; row < row_count; ++row) {
int tick = waterfall_data->ring_buffer.tick(row);
auto expected = expected_frequency.find(tick);
if (expected == expected_frequency.end())
return 5;
if (std::abs(waterfall_data->ring_buffer.frequency_data(row, 0, 0)[0] - expected->second) > 0.000001)
return 6;
if (row && tick >= previous_tick)
return 7;
ASSERT_NE(expected, expected_frequency.end());
EXPECT_NEAR(waterfall_data->ring_buffer.frequency_data(row, 0, 0)[0], expected->second, 0.000001);
if (row)
EXPECT_GT(tick, previous_tick);
previous_tick = tick;
}
auto* time_data = time_axis->d();
if (time_data->time_ticker.time_point_size != time_sizes[stage] || time_data->time_snapshot.size() != static_cast<std::size_t>(time_sizes[stage]))
return 8;
if (!time_data->time_data_count || time_data->time_snapshot[time_data->time_data_count - 1] != expected_time)
return 9;
int lower = qMin(time_data->time_ticker.lower, time_data->time_ticker.upper());
int upper = qMax(time_data->time_ticker.lower, time_data->time_ticker.upper());
for (const renderive::Time_Tick& tick : time_data->data) {
if (tick.tick < lower || tick.tick > upper)
return 10;
ASSERT_EQ(time_data->timeline_snapshot.time_point_size, time_sizes[stage]);
ASSERT_EQ(time_data->time_snapshot.size(), static_cast<std::size_t>(time_sizes[stage]));
ASSERT_NE(time_data->time_data_count, 0);
EXPECT_EQ(time_data->time_snapshot[time_data->time_data_count - 1], expected_time);
int lower = qMin(time_data->timeline_snapshot.lower, time_data->timeline_snapshot.upper());
int upper = qMax(time_data->timeline_snapshot.lower, time_data->timeline_snapshot.upper());
for (const renderive::Timeline_Tick& tick : time_data->data) {
EXPECT_GE(tick.tick, lower);
EXPECT_LE(tick.tick, upper);
}
auto* audio_data = audio->d();
if (audio_data->point_count != time_sizes[stage] || audio_data->power_snapshot.size() != static_cast<std::size_t>(time_sizes[stage]))
return 11;
if (!audio_data->data_count || std::abs(audio_data->power_snapshot[audio_data->data_count - 1] - expected_power) > 0.000001)
return 12;
auto* planisphere_data = planisphere->d();
if (planisphere_data->data_list.empty())
return 13;
auto* audio_data = renderive::Audio_Frequency_Private_Access::data(audio.get());
ASSERT_EQ(audio_data->point_count, time_sizes[stage]);
ASSERT_EQ(audio_data->power_snapshot.size(), static_cast<std::size_t>(time_sizes[stage]));
ASSERT_NE(audio_data->data_count, 0);
EXPECT_NEAR(audio_data->power_snapshot[audio_data->data_count - 1], expected_power, 0.000001);
auto* planisphere_data = renderive::Planisphere_Private_Access::data(planisphere.get());
ASSERT_FALSE(planisphere_data->data_list.empty());
QPointF actual_position = planisphere_data->data_list.back().pos;
if (std::abs(actual_position.x() - expected_position.x()) > 0.000001 || std::abs(actual_position.y() - expected_position.y()) > 0.000001)
return 14;
EXPECT_NEAR(actual_position.x(), expected_position.x(), 0.000001);
EXPECT_NEAR(actual_position.y(), expected_position.y(), 0.000001);
plot.start_render(240);
}
for (int frame = 0; frame < 180; ++frame) {
@@ -158,16 +166,109 @@ int stress_data_paths() {
process_events(8);
}
plot.pause_render();
if (!wait_idle(plot))
return 15;
if (!planisphere->d()->data_list.empty())
return 16;
ASSERT_TRUE(wait_idle(plot));
EXPECT_TRUE(renderive::Planisphere_Private_Access::data(planisphere.get())->data_list.empty());
renderive::Memory_Stats stats = renderive::memory_stats();
return stats.upstream_allocation_count && stats.upstream_peak_bytes ? 0 : 17;
EXPECT_GT(stats.upstream_allocation_count, 0);
EXPECT_GT(stats.upstream_peak_bytes, 0);
}
}
TEST(Memory_Data_Path_Stress, KeepsRingAndRenderableDataConsistentAcrossResizes) {
stress_data_paths();
}
TEST(Memory_Data_Path_Stress, WaterfallWritesNewestTimelineRowAtBottom) {
renderive::Latency_Eager_Plot plot;
plot.init();
plot.resize(1, 4);
auto data_node = plot.create_renderable_node(plot.get_root_renderable(), "Data_Renderable");
auto axis_node = plot.create_renderable_node(plot.get_root_renderable(), "Axis_Renderable");
auto frequency_axis = renderive::Frequency_Axis::Builder(axis_node, Qt::Horizontal).set_pixel_size(1).set_coord_range({0.0, 1.0}).build();
auto time_axis = renderive::Time_Axis::Builder(axis_node, Qt::Vertical).set_pixel_size(4).set_time_point_size(4).build();
renderive::Color_Map color_map({qRgb(10, 0, 0), qRgb(20, 0, 0), qRgb(30, 0, 0), qRgb(40, 0, 0)});
auto waterfall = renderive::Waterfall::Builder(data_node, frequency_axis, time_axis).set_frequency_range({0.0, 1.0}).set_power_range({0.0, 4.0}).set_frequency_point_size(1).set_color_map(color_map).build();
plot.show();
plot.start_render(240);
ASSERT_TRUE(wait_ready(waterfall, time_axis));
for (int frame = 0; frame < 4; ++frame) {
std::pmr::vector<double> row(renderive::memory_resource(renderive::Memory_Domain::Waterfall));
row.resize(1);
row[0] = static_cast<double>(frame);
waterfall->give_data(QTime::fromMSecsSinceStartOfDay(frame), std::move(row));
}
process_events(100);
plot.pause_render();
ASSERT_TRUE(wait_idle(plot));
auto* data = renderive::Waterfall_Private_Access::data(waterfall.get());
ASSERT_EQ(data->image.width(), 1);
ASSERT_EQ(data->image.height(), 4);
EXPECT_EQ(qRed(data->image.image().pixel(0, 0)), 10);
EXPECT_EQ(qRed(data->image.image().pixel(0, 3)), 40);
}
TEST(Memory_Data_Path_Stress, WaterfallWritesNewestTimelineRowAtTopWhenTimeAxisPushesToStart) {
renderive::Latency_Eager_Plot plot;
plot.init();
plot.resize(1, 4);
auto data_node = plot.create_renderable_node(plot.get_root_renderable(), "Data_Renderable");
auto axis_node = plot.create_renderable_node(plot.get_root_renderable(), "Axis_Renderable");
auto frequency_axis = renderive::Frequency_Axis::Builder(axis_node, Qt::Horizontal).set_pixel_size(1).set_coord_range({0.0, 1.0}).build();
auto time_axis = renderive::Time_Axis::Builder(axis_node, Qt::Vertical).set_pixel_size(4).set_time_point_size(4).set_start_coord_to_end_coord(true).build();
renderive::Color_Map color_map({qRgb(10, 0, 0), qRgb(20, 0, 0), qRgb(30, 0, 0), qRgb(40, 0, 0)});
auto waterfall = renderive::Waterfall::Builder(data_node, frequency_axis, time_axis).set_frequency_range({0.0, 1.0}).set_power_range({0.0, 4.0}).set_frequency_point_size(1).set_color_map(color_map).build();
plot.show();
plot.start_render(240);
ASSERT_TRUE(wait_ready(waterfall, time_axis));
for (int frame = 0; frame < 4; ++frame) {
std::pmr::vector<double> row(renderive::memory_resource(renderive::Memory_Domain::Waterfall));
row.resize(1);
row[0] = static_cast<double>(frame);
waterfall->give_data(QTime::fromMSecsSinceStartOfDay(frame), std::move(row));
}
process_events(100);
plot.pause_render();
ASSERT_TRUE(wait_idle(plot));
auto* data = renderive::Waterfall_Private_Access::data(waterfall.get());
ASSERT_EQ(data->image.width(), 1);
ASSERT_EQ(data->image.height(), 4);
EXPECT_EQ(qRed(data->image.image().pixel(0, 0)), 40);
EXPECT_EQ(qRed(data->image.image().pixel(0, 3)), 10);
}
TEST(Memory_Data_Path_Stress, DirtyLocalPixelCacheRebuildsAfterInput) {
renderive::Latency_Eager_Plot plot;
plot.init();
plot.resize(1, 4);
auto cache_node = plot.create_renderable_node(plot.get_root_renderable(), "Stream_Cache");
auto data_node = plot.create_renderable_node(cache_node, "Data_Renderable");
auto axis_node = plot.create_renderable_node(plot.get_root_renderable(), "Axis_Renderable");
cache_node->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
auto frequency_axis = renderive::Frequency_Axis::Builder(axis_node, Qt::Horizontal).set_pixel_size(1).set_coord_range({0.0, 1.0}).build();
auto time_axis = renderive::Time_Axis::Builder(axis_node, Qt::Vertical).set_pixel_size(4).set_time_point_size(4).build();
renderive::Color_Map color_map({qRgb(10, 0, 0), qRgb(20, 0, 0), qRgb(30, 0, 0), qRgb(40, 0, 0)});
auto waterfall = renderive::Waterfall::Builder(data_node, frequency_axis, time_axis).set_frequency_range({0.0, 1.0}).set_power_range({0.0, 4.0}).set_frequency_point_size(1).set_color_map(color_map).build();
plot.show();
plot.start_render(240);
ASSERT_TRUE(wait_ready(waterfall, time_axis));
process_events(80);
plot.pause_render();
ASSERT_TRUE(wait_idle(plot));
ASSERT_FALSE(cache_node->cache_image.isNull());
plot.start_render(240);
for (int frame = 0; frame < 4; ++frame) {
std::pmr::vector<double> row(renderive::memory_resource(renderive::Memory_Domain::Waterfall));
row.resize(1);
row[0] = static_cast<double>(frame);
waterfall->give_data(QTime::fromMSecsSinceStartOfDay(frame), std::move(row));
}
process_events(100);
plot.pause_render();
ASSERT_TRUE(wait_idle(plot));
ASSERT_EQ(cache_node->cache_image.width(), 1);
ASSERT_EQ(cache_node->cache_image.height(), 4);
EXPECT_EQ(qRed(cache_node->cache_image.image().pixel(0, 3)), 40);
}
int main(int argc, char** argv) {
qputenv("QT_QPA_PLATFORM", "offscreen");
QApplication app(argc, argv);
testing::InitGoogleTest(&argc, argv);
renderive::start_render_scheduler();
return stress_data_paths();
return RUN_ALL_TESTS();
}