116 lines
5.1 KiB
C++
116 lines
5.1 KiB
C++
#include <QApplication>
|
|
#include <QElapsedTimer>
|
|
#include <QEventLoop>
|
|
#include <QThread>
|
|
#include <QTime>
|
|
#include <gtest/gtest.h>
|
|
#include <atomic>
|
|
#include <cmath>
|
|
#include <thread>
|
|
#include <vector>
|
|
#include "../Renderive/Axis/Axis.h"
|
|
#include "../Renderive/Axis/Frequency_Axis.h"
|
|
#include "../Renderive/Axis/Time_Axis.h"
|
|
#include "../Renderive/architecture/Timeline_Stream.h"
|
|
#include "../Renderive/plot/Latency_Eager_Plot.h"
|
|
#include "../Renderive/base/Memory.h"
|
|
#include "../Renderive/plottable/Audio_Frequency.h"
|
|
#include "../Renderive/plottable/Spectrum.h"
|
|
#include "../Renderive/plottable/Waterfall.h"
|
|
#include "internal/Renderive_Private_Access.h"
|
|
namespace {
|
|
std::pmr::vector<double> make_data(int size, int seed, renderive::Memory_Domain domain) {
|
|
std::pmr::vector<double> data(renderive::memory_resource(domain));
|
|
data.resize(size);
|
|
for (int i = 0; i < size; ++i)
|
|
data[i] = -90.0 + 30.0 * std::sin(static_cast<double>(i + seed) * 0.017);
|
|
return data;
|
|
}
|
|
void process_events(int milliseconds) {
|
|
QElapsedTimer timer;
|
|
timer.start();
|
|
while (timer.elapsed() < milliseconds) {
|
|
QApplication::processEvents(QEventLoop::AllEvents, 10);
|
|
QThread::msleep(1);
|
|
}
|
|
}
|
|
bool wait_ready(const std::shared_ptr<renderive::Waterfall>& waterfall, const std::shared_ptr<renderive::Spectrum>& spectrum, const std::shared_ptr<renderive::Audio_Frequency>& audio, const std::shared_ptr<renderive::Time_Axis>& time_axis) {
|
|
QElapsedTimer timer;
|
|
timer.start();
|
|
while (timer.elapsed() < 3000) {
|
|
QApplication::processEvents(QEventLoop::AllEvents, 10);
|
|
if (waterfall->ok() && spectrum->ok() && audio->ok() && time_axis->ok())
|
|
return true;
|
|
QThread::msleep(1);
|
|
}
|
|
return false;
|
|
}
|
|
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 run_stress() {
|
|
renderive::Latency_Eager_Plot plot;
|
|
plot.init();
|
|
plot.resize(640, 360);
|
|
plot.set_max_render_fps(1000);
|
|
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(640).set_coord_range({0.0, 100.0}).build();
|
|
auto time_axis = renderive::Time_Axis::Builder(axis_node, Qt::Vertical).set_pixel_size(360).set_time_point_size(128).build();
|
|
auto power_axis = renderive::Axis::Builder(axis_node, Qt::Vertical).set_pixel_size(360).set_coord_range({0.0, -120.0}).build();
|
|
auto waterfall = renderive::Waterfall::Builder(data_node, frequency_axis, time_axis).set_frequency_range({0.0, 100.0}).set_power_range({-120.0, 0.0}).set_frequency_point_size(256).build();
|
|
auto spectrum = renderive::Spectrum::Builder(data_node, frequency_axis, power_axis).set_frequency_range({0.0, 100.0}).set_frequency_point_size(256).build();
|
|
auto audio = renderive::Audio_Frequency::Builder(data_node, time_axis, power_axis).build();
|
|
plot.show();
|
|
plot.start_render(1000);
|
|
ASSERT_TRUE(wait_ready(waterfall, spectrum, audio, time_axis));
|
|
std::atomic_bool running{true};
|
|
std::atomic<int> pushes{0};
|
|
std::vector<std::thread> workers;
|
|
for (int thread_index = 0; thread_index < 4; ++thread_index) {
|
|
workers.emplace_back([&, thread_index]() {
|
|
for (int frame = 0; running.load(std::memory_order_acquire) && frame < 2500; ++frame) {
|
|
int seed = frame + thread_index * 10000;
|
|
QTime time = QTime::fromMSecsSinceStartOfDay(seed % 86400000);
|
|
int tick = time_axis->timeline_stream()->push_time(time);
|
|
waterfall->give_data(tick, make_data(256, seed, renderive::Memory_Domain::Waterfall));
|
|
spectrum->give_data(make_data(256, seed, renderive::Memory_Domain::Spectrum));
|
|
audio->give_data(tick, -90.0 + static_cast<double>((seed % 120)));
|
|
pushes.fetch_add(1, std::memory_order_relaxed);
|
|
if ((frame & 15) == 15)
|
|
std::this_thread::yield();
|
|
}
|
|
});
|
|
}
|
|
QElapsedTimer timer;
|
|
timer.start();
|
|
while (timer.elapsed() < 8000 && pushes.load(std::memory_order_relaxed) < 10000)
|
|
process_events(5);
|
|
running.store(false, std::memory_order_release);
|
|
for (auto& worker : workers)
|
|
worker.join();
|
|
process_events(300);
|
|
plot.pause_render();
|
|
ASSERT_TRUE(wait_idle(plot));
|
|
EXPECT_GT(pushes.load(std::memory_order_relaxed), 1000);
|
|
}
|
|
}
|
|
TEST(Multithread_Input_Stress, AcceptsConcurrentRenderableInput) {
|
|
run_stress();
|
|
}
|
|
int main(int argc, char** argv) {
|
|
qputenv("QT_QPA_PLATFORM", "offscreen");
|
|
QApplication app(argc, argv);
|
|
testing::InitGoogleTest(&argc, argv);
|
|
renderive::start_render_scheduler();
|
|
return RUN_ALL_TESTS();
|
|
}
|