补充提交
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include "../../Renderive/base/Tool.h"
|
||||
static QWidget* create_frequent_axis_test_plot() {
|
||||
class Axis_Test_Plot : public YSG::Plot {
|
||||
public:
|
||||
std::shared_ptr<YSG::Frequency_Axis> axis{};
|
||||
QMenu* menu{};
|
||||
void init() override {
|
||||
Plot::init();
|
||||
object_name = "Axis_Test_Plot";
|
||||
auto axis_node = create_renderable_node(root_renderable(), "Axis_Renderable");
|
||||
axis = YSG::Frequency_Axis::Builder(axis_node, Qt::Horizontal)
|
||||
.set_tick_length(-10)
|
||||
.set_sub_tick_length(-5)
|
||||
.build();
|
||||
}
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* event) override {
|
||||
int space = 50;
|
||||
axis->set_x(space);
|
||||
axis->set_y(height() / 2);
|
||||
axis->set_pixel_size(width() - space * 2);
|
||||
}
|
||||
void contextMenuEvent(QContextMenuEvent* event) override {
|
||||
if (!menu) {
|
||||
auto ws = bw(cw(axis.get()), "轴");
|
||||
menu = create_menu(this, ws);
|
||||
}
|
||||
menu->exec(event->globalPos());
|
||||
}
|
||||
};
|
||||
auto w = new Axis_Test_Plot;
|
||||
w->init();
|
||||
return w;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include "../../Renderive/base/Tool.h"
|
||||
static QWidget* create_frequent_axis_test_plot() {
|
||||
class Axis_Test_Plot : public renderive::Plot {
|
||||
public:
|
||||
std::shared_ptr<renderive::Frequency_Axis> axis{};
|
||||
QMenu* menu{};
|
||||
void init() override {
|
||||
Plot::init();
|
||||
object_name = "Axis_Test_Plot";
|
||||
auto axis_node = create_renderable_node(get_root_renderable(), "Axis_Renderable");
|
||||
axis = renderive::Frequency_Axis::Builder(axis_node, Qt::Horizontal)
|
||||
.set_tick_length(-10)
|
||||
.set_sub_tick_length(-5)
|
||||
.set_coord_range({-12000, 12000})
|
||||
.build();
|
||||
}
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* event) override {
|
||||
int space = 50;
|
||||
axis->set_x(space);
|
||||
axis->set_y(height() / 2);
|
||||
axis->set_pixel_size(width() - space * 2);
|
||||
}
|
||||
void contextMenuEvent(QContextMenuEvent* event) override {
|
||||
if (!menu) {
|
||||
auto ws = bw(cw(axis.get()), "轴");
|
||||
menu = create_menu(this, ws);
|
||||
}
|
||||
menu->exec(event->globalPos());
|
||||
}
|
||||
};
|
||||
auto w = new Axis_Test_Plot;
|
||||
w->init();
|
||||
return w;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include "../../Renderive/base/Tool.h"
|
||||
static QWidget* create_Multi_Select_Plot() {
|
||||
class Multi_Select_Plot : public renderive::Plot {
|
||||
public:
|
||||
std::shared_ptr<renderive::Axis> x_axis{};
|
||||
std::shared_ptr<renderive::Axis> y_axis{};
|
||||
std::shared_ptr<renderive::Multi_Select_Rect> multi_select_rect{};
|
||||
QMenu* menu{};
|
||||
void init() override {
|
||||
Plot::init();
|
||||
object_name = "PlanispherePlot";
|
||||
auto axis = create_renderable_node(get_root_renderable(), "Axis_Renderable");
|
||||
auto overlay = create_renderable_node(get_root_renderable(), "Overlay_Renderable");
|
||||
x_axis = renderive::Axis::Builder(axis, Qt::Horizontal)
|
||||
.set_tick_length(-10)
|
||||
.set_sub_tick_length(-5)
|
||||
.build();
|
||||
y_axis = renderive::Axis::Builder(axis, Qt::Vertical).build();
|
||||
multi_select_rect = renderive::Multi_Select_Rect::Builder(overlay, x_axis, y_axis).build();
|
||||
}
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* event) override {
|
||||
x_axis->set_x(0);
|
||||
x_axis->set_y(height() - 1);
|
||||
x_axis->set_pixel_size(width());
|
||||
y_axis->set_x(0);
|
||||
y_axis->set_y(0);
|
||||
// y_axis->setCoordReserve(true);
|
||||
y_axis->set_pixel_size(height());
|
||||
}
|
||||
void contextMenuEvent(QContextMenuEvent* event) override {
|
||||
if (!menu) {
|
||||
auto ws = bw(cw(multi_select_rect.get()), "多选框");
|
||||
menu = create_menu(this, ws);
|
||||
}
|
||||
menu->exec(event->globalPos());
|
||||
}
|
||||
};
|
||||
auto w = new Multi_Select_Plot;
|
||||
w->init();
|
||||
return w;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <memory>
|
||||
int main() {
|
||||
auto values = std::make_unique<int[]>(64);
|
||||
for (int i = 0; i < 64; ++i)
|
||||
values[i] = i;
|
||||
return values[63] == 63 ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
#include <atomic>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory_resource>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include "../Renderive/base/Memory.h"
|
||||
namespace {
|
||||
struct Callback_State {
|
||||
std::atomic<std::uint64_t> allocation_count{};
|
||||
std::atomic<std::uint64_t> deallocation_count{};
|
||||
std::atomic_bool alignment_error{};
|
||||
std::atomic_bool deallocated_off_main_thread{};
|
||||
std::thread::id main_thread;
|
||||
};
|
||||
Callback_State callback_state;
|
||||
void* callback_allocate(void* context, std::size_t size, std::size_t alignment) {
|
||||
auto* state = static_cast<Callback_State*>(context);
|
||||
void* result{};
|
||||
try {
|
||||
result = std::pmr::new_delete_resource()->allocate(size, alignment);
|
||||
}
|
||||
catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
state->allocation_count.fetch_add(1, std::memory_order_relaxed);
|
||||
if (reinterpret_cast<std::uintptr_t>(result) % alignment)
|
||||
state->alignment_error.store(true, std::memory_order_relaxed);
|
||||
return result;
|
||||
}
|
||||
void callback_deallocate(void* context, void* address, std::size_t size, std::size_t alignment) {
|
||||
auto* state = static_cast<Callback_State*>(context);
|
||||
state->deallocation_count.fetch_add(1, std::memory_order_relaxed);
|
||||
if (std::this_thread::get_id() != state->main_thread)
|
||||
state->deallocated_off_main_thread.store(true, std::memory_order_relaxed);
|
||||
std::pmr::new_delete_resource()->deallocate(address, size, alignment);
|
||||
}
|
||||
struct Payload {
|
||||
std::uint64_t value{};
|
||||
};
|
||||
struct Large_Payload {
|
||||
std::array<std::byte, 128 * 1024> data{};
|
||||
};
|
||||
}
|
||||
int main() {
|
||||
callback_state.main_thread = std::this_thread::get_id();
|
||||
renderive::Memory_Callbacks invalid_callbacks;
|
||||
invalid_callbacks.context = &callback_state;
|
||||
invalid_callbacks.allocate = callback_allocate;
|
||||
try {
|
||||
renderive::set_memory_callbacks(invalid_callbacks);
|
||||
return 1;
|
||||
}
|
||||
catch (const std::invalid_argument&) {}
|
||||
invalid_callbacks.allocate = nullptr;
|
||||
invalid_callbacks.deallocate = callback_deallocate;
|
||||
try {
|
||||
renderive::set_memory_callbacks(invalid_callbacks);
|
||||
return 2;
|
||||
}
|
||||
catch (const std::invalid_argument&) {}
|
||||
renderive::Memory_Callbacks callbacks;
|
||||
callbacks.context = &callback_state;
|
||||
callbacks.allocate = callback_allocate;
|
||||
callbacks.deallocate = callback_deallocate;
|
||||
renderive::set_memory_callbacks(callbacks);
|
||||
try {
|
||||
renderive::set_memory_callbacks(callbacks);
|
||||
return 3;
|
||||
}
|
||||
catch (const std::logic_error&) {}
|
||||
{
|
||||
std::pmr::vector<std::uint64_t> values(renderive::memory_resource());
|
||||
values.resize(8192, 11);
|
||||
auto owner = renderive::make_shared<Payload>();
|
||||
auto large_owner = renderive::make_shared<Large_Payload>();
|
||||
owner->value = values.front();
|
||||
std::thread release_thread([owner = std::move(owner), large_owner = std::move(large_owner)]() mutable {
|
||||
owner.reset();
|
||||
large_owner.reset();
|
||||
});
|
||||
release_thread.join();
|
||||
}
|
||||
renderive::release_unused_memory();
|
||||
renderive::Memory_Stats stats = renderive::memory_stats();
|
||||
if (callback_state.alignment_error.load(std::memory_order_relaxed))
|
||||
return 4;
|
||||
if (!callback_state.allocation_count.load(std::memory_order_relaxed))
|
||||
return 5;
|
||||
if (stats.upstream_allocation_count != callback_state.allocation_count.load(std::memory_order_relaxed))
|
||||
return 6;
|
||||
if (!callback_state.deallocation_count.load(std::memory_order_relaxed) || !callback_state.deallocated_off_main_thread.load(std::memory_order_relaxed))
|
||||
return 7;
|
||||
renderive::shutdown_memory();
|
||||
if (renderive::memory_stats().upstream_current_bytes)
|
||||
return 8;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
#include <QApplication>
|
||||
#include <QElapsedTimer>
|
||||
#include <QEventLoop>
|
||||
#include <QThread>
|
||||
#include <QTime>
|
||||
#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/base/Memory.h"
|
||||
#include "../Renderive/plottable/Audio_Frequency.h"
|
||||
#include "../Renderive/plottable/Audio_Frequency_p.h"
|
||||
#include "../Renderive/plottable/Planisphere.h"
|
||||
#include "../Renderive/plottable/Planisphere_p.h"
|
||||
#include "../Renderive/plottable/Waterfall.h"
|
||||
#include "../Renderive/plottable/Waterfall_p.h"
|
||||
namespace {
|
||||
void process_events(int milliseconds) {
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
while (timer.elapsed() < milliseconds) {
|
||||
QApplication::processEvents(QEventLoop::AllEvents, 10);
|
||||
QThread::msleep(1);
|
||||
}
|
||||
}
|
||||
std::vector<double> make_frequency_data(int size, int frame) {
|
||||
std::vector<double> data(size);
|
||||
for (int i = 0; i < size; ++i)
|
||||
data[i] = -90.0 + 30.0 * std::sin(static_cast<double>(i + frame) * 0.017);
|
||||
return data;
|
||||
}
|
||||
bool wait_ready(const std::shared_ptr<renderive::Waterfall>& waterfall, const std::shared_ptr<renderive::Time_Axis>& time_axis, const std::shared_ptr<renderive::Audio_Frequency>& audio, const std::shared_ptr<renderive::Planisphere>& planisphere) {
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
while (timer.elapsed() < 3000) {
|
||||
QApplication::processEvents(QEventLoop::AllEvents, 10);
|
||||
if (waterfall->ok() && time_axis->ok() && audio->ok() && planisphere->ok())
|
||||
return true;
|
||||
QThread::msleep(1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool wait_idle(renderive::Plot& plot) {
|
||||
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))
|
||||
return true;
|
||||
QThread::msleep(1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int stress_data_paths() {
|
||||
renderive::Plot plot;
|
||||
plot.init();
|
||||
plot.resize(720, 420);
|
||||
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(700).set_coord_range({0.0, 100.0}).build();
|
||||
auto time_axis = renderive::Time_Axis::Builder(axis_node, Qt::Vertical).set_pixel_size(400).set_time_point_size(32).build();
|
||||
auto value_axis = renderive::Axis::Builder(axis_node, Qt::Vertical).set_pixel_size(400).set_coord_range({-120.0, 0.0}).build();
|
||||
auto i_axis = renderive::Axis::Builder(axis_node, Qt::Horizontal).set_pixel_size(700).set_coord_range({-2.0, 2.0}).build();
|
||||
auto q_axis = renderive::Axis::Builder(axis_node, Qt::Vertical).set_pixel_size(400).set_coord_range({-2.0, 2.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(64).build();
|
||||
auto audio = renderive::Audio_Frequency::Builder(data_node, time_axis, value_axis).set_time_point_size(32).set_key_range({-120.0, 0.0}).build();
|
||||
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;
|
||||
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[] = {
|
||||
renderive::Waterfall_Update_Mode::Batch,
|
||||
renderive::Waterfall_Update_Mode::Single_Line,
|
||||
renderive::Waterfall_Update_Mode::Timed_Latest,
|
||||
renderive::Waterfall_Update_Mode::Single_Line,
|
||||
renderive::Waterfall_Update_Mode::Batch
|
||||
};
|
||||
for (int stage = 0; stage < 5; ++stage) {
|
||||
waterfall->set_frequency_point_size(frequency_sizes[stage]);
|
||||
waterfall->set_update_mode(update_modes[stage]);
|
||||
waterfall->set_update_interval_ms(2);
|
||||
time_axis->set_time_point_size(time_sizes[stage]);
|
||||
audio->set_time_point_size(time_sizes[stage]);
|
||||
process_events(100);
|
||||
std::unordered_map<int, double> expected_frequency;
|
||||
QTime expected_time;
|
||||
double expected_power{};
|
||||
QPointF expected_position;
|
||||
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);
|
||||
waterfall->give_data(tick, frequency_data);
|
||||
expected_frequency[tick] = frequency_data.front();
|
||||
expected_power = frequency_data[frame % frequency_data.size()];
|
||||
audio->give_data(tick, expected_power);
|
||||
double phase = static_cast<double>(frame) * 0.13;
|
||||
expected_position = {1.5 * std::cos(phase), 1.5 * std::sin(phase)};
|
||||
planisphere->give_data(expected_position);
|
||||
if ((frame & 3) == 3)
|
||||
process_events(8);
|
||||
}
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
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;
|
||||
plot.start_render(240);
|
||||
}
|
||||
for (int frame = 0; frame < 180; ++frame) {
|
||||
planisphere->set_continue_millisecond(500 + (frame & 1));
|
||||
process_events(8);
|
||||
}
|
||||
plot.pause_render();
|
||||
if (!wait_idle(plot))
|
||||
return 15;
|
||||
if (!planisphere->d()->data_list.empty())
|
||||
return 16;
|
||||
renderive::Memory_Stats stats = renderive::memory_stats();
|
||||
return stats.upstream_allocation_count && stats.upstream_peak_bytes ? 0 : 17;
|
||||
}
|
||||
}
|
||||
int main(int argc, char** argv) {
|
||||
QApplication app(argc, argv);
|
||||
renderive::start_render_scheduler();
|
||||
return stress_data_paths();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#include <cstdint>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include "../Renderive/base/Memory.h"
|
||||
namespace {
|
||||
struct Payload {
|
||||
std::uint64_t value{};
|
||||
};
|
||||
}
|
||||
int main() {
|
||||
std::pmr::vector<std::uint64_t> values(renderive::memory_resource());
|
||||
values.resize(4096, 7);
|
||||
auto owner = renderive::make_shared<Payload>();
|
||||
owner->value = values.front();
|
||||
if (owner->value != 7)
|
||||
return 1;
|
||||
std::thread release_thread([owner = std::move(owner)]() mutable {
|
||||
owner.reset();
|
||||
});
|
||||
release_thread.join();
|
||||
renderive::Memory_Stats stats = renderive::memory_stats();
|
||||
if (!stats.upstream_allocation_count || !stats.upstream_current_bytes || !stats.upstream_peak_bytes)
|
||||
return 2;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
#include <atomic>
|
||||
#include <memory_resource>
|
||||
#include <new>
|
||||
#include <vector>
|
||||
#include "../Renderive/base/Memory.h"
|
||||
#include "../Renderive/base/Frame_Memory.h"
|
||||
#include "Core/Base/RingBuffer.hpp"
|
||||
namespace {
|
||||
struct Callback_State {
|
||||
std::atomic_bool fail{true};
|
||||
};
|
||||
Callback_State callback_state;
|
||||
void* callback_allocate(void* context, std::size_t size, std::size_t alignment) {
|
||||
auto* state = static_cast<Callback_State*>(context);
|
||||
if (state->fail.load(std::memory_order_relaxed))
|
||||
return nullptr;
|
||||
return std::pmr::new_delete_resource()->allocate(size, alignment);
|
||||
}
|
||||
void callback_deallocate(void*, void* address, std::size_t size, std::size_t alignment) {
|
||||
std::pmr::new_delete_resource()->deallocate(address, size, alignment);
|
||||
}
|
||||
}
|
||||
int main() {
|
||||
renderive::Memory_Callbacks callbacks;
|
||||
callbacks.context = &callback_state;
|
||||
callbacks.allocate = callback_allocate;
|
||||
callbacks.deallocate = callback_deallocate;
|
||||
callback_state.fail.store(false, std::memory_order_relaxed);
|
||||
renderive::set_memory_callbacks(callbacks);
|
||||
std::pmr::memory_resource* resource = renderive::memory_resource();
|
||||
callback_state.fail.store(true, std::memory_order_relaxed);
|
||||
bool direct_failure_caught = false;
|
||||
try {
|
||||
void* unexpected = resource->allocate(128 * 1024);
|
||||
resource->deallocate(unexpected, 128 * 1024);
|
||||
}
|
||||
catch (const std::bad_alloc&) {
|
||||
direct_failure_caught = true;
|
||||
}
|
||||
callback_state.fail.store(false, std::memory_order_relaxed);
|
||||
std::pmr::vector<int> values(resource);
|
||||
callback_state.fail.store(true, std::memory_order_relaxed);
|
||||
bool container_failure_caught = false;
|
||||
try {
|
||||
values.resize(1024 * 1024);
|
||||
}
|
||||
catch (const std::bad_alloc&) {
|
||||
container_failure_caught = true;
|
||||
}
|
||||
callback_state.fail.store(false, std::memory_order_relaxed);
|
||||
{
|
||||
renderive::Frame_Arena arena(64 * 1024);
|
||||
std::pmr::vector<double> scratch(arena.resource());
|
||||
scratch.resize(16 * 1024);
|
||||
}
|
||||
callback_state.fail.store(true, std::memory_order_relaxed);
|
||||
bool frame_failure_caught = false;
|
||||
try {
|
||||
renderive::Frame_Arena arena(1024 * 1024);
|
||||
std::pmr::vector<double> scratch(arena.resource());
|
||||
scratch.resize(1024 * 1024);
|
||||
}
|
||||
catch (const std::bad_alloc&) {
|
||||
frame_failure_caught = true;
|
||||
}
|
||||
bool ring_buffer_failure_caught = false;
|
||||
try {
|
||||
Psc::RingBuffer_ST buffer(renderive::memory_resource());
|
||||
buffer.init(1024 * 1024);
|
||||
}
|
||||
catch (const std::bad_alloc&) {
|
||||
ring_buffer_failure_caught = true;
|
||||
}
|
||||
return direct_failure_caught && container_failure_caught && frame_failure_caught && ring_buffer_failure_caught ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#include <stdexcept>
|
||||
#include "../Renderive/architecture/Plot.h"
|
||||
#include "../Renderive/base/Memory.h"
|
||||
namespace {
|
||||
void* callback_allocate(void*, std::size_t size, std::size_t alignment) {
|
||||
return std::pmr::new_delete_resource()->allocate(size, alignment);
|
||||
}
|
||||
void callback_deallocate(void*, void* address, std::size_t size, std::size_t alignment) {
|
||||
std::pmr::new_delete_resource()->deallocate(address, size, alignment);
|
||||
}
|
||||
}
|
||||
int main() {
|
||||
renderive::start_render_scheduler();
|
||||
renderive::Memory_Callbacks callbacks;
|
||||
callbacks.allocate = callback_allocate;
|
||||
callbacks.deallocate = callback_deallocate;
|
||||
try {
|
||||
renderive::set_memory_callbacks(callbacks);
|
||||
}
|
||||
catch (const std::logic_error&) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#include <QApplication>
|
||||
#include <stdexcept>
|
||||
#include "../Renderive/architecture/Plot.h"
|
||||
#include "../Renderive/base/Memory.h"
|
||||
namespace {
|
||||
void* callback_allocate(void*, std::size_t size, std::size_t alignment) {
|
||||
return std::pmr::new_delete_resource()->allocate(size, alignment);
|
||||
}
|
||||
void callback_deallocate(void*, void* address, std::size_t size, std::size_t alignment) {
|
||||
std::pmr::new_delete_resource()->deallocate(address, size, alignment);
|
||||
}
|
||||
}
|
||||
int main(int argc, char** argv) {
|
||||
QApplication app(argc, argv);
|
||||
renderive::start_render_scheduler();
|
||||
renderive::Plot plot;
|
||||
plot.init();
|
||||
renderive::Memory_Callbacks callbacks;
|
||||
callbacks.allocate = callback_allocate;
|
||||
callbacks.deallocate = callback_deallocate;
|
||||
try {
|
||||
renderive::set_memory_callbacks(callbacks);
|
||||
}
|
||||
catch (const std::logic_error&) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
#include <QTime>
|
||||
#include <vector>
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#include <Psapi.h>
|
||||
#else
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <memory_resource>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../Renderive/architecture/Renderable.h"
|
||||
namespace {
|
||||
class Counting_Resource final : public std::pmr::memory_resource {
|
||||
public:
|
||||
std::uint64_t allocation_count{};
|
||||
std::uint64_t deallocation_count{};
|
||||
std::uint64_t current_bytes{};
|
||||
std::uint64_t peak_bytes{};
|
||||
private:
|
||||
void* do_allocate(std::size_t size, std::size_t alignment) override {
|
||||
void* result = std::pmr::new_delete_resource()->allocate(size, alignment);
|
||||
++allocation_count;
|
||||
current_bytes += size;
|
||||
peak_bytes = std::max(peak_bytes, current_bytes);
|
||||
return result;
|
||||
}
|
||||
void do_deallocate(void* address, std::size_t size, std::size_t alignment) override {
|
||||
std::pmr::new_delete_resource()->deallocate(address, size, alignment);
|
||||
++deallocation_count;
|
||||
current_bytes -= size;
|
||||
}
|
||||
bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
|
||||
return this == &other;
|
||||
}
|
||||
};
|
||||
struct Benchmark_Waterfall_Row {
|
||||
int tick{};
|
||||
std::vector<double> frequency;
|
||||
};
|
||||
struct Benchmark_Time_Tick {
|
||||
QTime time;
|
||||
int tick{};
|
||||
};
|
||||
struct Benchmark_Result {
|
||||
double renderable_ms{};
|
||||
double waterfall_ms{};
|
||||
double time_audio_ms{};
|
||||
};
|
||||
volatile std::uint64_t benchmark_sink{};
|
||||
template <typename T>
|
||||
std::shared_ptr<T> make_two_allocation_shared(std::pmr::memory_resource* resource) {
|
||||
std::pmr::polymorphic_allocator<T> object_allocator(resource);
|
||||
T* object = object_allocator.allocate(1);
|
||||
try {
|
||||
std::allocator_traits<decltype(object_allocator)>::construct(object_allocator, object);
|
||||
}
|
||||
catch (...) {
|
||||
object_allocator.deallocate(object, 1);
|
||||
throw;
|
||||
}
|
||||
auto deleter = [object_allocator](T* value) mutable {
|
||||
std::allocator_traits<decltype(object_allocator)>::destroy(object_allocator, value);
|
||||
object_allocator.deallocate(value, 1);
|
||||
};
|
||||
return std::shared_ptr<T>(object, std::move(deleter), std::pmr::polymorphic_allocator<T>(resource));
|
||||
}
|
||||
template <typename Handler>
|
||||
double measure_milliseconds(Handler handler) {
|
||||
auto begin = std::chrono::steady_clock::now();
|
||||
handler();
|
||||
auto end = std::chrono::steady_clock::now();
|
||||
return std::chrono::duration<double, std::milli>(end - begin).count();
|
||||
}
|
||||
double benchmark_renderables(std::pmr::memory_resource* resource, bool two_allocations) {
|
||||
return measure_milliseconds([resource, two_allocations]() {
|
||||
std::pmr::vector<std::shared_ptr<renderive::Renderable>> owners(resource);
|
||||
owners.reserve(20000);
|
||||
for (int i = 0; i < 20000; ++i) {
|
||||
if (two_allocations)
|
||||
owners.push_back(make_two_allocation_shared<renderive::Renderable>(resource));
|
||||
else
|
||||
owners.push_back(std::allocate_shared<renderive::Renderable>(std::pmr::polymorphic_allocator<renderive::Renderable>(resource)));
|
||||
}
|
||||
benchmark_sink += owners.size();
|
||||
owners.clear();
|
||||
});
|
||||
}
|
||||
double benchmark_waterfall(std::pmr::memory_resource* resource) {
|
||||
static constexpr std::array<int, 5> frequency_sizes{64, 512, 8192, 8193, 128};
|
||||
static constexpr std::array<int, 5> time_sizes{32, 96, 48, 64, 24};
|
||||
return measure_milliseconds([resource]() {
|
||||
for (int round = 0; round < 32; ++round) {
|
||||
std::pmr::vector<std::uint8_t> write_buffer(resource);
|
||||
std::pmr::vector<std::uint8_t> record_buffer(resource);
|
||||
std::pmr::vector<std::uint8_t> snapshot_buffer(resource);
|
||||
std::pmr::vector<double> row_buffer(resource);
|
||||
std::pmr::vector<Benchmark_Waterfall_Row> pending_rows(resource);
|
||||
for (std::size_t stage = 0; stage < frequency_sizes.size(); ++stage) {
|
||||
std::size_t row_bytes = static_cast<std::size_t>(frequency_sizes[stage]) * sizeof(double);
|
||||
std::size_t record_bytes = row_bytes + sizeof(int);
|
||||
write_buffer.resize(record_bytes);
|
||||
record_buffer.resize(record_bytes);
|
||||
snapshot_buffer.resize(record_bytes * static_cast<std::size_t>(time_sizes[stage]));
|
||||
row_buffer.resize(frequency_sizes[stage]);
|
||||
pending_rows.clear();
|
||||
for (int row = 0; row < 8; ++row) {
|
||||
Benchmark_Waterfall_Row value;
|
||||
value.tick = row;
|
||||
value.frequency.resize(frequency_sizes[stage]);
|
||||
pending_rows.push_back(std::move(value));
|
||||
}
|
||||
write_buffer.front() = static_cast<std::uint8_t>(round);
|
||||
row_buffer.front() = static_cast<double>(round);
|
||||
}
|
||||
benchmark_sink += write_buffer.size() + snapshot_buffer.size() + pending_rows.size();
|
||||
}
|
||||
});
|
||||
}
|
||||
double benchmark_time_audio(std::pmr::memory_resource* resource) {
|
||||
static constexpr std::array<int, 4> point_sizes{32, 256, 4096, 64};
|
||||
return measure_milliseconds([resource]() {
|
||||
for (int round = 0; round < 64; ++round) {
|
||||
std::pmr::vector<QTime> time_snapshot(resource);
|
||||
std::pmr::vector<double> power_snapshot(resource);
|
||||
std::pmr::list<Benchmark_Time_Tick> time_ticks(resource);
|
||||
for (int size : point_sizes) {
|
||||
time_snapshot.resize(size);
|
||||
power_snapshot.resize(size);
|
||||
for (int tick = 0; tick < size; ++tick)
|
||||
time_ticks.push_front({QTime::fromMSecsSinceStartOfDay(tick), tick});
|
||||
while (time_ticks.size() > static_cast<std::size_t>(size))
|
||||
time_ticks.pop_back();
|
||||
if (!power_snapshot.empty())
|
||||
power_snapshot.back() = static_cast<double>(round);
|
||||
}
|
||||
benchmark_sink += time_snapshot.size() + power_snapshot.size() + time_ticks.size();
|
||||
}
|
||||
});
|
||||
}
|
||||
std::uint64_t peak_working_set_bytes() {
|
||||
#ifdef _WIN32
|
||||
PROCESS_MEMORY_COUNTERS_EX counters{};
|
||||
counters.cb = sizeof(counters);
|
||||
GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&counters), sizeof(counters));
|
||||
return counters.PeakWorkingSetSize;
|
||||
#else
|
||||
rusage usage{};
|
||||
getrusage(RUSAGE_SELF, &usage);
|
||||
return static_cast<std::uint64_t>(usage.ru_maxrss) * 1024;
|
||||
#endif
|
||||
}
|
||||
Benchmark_Result run_benchmark(std::pmr::memory_resource* resource, bool two_allocations) {
|
||||
Benchmark_Result result;
|
||||
result.renderable_ms = benchmark_renderables(resource, two_allocations);
|
||||
result.waterfall_ms = benchmark_waterfall(resource);
|
||||
result.time_audio_ms = benchmark_time_audio(resource);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
int main(int argc, char** argv) {
|
||||
if (argc != 2)
|
||||
return 1;
|
||||
std::string name = argv[1];
|
||||
Counting_Resource upstream;
|
||||
std::unique_ptr<std::pmr::synchronized_pool_resource> pool;
|
||||
std::pmr::memory_resource* resource = &upstream;
|
||||
bool two_allocations = false;
|
||||
if (name == "old") {
|
||||
two_allocations = true;
|
||||
}
|
||||
else if (name == "default") {
|
||||
pool = std::make_unique<std::pmr::synchronized_pool_resource>(&upstream);
|
||||
resource = pool.get();
|
||||
}
|
||||
else {
|
||||
std::pmr::pool_options options;
|
||||
if (name == "64x64k") {
|
||||
options.max_blocks_per_chunk = 64;
|
||||
options.largest_required_pool_block = 64 * 1024;
|
||||
}
|
||||
else if (name == "32x32k") {
|
||||
options.max_blocks_per_chunk = 32;
|
||||
options.largest_required_pool_block = 32 * 1024;
|
||||
}
|
||||
else if (name == "64x128k") {
|
||||
options.max_blocks_per_chunk = 64;
|
||||
options.largest_required_pool_block = 128 * 1024;
|
||||
}
|
||||
else {
|
||||
return 2;
|
||||
}
|
||||
pool = std::make_unique<std::pmr::synchronized_pool_resource>(options, &upstream);
|
||||
resource = pool.get();
|
||||
}
|
||||
Benchmark_Result result = run_benchmark(resource, two_allocations);
|
||||
std::cout << name << ',' << result.renderable_ms << ',' << result.waterfall_ms << ',' << result.time_audio_ms << ',' << upstream.allocation_count << ',' << upstream.current_bytes << ',' << upstream.peak_bytes << ',' << peak_working_set_bytes() << '\n';
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#include "../Renderive/architecture/Update_Completion.h"
|
||||
#include "asio.hpp"
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
int main() {
|
||||
asio::io_context io;
|
||||
auto state = renderive::make_update_state();
|
||||
renderive::Update_Ticket ticket(state);
|
||||
std::atomic_int rendered_count{};
|
||||
std::atomic_int input_count{};
|
||||
ticket.async_wait(renderive::Update_Stage::Rendered, asio::bind_executor(io.get_executor(), [&](std::error_code error, renderive::Update_Outcome outcome) {
|
||||
if (error || outcome != renderive::Update_Outcome::Completed)
|
||||
std::terminate();
|
||||
rendered_count.fetch_add(1, std::memory_order_relaxed);
|
||||
}));
|
||||
state->complete_until(renderive::Update_Stage::Input_Released);
|
||||
io.poll();
|
||||
if (rendered_count.load(std::memory_order_relaxed) != 0)
|
||||
return 1;
|
||||
state->complete_until(renderive::Update_Stage::Rendered);
|
||||
io.restart();
|
||||
io.run();
|
||||
if (rendered_count.load(std::memory_order_relaxed) != 1)
|
||||
return 2;
|
||||
ticket.async_wait(renderive::Update_Stage::Input_Released, asio::bind_executor(io.get_executor(), [&](std::error_code error, renderive::Update_Outcome outcome) {
|
||||
if (error || outcome != renderive::Update_Outcome::Completed)
|
||||
std::terminate();
|
||||
input_count.fetch_add(1, std::memory_order_relaxed);
|
||||
}));
|
||||
io.restart();
|
||||
io.run();
|
||||
if (input_count.load(std::memory_order_relaxed) != 1)
|
||||
return 3;
|
||||
auto cancelled_state = renderive::make_update_state();
|
||||
renderive::Update_Ticket cancelled_ticket(cancelled_state);
|
||||
std::atomic_int cancel_count{};
|
||||
cancelled_ticket.async_wait(renderive::Update_Stage::Presented, asio::bind_executor(io.get_executor(), [&](std::error_code error, renderive::Update_Outcome outcome) {
|
||||
if (!error || outcome != renderive::Update_Outcome::Cancelled)
|
||||
std::terminate();
|
||||
cancel_count.fetch_add(1, std::memory_order_relaxed);
|
||||
}));
|
||||
cancelled_state->complete_all(std::make_error_code(std::errc::operation_canceled), renderive::Update_Outcome::Cancelled);
|
||||
io.restart();
|
||||
io.run();
|
||||
return cancel_count.load(std::memory_order_relaxed) == 1 ? 0 : 4;
|
||||
}
|
||||
Reference in New Issue
Block a user