From d943d825549b236a2fce059fc4b26b915ea29f36 Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Wed, 29 Jul 2026 18:08:57 +0800 Subject: [PATCH] =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E5=86=85=E5=AD=98?= =?UTF-8?q?=E5=88=86=E9=85=8D=E6=B1=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Demo_Gallery/Afterglow_Plot.h | 10 +- Demo_Gallery/Audio_Plot.h | 10 +- Demo_Gallery/Demo_Gallery.cpp | 23 +- Demo_Gallery/Demo_Gallery.h | 4 +- Demo_Gallery/Normal_test.h | 76 ++-- Demo_Gallery/Planisphere_Plot.h | 8 +- Demo_Gallery/Spectrum_Plot.h | 10 +- Demo_Gallery/Sweep_Frequent_Plot.h | 10 +- Demo_Gallery/Waterfall_Plot.h | 10 +- Renderive/Axis/Abs_Axis.cpp | 46 +-- Renderive/Axis/Abs_Axis.h | 12 +- Renderive/Axis/Abs_Axis_p.h | 22 +- Renderive/Axis/Axis.cpp | 9 +- Renderive/Axis/Axis.h | 6 +- Renderive/Axis/Frequency_Axis.cpp | 9 +- Renderive/Axis/Frequency_Axis.h | 6 +- Renderive/Axis/Frequency_Axis_p.h | 8 +- Renderive/Axis/Time_Axis.cpp | 9 +- Renderive/Axis/Time_Axis.h | 6 +- Renderive/Axis/Time_Axis_p.h | 31 +- Renderive/Generate_Mock_Data.cpp | 17 +- Renderive/Generate_Mock_Data.h | 9 +- Renderive/algorithm.hpp | 4 +- Renderive/architecture/Bounded_Input_Buffer.h | 87 +++- Renderive/architecture/Plot.cpp | 378 +++++++++++++++--- Renderive/architecture/Plot.h | 6 +- Renderive/architecture/Plot_p.h | 2 + Renderive/architecture/Render_Data.cpp | 35 ++ Renderive/architecture/Render_Data.h | 94 ++++- Renderive/architecture/Render_Lease.h | 15 +- Renderive/architecture/Render_Pipeline.cpp | 2 + Renderive/architecture/Render_Utils.cpp | 4 +- Renderive/architecture/Renderable.cpp | 85 ++-- Renderive/architecture/Renderable_Core.h | 60 ++- Renderive/architecture/Update_Completion.cpp | 115 ++++++ Renderive/architecture/Update_Completion.h | 175 ++++++++ Renderive/architecture/global.h | 6 +- Renderive/base/Frame_Memory.cpp | 136 +++++++ Renderive/base/Frame_Memory.h | 34 ++ Renderive/base/Memory.cpp | 204 ++++++++++ Renderive/base/Memory.h | 65 +++ Renderive/base/Pmr_QImage_Buffer.h | 188 +++++++++ Renderive/base/Task.h | 168 ++++++++ Renderive/base/Tool.h | 171 ++++---- Renderive/base/global.cpp | 32 +- Renderive/base/global.h | 9 +- Renderive/export.h | 1 + Renderive/plottable/Afterglow.cpp | 35 +- Renderive/plottable/Afterglow.h | 24 +- Renderive/plottable/Afterglow_p.h | 54 ++- Renderive/plottable/Audio_Frequency.cpp | 9 +- Renderive/plottable/Audio_Frequency.h | 6 +- Renderive/plottable/Audio_Frequency_p.h | 9 +- Renderive/plottable/Hover_Info.cpp | 4 +- Renderive/plottable/Hover_Info.h | 2 +- Renderive/plottable/Interpolation_p.h | 8 +- Renderive/plottable/Multi_Select_Rect.cpp | 9 +- Renderive/plottable/Multi_Select_Rect.h | 6 +- Renderive/plottable/Multi_Select_Rect_p.h | 22 +- Renderive/plottable/Performance_Shower.cpp | 134 ++++++- Renderive/plottable/Performance_Shower_p.h | 163 +++++--- Renderive/plottable/Planisphere.cpp | 9 +- Renderive/plottable/Planisphere.h | 6 +- Renderive/plottable/Planisphere_p.h | 18 +- Renderive/plottable/Spectrum.cpp | 221 +++++----- Renderive/plottable/Spectrum.h | 29 +- Renderive/plottable/Spectrum_p.h | 70 +++- Renderive/plottable/Sweep_Frequency.cpp | 22 +- Renderive/plottable/Sweep_Frequency.h | 22 +- Renderive/plottable/Sweep_Frequency_p.h | 14 +- Renderive/plottable/Waterfall.cpp | 75 +++- Renderive/plottable/Waterfall.h | 30 +- Renderive/plottable/Waterfall_p.h | 153 +++++-- Renderive/primitive/Band_Region.cpp | 9 +- Renderive/primitive/Band_Region.h | 6 +- Renderive/primitive/Color_Bar.cpp | 9 +- Renderive/primitive/Color_Bar.h | 6 +- Renderive/primitive/Color_Map.h | 20 +- Renderive/primitive/Curve.cpp | 138 ++++++- Renderive/primitive/Curve.h | 96 +++-- Renderive/primitive/Fill_Area.cpp | 52 ++- Renderive/primitive/Fill_Area.h | 27 +- Renderive/primitive/Grid.cpp | 23 +- Renderive/primitive/Grid.h | 6 +- Renderive/primitive/Label.cpp | 36 +- Renderive/primitive/Label.h | 18 +- Renderive/primitive/Marker.cpp | 36 +- Renderive/primitive/Marker.h | 23 +- Renderive/primitive/Point_Set.cpp | 84 +++- Renderive/primitive/Point_Set.h | 34 +- Renderive/primitive/Raster_Image.cpp | 11 +- Renderive/primitive/Raster_Image.h | 26 +- Widget/Qt/Message.cpp | 15 +- Widget/Qt/Message.h | 6 +- Widget/base/Circular_Linked_List.hpp | 10 +- Widget/base/File_Dialog/File_Dialog.cpp | 9 +- Widget/base/Graphic.h | 6 +- Widget/base/Node.hpp | 67 ++-- Widget/base/Roll_Object.h | 63 ++- Widget/base/Virtual_Keyboard/Input_Core.cpp | 6 +- Widget/base/Virtual_Keyboard/Input_Core.h | 2 +- .../Virtual_Keyboard/Virtual_Keyboard.cpp | 7 +- Widget/base/algorithm.hpp | 4 +- Widget/component/Background.h | 4 +- Widget/component/Base/Delegate.h | 4 +- Widget/component/Base/Layout.cpp | 8 +- Widget/component/Base/Layout.h | 2 +- Widget/component/Base/global.h | 4 +- Widget/component/Button.h | 14 +- Widget/component/Content.h | 4 +- Widget/component/Table/Base_Table_Data.cpp | 6 +- Widget/component/Table/Base_Table_Model.cpp | 52 ++- Widget/component/Table/Base_Table_Model.h | 14 +- Widget/component/Table/Table_View.cpp | 20 +- Widget/component/Tree/Tree_Model.cpp | 82 ++-- Widget/component/Tree/Tree_Model.h | 27 +- Widget/component/Tree/Tree_View.cpp | 6 +- .../component/frameless/Frame_Less_Widget.cpp | 12 +- .../component/frameless/Frame_Less_Widget.h | 8 +- Widget/component/global.h | 14 +- main.cmake | 72 +++- tests/Renderable_Lifecycle_Stress.cpp | 28 +- 122 files changed, 3602 insertions(+), 1215 deletions(-) create mode 100644 Renderive/architecture/Update_Completion.cpp create mode 100644 Renderive/architecture/Update_Completion.h create mode 100644 Renderive/base/Frame_Memory.cpp create mode 100644 Renderive/base/Frame_Memory.h create mode 100644 Renderive/base/Memory.cpp create mode 100644 Renderive/base/Memory.h create mode 100644 Renderive/base/Pmr_QImage_Buffer.h create mode 100644 Renderive/base/Task.h diff --git a/Demo_Gallery/Afterglow_Plot.h b/Demo_Gallery/Afterglow_Plot.h index b80e9cb..2ee1d7a 100644 --- a/Demo_Gallery/Afterglow_Plot.h +++ b/Demo_Gallery/Afterglow_Plot.h @@ -35,11 +35,11 @@ static QWidget* create_afterglow() { } void contextMenuEvent(QContextMenuEvent* event) override { if (!menu) { - auto ws = - bw(cw(ag.get(), -1), "余辉图") + - bw(cw(x_axis.get()), "频率轴") + - bw(cw(y_axis.get()), "功率概率轴") + - bw(cw(msr.get()), "多选框"); + auto ws = concat_widgets( + bw(cw(ag.get(), -1), "余辉图"), + bw(cw(x_axis.get()), "频率轴"), + bw(cw(y_axis.get()), "功率概率轴"), + bw(cw(msr.get()), "多选框")); menu = create_menu(this, ws); } menu->exec(event->globalPos()); diff --git a/Demo_Gallery/Audio_Plot.h b/Demo_Gallery/Audio_Plot.h index bf5a287..014ded0 100644 --- a/Demo_Gallery/Audio_Plot.h +++ b/Demo_Gallery/Audio_Plot.h @@ -34,11 +34,11 @@ static QWidget* create_audio_plot() { } void contextMenuEvent(QContextMenuEvent* event) override { if (!menu) { - auto ws = - bw(cw(audio.get(), -1), "音频图") + - bw(cw(y_axis.get()), "功率y轴") + - bw(cw(x_axis.get()), "时间轴") + - bw(cw(msr.get()), "多选框"); + auto ws = concat_widgets( + bw(cw(audio.get(), -1), "音频图"), + bw(cw(y_axis.get()), "功率y轴"), + bw(cw(x_axis.get()), "时间轴"), + bw(cw(msr.get()), "多选框")); menu = create_menu(this, ws); } menu->exec(event->globalPos()); diff --git a/Demo_Gallery/Demo_Gallery.cpp b/Demo_Gallery/Demo_Gallery.cpp index 15879b7..dadd3d8 100644 --- a/Demo_Gallery/Demo_Gallery.cpp +++ b/Demo_Gallery/Demo_Gallery.cpp @@ -12,10 +12,10 @@ QStandardItem* Demo_Gallery::get_from_data(const T_Data& data) { data_map[data.name] = w; return new QStandardItem(data.name); } -QList Demo_Gallery::get_list(const QVector& datas) { - QList ret; +std::vector Demo_Gallery::get_list(const std::vector& datas) { + std::vector ret; for (const T_Data& data : datas) { - ret.append(get_from_data(data)); + ret.push_back(get_from_data(data)); } return ret; } @@ -71,8 +71,12 @@ Demo_Gallery::Demo_Gallery() : // tree_view->setItemDelegate(new Delegate); tree_view->setHeaderHidden(true); QStandardItem* root = standard_model->invisibleRootItem(); + auto append_rows = [](QStandardItem* parent, const std::vector& rows) { + for (QStandardItem* row : rows) + parent->appendRow(row); + }; auto plottable = new QStandardItem("plottable"); - plottable->appendRows(get_list({ + append_rows(plottable, get_list({ {"余晖图", create_afterglow()}, {"音频图", create_audio_plot()}, {"星座图", create_planisphere_plot()}, @@ -81,7 +85,7 @@ Demo_Gallery::Demo_Gallery() : {"扫频图", create_sweep_frequent_plot()}, })); auto base = new QStandardItem("基础元素"); - base->appendRows(get_list({ + append_rows(base, get_list({ {"轴测试", create_axis_test_plot()}, {"时间轴测试", create_time_axis_test_plot()}, {"频率轴测试", create_frequent_axis_test_plot()}, @@ -91,7 +95,7 @@ Demo_Gallery::Demo_Gallery() : auto t2 = new Test_File_Gather_Widget; auto t3 = new Test_File_Play_Widget; auto custom_widget = new QStandardItem("自定义控件"); - custom_widget->appendRows(get_list({ + append_rows(custom_widget, get_list({ {"Enum_Select", t1}, {"Test_File_Gather_Widget", t2}, {"Test_File_Play_Widget", t3}, @@ -99,7 +103,7 @@ Demo_Gallery::Demo_Gallery() : {"树", create_Tree_View()}, })); auto Qt_raw_widget = new QStandardItem("Qt原生元素"); - Qt_raw_widget->appendRows(get_list({ + append_rows(Qt_raw_widget, get_list({ // QSizeGrip 一个改变大小的注脚 // QDesktopWidget 不知道干嘛的 // QFocusFrame 不知道干嘛的 @@ -138,7 +142,10 @@ Demo_Gallery::Demo_Gallery() : // change_to("表格"); change_to("Test_File_Gather_Widget"); // change_to("树"); - root->appendRows({plottable, base, custom_widget, Qt_raw_widget}); + root->appendRow(plottable); + root->appendRow(base); + root->appendRow(custom_widget); + root->appendRow(Qt_raw_widget); tree_view->setModel(standard_model); tree_view->expandAll(); QObject::connect(tab_widget, &QTabWidget::tabCloseRequested, [this](int index) { diff --git a/Demo_Gallery/Demo_Gallery.h b/Demo_Gallery/Demo_Gallery.h index 1b3efb4..ef52838 100644 --- a/Demo_Gallery/Demo_Gallery.h +++ b/Demo_Gallery/Demo_Gallery.h @@ -34,8 +34,8 @@ public: QString name; QWidget* plot; }; - QMap data_map; + std::map data_map; QStandardItem* get_from_data(const T_Data& data); - QList get_list(const QVector& datas); + std::vector get_list(const std::vector& datas); void change_to(const QString& name); }; diff --git a/Demo_Gallery/Normal_test.h b/Demo_Gallery/Normal_test.h index 78249d8..172f224 100644 --- a/Demo_Gallery/Normal_test.h +++ b/Demo_Gallery/Normal_test.h @@ -3,8 +3,8 @@ #include "../Renderive/base/Tool.h" namespace Psc { // -// static QVector cw(Table_View* view) { -// QVector ret; +// static std::vector cw(Table_View* view) { +// std::vector ret; // ret.append({ // renderive::create_edit_widget("隐藏行", [view, m](const int& value) { // m->row_info_list[value]->hide = true; @@ -24,7 +24,7 @@ namespace Psc { // }, 1), // // -// renderive::create_edit_widget_list("合并单元格", [view, m](const QVector& values) { +// renderive::create_edit_widget_list("合并单元格", [view, m](const std::vector& values) { // int row = values[0]; // int col = values[1]; // int row_span = values[2]; @@ -32,7 +32,7 @@ namespace Psc { // m->merge(row, col, row_span, col_span); // view->update(); // }, {4, 4, 4, 4}), -// renderive::create_edit_widget_list("拆分单元格", [view, m](const QVector& values) { +// renderive::create_edit_widget_list("拆分单元格", [view, m](const std::vector& values) { // int row = values[0]; // int col = values[1]; // int row_span = values[2]; @@ -41,20 +41,20 @@ namespace Psc { // view->update(); // }, {4, 4, 2, 4}), // -// renderive::create_edit_widget_list("添加行", [view, m](const QVector& values) { +// renderive::create_edit_widget_list("添加行", [view, m](const std::vector& values) { // int pos = values[0]; // int num = values[1]; // int col_num = m->col_info_list.size(); -// QVector row_infos; +// std::vector row_infos; // for (int i = 0; i < num; i++) // { // auto info = std::make_shared(10, "asdasdasd"); // row_infos.append(info); // } -// QVector> data; +// std::vector> data; // for (int i = 0; i < num; i++) // { -// QVector row; +// std::vector row; // for (int j = 0; j < col_num; j++) // { // auto cur = std::make_shared(); @@ -68,20 +68,20 @@ namespace Psc { // view->update(); // }, {4, 1}), // -// renderive::create_edit_widget_list("添加列", [view, m](const QVector& values) { +// renderive::create_edit_widget_list("添加列", [view, m](const std::vector& values) { // int pos = values[0]; // int num = values[1]; // int row_num = m->row_info_list.size(); -// QVector col_infos; +// std::vector col_infos; // for (int i = 0; i < num; i++) // { // auto info = std::make_shared(10, "asdasdasd"); // col_infos.append(info); // } -// QVector> data; +// std::vector> data; // for (int i = 0; i < num; i++) // { -// QVector col; +// std::vector col; // for (int j = 0; j < row_num; j++) // { // auto cur = std::make_shared(); @@ -95,7 +95,7 @@ namespace Psc { // view->update(); // }, {5, 1}), // -// renderive::create_edit_widget_list("重新设置所有行列内容", [view, m](const QVector& values) { +// renderive::create_edit_widget_list("重新设置所有行列内容", [view, m](const std::vector& values) { // for (int i = 0; i < m->total_col(); i++) // { // m->col_info_list[i]->content = QString("%1").arg(i); @@ -119,10 +119,10 @@ namespace Psc { // // return ret; // } -static QVector cw(Table_View* view) { - QVector ret; +static std::vector cw(Table_View* view) { + std::vector ret; auto m = dynamic_cast(view->model); - ret.append({ + ret = { renderive::create_edit_widget("隐藏行", [view, m](const int& value) { m->get_row_info(value)->hide = true; view->update(); }, 1), @@ -135,63 +135,63 @@ static QVector cw(Table_View* view) { renderive::create_edit_widget("显示列", [view, m](const int& value) { m->get_col_info(value)->hide = false; view->update(); }, 1), - renderive::create_edit_widget_list("合并单元格", [view, m](const QVector& values) { + renderive::create_edit_widget_list("合并单元格", [view, m](const std::vector& values) { int row = values[0]; int col = values[1]; int row_span = values[2]; int col_span = values[3]; m->merge(row, col, row_span, col_span); view->update(); }, {4, 4, 4, 4}), - renderive::create_edit_widget_list("拆分单元格", [view, m](const QVector& values) { + renderive::create_edit_widget_list("拆分单元格", [view, m](const std::vector& values) { int row = values[0]; int col = values[1]; int row_span = values[2]; int col_span = values[3]; m->split(row, col, row_span, col_span); view->update(); }, {4, 4, 2, 4}), - renderive::create_edit_widget_list("添加行", [view, m](const QVector& values) { + renderive::create_edit_widget_list("添加行", [view, m](const std::vector& values) { int pos = values[0]; int num = values[1]; int col_num = m->total_col(); - QVector row_infos; + std::vector row_infos; for (int i = 0; i < num; i++) { auto info = new Row_Info(10, "asdasdasd"); - row_infos.append(info); + row_infos.push_back(info); } - QVector> data; + std::vector> data; for (int i = 0; i < num; i++) { - QVector row; + std::vector row; for (int j = 0; j < col_num; j++) { auto cur = new Table_Paint_Data(""); cur->content = "2222"; - row.append(cur); + row.push_back(cur); } - data.append(row); + data.push_back(row); } m->insert_row(pos, row_infos, data, Edge_Expand_Type::None); view->update(); }, {4, 1}), - renderive::create_edit_widget_list("添加列", [view, m](const QVector& values) { + renderive::create_edit_widget_list("添加列", [view, m](const std::vector& values) { int pos = values[0]; int num = values[1]; int row_num = m->total_row(); - QVector col_infos; + std::vector col_infos; for (int i = 0; i < num; i++) { auto info = new Col_Info(10, "asdasdasd"); - col_infos.append(info); + col_infos.push_back(info); } - QVector> data; + std::vector> data; for (int i = 0; i < num; i++) { - QVector col; + std::vector col; for (int j = 0; j < row_num; j++) { auto cur = new Table_Paint_Data(""); cur->content = "2222"; - col.append(cur); + col.push_back(cur); } - data.append(col); + data.push_back(col); } m->insert_col(pos, col_infos, data, Edge_Expand_Type::None); view->update(); }, {5, 1}), - renderive::create_edit_widget_list("重新设置所有行列内容", [view, m](const QVector& values) { + renderive::create_edit_widget_list("重新设置所有行列内容", [view, m](const std::vector& values) { for (int i = 0; i < m->total_col(); i++) { m->get_col_info(i)->content = QString("%1").arg(i); } @@ -205,7 +205,7 @@ static QVector cw(Table_View* view) { } } view->update(); }, {}), - }); + }; return ret; } inline Table_View* create_table_view2() { @@ -226,16 +226,16 @@ inline Table_View* create_table_view2() { // int col_num = 15; auto m = new Memory_Table_Model(); ret->model = m; - QVector col_infos; + std::vector col_infos; for (int i = 0; i < col_num; ++i) { col_infos.push_back(new Col_Info(80, QString::number(i))); } - QVector> data; + std::vector> data; data.resize(col_infos.size()); m->insert_col(0, col_infos, data, Edge_Expand_Type::None); for (int i = 0; i < row_num; ++i) { auto row_info = new Row_Info(50, QString::number(i)); - QVector row; + std::vector row; for (int j = 0; j < col_num; ++j) { auto d = new Table_Paint_Data(""); d->parent = d; @@ -271,7 +271,7 @@ inline Table_View* create_table_view() { m->row_info_list.push_back(new Row_Info(50, QString::number(i))); } for (int i = 0; i < col_num; ++i) { - QVector row; + std::vector row; for (int j = 0; j < row_num; ++j) { auto d = new Table_Paint_Data(""); d->parent = d; diff --git a/Demo_Gallery/Planisphere_Plot.h b/Demo_Gallery/Planisphere_Plot.h index f561304..b512ab3 100644 --- a/Demo_Gallery/Planisphere_Plot.h +++ b/Demo_Gallery/Planisphere_Plot.h @@ -37,10 +37,10 @@ static QWidget* create_planisphere_plot() { } void contextMenuEvent(QContextMenuEvent* event) override { if (!menu) { - auto ws = - bw(cw(planisphere.get(), -1), "星座图") + - bw(cw(x_axis.get()), "I轴") + - bw(cw(y_axis.get()), "Q轴"); + auto ws = concat_widgets( + bw(cw(planisphere.get(), -1), "星座图"), + bw(cw(x_axis.get()), "I轴"), + bw(cw(y_axis.get()), "Q轴")); menu = create_menu(this, ws); } menu->exec(event->globalPos()); diff --git a/Demo_Gallery/Spectrum_Plot.h b/Demo_Gallery/Spectrum_Plot.h index e9581df..4e4b235 100644 --- a/Demo_Gallery/Spectrum_Plot.h +++ b/Demo_Gallery/Spectrum_Plot.h @@ -46,11 +46,11 @@ static QWidget* create_spectrun_plot() { } void contextMenuEvent(QContextMenuEvent* event) override { if (!menu) { - auto ws = - bw(cw(sp.get(), -1), "频谱图") + - bw(cw(x_axis.get()), "频率x轴") + - bw(cw(y_axis.get()), "功率y轴") + - bw(cw(msr.get()), "多选框"); + auto ws = concat_widgets( + bw(cw(sp.get(), -1), "频谱图"), + bw(cw(x_axis.get()), "频率x轴"), + bw(cw(y_axis.get()), "功率y轴"), + bw(cw(msr.get()), "多选框")); menu = create_menu(this, ws); } menu->exec(event->globalPos()); diff --git a/Demo_Gallery/Sweep_Frequent_Plot.h b/Demo_Gallery/Sweep_Frequent_Plot.h index 32dffb6..bbd9b69 100644 --- a/Demo_Gallery/Sweep_Frequent_Plot.h +++ b/Demo_Gallery/Sweep_Frequent_Plot.h @@ -39,11 +39,11 @@ static QWidget* create_sweep_frequent_plot() { } void contextMenuEvent(QContextMenuEvent* event) override { if (!menu) { - auto ws = - bw(cw(sf.get(), -1), "扫频图") + - bw(cw(x_axis.get()), "x轴") + - bw(cw(y_axis.get()), "y轴") + - bw(cw(msr.get()), "多选框"); + auto ws = concat_widgets( + bw(cw(sf.get(), -1), "扫频图"), + bw(cw(x_axis.get()), "x轴"), + bw(cw(y_axis.get()), "y轴"), + bw(cw(msr.get()), "多选框")); menu = create_menu(this, ws); } menu->exec(event->globalPos()); diff --git a/Demo_Gallery/Waterfall_Plot.h b/Demo_Gallery/Waterfall_Plot.h index 41db70d..a834e49 100644 --- a/Demo_Gallery/Waterfall_Plot.h +++ b/Demo_Gallery/Waterfall_Plot.h @@ -40,11 +40,11 @@ static QWidget* create_water_fall_plot() { } void contextMenuEvent(QContextMenuEvent* event) override { if (!menu) { - auto ws = - bw(cw(wf.get(), -1), "瀑布图") + - bw(cw(x_axis.get()), "频率轴") + - bw(cw(y_axis.get()), "时间轴") + - bw(cw(msr.get()), "多选框"); + auto ws = concat_widgets( + bw(cw(wf.get(), -1), "瀑布图"), + bw(cw(x_axis.get()), "频率轴"), + bw(cw(y_axis.get()), "时间轴"), + bw(cw(msr.get()), "多选框")); menu = create_menu(this, ws); } menu->exec(event->globalPos()); diff --git a/Renderive/Axis/Abs_Axis.cpp b/Renderive/Axis/Abs_Axis.cpp index a1729db..3b1da22 100644 --- a/Renderive/Axis/Abs_Axis.cpp +++ b/Renderive/Axis/Abs_Axis.cpp @@ -9,13 +9,13 @@ Abs_Axis::Abs_Axis() { Abs_Axis_Private* Abs_Axis::d() { return Abs_Axis_Binding::d(this); } -Render_Data* Abs_Axis::create_render_data() { +Render_Object_Owner Abs_Axis::create_render_data() { return Abs_Axis_Binding::create_render_data(); } -Render_State* Abs_Axis::create_render_state() { +Render_Object_Owner Abs_Axis::create_render_state() { return Abs_Axis_Binding::create_render_state(); } -Input_Data* Abs_Axis::create_input_data() { +Render_Object_Owner Abs_Axis::create_input_data() { return Abs_Axis_Binding::create_input_data(); } int Abs_Axis::x(SRC src) { @@ -108,9 +108,9 @@ double Abs_Axis::pixel_to_coord(double pixel, SRC src) { double Abs_Axis::coord_to_pixel(double coord, SRC src) { return d()->coord_to_pixel(coord, src); } -QVector Abs_Axis_Private::create_tick_vector(double tick_step, const Range& range) { +std::vector Abs_Axis_Private::create_tick_vector(double tick_step, const Range& range) { double tick_origin = render_state()->coord_start; - QVector result; + std::vector result; // Generate tick positions according to tick_step: // do not use qFloor here, or we'll lose 64 bit precision // qDebug() << "tick_step:" << tick_step; @@ -131,25 +131,25 @@ QVector Abs_Axis_Private::create_tick_vector(double tick_step, const Ran // } return result; } -QVector Abs_Axis_Private::create_label_vector(const QVector& ticks, SRC src) { - QVector result; +std::vector Abs_Axis_Private::create_label_vector(const std::vector& ticks, SRC src) { + std::vector result; result.reserve(ticks.size()); - foreach(double tick_coord, ticks) - result.append(get_tick_label(tick_coord, src)); + for (double tick_coord : ticks) + result.push_back(get_tick_label(tick_coord, src)); return result; } -QVector Abs_Axis_Private::create_sub_tick_vector(int sub_tick_count, const QVector& ticks, const Range& range) { - QVector result; +std::vector Abs_Axis_Private::create_sub_tick_vector(int sub_tick_count, const std::vector& ticks, const Range& range) { + std::vector result; if (sub_tick_count <= 0 || ticks.size() < 2) return result; result.reserve((ticks.size() - 1) * sub_tick_count); for (int i = 1; i < ticks.size(); ++i) { double sub_tick_step = (ticks.at(i) - ticks.at(i - 1)) / double(sub_tick_count + 1); for (int k = 1; k <= sub_tick_count; ++k) - result.append(ticks.at(i - 1) + k * sub_tick_step); + result.push_back(ticks.at(i - 1) + k * sub_tick_step); } - while (!result.empty() && !range.contain(result.last())) { - result.removeLast(); + while (!result.empty() && !range.contain(result.back())) { + result.pop_back(); } return result; } @@ -242,17 +242,17 @@ double Abs_Axis_Private::get_mantissa(double input, double* magnitude) { double Abs_Axis_Private::clean_mantissa(double input) { double magnitude; const double mantissa = get_mantissa(input, &magnitude); - return pick_closest(mantissa, QVector() << 1.0 << 2.0 << 2.5 << 5.0 << 10.0) * magnitude; + return pick_closest(mantissa, {1.0, 2.0, 2.5, 5.0, 10.0}) * magnitude; } -double Abs_Axis_Private::pick_closest(double target, const QVector& candidates) { +double Abs_Axis_Private::pick_closest(double target, const std::vector& candidates) { if (candidates.empty()) return target; if (candidates.size() == 1) - return candidates.first(); - QVector::const_iterator it = std::lower_bound(candidates.constBegin(), candidates.constEnd(), target); - if (it == candidates.constEnd()) + return candidates.front(); + std::vector::const_iterator it = std::lower_bound(candidates.cbegin(), candidates.cend(), target); + if (it == candidates.cend()) return *(it - 1); - else if (it == candidates.constBegin()) + else if (it == candidates.cbegin()) return *it; else return target - *(it - 1) < *it - target ? *(it - 1) : *it; @@ -293,13 +293,13 @@ int Abs_Axis::get_sub_tick_count(double tick_step) { QString Abs_Axis::get_tick_label(double tick, SRC src) { return d()->get_tick_label(tick, src); } -QVector Abs_Axis::create_label_vector(const QVector& ticks, SRC src) { +std::vector Abs_Axis::create_label_vector(const std::vector& ticks, SRC src) { return d()->create_label_vector(ticks, src); } -QVector Abs_Axis::create_tick_vector(double tick_step, const Range& range) { +std::vector Abs_Axis::create_tick_vector(double tick_step, const Range& range) { return d()->create_tick_vector(tick_step, range); } -QVector Abs_Axis::create_sub_tick_vector(int sub_tick_count, const QVector& ticks, const Range& range) { +std::vector Abs_Axis::create_sub_tick_vector(int sub_tick_count, const std::vector& ticks, const Range& range) { return d()->create_sub_tick_vector(sub_tick_count, ticks, range); } } // namespace renderive diff --git a/Renderive/Axis/Abs_Axis.h b/Renderive/Axis/Abs_Axis.h index 51c0db3..687a97d 100644 --- a/Renderive/Axis/Abs_Axis.h +++ b/Renderive/Axis/Abs_Axis.h @@ -24,9 +24,9 @@ class LIB_DECL Abs_Axis : public Renderable { public: /// 返回私有渲染数据。 Abs_Axis_Private* d(); - Render_Data* create_render_data() override; - Render_State* create_render_state() override; - Input_Data* create_input_data() override; + Render_Object_Owner create_render_data() override; + Render_Object_Owner create_render_state() override; + Render_Object_Owner create_input_data() override; Abs_Axis(); PROP(int, x) PROP(int, y) @@ -54,11 +54,11 @@ public: /// 生成指定刻度的显示文本。 virtual QString get_tick_label(double tick, SRC); /// 生成主刻度向量。 - virtual QVector create_tick_vector(double tick_step, const Range& range); + virtual std::vector create_tick_vector(double tick_step, const Range& range); /// 生成子刻度向量。 - virtual QVector create_sub_tick_vector(int sub_tick_count, const QVector& ticks, const Range& range); + virtual std::vector create_sub_tick_vector(int sub_tick_count, const std::vector& ticks, const Range& range); /// 生成刻度文本向量。 - virtual QVector create_label_vector(const QVector& ticks, SRC); + virtual std::vector create_label_vector(const std::vector& ticks, SRC); /// 返回轴起点坐标。 double start_coord(SRC = SRC::Edit); /// 返回轴终点坐标。 diff --git a/Renderive/Axis/Abs_Axis_p.h b/Renderive/Axis/Abs_Axis_p.h index a08dc01..a0f73b5 100644 --- a/Renderive/Axis/Abs_Axis_p.h +++ b/Renderive/Axis/Abs_Axis_p.h @@ -19,7 +19,7 @@ struct Abs_Axis_Private : Typed_Render_Data pixel_start{}; double get_mantissa(double input, double* magnitude = nullptr); [[nodiscard]] double clean_mantissa(double input); - [[nodiscard]] double pick_closest(double target, const QVector& candidates); + [[nodiscard]] double pick_closest(double target, const std::vector& candidates); void prepare_data(const Plot_Render_Snapshot&) override { sync_state_pipeline(); } @@ -29,15 +29,15 @@ struct Abs_Axis_Private : Typed_Render_Datacoord_start, s->coord_start + s->coord_length}; Abs_Axis* q = this->q(); double tick_step = q->get_tick_step(range); - QVector ticks = q->create_tick_vector(tick_step, range); + std::vector ticks = q->create_tick_vector(tick_step, range); int sub_tick_count = q->get_sub_tick_count(tick_step); - QVector sub_ticks = q->create_sub_tick_vector(sub_tick_count, ticks, range); - QVector labels = q->create_label_vector(ticks, SRC::Render); - while (!ticks.empty() && !range.contain(ticks.last())) { - ticks.removeLast(); + std::vector sub_ticks = q->create_sub_tick_vector(sub_tick_count, ticks, range); + std::vector labels = q->create_label_vector(ticks, SRC::Render); + while (!ticks.empty() && !range.contain(ticks.back())) { + ticks.pop_back(); } - while (!sub_ticks.empty() && !range.contain(sub_ticks.last())) { - sub_ticks.removeLast(); + while (!sub_ticks.empty() && !range.contain(sub_ticks.back())) { + sub_ticks.pop_back(); } QFontMetrics fm(painter->font()); int h = fm.height(); @@ -118,10 +118,10 @@ struct Abs_Axis_Private : Typed_Render_Data create_tick_vector(double tick_step, const Range& range); - virtual QVector create_sub_tick_vector(int sub_tick_count, const QVector& ticks, const Range& range); + virtual std::vector create_tick_vector(double tick_step, const Range& range); + virtual std::vector create_sub_tick_vector(int sub_tick_count, const std::vector& ticks, const Range& range); virtual QString get_tick_label(double tick, SRC); - virtual QVector create_label_vector(const QVector& ticks, SRC); + virtual std::vector create_label_vector(const std::vector& ticks, SRC); }; template void Abs_Axis::Builder_T::set(Abs_Axis* ret) { diff --git a/Renderive/Axis/Axis.cpp b/Renderive/Axis/Axis.cpp index 639c052..1188d5b 100644 --- a/Renderive/Axis/Axis.cpp +++ b/Renderive/Axis/Axis.cpp @@ -1,4 +1,5 @@ #include "Axis_p.h" +#include "../base/Memory.h" namespace renderive { using Axis_Binding = Renderable_Binding; Axis::Axis() { @@ -7,13 +8,13 @@ Axis::Axis() { Axis_Private* Axis::d() { return Axis_Binding::d(this); } -Render_Data* Axis::create_render_data() { +Render_Object_Owner Axis::create_render_data() { return Axis_Binding::create_render_data(); } -Render_State* Axis::create_render_state() { +Render_Object_Owner Axis::create_render_state() { return Axis_Binding::create_render_state(); } -Input_Data* Axis::create_input_data() { +Render_Object_Owner Axis::create_input_data() { return Axis_Binding::create_input_data(); } int Axis::number_precision(SRC src) { @@ -49,7 +50,7 @@ Axis::Builder::Builder(std::shared_ptr parent, Qt::Orientation orien this->orientation = orientation; } std::shared_ptr Axis::Builder::build() { - std::shared_ptr < Axis > ret(new Axis()); + auto ret = renderive::make_shared(); Axis::Builder_T::set(ret.get()); return ret; } diff --git a/Renderive/Axis/Axis.h b/Renderive/Axis/Axis.h index 46edef7..f2deb71 100644 --- a/Renderive/Axis/Axis.h +++ b/Renderive/Axis/Axis.h @@ -10,9 +10,9 @@ class LIB_DECL Axis : public Abs_Axis { public: /// 返回私有渲染数据。 Axis_Private* d(); - Render_Data* create_render_data() override; - Render_State* create_render_state() override; - Input_Data* create_input_data() override; + Render_Object_Owner create_render_data() override; + Render_Object_Owner create_render_state() override; + Render_Object_Owner create_input_data() override; Axis(); PROP(int, number_precision) PROP(double, coord_start) diff --git a/Renderive/Axis/Frequency_Axis.cpp b/Renderive/Axis/Frequency_Axis.cpp index ce22f57..d4ab8c1 100644 --- a/Renderive/Axis/Frequency_Axis.cpp +++ b/Renderive/Axis/Frequency_Axis.cpp @@ -1,5 +1,6 @@ #include "Axis_p.h" #include "Frequency_Axis_p.h" +#include "../base/Memory.h" namespace renderive { using Frequency_Axis_Binding = Renderable_Binding; Frequency_Axis::Frequency_Axis() { @@ -8,13 +9,13 @@ Frequency_Axis::Frequency_Axis() { Frequency_Axis_Private* Frequency_Axis::d() { return Frequency_Axis_Binding::d(this); } -Render_Data* Frequency_Axis::create_render_data() { +Render_Object_Owner Frequency_Axis::create_render_data() { return Frequency_Axis_Binding::create_render_data(); } -Render_State* Frequency_Axis::create_render_state() { +Render_Object_Owner Frequency_Axis::create_render_state() { return Frequency_Axis_Binding::create_render_state(); } -Input_Data* Frequency_Axis::create_input_data() { +Render_Object_Owner Frequency_Axis::create_input_data() { return Frequency_Axis_Binding::create_input_data(); } Frequency_Axis::Builder::Builder(std::shared_ptr parent, Qt::Orientation orientation) { @@ -23,7 +24,7 @@ Frequency_Axis::Builder::Builder(std::shared_ptr parent, Qt::Orienta this->orientation = orientation; } std::shared_ptr Frequency_Axis::Builder::build() { - std::shared_ptr < Frequency_Axis > ret(new Frequency_Axis()); + auto ret = renderive::make_shared(); set(ret.get()); return ret; } diff --git a/Renderive/Axis/Frequency_Axis.h b/Renderive/Axis/Frequency_Axis.h index 23a80a5..01c47a2 100644 --- a/Renderive/Axis/Frequency_Axis.h +++ b/Renderive/Axis/Frequency_Axis.h @@ -8,9 +8,9 @@ class LIB_DECL Frequency_Axis : public Axis { public: /// 返回私有渲染数据。 Frequency_Axis_Private* d(); - Render_Data* create_render_data() override; - Render_State* create_render_state() override; - Input_Data* create_input_data() override; + Render_Object_Owner create_render_data() override; + Render_Object_Owner create_render_state() override; + Render_Object_Owner create_input_data() override; Frequency_Axis(); struct Builder; }; diff --git a/Renderive/Axis/Frequency_Axis_p.h b/Renderive/Axis/Frequency_Axis_p.h index 02d088e..3a50e1f 100644 --- a/Renderive/Axis/Frequency_Axis_p.h +++ b/Renderive/Axis/Frequency_Axis_p.h @@ -28,9 +28,9 @@ struct Frequency_Axis_Private : Typed_Render_Data_Base create_label_vector(const QVector& ticks, SRC src) override { + std::vector create_label_vector(const std::vector& ticks, SRC src) override { if (!ticks.empty()) { - double range = ticks.last() - ticks.first(); + double range = ticks.back() - ticks.front(); X_Unit_Type before_unit_type = x_unit_type; if (range >= 1e9) { x_unit_type = GHz; @@ -48,10 +48,10 @@ struct Frequency_Axis_Private : Typed_Render_Data_Baseunit_text = "频率/" + get_x_unit_type_string(x_unit_type); } } - QVector result; + std::vector result; result.reserve(ticks.size()); for (const double& tick_coord : ticks) { - result.append(get_true_value(get_tick_label(tick_coord, src))); + result.push_back(get_true_value(get_tick_label(tick_coord, src))); } return result; } diff --git a/Renderive/Axis/Time_Axis.cpp b/Renderive/Axis/Time_Axis.cpp index fdfc021..475d2f4 100644 --- a/Renderive/Axis/Time_Axis.cpp +++ b/Renderive/Axis/Time_Axis.cpp @@ -1,4 +1,5 @@ #include "Time_Axis_p.h" +#include "../base/Memory.h" namespace renderive { using Time_Axis_Binding = Renderable_Binding; Time_Axis::Time_Axis() { @@ -7,13 +8,13 @@ Time_Axis::Time_Axis() { Time_Axis_Private* Time_Axis::d() { return Time_Axis_Binding::d(this); } -Render_Data* Time_Axis::create_render_data() { +Render_Object_Owner Time_Axis::create_render_data() { return Time_Axis_Binding::create_render_data(); } -Render_State* Time_Axis::create_render_state() { +Render_Object_Owner Time_Axis::create_render_state() { return Time_Axis_Binding::create_render_state(); } -Input_Data* Time_Axis::create_input_data() { +Render_Object_Owner Time_Axis::create_input_data() { return Time_Axis_Binding::create_input_data(); } int Time_Axis::time_point_size(SRC src) { @@ -63,7 +64,7 @@ Time_Axis::Builder::Builder(std::shared_ptr parent, Qt::Orientation this->orientation = orientation; } std::shared_ptr Time_Axis::Builder::build() { - std::shared_ptr < Time_Axis > ret(new Time_Axis()); + auto ret = renderive::make_shared(); set(ret.get()); Time_Axis_Private* pd = ret->d(); Time_Axis_Render_State* sc = reinterpret_cast(pd->state(State_Edit)); diff --git a/Renderive/Axis/Time_Axis.h b/Renderive/Axis/Time_Axis.h index 796a90c..feb8a65 100644 --- a/Renderive/Axis/Time_Axis.h +++ b/Renderive/Axis/Time_Axis.h @@ -9,9 +9,9 @@ class LIB_DECL Time_Axis : public Abs_Axis { public: /// 返回私有渲染数据。 Time_Axis_Private* d(); - Render_Data* create_render_data() override; - Render_State* create_render_state() override; - Input_Data* create_input_data() override; + Render_Object_Owner create_render_data() override; + Render_Object_Owner create_render_state() override; + Render_Object_Owner create_input_data() override; Time_Axis(); PROP(int, time_point_size) PROP(int, tick_pixel_space) diff --git a/Renderive/Axis/Time_Axis_p.h b/Renderive/Axis/Time_Axis_p.h index 3d1763c..9bf8ef9 100644 --- a/Renderive/Axis/Time_Axis_p.h +++ b/Renderive/Axis/Time_Axis_p.h @@ -2,8 +2,11 @@ #include #include #include +#include +#include #include #include "../architecture/Bounded_Input_Buffer.h" +#include "../base/Memory.h" #include "Abs_Axis_p.h" #include "Core/Base/RingBuffer.hpp" #include "Time_Axis.h" @@ -18,9 +21,9 @@ struct Time_Ticker { return lower - 1 + time_point_size; } void set_tick_space(int tick_space) { - if (tick_space == tick_space) + if (this->tick_space == tick_space) return; - tick_space = tick_space; + this->tick_space = tick_space; tick_start = 0; } void set_time_point_size(int value) { @@ -63,8 +66,8 @@ struct Time_Tick { int tick{}; }; struct Time_Axis_Input_Data : Abs_Axis_Input_Data { - static constexpr std::size_t Max_Pending_Time_Count = 32; - static constexpr std::size_t Max_Pending_Tick_Count = 32; + static constexpr std::size_t Max_Pending_Time_Count = 256; + static constexpr std::size_t Max_Pending_Tick_Count = 256; Bounded_Input_Buffer data; Bounded_Input_Buffer all_time; void push_time(const QTime& time) { @@ -129,10 +132,10 @@ struct Time_Axis_Private : Typed_Render_Data_Base time_snapshot; + Psc::StreamRingBuffer_ST buffer{memory_resource(Memory_Domain::Axis)}; + std::pmr::vector time_snapshot{memory_resource(Memory_Domain::Axis)}; int time_data_count{}; - std::list data; + std::pmr::list data{memory_resource(Memory_Domain::Axis)}; void resize_time_buffer(int count) { buffer.init(sizeof(QTime) * count); time_snapshot.resize(count); @@ -222,27 +225,27 @@ struct Time_Axis_Private : Typed_Render_Data_Basetime_format)); + ticks.push_back(t.tick); + labels.push_back(t.time.toString(s->time_format)); ++i; } state_control.unmark_use(state_lease); input_control.unmark_use(input_lease); } - QVector create_label_vector(const QVector& ticks, SRC src) override { + std::vector create_label_vector(const std::vector& ticks, SRC src) override { return labels; } - QVector create_tick_vector(double tick_step, const Range& range) override { + std::vector create_tick_vector(double tick_step, const Range& range) override { return ticks; } - QVector create_sub_tick_vector(int sub_tick_count, const QVector& ticks, const Range& range) override { + std::vector create_sub_tick_vector(int sub_tick_count, const std::vector& ticks, const Range& range) override { return {}; } // 最小值的范围 lower在这里面循环变化 int lower = 0, lower_min = 0, lower_max{}; // 闭区间 int tick_start = 0; - QVector labels; - QVector ticks; + std::vector labels; + std::vector ticks; void draw(QPainter* painter, const Plot_Render_Snapshot& snapshot) override { Abs_Axis_Private::draw(painter, snapshot); } diff --git a/Renderive/Generate_Mock_Data.cpp b/Renderive/Generate_Mock_Data.cpp index 2ef324a..56a8230 100644 --- a/Renderive/Generate_Mock_Data.cpp +++ b/Renderive/Generate_Mock_Data.cpp @@ -3,15 +3,18 @@ namespace renderive { std::random_device rd; std::default_random_engine eng(rd()); -QVector get_data(Range value_range, int size) { +void fill_data(Range value_range, std::span output) { if (value_range.origin > value_range.target) { std::swap(value_range.origin, value_range.target); } std::uniform_real_distribution dist(value_range.origin, value_range.target); - QVector ret(size); - for (auto& value : ret) { + for (auto& value : output) { value = dist(eng); } +} +std::vector get_data(Range value_range, int size) { + std::vector ret(size); + fill_data(value_range, ret); return ret; } double get_data(Range value_range) { @@ -21,15 +24,15 @@ double get_data(Range value_range) { std::uniform_real_distribution dist(value_range.origin, value_range.target); return dist(eng); } -QVector get_data(int value, int size) { - QVector ret(size); +std::vector get_data(int value, int size) { + std::vector ret(size); for (auto& v : ret) { v = value; } return ret; } -QVector get_colored_data(int size) { - QVector ret(size); +std::vector get_colored_data(int size) { + std::vector ret(size); for (int i = 0; i < size; ++i) { ret[i] = i; } diff --git a/Renderive/Generate_Mock_Data.h b/Renderive/Generate_Mock_Data.h index cee6e58..27bc4f6 100644 --- a/Renderive/Generate_Mock_Data.h +++ b/Renderive/Generate_Mock_Data.h @@ -1,8 +1,11 @@ #pragma once +#include +#include #include "architecture/global.h" namespace renderive { -QVector LIB_DECL get_data(Range value_range, int size); +void LIB_DECL fill_data(Range value_range, std::span output); +std::vector LIB_DECL get_data(Range value_range, int size); double LIB_DECL get_data(Range value_range); -QVector LIB_DECL get_data(int value, int size); -QVector LIB_DECL get_colored_data(int size); +std::vector LIB_DECL get_data(int value, int size); +std::vector LIB_DECL get_colored_data(int size); } // namespace renderive diff --git a/Renderive/algorithm.hpp b/Renderive/algorithm.hpp index bfb7f5a..7dac197 100644 --- a/Renderive/algorithm.hpp +++ b/Renderive/algorithm.hpp @@ -44,8 +44,8 @@ int binary_search(double value, T** spaces, int space_size, std::function& data, bool& ok) { - if (data.isEmpty()) { +static int binary_search(double value, const std::vector& data, bool& ok) { + if (data.empty()) { ok = false; return -1; // 返回 -1 表示数组为空 } diff --git a/Renderive/architecture/Bounded_Input_Buffer.h b/Renderive/architecture/Bounded_Input_Buffer.h index 662aa92..0025f35 100644 --- a/Renderive/architecture/Bounded_Input_Buffer.h +++ b/Renderive/architecture/Bounded_Input_Buffer.h @@ -1,10 +1,15 @@ #pragma once -#include #include +#include #include #include #include +#include +#include +#include +#include #include +#include "../base/Memory.h" #include "Core/Base/RingBuffer.hpp" namespace renderive { class Bounded_Input_Buffer { @@ -87,49 +92,91 @@ private: std::atomic rebuild_dropped_count{}; std::atomic consumer_dropped_count{}; std::atomic pushed_count{}; - Psc::RingBuffer_ST buffer; - std::vector read_buffer; + Psc::RingBuffer_ST buffer{memory_resource(Memory_Domain::InputBuffer)}; + std::pmr::vector read_buffer{memory_resource(Memory_Domain::InputBuffer)}; }; class Bounded_Vector_Double_Input_Buffer { public: - void push(const QVector& data, std::size_t max_packet_count) { - buffer.push(data.constData(), static_cast(data.size()) * sizeof(double), max_packet_count); + void push(std::span data, std::size_t max_packet_count) { + prepare_push(max_packet_count); + std::pmr::vector& row = row_at(row_count); + row.assign(data.begin(), data.end()); + ++row_count; + pending_count_value.store(row_count, std::memory_order_relaxed); + pushed_count_value.fetch_add(1, std::memory_order_relaxed); + } + void push(std::pmr::vector&& data, std::size_t max_packet_count) { + prepare_push(max_packet_count); + rows[row_count].emplace(std::move(data)); + ++row_count; + pending_count_value.store(row_count, std::memory_order_relaxed); + pushed_count_value.fetch_add(1, std::memory_order_relaxed); } template void read_all(Handler handler) { - buffer.read_all([this, &handler](const void* data, std::size_t size) { - vector_buffer.resize(static_cast(size / sizeof(double))); - std::memcpy(vector_buffer.data(), data, size); - handler(vector_buffer); - }); + for (std::size_t i = 0; i < row_count; ++i) + handler(*rows[i]); + row_count = 0; + pending_count_value.store(0, std::memory_order_relaxed); } void clear() { - buffer.clear(); + row_count = 0; + pending_count_value.store(0, std::memory_order_relaxed); } [[nodiscard]] std::uint64_t pending_count() const { - return buffer.get_pending_count(); + return pending_count_value.load(std::memory_order_relaxed); } [[nodiscard]] std::uint64_t dropped_count() const { - return buffer.get_dropped_count(); + return dropped_count_value.load(std::memory_order_relaxed); } [[nodiscard]] std::uint64_t capacity_dropped_count() const { - return buffer.get_capacity_dropped_count(); + return capacity_dropped_value.load(std::memory_order_relaxed); } [[nodiscard]] std::uint64_t rebuild_dropped_count() const { - return buffer.get_rebuild_dropped_count(); + return 0; } [[nodiscard]] std::uint64_t consumer_dropped_count() const { - return buffer.get_consumer_dropped_count(); + return consumer_dropped_value.load(std::memory_order_relaxed); } [[nodiscard]] std::uint64_t pushed_count() const { - return buffer.get_pushed_count(); + return pushed_count_value.load(std::memory_order_relaxed); } void record_consumer_dropped() { - buffer.record_consumer_dropped(); + dropped_count_value.fetch_add(1, std::memory_order_relaxed); + consumer_dropped_value.fetch_add(1, std::memory_order_relaxed); } private: - Bounded_Input_Buffer buffer; - QVector vector_buffer; + static constexpr std::size_t Storage_Count = 64; + std::pmr::vector& row_at(std::size_t index) { + if (!rows[index]) + rows[index].emplace(memory_resource(Memory_Domain::InputBuffer)); + return *rows[index]; + } + void prepare_push(std::size_t max_packet_count) { + max_packet_count = std::clamp(max_packet_count, 1, Storage_Count); + while (row_count >= max_packet_count) + drop_front(); + } + void drop_front() { + if (!row_count) + return; + rows[0].reset(); + for (std::size_t i = 1; i < row_count; ++i) { + rows[i - 1].emplace(std::move(*rows[i])); + rows[i].reset(); + } + --row_count; + pending_count_value.store(row_count, std::memory_order_relaxed); + dropped_count_value.fetch_add(1, std::memory_order_relaxed); + capacity_dropped_value.fetch_add(1, std::memory_order_relaxed); + } + std::array>, Storage_Count> rows; + std::size_t row_count{}; + std::atomic pending_count_value{}; + std::atomic dropped_count_value{}; + std::atomic capacity_dropped_value{}; + std::atomic consumer_dropped_value{}; + std::atomic pushed_count_value{}; }; class Bounded_Double_Input_Buffer { public: diff --git a/Renderive/architecture/Plot.cpp b/Renderive/architecture/Plot.cpp index aad0d1d..dc216f6 100644 --- a/Renderive/architecture/Plot.cpp +++ b/Renderive/architecture/Plot.cpp @@ -1,9 +1,15 @@ #include #include #include +#include "../base/Frame_Memory.h" +#include "../base/Memory.h" +#include "../base/Pmr_QImage_Buffer.h" +#include "../base/Task.h" #include "../base/global.h" +#include "../Axis/Time_Axis.h" #include "../plottable/Performance_Shower_p.h" #include "Plot_p.h" +#include "Update_Completion.h" namespace renderive { static std::uint64_t steady_now_ns() { return static_cast(std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count()); @@ -27,6 +33,9 @@ static QString renderable_name(Renderable* able) { return able->object_name; return QString::number(reinterpret_cast(able), 16); } +static int renderable_prepare_priority(const std::shared_ptr& able) { + return dynamic_cast(able.get()) ? 0 : 1; +} static std::shared_ptr performance_shower_owner(Plot_Private* data) { if (!data || !data->q) return {}; @@ -125,11 +134,67 @@ static void publish_renderable_buffer_stats(Performance_Shower* shower, Renderab publish_triple_buffer_stats(shower, name + ".State", able->d_ptr->state_buffer_stats); publish_triple_buffer_stats(shower, name + ".Input", able->d_ptr->input_buffer_stats); } +static std::size_t estimate_frame_memory(const std::vector>& snapshot, const Plot_Render_Snapshot& frame_snapshot) { + std::size_t bytes = 64 * 1024; + for (const auto& able : snapshot) { + if (able && able->d_ptr) + bytes += able->d_ptr->estimated_frame_memory(frame_snapshot); + } + return bytes; +} +static void publish_memory_domain_stats(Performance_Shower* shower, Memory_Domain domain, const QString& name) { + Memory_Domain_Stats stats = memory_domain_stats(domain); + shower->set_gauge("MemoryDomain", name + ".allocationCount", static_cast(stats.allocation_count)); + shower->set_gauge("MemoryDomain", name + ".deallocationCount", static_cast(stats.deallocation_count)); + shower->set_gauge("MemoryDomain", name + ".currentBytes", static_cast(stats.current_bytes)); + shower->set_gauge("MemoryDomain", name + ".peakBytes", static_cast(stats.peak_bytes)); +} static void publish_pipeline_stats(Plot_Private* data) { auto shower_owner = performance_shower_owner(data); Performance_Shower* shower = shower_owner.get(); if (!shower || !shower->enabled()) return; + Memory_Stats memory = memory_stats(); + shower->set_gauge("Memory", "upstreamAllocationCount", static_cast(memory.upstream_allocation_count)); + shower->set_gauge("Memory", "upstreamDeallocationCount", static_cast(memory.upstream_deallocation_count)); + shower->set_gauge("Memory", "upstreamCurrentBytes", static_cast(memory.upstream_current_bytes)); + shower->set_gauge("Memory", "upstreamPeakBytes", static_cast(memory.upstream_peak_bytes)); + Frame_Memory_Stats frame_memory = frame_memory_stats(); + shower->set_gauge("FrameArena", "frameCount", static_cast(frame_memory.frame_count)); + shower->set_gauge("FrameArena", "overflowCount", static_cast(frame_memory.overflow_count)); + shower->set_gauge("FrameArena", "overflowBytes", static_cast(frame_memory.overflow_bytes)); + shower->set_gauge("FrameArena", "highWaterBytes", static_cast(frame_memory.high_water_bytes)); + shower->set_gauge("FrameArena", "scratchGrowCount", static_cast(frame_memory.scratch_grow_count)); + shower->set_gauge("FrameArena", "scratchBytes", static_cast(frame_memory.scratch_bytes)); + Task_Stats task_memory = task_stats(); + shower->set_gauge("Task", "sizeofTask", static_cast(task_memory.sizeof_task)); + shower->set_gauge("Task", "inlineSize", static_cast(task_memory.inline_size)); + shower->set_gauge("Task", "totalCount", static_cast(task_memory.total_count)); + shower->set_gauge("Task", "inlineCount", static_cast(task_memory.inline_count)); + shower->set_gauge("Task", "heapCount", static_cast(task_memory.heap_count)); + shower->set_gauge("Task", "heapBytes", static_cast(task_memory.heap_bytes)); + Pmr_QImage_Stats image_memory = pmr_qimage_stats(); + shower->set_gauge("PmrQImage", "resetCount", static_cast(image_memory.reset_count)); + shower->set_gauge("PmrQImage", "allocatedBytes", static_cast(image_memory.allocated_bytes)); + shower->set_gauge("PmrQImage", "detachCount", static_cast(image_memory.detach_count)); + shower->set_gauge("PmrQImage", "detachBytes", static_cast(image_memory.detach_bytes)); + publish_memory_domain_stats(shower, Memory_Domain::Renderable, "Renderable"); + publish_memory_domain_stats(shower, Memory_Domain::InputBuffer, "InputBuffer"); + publish_memory_domain_stats(shower, Memory_Domain::Waterfall, "Waterfall"); + publish_memory_domain_stats(shower, Memory_Domain::Spectrum, "Spectrum"); + publish_memory_domain_stats(shower, Memory_Domain::FrameArena, "FrameArena"); + publish_memory_domain_stats(shower, Memory_Domain::Image, "Image"); + publish_memory_domain_stats(shower, Memory_Domain::Audio, "Audio"); + publish_memory_domain_stats(shower, Memory_Domain::Afterglow, "Afterglow"); + publish_memory_domain_stats(shower, Memory_Domain::Planisphere, "Planisphere"); + publish_memory_domain_stats(shower, Memory_Domain::Curve, "Curve"); + publish_memory_domain_stats(shower, Memory_Domain::PointSet, "PointSet"); + publish_memory_domain_stats(shower, Memory_Domain::Axis, "Axis"); + publish_memory_domain_stats(shower, Memory_Domain::SchedulerTask, "SchedulerTask"); + publish_memory_domain_stats(shower, Memory_Domain::RenderableCacheImage, "RenderableCacheImage"); + publish_memory_domain_stats(shower, Memory_Domain::PlotColorBuffer, "PlotColorBuffer"); + publish_memory_domain_stats(shower, Memory_Domain::UpdateCompletion, "UpdateCompletion"); + publish_memory_domain_stats(shower, Memory_Domain::Other, "Other"); Renderable::Render_Pipeline& pipeline = data->pipeline; shower->set_state("Pipeline", "jobState", job_state_text(pipeline.job_state)); shower->set_state("Pipeline", "render_schedule_mode", plot_schedule_mode_text(data->render_schedule_mode.load(std::memory_order_acquire))); @@ -140,11 +205,75 @@ static void publish_pipeline_stats(Plot_Private* data) { shower->set_gauge("Pipeline", "active_render_tasks", data->active_render_tasks.load(std::memory_order_acquire)); shower->set_gauge("Pipeline", "renderEnabled", data->render_enabled.load(std::memory_order_acquire) ? 1.0 : 0.0); shower->set_gauge("Pipeline", "render_submit_pending", data->render_submit_pending.load(std::memory_order_acquire) ? 1.0 : 0.0); + shower->set_gauge("Pipeline", "render_request_posted", data->render_request_posted.load(std::memory_order_acquire) ? 1.0 : 0.0); shower->set_gauge("Pipeline", "paint_request_pending", pipeline.paint_request_pending.load(std::memory_order_acquire) ? 1.0 : 0.0); shower->set_gauge("Pipeline", "edit_state_version", static_cast(pipeline.edit_state_version.load(std::memory_order_acquire))); shower->set_gauge("Pipeline", "render_state_version", static_cast(pipeline.render_state_version)); publish_triple_buffer_stats(shower, "color_buffer_stats", pipeline.color_buffer_stats); } +static double frame_ratio_percent(double value, double total) { + return total > 0.0 ? value * 100.0 / total : 0.0; +} +static double positive_frame_cost(double value) { + return value > 0.0 ? value : 0.0; +} +struct Frame_Phase_Breakdown { + double data_ms{}; + double painter_ms{}; + double queue_ms{}; + double lock_wait_ms{}; + double measured_ms{}; + double other_ms{}; + double phase_total_ms{}; + double necessary_ms{}; + double unnecessary_ms{}; +}; +static Frame_Phase_Breakdown frame_phase_breakdown(const Frame_Lifecycle_Sample& sample) { + Frame_Phase_Breakdown value; + value.data_ms = sample.prepare_data_ms + sample.image_prepare_ms; + value.painter_ms = sample.painter_draw_ms + sample.painter_end_ms + sample.paint_draw_image_ms; + value.queue_ms = sample.request_to_submit_ms + sample.cpu_queue_wait_ms + sample.update_to_paint_ms; + value.lock_wait_ms = sample.color_wait_ms + sample.paint_consume_ms + sample.publish_color_ms; + value.measured_ms = value.data_ms + value.painter_ms + value.queue_ms + value.lock_wait_ms + sample.stats_log_ms; + value.phase_total_ms = sample.frame_total_ms > value.measured_ms ? sample.frame_total_ms : value.measured_ms; + value.other_ms = positive_frame_cost(value.phase_total_ms - value.measured_ms); + value.necessary_ms = value.data_ms + value.painter_ms; + value.unnecessary_ms = positive_frame_cost(value.phase_total_ms - value.necessary_ms); + return value; +} +static void publish_frame_lifecycle_sample(Performance_Shower* shower, Frame_Lifecycle_Sample& sample) { + std::uint64_t stats_start_ns = steady_now_ns(); + Frame_Phase_Breakdown phase = frame_phase_breakdown(sample); + if (shower && shower->enabled()) { + shower->set_gauge("FrameLife", "totalMs", sample.frame_total_ms); + shower->set_gauge("FrameLife", "phaseTotalMs", phase.phase_total_ms); + shower->set_gauge("FrameLife", "necessaryMs", phase.necessary_ms); + shower->set_gauge("FrameLife", "unnecessaryMs", phase.unnecessary_ms); + shower->set_gauge("FrameLife", "dataMs", phase.data_ms); + shower->set_gauge("FrameLife", "painterMs", phase.painter_ms); + shower->set_gauge("FrameLife", "queueMs", phase.queue_ms); + shower->set_gauge("FrameLife", "lockWaitMs", phase.lock_wait_ms); + shower->set_gauge("FrameLife", "otherMs", phase.other_ms); + shower->set_gauge("FrameLife", "necessaryRatio", frame_ratio_percent(phase.necessary_ms, phase.phase_total_ms)); + shower->set_gauge("FrameLife", "unnecessaryRatio", frame_ratio_percent(phase.unnecessary_ms, phase.phase_total_ms)); + shower->set_gauge("FrameLife", "otherRatio", frame_ratio_percent(phase.other_ms, phase.phase_total_ms)); + shower->set_gauge("FrameLife", "dataRatio", frame_ratio_percent(phase.data_ms, phase.phase_total_ms)); + shower->set_gauge("FrameLife", "painterRatio", frame_ratio_percent(phase.painter_ms, phase.phase_total_ms)); + shower->set_gauge("FrameLife", "queueRatio", frame_ratio_percent(phase.queue_ms, phase.phase_total_ms)); + shower->set_gauge("FrameLife", "lockWaitRatio", frame_ratio_percent(phase.lock_wait_ms, phase.phase_total_ms)); + shower->set_gauge("FrameLife", "renderableCount", static_cast(sample.renderable_count)); + shower->set_gauge("FrameLife", "visibleRenderableCount", static_cast(sample.visible_renderable_count)); + } + sample.stats_log_ms += elapsed_ms(stats_start_ns, steady_now_ns()); + std::uint64_t log_start_ns = steady_now_ns(); + log_frame_lifecycle_sample(sample); + sample.stats_log_ms += elapsed_ms(log_start_ns, steady_now_ns()); + if (shower && shower->enabled()) { + shower->set_gauge("FrameLife", "statsLogMs", sample.stats_log_ms); + Frame_Phase_Breakdown logged_phase = frame_phase_breakdown(sample); + shower->set_gauge("FrameLife", "statsLogRatio", frame_ratio_percent(sample.stats_log_ms, logged_phase.phase_total_ms)); + } +} static void notify_render_task_done(Plot_Private* data) { if (data->active_render_tasks.fetch_sub(1, std::memory_order_acq_rel) == 1) data->render_task_done.notify_all(); @@ -154,7 +283,7 @@ void start_render_scheduler() { } Abs_Plot::Abs_Plot() { d = new Plot_Private(this); - render_context = std::make_shared(); + render_context = renderive::make_shared(); { std::lock_guard lock(render_context->mutex); render_context->background_color = d->background; @@ -170,7 +299,7 @@ void Abs_Plot::init() { init_renderable_tree(); } void Abs_Plot::init_renderable_tree() { - root_renderable = std::make_shared(); + root_renderable = renderive::make_shared(); root_renderable->object_name = "Root_Renderable"; root_renderable->init_root(this); } @@ -196,7 +325,7 @@ void Abs_Plot::remove_renderable(const std::shared_ptr& able) { void Abs_Plot::set_use_performance_shower(bool use) { auto shower = performance_shower_owner(d); if (!shower && use) { - auto created_shower = std::make_shared(); + auto created_shower = renderive::make_shared(); created_shower->init(root_renderable); std::lock_guard lock(render_context->mutex); render_context->performance_shower_value = created_shower; @@ -220,6 +349,8 @@ Abs_Plot::~Abs_Plot() { return d->active_render_tasks.load(std::memory_order_acquire) == 0; }); } + for (auto& color : d->pipeline.color_buffers) + cancel_update_states(color.presented_update_states); { std::lock_guard lock(renderable_mutex); if (root_renderable) @@ -244,7 +375,7 @@ std::shared_ptr Abs_Plot::create_renderable_node(const std::shared_p ASSERT(parent && parent->plot == this, "Abs_Plot create_renderable_node error! parent invalid"); return {}; } - auto able = std::make_shared(); + auto able = renderive::make_shared(); able->object_name = object_name; able->init(parent); return able; @@ -258,13 +389,13 @@ Abs_Plot::Renderable_Snapshot Abs_Plot::renderable_snapshot() const { void Abs_Plot::append_renderable_snapshot(Renderable_Snapshot& snapshot, const std::shared_ptr& able) const { if (!able) return; - snapshot.append(able); - QVector> children = able->children_snapshot(); + snapshot.push_back(able); + std::vector> children = able->children_snapshot(); for (const auto& child : children) append_renderable_snapshot(snapshot, child); } std::shared_ptr Abs_Plot::render_snapshot() const { - auto snapshot = std::make_shared(); + auto snapshot = renderive::make_shared(); snapshot->context = render_context; if (!render_context) return snapshot; @@ -327,32 +458,65 @@ void Abs_Plot::paintEvent(QPaintEvent* event) { Q_UNUSED(event) auto shower_owner = performance_shower_owner(d); Performance_Shower* shower = shower_owner.get(); + std::uint64_t paint_start_ns = steady_now_ns(); Performance_Scope_Timer paint_data("Pipeline", "paintEvent", shower); - std::uint64_t paint_ns = steady_now_ns(); + std::uint64_t paint_ns = paint_start_ns; std::uint64_t update_request_ns = d->last_update_request_ns.exchange(0, std::memory_order_acq_rel); if (update_request_ns && shower) shower->record_duration("Pipeline", "updateToPaint", elapsed_ms(update_request_ns, paint_ns)); QPainter painter(this); auto mode = to_triple_buffer_mode(d->paint_buffer_acquire_mode.load(std::memory_order_acquire)); bool color_lease_acquired = false; + bool has_frame_lifecycle_sample = false; + Frame_Lifecycle_Sample frame_lifecycle_sample; + std::pmr::vector> presented_update_states(memory_resource(Memory_Domain::UpdateCompletion)); { - Performance_Scope_Timer consume_data("Buffer", "Color.consumeReady", shower); + std::uint64_t consume_start_ns = steady_now_ns(); auto color_lease = d->pipeline.consume_ready_color(mode); + std::uint64_t consume_end_ns = steady_now_ns(); + if (shower) + shower->record_duration("Buffer", "Color.consumeReady", elapsed_ms(consume_start_ns, consume_end_ns)); color_lease_acquired = static_cast(color_lease); Renderable::Color_Buffer* color = color_lease.buffer; - if (color && !color->image.isNull()) - painter.drawImage(QPoint(0, 0), color->image); - else + if (color && !color->image.isNull()) { + std::uint64_t color_version = color->version.load(std::memory_order_acquire); + if (Renderable::version_newer(color_version, d->last_painted_color_version)) { + d->last_painted_color_version = color_version; + frame_lifecycle_sample = color->frame_lifecycle_sample; + frame_lifecycle_sample.paint_consume_ms = elapsed_ms(consume_start_ns, consume_end_ns); + has_frame_lifecycle_sample = true; + } + std::uint64_t draw_start_ns = steady_now_ns(); + painter.drawImage(QPoint(0, 0), color->image.image()); + if (has_frame_lifecycle_sample) + frame_lifecycle_sample.paint_draw_image_ms = elapsed_ms(draw_start_ns, steady_now_ns()); + presented_update_states = std::move(color->presented_update_states); + } + else { painter.fillRect(rect(), d->background); + } } painter.end(); + std::uint64_t paint_end_ns = steady_now_ns(); + complete_update_states(presented_update_states, Update_Stage::Presented); if (shower) { + std::uint64_t stats_start_ns = steady_now_ns(); if (color_lease_acquired) shower->increment_counter("Buffer", "Color.frontLeaseSuccess"); else shower->increment_counter("Buffer", "Color.frontLeaseMiss"); shower->increment_counter("Pipeline", "paint"); publish_pipeline_stats(d); + if (has_frame_lifecycle_sample) + frame_lifecycle_sample.stats_log_ms += elapsed_ms(stats_start_ns, steady_now_ns()); + } + if (has_frame_lifecycle_sample) { + frame_lifecycle_sample.paint_ns = paint_ns; + frame_lifecycle_sample.update_to_paint_ms = frame_lifecycle_sample.update_request_ns ? elapsed_ms(frame_lifecycle_sample.update_request_ns, paint_ns) : 0.0; + frame_lifecycle_sample.paint_event_ms = elapsed_ms(paint_start_ns, paint_end_ns); + std::uint64_t frame_start_ns = frame_lifecycle_sample.request_ns ? frame_lifecycle_sample.request_ns : frame_lifecycle_sample.submit_ns; + frame_lifecycle_sample.frame_total_ms = frame_start_ns ? elapsed_ms(frame_start_ns, paint_end_ns) : frame_lifecycle_sample.paint_event_ms; + publish_frame_lifecycle_sample(shower, frame_lifecycle_sample); } } void Abs_Plot::render() { @@ -414,10 +578,15 @@ bool Abs_Plot::submit_render_once() { std::uint64_t submit_ns = steady_now_ns(); Render_Request_Source source = d->submit_source; std::uint64_t request_ns = d->last_request_ns.exchange(0, std::memory_order_acq_rel); + Frame_Lifecycle_Sample frame_lifecycle_sample; + frame_lifecycle_sample.plot_id = reinterpret_cast(this); + frame_lifecycle_sample.request_ns = request_ns; + frame_lifecycle_sample.submit_ns = submit_ns; + frame_lifecycle_sample.request_to_submit_ms = request_ns ? elapsed_ms(request_ns, submit_ns) : 0.0; { auto shower = performance_shower_owner(d); if (request_ns && shower) - shower->record_duration("Pipeline", "request_to_submit", elapsed_ms(request_ns, submit_ns)); + shower->record_duration("Pipeline", "request_to_submit", frame_lifecycle_sample.request_to_submit_ms); } if (first_resize) return false; @@ -433,6 +602,9 @@ bool Abs_Plot::submit_render_once() { if (pipeline.job_state != Renderable::Job_State::Idle) return false; auto snapshot = renderable_snapshot(); + std::stable_sort(snapshot.begin(), snapshot.end(), [](const auto& a, const auto& b) { + return renderable_prepare_priority(a) < renderable_prepare_priority(b); + }); auto root = get_root_renderable(); if (!root) return false; @@ -440,6 +612,8 @@ bool Abs_Plot::submit_render_once() { return false; pipeline.job_state = Renderable::Job_State::Queued; pipeline.render_state_version = pipeline.edit_state_version.load(std::memory_order_acquire); + frame_lifecycle_sample.version = pipeline.render_state_version; + std::pmr::vector> frame_update_states(memory_resource(Memory_Domain::UpdateCompletion)); Performance_Shower* shower = frame_snapshot->performance_shower.get(); if (shower) { shower->increment_counter("Pipeline", "renderFrame"); @@ -447,17 +621,20 @@ bool Abs_Plot::submit_render_once() { publish_pipeline_stats(d); } { - Performance_Scope_Timer prepare_data("Pipeline", "prepare_data", shower); + std::uint64_t prepare_start_ns = steady_now_ns(); for (const auto& cur : snapshot) { if (!cur || cur->get_retiring() || !cur->visible || !cur->d_ptr) continue; if (!renderable_ready_for_submit(cur, source, submit_ns)) continue; if (!cur->prepared) { - { - Performance_Scope_Timer renderable_prepare("Renderable", renderable_name(cur.get()) + ".prepare_data", shower); - cur->prepare_data(*frame_snapshot); - } + std::uint64_t renderable_prepare_start_ns = steady_now_ns(); + cur->prepare_data(*frame_snapshot); + double renderable_prepare_ms = elapsed_ms(renderable_prepare_start_ns, steady_now_ns()); + frame_lifecycle_sample.renderable_prepare_ms += renderable_prepare_ms; + if (shower) + shower->record_duration("Renderable", renderable_name(cur.get()) + ".prepare_data", renderable_prepare_ms); + cur->d_ptr->drain_committed_update_states(frame_update_states); mark_renderable_submitted(cur, submit_ns); publish_renderable_buffer_stats(shower, cur.get()); cur->prepared = true; @@ -467,6 +644,9 @@ bool Abs_Plot::submit_render_once() { first_prepare_data = false; render_context->first_prepare_data.store(false, std::memory_order_release); } + frame_lifecycle_sample.prepare_data_ms = elapsed_ms(prepare_start_ns, steady_now_ns()); + if (shower) + shower->record_duration("Pipeline", "prepare_data", frame_lifecycle_sample.prepare_data_ms); } for (const auto& able : snapshot) { if (able) @@ -474,34 +654,70 @@ bool Abs_Plot::submit_render_once() { } Renderable::Color_Buffer_Lease color_lease; { - Performance_Scope_Timer wait_render_color("Buffer", "Color.waitRenderLease", shower); + std::uint64_t color_wait_start_ns = steady_now_ns(); color_lease = pipeline.wait_render_color(); + frame_lifecycle_sample.color_wait_ms = elapsed_ms(color_wait_start_ns, steady_now_ns()); + if (shower) + shower->record_duration("Buffer", "Color.waitRenderLease", frame_lifecycle_sample.color_wait_ms); } Renderable::Color_Buffer* color = color_lease.buffer; - auto color_lease_owner = std::make_shared(std::move(color_lease)); + auto color_lease_owner = renderive::make_shared(std::move(color_lease)); std::uint64_t version = pipeline.render_state_version; pipeline.job_state = Renderable::Job_State::Running; if (shower) publish_pipeline_stats(d); Plot_Private* private_data = d; std::uint64_t cpu_post_ns = steady_now_ns(); - auto renderable_snapshot_owner = std::make_shared(std::move(snapshot)); + auto renderable_snapshot_owner = renderive::make_shared(std::move(snapshot)); + std::size_t frame_memory_hint = estimate_frame_memory(*renderable_snapshot_owner, *frame_snapshot); Abs_Plot * plot = this; + struct Render_Color_Task_Context { + Plot_Private* private_data{}; + std::shared_ptr color_lease_owner; + Renderable::Color_Buffer* color{}; + std::uint64_t version{}; + std::uint64_t cpu_post_ns{}; + std::shared_ptr root; + std::shared_ptr renderable_snapshot_owner; + std::shared_ptr frame_snapshot; + std::pmr::vector> update_states{memory_resource(Memory_Domain::UpdateCompletion)}; + std::size_t frame_memory_hint{}; + Abs_Plot* plot{}; + Frame_Lifecycle_Sample frame_lifecycle_sample; + }; + auto task_context = renderive::make_shared(); + task_context->private_data = private_data; + task_context->color_lease_owner = color_lease_owner; + task_context->color = color; + task_context->version = version; + task_context->cpu_post_ns = cpu_post_ns; + task_context->root = root; + task_context->renderable_snapshot_owner = renderable_snapshot_owner; + task_context->frame_snapshot = frame_snapshot; + task_context->update_states = std::move(frame_update_states); + task_context->frame_memory_hint = frame_memory_hint; + task_context->plot = plot; + task_context->frame_lifecycle_sample = frame_lifecycle_sample; private_data->active_render_tasks.fetch_add(1, std::memory_order_acq_rel); - scheduler.post_cpu([private_data, color_lease_owner, color, version, cpu_post_ns, root, renderable_snapshot_owner, frame_snapshot, plot]() { - if (frame_snapshot->performance_shower) - frame_snapshot->performance_shower->record_duration("Pipeline", "cpuQueueWait", elapsed_ms(cpu_post_ns, steady_now_ns())); + scheduler.post_cpu([task_context]() { + Plot_Private* private_data = task_context->private_data; + task_context->frame_lifecycle_sample.cpu_queue_wait_ms = elapsed_ms(task_context->cpu_post_ns, steady_now_ns()); + if (task_context->frame_snapshot->performance_shower) + task_context->frame_snapshot->performance_shower->record_duration("Pipeline", "cpuQueueWait", task_context->frame_lifecycle_sample.cpu_queue_wait_ms); if (private_data->destroying.load(std::memory_order_acquire)) { - color_lease_owner->reset(); + cancel_update_states(task_context->update_states); + task_context->color_lease_owner->reset(); notify_render_task_done(private_data); return; } - Abs_Plot::render_color(color, version, root, *renderable_snapshot_owner, *frame_snapshot); - color_lease_owner->reset(); + Frame_Arena frame_arena(task_context->frame_memory_hint); + Abs_Plot::render_color(task_context->color, task_context->version, task_context->root, *task_context->renderable_snapshot_owner, *task_context->frame_snapshot, std::move(task_context->update_states), task_context->frame_lifecycle_sample); + task_context->color_lease_owner->reset(); if (private_data->destroying.load(std::memory_order_acquire)) { notify_render_task_done(private_data); return; } + Abs_Plot* plot = task_context->plot; Global::instance()->render_scheduler().post([plot, private_data]() { if (!private_data->destroying.load(std::memory_order_acquire)) plot->finish_render(); @@ -629,60 +845,85 @@ void Abs_Plot::finish_render() { QRegion update_region; auto shower_owner = performance_shower_owner(d); Performance_Shower* shower = shower_owner.get(); + Renderable::Color_Buffer* ready_color{}; { - Performance_Scope_Timer publish_data("Pipeline", "publish_render_color", shower); + std::uint64_t publish_start_ns = steady_now_ns(); update_region = pipeline.publish_render_color(); + double publish_ms = elapsed_ms(publish_start_ns, steady_now_ns()); + ready_color = pipeline.color_by_role(Color_Ready); + ready_color->frame_lifecycle_sample.publish_color_ms = publish_ms; + if (shower) + shower->record_duration("Pipeline", "publish_render_color", publish_ms); } pipeline.job_state = Renderable::Job_State::Idle; if (shower) publish_pipeline_stats(d); - if (!update_region.isEmpty()) - request_update(update_region); + if (!update_region.isEmpty()) { + std::uint64_t update_request_ns = steady_now_ns(); + ready_color->frame_lifecycle_sample.update_request_ns = update_request_ns; + request_update(update_region, update_request_ns); + } on_render_request(Render_Request_Source::Finish); } -void Abs_Plot::render_color(Renderable::Color_Buffer* color, std::uint64_t version, const std::shared_ptr& root, const Renderable_Snapshot& snapshot, const Plot_Render_Snapshot& render_snapshot_data) { - Performance_Scope_Timer render_data("Pipeline", "render_color", render_snapshot_data.performance_shower.get()); +void Abs_Plot::render_color(Renderable::Color_Buffer* color, std::uint64_t version, const std::shared_ptr& root, const Renderable_Snapshot& snapshot, const Plot_Render_Snapshot& render_snapshot_data, std::pmr::vector>&& update_states, Frame_Lifecycle_Sample& frame_lifecycle_sample) { + std::uint64_t render_start_ns = steady_now_ns(); + Performance_Shower* shower = render_snapshot_data.performance_shower.get(); QSize size = render_snapshot_data.size; - if (color->image.size() != size || color->image.format() != QImage::Format_ARGB32) { - color->image = QImage(size, QImage::Format_ARGB32); - } - color->image.detach(); + std::uint64_t image_prepare_start_ns = steady_now_ns(); + supersede_update_states(color->presented_update_states); + color->image.resize(size, QImage::Format_ARGB32); color->image.fill(render_snapshot_data.background_color); - QPainter painter(&color->image); + frame_lifecycle_sample.image_prepare_ms = elapsed_ms(image_prepare_start_ns, steady_now_ns()); + QPainter painter(&color->image.writable_image()); int renderable_count = 0; int visible_renderable_count = 0; int direct_draw_count = 0; int local_cache_count = 0; - { - Performance_Scope_Timer draw_data("Pipeline", "draw", render_snapshot_data.performance_shower.get()); - for (const auto& cur : snapshot) { - ++renderable_count; - if (!cur || cur->get_retiring() || !cur->visible) - continue; - ++visible_renderable_count; - if (cur->get_cache_mode() == Renderable_Cache_Mode::Local_Pixel) - ++local_cache_count; - else - ++direct_draw_count; - } - if (root) { - Performance_Scope_Timer renderable_draw("Renderable", renderable_name(root.get()) + ".render", render_snapshot_data.performance_shower.get()); - root->render(&painter, render_snapshot_data); - } + for (const auto& cur : snapshot) { + ++renderable_count; + if (!cur || cur->get_retiring() || !cur->visible) + continue; + ++visible_renderable_count; + if (cur->get_cache_mode() == Renderable_Cache_Mode::Local_Pixel) + ++local_cache_count; + else + ++direct_draw_count; } + std::uint64_t draw_start_ns = steady_now_ns(); + if (root) { + root->render(&painter, render_snapshot_data); + } + frame_lifecycle_sample.painter_draw_ms = elapsed_ms(draw_start_ns, steady_now_ns()); + std::uint64_t painter_end_start_ns = steady_now_ns(); painter.end(); + frame_lifecycle_sample.painter_end_ms = elapsed_ms(painter_end_start_ns, steady_now_ns()); + frame_lifecycle_sample.render_color_ms = elapsed_ms(render_start_ns, steady_now_ns()); + frame_lifecycle_sample.renderable_count = renderable_count; + frame_lifecycle_sample.visible_renderable_count = visible_renderable_count; + frame_lifecycle_sample.direct_draw_count = direct_draw_count; + frame_lifecycle_sample.local_cache_count = local_cache_count; color->version.store(version, std::memory_order_release); color->dirty_region = QRegion(QRect(QPoint(0, 0), size)); - if (render_snapshot_data.performance_shower) { + for (auto& state : update_states) { + if (state) + state->complete_until(Update_Stage::Rendered); + } + color->presented_update_states = std::move(update_states); + color->frame_lifecycle_sample = frame_lifecycle_sample; + if (shower) { double area = static_cast(size.width()) * static_cast(size.height()); - render_snapshot_data.performance_shower->set_gauge("Pipeline", "main_render_area", area); - render_snapshot_data.performance_shower->set_gauge("Pipeline", "main_render_full_ratio", area > 0.0 ? 1.0 : 0.0); - render_snapshot_data.performance_shower->set_gauge("Pipeline", "main_root_child_count", root ? static_cast(root->children_snapshot().size()) : 0.0); - render_snapshot_data.performance_shower->set_gauge("Pipeline", "main_renderable_count", static_cast(renderable_count)); - render_snapshot_data.performance_shower->set_gauge("Pipeline", "main_visible_renderable_count", static_cast(visible_renderable_count)); - render_snapshot_data.performance_shower->set_gauge("Pipeline", "main_direct_draw_count", static_cast(direct_draw_count)); - render_snapshot_data.performance_shower->set_gauge("Pipeline", "main_local_cache_count", static_cast(local_cache_count)); - render_snapshot_data.performance_shower->set_gauge("Pipeline", "main_state_lease_miss_count", 0.0); + shower->record_duration("Pipeline", "render_color", frame_lifecycle_sample.render_color_ms); + shower->record_duration("Pipeline", "draw", frame_lifecycle_sample.painter_draw_ms); + shower->record_duration("Pipeline", "image_prepare", frame_lifecycle_sample.image_prepare_ms); + shower->record_duration("Pipeline", "painter_end", frame_lifecycle_sample.painter_end_ms); + shower->set_gauge("Pipeline", "main_render_area", area); + shower->set_gauge("Pipeline", "main_render_full_ratio", area > 0.0 ? 1.0 : 0.0); + shower->set_gauge("Pipeline", "main_root_child_count", root ? static_cast(root->children_snapshot().size()) : 0.0); + shower->set_gauge("Pipeline", "main_renderable_count", static_cast(renderable_count)); + shower->set_gauge("Pipeline", "main_visible_renderable_count", static_cast(visible_renderable_count)); + shower->set_gauge("Pipeline", "main_direct_draw_count", static_cast(direct_draw_count)); + shower->set_gauge("Pipeline", "main_local_cache_count", static_cast(local_cache_count)); + shower->set_gauge("Pipeline", "main_state_lease_miss_count", 0.0); } } void Abs_Plot::schedule_render_timer() { @@ -713,16 +954,23 @@ void Abs_Plot::request_render(Render_Request_Source source) { if (d->destroying.load(std::memory_order_acquire)) return; d->last_request_ns.store(steady_now_ns(), std::memory_order_release); + bool coalesced_request = source != Render_Request_Source::Manual; + if (coalesced_request && d->render_request_posted.exchange(true, std::memory_order_acq_rel)) + return; Plot_Private* private_data = d; private_data->active_render_tasks.fetch_add(1, std::memory_order_acq_rel); - Global::instance()->render_scheduler().post([this, private_data, source]() { + Global::instance()->render_scheduler().post([this, private_data, source, coalesced_request]() { + if (coalesced_request) + private_data->render_request_posted.store(false, std::memory_order_release); if (!d->destroying.load(std::memory_order_acquire)) on_render_request(source); notify_render_task_done(private_data); }); } -void Abs_Plot::request_update(const QRegion& region) { - d->last_update_request_ns.store(steady_now_ns(), std::memory_order_release); +void Abs_Plot::request_update(const QRegion& region, std::uint64_t request_ns) { + if (!request_ns) + request_ns = steady_now_ns(); + d->last_update_request_ns.store(request_ns, std::memory_order_release); QTimer::singleShot(0, this, [this, region]() { if (d->destroying.load(std::memory_order_acquire)) return; diff --git a/Renderive/architecture/Plot.h b/Renderive/architecture/Plot.h index ace2a36..686226a 100644 --- a/Renderive/architecture/Plot.h +++ b/Renderive/architecture/Plot.h @@ -69,7 +69,7 @@ private: std::shared_ptr render_context; std::shared_ptr root_renderable; mutable std::mutex renderable_mutex; - using Renderable_Snapshot = QVector>; + using Renderable_Snapshot = std::vector>; bool event(QEvent* event) final; void paintEvent(QPaintEvent* event) final; Renderable_Snapshot renderable_snapshot() const; @@ -83,8 +83,8 @@ private: bool renderable_ready_for_submit(const std::shared_ptr& able, Render_Request_Source source, std::uint64_t now_ns); void mark_renderable_submitted(const std::shared_ptr& able, std::uint64_t now_ns); void finish_render(); - void request_update(const QRegion& region); - static void render_color(Renderable::Color_Buffer* color, std::uint64_t version, const std::shared_ptr& root, const Renderable_Snapshot& snapshot, const Plot_Render_Snapshot& render_snapshot_data); + void request_update(const QRegion& region, std::uint64_t request_ns = 0); + static void render_color(Renderable::Color_Buffer* color, std::uint64_t version, const std::shared_ptr& root, const Renderable_Snapshot& snapshot, const Plot_Render_Snapshot& render_snapshot_data, std::pmr::vector>&& update_states, Frame_Lifecycle_Sample& frame_lifecycle_sample); friend class Render_Scheduler; friend struct Renderable; }; diff --git a/Renderive/architecture/Plot_p.h b/Renderive/architecture/Plot_p.h index 66ae7ff..2761b5a 100644 --- a/Renderive/architecture/Plot_p.h +++ b/Renderive/architecture/Plot_p.h @@ -18,6 +18,7 @@ struct Plot_Private { std::atomic render_schedule_mode{Plot_Render_Schedule_Mode::Chase_Latest}; std::atomic max_render_fps{0.0}; std::atomic_bool render_submit_pending{false}; + std::atomic_bool render_request_posted{false}; std::uint64_t next_submit_ns = 0; std::uint64_t submit_timer_target_ns = 0; Render_Request_Source submit_source = Render_Request_Source::Dirty; @@ -28,6 +29,7 @@ struct Plot_Private { std::atomic_int pending_render_height{0}; std::atomic last_request_ns{0}; std::atomic last_update_request_ns{0}; + std::uint64_t last_painted_color_version{}; QSize render_size; Renderable::Render_Pipeline pipeline; std::atomic paint_buffer_acquire_mode{Plot_Buffer_Acquire_Mode::Try}; diff --git a/Renderive/architecture/Render_Data.cpp b/Renderive/architecture/Render_Data.cpp index 5486215..fd7df70 100644 --- a/Renderive/architecture/Render_Data.cpp +++ b/Renderive/architecture/Render_Data.cpp @@ -28,6 +28,41 @@ void Render_Data::mark_input_dirty(Triple_Buffer_Lease lease, bool notify_render if (notify_render && q_ptr) q_ptr->mark_render_dirty(); } +void Render_Data::set_input_completion(Triple_Buffer_Lease lease, std::shared_ptr state) { + Input_Buffer_Slot& slot = input_slot_by_index(lease.index); + if (slot.completion_state && slot.completion_state != state) + slot.completion_state->complete_all({}, Update_Outcome::Superseded); + slot.completion_state = std::move(state); +} +void Render_Data::drain_committed_update_states(std::pmr::vector>& output) { + for (auto& state : committed_update_states) + output.push_back(std::move(state)); + committed_update_states.clear(); +} +void Render_Data::clear_input_role(std::uint8_t role) { + Input_Buffer_Slot& slot = input_slot(role); + if (slot.completion_state) { + if (role == Input_Render) { + slot.completion_state->complete_until(Update_Stage::Committed); + committed_update_states.push_back(std::move(slot.completion_state)); + } + else { + slot.completion_state->complete_all({}, Update_Outcome::Superseded); + slot.completion_state.reset(); + } + } + slot.data->clear(); + slot.pending = false; +} +void Render_Data::cancel_pending_completions() { + for (Input_Buffer_Slot& slot : input_buffers) { + if (slot.completion_state) { + slot.completion_state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); + slot.completion_state.reset(); + } + } + cancel_update_states(committed_update_states); +} SRC Render_State::src() { auto view = d_ptr->state_control.read_view(); for (std::uint8_t role = State_Edit; role <= State_Render; ++role) { diff --git a/Renderive/architecture/Render_Data.h b/Renderive/architecture/Render_Data.h index 6f7ca65..3381b51 100644 --- a/Renderive/architecture/Render_Data.h +++ b/Renderive/architecture/Render_Data.h @@ -5,8 +5,13 @@ #include #include #include +#include +#include +#include +#include "../base/Memory.h" #include "Render_Config.h" #include "Plot_Render_Context.h" +#include "Update_Completion.h" #include "global.h" class QEvent; class QKeyEvent; @@ -46,10 +51,66 @@ struct Render_State { Render_Data* d_ptr{}; SRC src(); }; +struct Render_Object_Owner { + Render_Object_Owner() noexcept = default; + Render_Object_Owner(void* pointer, std::pmr::memory_resource* resource, void (*destroy)(void*, std::pmr::memory_resource*)) noexcept : pointer(pointer), resource(resource), destroy(destroy) {} + Render_Object_Owner(Render_Object_Owner&& other) noexcept : pointer(std::exchange(other.pointer, nullptr)), resource(std::exchange(other.resource, nullptr)), destroy(std::exchange(other.destroy, nullptr)) {} + Render_Object_Owner& operator=(Render_Object_Owner&& other) noexcept { + if (this == &other) + return *this; + reset(); + pointer = std::exchange(other.pointer, nullptr); + resource = std::exchange(other.resource, nullptr); + destroy = std::exchange(other.destroy, nullptr); + return *this; + } + Render_Object_Owner(const Render_Object_Owner&) = delete; + Render_Object_Owner& operator=(const Render_Object_Owner&) = delete; + ~Render_Object_Owner() { + reset(); + } + template + T* get() const { + return static_cast(pointer); + } + void reset() noexcept { + if (!pointer) + return; + destroy(pointer, resource); + pointer = nullptr; + resource = nullptr; + destroy = nullptr; + } + explicit operator bool() const noexcept { + return pointer != nullptr; + } + void* pointer{}; + std::pmr::memory_resource* resource{}; + void (*destroy)(void*, std::pmr::memory_resource*){}; +}; +template +Render_Object_Owner make_render_object(Args&&... args) { + std::pmr::memory_resource* resource = memory_resource(Memory_Domain::Renderable); + std::pmr::polymorphic_allocator allocator(resource); + T* pointer = allocator.allocate(1); + try { + std::construct_at(pointer, std::forward(args)...); + } + catch (...) { + allocator.deallocate(pointer, 1); + throw; + } + return Render_Object_Owner(pointer, resource, [](void* object, std::pmr::memory_resource* resource) { + std::pmr::polymorphic_allocator allocator(resource); + std::destroy_at(static_cast(object)); + allocator.deallocate(static_cast(object), 1); + }); +} struct Input_Buffer_Slot { Input_Data* data{}; std::uint64_t version = 0; bool pending = false; + std::shared_ptr completion_state; }; struct Render_Data { Render_Data() { @@ -61,9 +122,12 @@ struct Render_Data { Triple_Role_Buffer_Control state_control; Triple_Buffer_Stats state_buffer_stats; std::array state_buffers{}; + std::array state_buffer_owners{}; Triple_Role_Buffer_Control input_control; Triple_Buffer_Stats input_buffer_stats; std::array input_buffers{}; + std::array input_buffer_owners{}; + std::pmr::vector> committed_update_states{memory_resource(Memory_Domain::UpdateCompletion)}; std::uint64_t input_version = 0; std::atomic buffer_acquire_mode{Plot_Buffer_Acquire_Mode::Wait}; std::atomic render_schedule_mode{Renderable_Render_Schedule_Mode::Inherit}; @@ -110,9 +174,9 @@ struct Render_Data { state_control.unmark_use(lease); } void clear_state_buffers() { - for (Render_State*& state : state_buffers) { - delete state; - state = nullptr; + for (std::size_t i = 0; i < state_buffers.size(); ++i) { + state_buffer_owners[i].reset(); + state_buffers[i] = nullptr; } state_control.reset(); } @@ -149,24 +213,28 @@ struct Render_Data { return has_pending_state() || has_pending_input(); } void mark_input_dirty(Triple_Buffer_Lease lease, bool notify_render = true); - void clear_input_role(std::uint8_t role) { - Input_Buffer_Slot& slot = input_slot(role); - slot.data->clear(); - slot.pending = false; - } + void set_input_completion(Triple_Buffer_Lease lease, std::shared_ptr state); + void drain_committed_update_states(std::pmr::vector>& output); + void clear_input_role(std::uint8_t role); void clear_render_input() { clear_input_role(Input_Render); } + virtual std::size_t estimated_frame_memory(const Plot_Render_Snapshot&) { + return 0; + } void clear_input_buffers() { - for (Input_Buffer_Slot& slot : input_buffers) { - delete slot.data; - slot.data = nullptr; - slot.version = 0; - slot.pending = false; + cancel_pending_completions(); + for (std::size_t i = 0; i < input_buffers.size(); ++i) { + input_buffer_owners[i].reset(); + input_buffers[i].data = nullptr; + input_buffers[i].version = 0; + input_buffers[i].pending = false; + input_buffers[i].completion_state.reset(); } input_version = 0; input_control.reset(); } + void cancel_pending_completions(); public: /// 同步 State/Input 的 ready 角色到 render 角色。 virtual void sync_state_pipeline() = 0; diff --git a/Renderive/architecture/Render_Lease.h b/Renderive/architecture/Render_Lease.h index 00e626a..f404479 100644 --- a/Renderive/architecture/Render_Lease.h +++ b/Renderive/architecture/Render_Lease.h @@ -85,20 +85,21 @@ using Typed_Render_Data = Typed_Render_Data_Base struct Renderable_Binding { static void init(Owner* owner, const char* name) { - owner->d_ptr = new Data; + owner->d_owner = make_render_object(); + owner->d_ptr = owner->d_owner.template get(); owner->object_name = name; } static Data* d(Owner* owner) { return reinterpret_cast(owner->d_ptr); } - static Render_Data* create_render_data() { - return new Data; + static Render_Object_Owner create_render_data() { + return make_render_object(); } - static Render_State* create_render_state() { - return new State; + static Render_Object_Owner create_render_state() { + return make_render_object(); } - static Input_Data* create_input_data() { - return new Input; + static Render_Object_Owner create_input_data() { + return make_render_object(); } }; template diff --git a/Renderive/architecture/Render_Pipeline.cpp b/Renderive/architecture/Render_Pipeline.cpp index 24bf5c1..4a91417 100644 --- a/Renderive/architecture/Render_Pipeline.cpp +++ b/Renderive/architecture/Render_Pipeline.cpp @@ -1,5 +1,7 @@ #include "Renderable_Core.h" +#include "Update_Completion.h" namespace renderive { +Renderable::Color_Buffer::Color_Buffer() : presented_update_states(memory_resource(Memory_Domain::UpdateCompletion)) {} Renderable::Render_Pipeline::Render_Pipeline() { color_control.set_stats(&color_buffer_stats); color_control.reset(); diff --git a/Renderive/architecture/Render_Utils.cpp b/Renderive/architecture/Render_Utils.cpp index ced931e..6797b1f 100644 --- a/Renderive/architecture/Render_Utils.cpp +++ b/Renderive/architecture/Render_Utils.cpp @@ -4,8 +4,8 @@ #include "psc_global_include/Fail_Fast.h" #include "psc_global_include/export.h" namespace renderive { -QVector get_turbo_color_gradient() { - QVector colors; +std::vector get_turbo_color_gradient() { + std::vector colors; QFile file(":/turbo.color_map"); if (!file.open(QIODevice::ReadOnly)) { std::ostringstream oss; diff --git a/Renderive/architecture/Renderable.cpp b/Renderive/architecture/Renderable.cpp index d19208c..0905e13 100644 --- a/Renderive/architecture/Renderable.cpp +++ b/Renderive/architecture/Renderable.cpp @@ -1,5 +1,6 @@ #include #include +#include "../base/Memory.h" #include "../base/global.h" #include "Plot.h" #include "Renderable_Core.h" @@ -96,17 +97,25 @@ void Renderable::init_common(Abs_Plot* plot) { this->plot = plot; plot_context = plot->get_render_context(); retiring.store(false, std::memory_order_release); - if (!d_ptr) - d_ptr = create_render_data(); + if (!d_ptr) { + d_owner = create_render_data(); + d_ptr = d_owner.get(); + } d_ptr->set_buffer_acquire_mode(plot->get_renderable_buffer_acquire_mode()); d_ptr->clear_state_buffers(); d_ptr->clear_input_buffers(); - d_ptr->state_buffers[State_Edit] = create_render_state(); - d_ptr->state_buffers[State_Ready] = create_render_state(); - d_ptr->state_buffers[State_Render] = create_render_state(); - d_ptr->input_buffers[Input_Edit].data = create_input_data(); - d_ptr->input_buffers[Input_Ready].data = create_input_data(); - d_ptr->input_buffers[Input_Render].data = create_input_data(); + d_ptr->state_buffer_owners[State_Edit] = create_render_state(); + d_ptr->state_buffer_owners[State_Ready] = create_render_state(); + d_ptr->state_buffer_owners[State_Render] = create_render_state(); + d_ptr->state_buffers[State_Edit] = d_ptr->state_buffer_owners[State_Edit].get(); + d_ptr->state_buffers[State_Ready] = d_ptr->state_buffer_owners[State_Ready].get(); + d_ptr->state_buffers[State_Render] = d_ptr->state_buffer_owners[State_Render].get(); + d_ptr->input_buffer_owners[Input_Edit] = create_input_data(); + d_ptr->input_buffer_owners[Input_Ready] = create_input_data(); + d_ptr->input_buffer_owners[Input_Render] = create_input_data(); + d_ptr->input_buffers[Input_Edit].data = d_ptr->input_buffer_owners[Input_Edit].get(); + d_ptr->input_buffers[Input_Ready].data = d_ptr->input_buffer_owners[Input_Ready].get(); + d_ptr->input_buffers[Input_Render].data = d_ptr->input_buffer_owners[Input_Render].get(); for (Render_State* state : d_ptr->state_buffers) { state->d_ptr = d_ptr; } @@ -120,10 +129,10 @@ void Renderable::add_child(const std::shared_ptr& child) { return; } std::lock_guard lock(children_mutex); - if (children.contains(child)) + if (std::find(children.begin(), children.end(), child) != children.end()) return; child->parent_renderable = shared_from_this(); - children.append(child); + children.push_back(child); mark_render_dirty(); } void Renderable::remove_child(const std::shared_ptr& child) { @@ -136,28 +145,28 @@ void Renderable::remove_child(const std::shared_ptr& child) { child->detach_from_plot_tree(); mark_render_dirty(); } -QVector> Renderable::children_snapshot() const { +std::vector> Renderable::children_snapshot() const { std::lock_guard lock(children_mutex); return children; } -QVector> Renderable::hit_renderables(const QPointF& pos) { - QVector> hits; +std::vector> Renderable::hit_renderables(const QPointF& pos) { + std::vector> hits; append_hit_renderables(hits, pos); return hits; } -void Renderable::append_hit_renderables(QVector>& hits, const QPointF& pos) { +void Renderable::append_hit_renderables(std::vector>& hits, const QPointF& pos) { if (get_retiring() || !visible) return; if (get_child_render_order() == Renderable_Child_Render_Order::Children_Before_Self && select_test(pos)) - hits.append(shared_from_this()); - QVector> children = children_snapshot(); + hits.push_back(shared_from_this()); + std::vector> children = children_snapshot(); for (int i = children.size() - 1; i >= 0; --i) { const auto& child = children.at(i); if (child) child->append_hit_renderables(hits, pos); } if (get_child_render_order() == Renderable_Child_Render_Order::Self_Before_Children && select_test(pos)) - hits.append(shared_from_this()); + hits.push_back(shared_from_this()); } void Renderable::render(QPainter* painter, const Plot_Render_Snapshot& snapshot) { if (get_retiring() || !visible) @@ -178,7 +187,7 @@ void Renderable::render_direct(QPainter* painter, const Plot_Render_Snapshot& sn } } void Renderable::render_children(QPainter* painter, const Plot_Render_Snapshot& snapshot) { - QVector> children = children_snapshot(); + std::vector> children = children_snapshot(); for (const auto& child : children) { if (child) child->render(painter, snapshot); @@ -194,27 +203,28 @@ void Renderable::render_cached(QPainter* painter, const Plot_Render_Snapshot& sn std::lock_guard lock(cache_mutex); bool ready = !cache_dirty.load(std::memory_order_acquire) && cache_ready_version.load(std::memory_order_acquire) == edit_version && cache_image.size() == bounds.size() && cache_image.format() == QImage::Format_ARGB32 && cache_bounds == bounds; if (ready) { - painter->drawImage(bounds.topLeft(), cache_image); + painter->drawImage(bounds.topLeft(), cache_image.image()); return; } if (!cache_image.isNull() && cache_bounds == bounds) { schedule_cache_render(bounds, snapshot, edit_version); - painter->drawImage(bounds.topLeft(), cache_image); + painter->drawImage(bounds.topLeft(), cache_image.image()); return; } } render_cache_image(bounds, snapshot, edit_version); std::lock_guard lock(cache_mutex); if (!cache_image.isNull()) - painter->drawImage(bounds.topLeft(), cache_image); + painter->drawImage(bounds.topLeft(), cache_image.image()); } void Renderable::render_cache_image(const QRect& bounds, const Plot_Render_Snapshot& snapshot, std::uint64_t version) { if (get_retiring()) return; - QImage cache_image(bounds.size(), QImage::Format_ARGB32); + Pmr_QImage_Buffer cache_image(Memory_Domain::RenderableCacheImage); + cache_image.resize(bounds.size(), QImage::Format_ARGB32); cache_image.fill(Qt::transparent); { - QPainter cache_painter(&cache_image); + QPainter cache_painter(&cache_image.writable_image()); cache_painter.translate(-bounds.topLeft()); render_direct(&cache_painter, snapshot); cache_painter.end(); @@ -222,7 +232,7 @@ void Renderable::render_cache_image(const QRect& bounds, const Plot_Render_Snaps { std::lock_guard lock(cache_mutex); cache_bounds = bounds; - cache_image = cache_image; + this->cache_image = std::move(cache_image); cache_ready_version.store(version, std::memory_order_release); bool dirty = cache_edit_version.load(std::memory_order_acquire) != version; cache_dirty.store(dirty, std::memory_order_release); @@ -231,14 +241,25 @@ void Renderable::render_cache_image(const QRect& bounds, const Plot_Render_Snaps void Renderable::schedule_cache_render(const QRect& bounds, const Plot_Render_Snapshot& snapshot, std::uint64_t version) { if (cache_render_queued.exchange(true, std::memory_order_acq_rel)) return; - auto self = shared_from_this(); - auto snapshot_owner = std::make_shared(snapshot); - Global::instance()->render_scheduler().post_cpu([self, snapshot_owner, bounds, version]() { + struct Cache_Render_Task_Context { + std::shared_ptr self; + std::shared_ptr snapshot_owner; + QRect bounds; + std::uint64_t version{}; + }; + auto task_context = renderive::make_shared(); + task_context->self = shared_from_this(); + task_context->snapshot_owner = renderive::make_shared(snapshot); + task_context->bounds = bounds; + task_context->version = version; + Global::instance()->render_scheduler().post_cpu([task_context]() { + auto& self = task_context->self; + auto& snapshot_owner = task_context->snapshot_owner; if (self->get_retiring() || !snapshot_owner || snapshot_owner->destroying()) { self->cache_render_queued.store(false, std::memory_order_release); return; } - self->render_cache_image(bounds, *snapshot_owner, version); + self->render_cache_image(task_context->bounds, *snapshot_owner, task_context->version); self->cache_render_queued.store(false, std::memory_order_release); if (!self->get_retiring()) self->notify_parent_render_dirty(); @@ -370,7 +391,9 @@ bool Renderable::get_retiring() const { } void Renderable::detach_from_plot_tree() { retiring.store(true, std::memory_order_release); - QVector> children = children_snapshot(); + if (d_ptr) + d_ptr->cancel_pending_completions(); + std::vector> children = children_snapshot(); { std::lock_guard lock(children_mutex); children.clear(); @@ -383,13 +406,13 @@ void Renderable::detach_from_plot_tree() { cache_render_queued.store(false, std::memory_order_release); { std::lock_guard lock(cache_mutex); - cache_image = QImage(); + cache_image.clear(); cache_bounds = {}; } } Renderable::~Renderable() { detach_from_plot_tree(); - delete d_ptr; + d_owner.reset(); d_ptr = nullptr; } } // namespace renderive diff --git a/Renderive/architecture/Renderable_Core.h b/Renderive/architecture/Renderable_Core.h index ffe104f..cf13e5a 100644 --- a/Renderive/architecture/Renderable_Core.h +++ b/Renderive/architecture/Renderable_Core.h @@ -1,13 +1,43 @@ #pragma once #include -#include +#include #include #include #include #include #include +#include "../base/Pmr_QImage_Buffer.h" #include "Render_Data.h" namespace renderive { +struct Update_State; +struct Frame_Lifecycle_Sample { + std::uintptr_t plot_id{}; + std::uint64_t version{}; + std::uint64_t request_ns{}; + std::uint64_t submit_ns{}; + std::uint64_t update_request_ns{}; + std::uint64_t paint_ns{}; + double request_to_submit_ms{}; + double prepare_data_ms{}; + double renderable_prepare_ms{}; + double color_wait_ms{}; + double cpu_queue_wait_ms{}; + double render_color_ms{}; + double image_prepare_ms{}; + double painter_draw_ms{}; + double painter_end_ms{}; + double publish_color_ms{}; + double update_to_paint_ms{}; + double paint_event_ms{}; + double paint_consume_ms{}; + double paint_draw_image_ms{}; + double stats_log_ms{}; + double frame_total_ms{}; + int renderable_count{}; + int visible_renderable_count{}; + int direct_draw_count{}; + int local_cache_count{}; +}; /// 可渲染对象的基础接口和每对象渲染管线。 struct Renderable : std::enable_shared_from_this { /// 后台渲染任务状态。 @@ -21,9 +51,12 @@ struct Renderable : std::enable_shared_from_this { }; /// Plot 级颜色三缓冲中的一个物理颜色缓冲。 struct Color_Buffer { + Color_Buffer(); std::atomic version{0}; - QImage image; + Pmr_QImage_Buffer image{Memory_Domain::PlotColorBuffer}; QRegion dirty_region; + std::pmr::vector> presented_update_states; + Frame_Lifecycle_Sample frame_lifecycle_sample; }; /// 颜色缓冲占用租约,析构时自动释放底层 busy 标记。 struct Color_Buffer_Lease { @@ -67,16 +100,16 @@ struct Renderable : std::enable_shared_from_this { Color_Buffer_Lease consume_ready_color(Triple_Buffer_Mode mode); }; /// 创建派生对象的私有渲染数据。 - virtual Render_Data* create_render_data() { - return new Base_Render_Data; + virtual Render_Object_Owner create_render_data() { + return make_render_object(); } /// 创建派生对象的 State 缓冲实体。 - virtual Render_State* create_render_state() { - return new Render_State; + virtual Render_Object_Owner create_render_state() { + return make_render_object(); } /// 创建派生对象的 Input 缓冲实体。 - virtual Input_Data* create_input_data() { - return new Input_Data; + virtual Render_Object_Owner create_input_data() { + return make_render_object(); } /// 把对象挂接到 parent renderable。 virtual void init(const std::shared_ptr& parent); @@ -84,8 +117,8 @@ struct Renderable : std::enable_shared_from_this { virtual void init_root(Abs_Plot* plot); void add_child(const std::shared_ptr& child); void remove_child(const std::shared_ptr& child); - QVector> children_snapshot() const; - QVector> hit_renderables(const QPointF& pos); + std::vector> children_snapshot() const; + std::vector> hit_renderables(const QPointF& pos); void render(QPainter* painter, const Plot_Render_Snapshot& snapshot); /// 绘制已准备好的 Render_Data。 virtual void draw(QPainter* painter, const Plot_Render_Snapshot& snapshot); @@ -127,6 +160,7 @@ struct Renderable : std::enable_shared_from_this { /// 请求父 Plot 调度一次渲染。 void mark_render_dirty(); QString object_name; + Render_Object_Owner d_owner; Render_Data* d_ptr{}; Abs_Plot* plot{}; std::weak_ptr parent_renderable; @@ -138,7 +172,7 @@ struct Renderable : std::enable_shared_from_this { return false; } bool visible = true; - QVector> children; + std::vector> children; mutable std::mutex children_mutex; std::weak_ptr plot_context; std::atomic cache_mode{Renderable_Cache_Mode::Direct}; @@ -148,7 +182,7 @@ struct Renderable : std::enable_shared_from_this { std::atomic cache_edit_version{1}; std::atomic cache_ready_version{0}; mutable std::mutex cache_mutex; - QImage cache_image; + Pmr_QImage_Buffer cache_image{Memory_Domain::RenderableCacheImage}; QRect cache_bounds; std::atomic_bool retiring{true}; std::atomic_bool initialized{false}; @@ -161,7 +195,7 @@ struct Renderable : std::enable_shared_from_this { void render_cache_image(const QRect& bounds, const Plot_Render_Snapshot& snapshot, std::uint64_t version); void schedule_cache_render(const QRect& bounds, const Plot_Render_Snapshot& snapshot, std::uint64_t version); void notify_parent_render_dirty(); - void append_hit_renderables(QVector>& hits, const QPointF& pos); + void append_hit_renderables(std::vector>& hits, const QPointF& pos); void detach_from_plot_tree(); }; } // namespace renderive diff --git a/Renderive/architecture/Update_Completion.cpp b/Renderive/architecture/Update_Completion.cpp new file mode 100644 index 0000000..99873cc --- /dev/null +++ b/Renderive/architecture/Update_Completion.cpp @@ -0,0 +1,115 @@ +#include "Update_Completion.h" +#include +namespace renderive { +namespace { +std::atomic next_update_id{1}; +int stage_index(Update_Stage stage) noexcept { + return static_cast(stage); +} +} +Update_State::Stage_Record::Stage_Record(std::pmr::memory_resource* resource) : waiters(resource) {} +Update_State::Update_State(Update_Id id) : update_id(id), input_released(memory_resource(Memory_Domain::UpdateCompletion)), committed(memory_resource(Memory_Domain::UpdateCompletion)), rendered(memory_resource(Memory_Domain::UpdateCompletion)), presented(memory_resource(Memory_Domain::UpdateCompletion)) {} +Update_Id Update_State::id() const noexcept { + return update_id; +} +bool Update_State::is_complete() const noexcept { + return terminal.load(std::memory_order_acquire); +} +bool Update_State::stage_complete(Update_Stage stage) const noexcept { + std::lock_guard lock(mutex); + return slot(stage).completed; +} +Update_State::Stage_Record& Update_State::slot(Update_Stage stage) { + switch (stage) { + case Update_Stage::Input_Released: + return input_released; + case Update_Stage::Committed: + return committed; + case Update_Stage::Rendered: + return rendered; + case Update_Stage::Presented: + case Update_Stage::Count: + return presented; + } + return presented; +} +const Update_State::Stage_Record& Update_State::slot(Update_Stage stage) const { + return const_cast(this)->slot(stage); +} +void Update_State::register_waiter(Update_Stage stage, Update_Waiter waiter) { + std::error_code error; + Update_Outcome outcome = Update_Outcome::Cancelled; + bool ready = false; + { + std::lock_guard lock(mutex); + Stage_Record& record = slot(stage); + if (record.completed) { + error = record.error; + outcome = record.outcome; + ready = true; + } + else { + record.waiters.push_back(std::move(waiter)); + } + } + if (ready) + waiter.complete(error, outcome); +} +void Update_State::drain_completed_waiters(Stage_Record& record, std::pmr::vector& waiters, std::error_code error, Update_Outcome outcome) { + if (record.completed) + return; + record.completed = true; + record.error = error; + record.outcome = outcome; + for (auto& waiter : record.waiters) + waiters.push_back(std::move(waiter)); + record.waiters.clear(); +} +void Update_State::complete_until(Update_Stage stage, std::error_code error, Update_Outcome outcome) { + std::pmr::vector waiters(memory_resource(Memory_Domain::UpdateCompletion)); + { + std::lock_guard lock(mutex); + int last = std::min(stage_index(stage), stage_index(Update_Stage::Presented)); + if (last >= stage_index(Update_Stage::Input_Released)) + drain_completed_waiters(input_released, waiters, error, outcome); + if (last >= stage_index(Update_Stage::Committed)) + drain_completed_waiters(committed, waiters, error, outcome); + if (last >= stage_index(Update_Stage::Rendered)) + drain_completed_waiters(rendered, waiters, error, outcome); + if (last >= stage_index(Update_Stage::Presented)) { + drain_completed_waiters(presented, waiters, error, outcome); + terminal.store(true, std::memory_order_release); + } + } + for (auto& waiter : waiters) + waiter.complete(error, outcome); +} +void Update_State::complete_all(std::error_code error, Update_Outcome outcome) { + complete_until(Update_Stage::Presented, error, outcome); +} +std::shared_ptr make_update_state() { + std::pmr::polymorphic_allocator allocator(memory_resource(Memory_Domain::UpdateCompletion)); + return std::allocate_shared(allocator, next_update_id.fetch_add(1, std::memory_order_relaxed)); +} +void complete_update_states(std::pmr::vector>& states, Update_Stage stage, std::error_code error, Update_Outcome outcome) { + for (auto& state : states) { + if (state) + state->complete_until(stage, error, outcome); + } + states.clear(); +} +void cancel_update_states(std::pmr::vector>& states) { + for (auto& state : states) { + if (state) + state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); + } + states.clear(); +} +void supersede_update_states(std::pmr::vector>& states) { + for (auto& state : states) { + if (state) + state->complete_all({}, Update_Outcome::Superseded); + } + states.clear(); +} +} diff --git a/Renderive/architecture/Update_Completion.h b/Renderive/architecture/Update_Completion.h new file mode 100644 index 0000000..bd83041 --- /dev/null +++ b/Renderive/architecture/Update_Completion.h @@ -0,0 +1,175 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../base/Memory.h" +#include "../base/Task.h" +#include "asio.hpp" +namespace renderive { +enum class Update_Stage : std::uint8_t { + Input_Released, + Committed, + Rendered, + Presented, + Count +}; +enum class Update_Outcome : std::uint8_t { + Completed, + Superseded, + Cancelled +}; +using Update_Id = std::uint64_t; +struct Submit_Options {}; +class Update_Waiter { +public: + Update_Waiter() noexcept = default; + template + explicit Update_Waiter(Handler&& handler) { + using Model = Waiter_Model>; + resource = memory_resource(Memory_Domain::UpdateCompletion); + std::pmr::polymorphic_allocator allocator(resource); + object = allocator.allocate(1); + try { + std::construct_at(static_cast(object), std::forward(handler)); + } + catch (...) { + allocator.deallocate(static_cast(object), 1); + object = nullptr; + resource = nullptr; + throw; + } + complete_function = &complete_model; + destroy_function = &destroy_model; + } + Update_Waiter(Update_Waiter&& other) noexcept { + move_from(std::move(other)); + } + Update_Waiter& operator=(Update_Waiter&& other) noexcept { + if (this == &other) + return *this; + reset(); + move_from(std::move(other)); + return *this; + } + Update_Waiter(const Update_Waiter&) = delete; + Update_Waiter& operator=(const Update_Waiter&) = delete; + ~Update_Waiter() { + reset(); + } + void complete(std::error_code error, Update_Outcome outcome) { + if (complete_function) + complete_function(object, error, outcome); + } + explicit operator bool() const noexcept { + return object != nullptr; + } +private: + template + struct Waiter_Model { + using Executor = asio::associated_executor_t; + template + explicit Waiter_Model(Source&& value) : handler(std::forward(value)), executor(asio::get_associated_executor(handler, asio::system_executor())) {} + Handler handler; + Executor executor; + }; + template + static void complete_model(void* object, std::error_code error, Update_Outcome outcome) { + auto& model = *static_cast(object); + auto handler = std::move(model.handler); + auto executor = model.executor; + asio::post(executor, [handler = std::move(handler), error, outcome]() mutable { + handler(error, outcome); + }); + } + template + static void destroy_model(void* object, std::pmr::memory_resource* resource) { + std::pmr::polymorphic_allocator allocator(resource); + std::destroy_at(static_cast(object)); + allocator.deallocate(static_cast(object), 1); + } + void move_from(Update_Waiter&& other) noexcept { + object = std::exchange(other.object, nullptr); + resource = std::exchange(other.resource, nullptr); + complete_function = std::exchange(other.complete_function, nullptr); + destroy_function = std::exchange(other.destroy_function, nullptr); + } + void reset() noexcept { + if (!object) + return; + destroy_function(object, resource); + object = nullptr; + resource = nullptr; + complete_function = nullptr; + destroy_function = nullptr; + } + void* object{}; + std::pmr::memory_resource* resource{}; + void (*complete_function)(void*, std::error_code, Update_Outcome){}; + void (*destroy_function)(void*, std::pmr::memory_resource*){}; +}; +struct LIB_DECL Update_State { + explicit Update_State(Update_Id id); + Update_Id id() const noexcept; + bool is_complete() const noexcept; + bool stage_complete(Update_Stage stage) const noexcept; + void register_waiter(Update_Stage stage, Update_Waiter waiter); + void complete_until(Update_Stage stage, std::error_code error = {}, Update_Outcome outcome = Update_Outcome::Completed); + void complete_all(std::error_code error = {}, Update_Outcome outcome = Update_Outcome::Completed); +private: + struct Stage_Record { + explicit Stage_Record(std::pmr::memory_resource* resource); + bool completed = false; + std::error_code error; + Update_Outcome outcome = Update_Outcome::Cancelled; + std::pmr::vector waiters; + }; + Stage_Record& slot(Update_Stage stage); + const Stage_Record& slot(Update_Stage stage) const; + void drain_completed_waiters(Stage_Record& record, std::pmr::vector& waiters, std::error_code error, Update_Outcome outcome); + Update_Id update_id{}; + mutable std::mutex mutex; + Stage_Record input_released; + Stage_Record committed; + Stage_Record rendered; + Stage_Record presented; + std::atomic_bool terminal{}; +}; +class Update_Ticket { +public: + Update_Ticket() noexcept = default; + explicit Update_Ticket(std::shared_ptr state) : state_(std::move(state)) {} + Update_Id id() const noexcept { + return state_ ? state_->id() : 0; + } + explicit operator bool() const noexcept { + return static_cast(state_); + } + bool is_complete() const noexcept { + return state_ && state_->is_complete(); + } + template + auto async_wait(Update_Stage stage, CompletionToken&& token) { + return asio::async_initiate( + [state = state_, stage](auto handler) mutable { + if (!state) { + Update_Waiter waiter(std::move(handler)); + waiter.complete(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); + return; + } + state->register_waiter(stage, Update_Waiter(std::move(handler))); + }, token); + } +private: + std::shared_ptr state_; +}; +LIB_DECL std::shared_ptr make_update_state(); +LIB_DECL void complete_update_states(std::pmr::vector>& states, Update_Stage stage, std::error_code error = {}, Update_Outcome outcome = Update_Outcome::Completed); +LIB_DECL void cancel_update_states(std::pmr::vector>& states); +LIB_DECL void supersede_update_states(std::pmr::vector>& states); +} diff --git a/Renderive/architecture/global.h b/Renderive/architecture/global.h index e855d04..6900ab8 100644 --- a/Renderive/architecture/global.h +++ b/Renderive/architecture/global.h @@ -1,9 +1,9 @@ #pragma once #pragma once #include -#include +#include #include -#include +#include #include #include #include @@ -38,7 +38,7 @@ #define LIB_DECL #endif namespace renderive { -QVector get_turbo_color_gradient(); +std::vector get_turbo_color_gradient(); class Abs_Axis; class Axis; struct Render_Data; diff --git a/Renderive/base/Frame_Memory.cpp b/Renderive/base/Frame_Memory.cpp new file mode 100644 index 0000000..0c24691 --- /dev/null +++ b/Renderive/base/Frame_Memory.cpp @@ -0,0 +1,136 @@ +#include "Frame_Memory.h" +#include "Memory.h" +#include +#include +#include +#include +namespace renderive { +namespace { +constexpr std::size_t Frame_Arena_Min_Size = 64 * 1024; +constexpr std::size_t Frame_Arena_Max_Retained_Size = 64 * 1024 * 1024; +struct Frame_Global_Stats { + std::atomic frame_count{}; + std::atomic overflow_count{}; + std::atomic overflow_bytes{}; + std::atomic high_water_bytes{}; + std::atomic scratch_grow_count{}; + std::atomic scratch_bytes{}; +}; +Frame_Global_Stats frame_global_stats; +void update_peak(std::atomic& target, std::uint64_t value) { + std::uint64_t peak = target.load(std::memory_order_relaxed); + while (peak < value && !target.compare_exchange_weak(peak, value, std::memory_order_relaxed)) {} +} +} +struct Thread_Frame_Scratch; +class Frame_Overflow_Resource final : public std::pmr::memory_resource { +public: + explicit Frame_Overflow_Resource(Thread_Frame_Scratch* scratch) : scratch(scratch) {} +private: + void* do_allocate(std::size_t size, std::size_t alignment) override; + void do_deallocate(void* address, std::size_t size, std::size_t alignment) override; + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { + return this == &other; + } + Thread_Frame_Scratch* scratch{}; +}; +struct Thread_Frame_Scratch { + Thread_Frame_Scratch() { + storage.resize(Frame_Arena_Min_Size); + desired_size = Frame_Arena_Min_Size; + frame_global_stats.scratch_bytes.fetch_add(storage.size(), std::memory_order_relaxed); + } + std::pmr::memory_resource* overflow_upstream() { + if (!overflow_pool) + overflow_pool = std::make_unique(memory_resource(Memory_Domain::FrameArena)); + return overflow_pool.get(); + } + void begin_frame(std::size_t initial_buffer_size) { + frame_overflow_count = 0; + frame_overflow_bytes = 0; + std::size_t target_size = std::max(initial_buffer_size, desired_size); + target_size = std::max(target_size, Frame_Arena_Min_Size); + target_size = std::min(target_size, Frame_Arena_Max_Retained_Size); + if (storage.size() < target_size) { + std::size_t old_size = storage.size(); + storage.resize(target_size); + frame_global_stats.scratch_bytes.fetch_add(storage.size() - old_size, std::memory_order_relaxed); + frame_global_stats.scratch_grow_count.fetch_add(1, std::memory_order_relaxed); + } + } + void finish_frame() { + frame_global_stats.frame_count.fetch_add(1, std::memory_order_relaxed); + std::uint64_t frame_bytes = static_cast(storage.size() + frame_overflow_bytes); + update_peak(frame_global_stats.high_water_bytes, frame_bytes); +#if RENDERIVE_FRAME_ARENA_GROW + if (frame_overflow_bytes) { + std::size_t next_size = static_cast(frame_bytes + frame_bytes / 8 + 4096); + desired_size = std::clamp(next_size, Frame_Arena_Min_Size, Frame_Arena_Max_Retained_Size); + } +#endif + frame_overflow_count = 0; + frame_overflow_bytes = 0; + } + std::vector storage; + std::unique_ptr overflow_pool; + Frame_Overflow_Resource overflow_resource{this}; + std::size_t desired_size{}; + std::size_t frame_overflow_count{}; + std::size_t frame_overflow_bytes{}; + int depth{}; +}; +void* Frame_Overflow_Resource::do_allocate(std::size_t size, std::size_t alignment) { + scratch->frame_overflow_count += 1; + scratch->frame_overflow_bytes += size; + frame_global_stats.overflow_count.fetch_add(1, std::memory_order_relaxed); + frame_global_stats.overflow_bytes.fetch_add(size, std::memory_order_relaxed); + return scratch->overflow_upstream()->allocate(size, alignment); +} +void Frame_Overflow_Resource::do_deallocate(void* address, std::size_t size, std::size_t alignment) { + scratch->overflow_upstream()->deallocate(address, size, alignment); +} +namespace { +thread_local std::pmr::memory_resource* current_frame_resource{}; +Thread_Frame_Scratch& thread_frame_scratch() { + thread_local Thread_Frame_Scratch value; + return value; +} +} +Frame_Arena::Frame_Arena(std::size_t initial_buffer_size) { + scratch = &thread_frame_scratch(); + previous = current_frame_resource; + if (scratch->depth == 0) { + scratch->begin_frame(initial_buffer_size); + arena.emplace(scratch->storage.data(), scratch->storage.size(), &scratch->overflow_resource); + } + else { + arena.emplace(&scratch->overflow_resource); + } + ++scratch->depth; + current_frame_resource = resource(); +} +Frame_Arena::~Frame_Arena() { + current_frame_resource = previous; + bool outer = scratch && scratch->depth == 1; + arena.reset(); + if (outer) + scratch->finish_frame(); + --scratch->depth; +} +std::pmr::memory_resource* Frame_Arena::resource() noexcept { + return arena ? &*arena : memory_resource(); +} +std::pmr::memory_resource* frame_memory_resource() { + return current_frame_resource ? current_frame_resource : memory_resource(); +} +Frame_Memory_Stats frame_memory_stats() noexcept { + Frame_Memory_Stats result; + result.frame_count = frame_global_stats.frame_count.load(std::memory_order_relaxed); + result.overflow_count = frame_global_stats.overflow_count.load(std::memory_order_relaxed); + result.overflow_bytes = frame_global_stats.overflow_bytes.load(std::memory_order_relaxed); + result.high_water_bytes = frame_global_stats.high_water_bytes.load(std::memory_order_relaxed); + result.scratch_grow_count = frame_global_stats.scratch_grow_count.load(std::memory_order_relaxed); + result.scratch_bytes = frame_global_stats.scratch_bytes.load(std::memory_order_relaxed); + return result; +} +} diff --git a/Renderive/base/Frame_Memory.h b/Renderive/base/Frame_Memory.h new file mode 100644 index 0000000..41fab7c --- /dev/null +++ b/Renderive/base/Frame_Memory.h @@ -0,0 +1,34 @@ +#pragma once +#include +#include +#include +#include +#include "../architecture/global.h" +#ifndef RENDERIVE_FRAME_ARENA_GROW +#define RENDERIVE_FRAME_ARENA_GROW 1 +#endif +namespace renderive { +struct Frame_Memory_Stats { + std::uint64_t frame_count{}; + std::uint64_t overflow_count{}; + std::uint64_t overflow_bytes{}; + std::uint64_t high_water_bytes{}; + std::uint64_t scratch_grow_count{}; + std::uint64_t scratch_bytes{}; +}; +struct Thread_Frame_Scratch; +class LIB_DECL Frame_Arena { +public: + explicit Frame_Arena(std::size_t initial_buffer_size = 64 * 1024); + ~Frame_Arena(); + Frame_Arena(const Frame_Arena&) = delete; + Frame_Arena& operator=(const Frame_Arena&) = delete; + std::pmr::memory_resource* resource() noexcept; +private: + Thread_Frame_Scratch* scratch{}; + std::pmr::memory_resource* previous{}; + std::optional arena; +}; +LIB_DECL std::pmr::memory_resource* frame_memory_resource(); +LIB_DECL Frame_Memory_Stats frame_memory_stats() noexcept; +} diff --git a/Renderive/base/Memory.cpp b/Renderive/base/Memory.cpp new file mode 100644 index 0000000..790a729 --- /dev/null +++ b/Renderive/base/Memory.cpp @@ -0,0 +1,204 @@ +#include "Memory.h" +#include +#include +#include +#include +#include +#include +namespace renderive { +namespace { +void* default_allocate(void*, std::size_t size, std::size_t alignment) { + return std::pmr::new_delete_resource()->allocate(size, alignment); +} +void default_deallocate(void*, void* address, std::size_t size, std::size_t alignment) { + std::pmr::new_delete_resource()->deallocate(address, size, alignment); +} +class Callback_Memory_Resource final : public std::pmr::memory_resource { +public: + Callback_Memory_Resource() : callbacks{nullptr, default_allocate, default_deallocate} {} + void set_callbacks(const Memory_Callbacks& value) { + callbacks = value.allocate ? value : Memory_Callbacks{nullptr, default_allocate, default_deallocate}; + } + Memory_Stats stats() const noexcept { + Memory_Stats result; + result.upstream_allocation_count = allocation_count.load(std::memory_order_relaxed); + result.upstream_deallocation_count = deallocation_count.load(std::memory_order_relaxed); + result.upstream_current_bytes = current_bytes.load(std::memory_order_relaxed); + result.upstream_peak_bytes = peak_bytes.load(std::memory_order_relaxed); + return result; + } +private: + void* do_allocate(std::size_t size, std::size_t alignment) override { + void* result = callbacks.allocate(callbacks.context, size, alignment); + if (!result) + throw std::bad_alloc(); + allocation_count.fetch_add(1, std::memory_order_relaxed); + std::uint64_t current = current_bytes.fetch_add(size, std::memory_order_relaxed) + size; + std::uint64_t peak = peak_bytes.load(std::memory_order_relaxed); + while (peak < current && !peak_bytes.compare_exchange_weak(peak, current, std::memory_order_relaxed)) {} + return result; + } + void do_deallocate(void* address, std::size_t size, std::size_t alignment) override { + callbacks.deallocate(callbacks.context, address, size, alignment); + deallocation_count.fetch_add(1, std::memory_order_relaxed); + current_bytes.fetch_sub(size, std::memory_order_relaxed); + } + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { + return this == &other; + } + Memory_Callbacks callbacks; + std::atomic allocation_count{}; + std::atomic deallocation_count{}; + std::atomic current_bytes{}; + std::atomic peak_bytes{}; +}; +class alignas(64) Domain_Memory_Resource final : public std::pmr::memory_resource { +public: + void set_domain(Memory_Domain value) noexcept { + domain = value; + } + Memory_Domain_Stats stats() const noexcept { + Memory_Domain_Stats result; +#if RENDERIVE_MEMORY_DOMAIN_STATS + result.allocation_count = allocation_count.load(std::memory_order_relaxed); + result.deallocation_count = deallocation_count.load(std::memory_order_relaxed); + result.current_bytes = current_bytes.load(std::memory_order_relaxed); + result.peak_bytes = peak_bytes.load(std::memory_order_relaxed); +#endif + return result; + } +private: + void* do_allocate(std::size_t size, std::size_t alignment) override; + void do_deallocate(void* address, std::size_t size, std::size_t alignment) override; + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { + return this == &other; + } + Memory_Domain domain = Memory_Domain::Other; +#if RENDERIVE_MEMORY_DOMAIN_STATS + std::atomic allocation_count{}; + std::atomic deallocation_count{}; + std::atomic current_bytes{}; + std::atomic peak_bytes{}; +#endif +}; +struct Memory_System { + Memory_System() { + for (std::size_t i = 0; i < domains.size(); ++i) + domains[i].set_domain(static_cast(i)); + } + std::mutex mutex; + Callback_Memory_Resource upstream; + std::unique_ptr small_pool; + std::atomic small_resource{}; + std::atomic_bool initialized{}; + bool configured{}; + std::array(Memory_Domain::Count)> domains; +}; +Memory_System& memory_system() { + static Memory_System value; + return value; +} +bool domain_uses_small_pool(Memory_Domain domain) { + switch (domain) { + case Memory_Domain::Renderable: + case Memory_Domain::SchedulerTask: + case Memory_Domain::UpdateCompletion: + case Memory_Domain::Other: + return true; + default: + return false; + } +} +std::pmr::memory_resource* upstream_resource(Memory_System& system) { + system.initialized.store(true, std::memory_order_release); + return &system.upstream; +} +std::pmr::memory_resource* small_object_resource(Memory_System& system) { + system.initialized.store(true, std::memory_order_release); +#if RENDERIVE_MEMORY_GLOBAL_POOL + std::pmr::memory_resource* resource = system.small_resource.load(std::memory_order_acquire); + if (resource) + return resource; + std::lock_guard lock(system.mutex); + resource = system.small_resource.load(std::memory_order_relaxed); + if (resource) + return resource; + std::pmr::pool_options options; + options.max_blocks_per_chunk = 64; + options.largest_required_pool_block = 4 * 1024; + system.small_pool = std::make_unique(options, &system.upstream); + resource = system.small_pool.get(); + system.small_resource.store(resource, std::memory_order_release); + return resource; +#else + return &system.upstream; +#endif +} +std::pmr::memory_resource* domain_resource(Memory_Domain domain) { + Memory_System& system = memory_system(); + if (domain_uses_small_pool(domain)) + return small_object_resource(system); + return upstream_resource(system); +} +void* Domain_Memory_Resource::do_allocate(std::size_t size, std::size_t alignment) { + void* result = domain_resource(domain)->allocate(size, alignment); +#if RENDERIVE_MEMORY_DOMAIN_STATS + allocation_count.fetch_add(1, std::memory_order_relaxed); + std::uint64_t current = current_bytes.fetch_add(size, std::memory_order_relaxed) + size; + std::uint64_t peak = peak_bytes.load(std::memory_order_relaxed); + while (peak < current && !peak_bytes.compare_exchange_weak(peak, current, std::memory_order_relaxed)) {} +#endif + return result; +} +void Domain_Memory_Resource::do_deallocate(void* address, std::size_t size, std::size_t alignment) { + domain_resource(domain)->deallocate(address, size, alignment); +#if RENDERIVE_MEMORY_DOMAIN_STATS + deallocation_count.fetch_add(1, std::memory_order_relaxed); + current_bytes.fetch_sub(size, std::memory_order_relaxed); +#endif +} +} +void set_memory_callbacks(const Memory_Callbacks& callbacks) { + if (!!callbacks.allocate != !!callbacks.deallocate) + throw std::invalid_argument("allocate and deallocate must be configured together"); + Memory_System& system = memory_system(); + std::lock_guard lock(system.mutex); + if (system.configured || system.initialized.load(std::memory_order_acquire)) + throw std::logic_error("Renderive memory resource is already configured"); + system.upstream.set_callbacks(callbacks); + system.configured = true; +} +Memory_Stats memory_stats() noexcept { + return memory_system().upstream.stats(); +} +Memory_Domain_Stats memory_domain_stats(Memory_Domain domain) noexcept { + return memory_system().domains[static_cast(domain)].stats(); +} +std::pmr::memory_resource* memory_resource() { + return small_object_resource(memory_system()); +} +std::pmr::memory_resource* memory_resource(Memory_Domain domain) { +#if RENDERIVE_MEMORY_DOMAIN_STATS + Memory_System& system = memory_system(); + system.initialized.store(true, std::memory_order_release); + return &system.domains[static_cast(domain)]; +#else + return domain_resource(domain); +#endif +} +void release_unused_memory() { + Memory_System& system = memory_system(); + std::lock_guard lock(system.mutex); + if (system.small_pool) + system.small_pool->release(); +} +void shutdown_memory() { + Memory_System& system = memory_system(); + std::lock_guard lock(system.mutex); + system.small_resource.store(nullptr, std::memory_order_release); + system.small_pool.reset(); + system.initialized.store(false, std::memory_order_release); + system.configured = false; + system.upstream.set_callbacks(Memory_Callbacks{}); +} +} diff --git a/Renderive/base/Memory.h b/Renderive/base/Memory.h new file mode 100644 index 0000000..8f7361e --- /dev/null +++ b/Renderive/base/Memory.h @@ -0,0 +1,65 @@ +#pragma once +#include +#include +#include +#include +#include +#include "../architecture/global.h" +#ifndef RENDERIVE_MEMORY_DOMAIN_STATS +#define RENDERIVE_MEMORY_DOMAIN_STATS 1 +#endif +#ifndef RENDERIVE_MEMORY_GLOBAL_POOL +#define RENDERIVE_MEMORY_GLOBAL_POOL 1 +#endif +namespace renderive { +struct Memory_Callbacks { + void* context{}; + void* (*allocate)(void* context, std::size_t size, std::size_t alignment){}; + void (*deallocate)(void* context, void* address, std::size_t size, std::size_t alignment){}; +}; +struct Memory_Stats { + std::uint64_t upstream_allocation_count{}; + std::uint64_t upstream_deallocation_count{}; + std::uint64_t upstream_current_bytes{}; + std::uint64_t upstream_peak_bytes{}; +}; +enum class Memory_Domain : std::uint8_t { + Renderable, + InputBuffer, + Waterfall, + Spectrum, + FrameArena, + RingBuffer, + Image, + Audio, + Afterglow, + Planisphere, + Curve, + PointSet, + Axis, + SchedulerTask, + RenderableCacheImage, + PlotColorBuffer, + UpdateCompletion, + Other, + Count +}; +struct Memory_Domain_Stats { + std::uint64_t allocation_count{}; + std::uint64_t deallocation_count{}; + std::uint64_t current_bytes{}; + std::uint64_t peak_bytes{}; +}; +LIB_DECL void set_memory_callbacks(const Memory_Callbacks& callbacks); +LIB_DECL Memory_Stats memory_stats() noexcept; +LIB_DECL Memory_Domain_Stats memory_domain_stats(Memory_Domain domain) noexcept; +LIB_DECL std::pmr::memory_resource* memory_resource(); +LIB_DECL std::pmr::memory_resource* memory_resource(Memory_Domain domain); +LIB_DECL void release_unused_memory(); +LIB_DECL void shutdown_memory(); +template +std::shared_ptr make_shared(Args&&... args) { + std::pmr::polymorphic_allocator allocator(memory_resource()); + return std::allocate_shared(allocator, std::forward(args)...); +} +} diff --git a/Renderive/base/Pmr_QImage_Buffer.h b/Renderive/base/Pmr_QImage_Buffer.h new file mode 100644 index 0000000..16e2439 --- /dev/null +++ b/Renderive/base/Pmr_QImage_Buffer.h @@ -0,0 +1,188 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Memory.h" +namespace renderive { +struct Pmr_QImage_Stats { + std::uint64_t reset_count{}; + std::uint64_t allocated_bytes{}; + std::uint64_t detach_count{}; + std::uint64_t detach_bytes{}; +}; +namespace pmr_qimage_detail { +inline std::atomic reset_count{}; +inline std::atomic allocated_bytes{}; +inline std::atomic detach_count{}; +inline std::atomic detach_bytes{}; +} +class Pmr_QImage_Buffer { +public: + explicit Pmr_QImage_Buffer(Memory_Domain domain = Memory_Domain::Image) : domain(domain) {} + [[nodiscard]] int width() const { + return image_data.width(); + } + [[nodiscard]] int height() const { + return image_data.height(); + } + [[nodiscard]] QSize size() const { + return image_data.size(); + } + [[nodiscard]] QImage::Format format() const { + return image_data.format(); + } + [[nodiscard]] bool isNull() const { + return image_data.isNull(); + } + [[nodiscard]] qsizetype sizeInBytes() const { + return image_data.sizeInBytes(); + } + [[nodiscard]] const QImage& image() const { + return image_data; + } + QImage& image() { + return image_data; + } + QImage& writable_image() { + detach_for_write(); + return image_data; + } + void clear() { + image_data = QImage(); + pixel_buffer.reset(); + } + void resize(const QSize& size, QImage::Format format = QImage::Format_ARGB32) { + resize(size.width(), size.height(), format); + } + void resize(int width, int height, QImage::Format format = QImage::Format_ARGB32) { + if (image_data.width() != width || image_data.height() != height || image_data.format() != format) + reset_image(width, height, format); + } + void fill(const QColor& color) { + if (pixel_buffer && !image_data.isNull()) { + QRgb value = image_data.format() == QImage::Format_ARGB32_Premultiplied ? qPremultiply(color.rgba()) : color.rgba(); + for (int row = 0; row < image_data.height(); ++row) { + auto* line = reinterpret_cast(pixel_buffer->data + static_cast(row) * static_cast(pixel_buffer->bytes_per_line)); + std::fill(line, line + image_data.width(), value); + } + return; + } + detach_for_write(); + image_data.fill(color); + } + QRgb* scan_line(int row) { + if (pixel_buffer) + return reinterpret_cast(pixel_buffer->data + static_cast(row) * static_cast(pixel_buffer->bytes_per_line)); + detach_for_write(); + return reinterpret_cast(image_data.scanLine(row)); + } + void set_image(QImage image) { + image_data = std::move(image); + pixel_buffer.reset(); + } +private: + struct Pixel_Buffer { + explicit Pixel_Buffer(Memory_Domain domain) : resource(memory_resource(domain)) {} + ~Pixel_Buffer() { + if (data) + resource->deallocate(data, size, alignof(QRgb)); + } + void allocate(std::size_t bytes) { + data = static_cast(resource->allocate(bytes, alignof(QRgb))); + size = bytes; + } + std::pmr::memory_resource* resource{}; + uchar* data{}; + std::size_t size{}; + int bytes_per_line{}; + }; + struct Pixel_Buffer_Owner { + Pixel_Buffer_Owner(Memory_Domain domain, std::shared_ptr buffer) : domain(domain), buffer(std::move(buffer)) {} + Memory_Domain domain; + std::shared_ptr buffer; + }; + static int image_depth(QImage::Format format) { + switch (format) { + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: + case QImage::Format_RGB32: + return 32; + default: + return 32; + } + } + static int image_bytes_per_line(int width, QImage::Format format) { + return ((width * image_depth(format) + 31) / 32) * 4; + } + static Pixel_Buffer_Owner* create_pixel_buffer_owner(Memory_Domain domain, const std::shared_ptr& buffer) { + std::pmr::polymorphic_allocator allocator(memory_resource(domain)); + Pixel_Buffer_Owner* owner = allocator.allocate(1); + try { + std::construct_at(owner, domain, buffer); + } + catch (...) { + allocator.deallocate(owner, 1); + throw; + } + return owner; + } + static void cleanup_pixel_buffer(void* info) { + auto* owner = static_cast(info); + Memory_Domain domain = owner->domain; + std::pmr::polymorphic_allocator allocator(memory_resource(domain)); + std::destroy_at(owner); + allocator.deallocate(owner, 1); + } + void reset_image(int width, int height, QImage::Format format) { + if (width <= 0 || height <= 0) { + clear(); + return; + } + int bytes_per_line = image_bytes_per_line(width, format); + auto buffer = renderive::make_shared(domain); + buffer->bytes_per_line = bytes_per_line; + std::size_t bytes = static_cast(bytes_per_line) * static_cast(height); + buffer->allocate(bytes); + auto* cleanup_owner = create_pixel_buffer_owner(domain, buffer); + image_data = QImage(buffer->data, width, height, bytes_per_line, format, cleanup_pixel_buffer, cleanup_owner); + pixel_buffer = std::move(buffer); +#if RENDERIVE_MEMORY_DOMAIN_STATS + pmr_qimage_detail::reset_count.fetch_add(1, std::memory_order_relaxed); + pmr_qimage_detail::allocated_bytes.fetch_add(bytes, std::memory_order_relaxed); +#endif + } + void detach_for_write() { + if (image_data.isNull() || image_data.isDetached()) + return; + QImage old_image = image_data; + std::size_t bytes = static_cast(old_image.sizeInBytes()); + reset_image(old_image.width(), old_image.height(), old_image.format()); + std::memcpy(image_data.bits(), old_image.constBits(), bytes); +#if RENDERIVE_MEMORY_DOMAIN_STATS + pmr_qimage_detail::detach_count.fetch_add(1, std::memory_order_relaxed); + pmr_qimage_detail::detach_bytes.fetch_add(bytes, std::memory_order_relaxed); +#endif + } + std::shared_ptr pixel_buffer; + QImage image_data; + Memory_Domain domain; +}; +inline Pmr_QImage_Stats pmr_qimage_stats() noexcept { + Pmr_QImage_Stats result; +#if RENDERIVE_MEMORY_DOMAIN_STATS + result.reset_count = pmr_qimage_detail::reset_count.load(std::memory_order_relaxed); + result.allocated_bytes = pmr_qimage_detail::allocated_bytes.load(std::memory_order_relaxed); + result.detach_count = pmr_qimage_detail::detach_count.load(std::memory_order_relaxed); + result.detach_bytes = pmr_qimage_detail::detach_bytes.load(std::memory_order_relaxed); +#endif + return result; +} +} diff --git a/Renderive/base/Task.h b/Renderive/base/Task.h new file mode 100644 index 0000000..958c28a --- /dev/null +++ b/Renderive/base/Task.h @@ -0,0 +1,168 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include "Memory.h" +namespace renderive { +struct Task_Stats { + std::uint64_t sizeof_task{}; + std::uint64_t inline_size{}; + std::uint64_t total_count{}; + std::uint64_t inline_count{}; + std::uint64_t heap_count{}; + std::uint64_t heap_bytes{}; +}; +namespace task_detail { +inline std::atomic total_count{}; +inline std::atomic inline_count{}; +inline std::atomic heap_count{}; +inline std::atomic heap_bytes{}; +} +class Task { +public: + static constexpr std::size_t Inline_Size = 64; + static constexpr std::size_t Inline_Align = alignof(std::max_align_t); + Task() noexcept = default; + Task(std::nullptr_t) noexcept {} + template + requires (!std::is_same_v, Task>) + Task(Callable&& callable) { + using Model = Task_Model>; + if constexpr (sizeof(Model) <= Inline_Size && alignof(Model) <= Inline_Align && std::is_nothrow_move_constructible_v) { + object = inline_storage(); + std::construct_at(static_cast(object), std::forward(callable)); + inline_object = true; +#if RENDERIVE_MEMORY_DOMAIN_STATS + task_detail::inline_count.fetch_add(1, std::memory_order_relaxed); +#endif + } + else { + resource = memory_resource(Memory_Domain::SchedulerTask); + std::pmr::polymorphic_allocator allocator(resource); + object = allocator.allocate(1); + try { + std::construct_at(static_cast(object), std::forward(callable)); + } + catch (...) { + allocator.deallocate(static_cast(object), 1); + object = nullptr; + resource = nullptr; + throw; + } +#if RENDERIVE_MEMORY_DOMAIN_STATS + task_detail::heap_count.fetch_add(1, std::memory_order_relaxed); + task_detail::heap_bytes.fetch_add(sizeof(Model), std::memory_order_relaxed); +#endif + } +#if RENDERIVE_MEMORY_DOMAIN_STATS + task_detail::total_count.fetch_add(1, std::memory_order_relaxed); +#endif + invoke_function = &invoke; + destroy_function = &destroy; + move_function = &move; + } + Task(Task&& other) noexcept { + move_from(std::move(other)); + } + Task& operator=(Task&& other) noexcept { + if (this == &other) + return *this; + reset(); + move_from(std::move(other)); + return *this; + } + Task(const Task&) = delete; + Task& operator=(const Task&) = delete; + ~Task() { + reset(); + } + void operator()() { + if (invoke_function) + invoke_function(object); + } + explicit operator bool() const noexcept { + return object != nullptr; + } +private: + template + struct Task_Model { + template + explicit Task_Model(Source&& callable) : callable(std::forward(callable)) {} + Callable callable; + }; + template + static void invoke(void* object) { + static_cast(object)->callable(); + } + template + static void destroy(void* object, std::pmr::memory_resource* resource) { + std::destroy_at(static_cast(object)); + if (resource) { + std::pmr::polymorphic_allocator allocator(resource); + allocator.deallocate(static_cast(object), 1); + } + } + template + static void move(void* destination, void* source) { + std::construct_at(static_cast(destination), std::move(*static_cast(source))); + std::destroy_at(static_cast(source)); + } + void* inline_storage() noexcept { + return static_cast(storage); + } + void move_from(Task&& other) noexcept { + invoke_function = std::exchange(other.invoke_function, nullptr); + destroy_function = std::exchange(other.destroy_function, nullptr); + move_function = std::exchange(other.move_function, nullptr); + inline_object = std::exchange(other.inline_object, false); + if (!other.object) { + object = nullptr; + resource = nullptr; + return; + } + if (inline_object) { + object = inline_storage(); + resource = nullptr; + move_function(object, other.object); + other.object = nullptr; + return; + } + object = std::exchange(other.object, nullptr); + resource = std::exchange(other.resource, nullptr); + } + void reset() noexcept { + if (!object) + return; + destroy_function(object, resource); + object = nullptr; + resource = nullptr; + invoke_function = nullptr; + destroy_function = nullptr; + move_function = nullptr; + inline_object = false; + } + alignas(std::max_align_t) std::byte storage[Inline_Size]{}; + void* object{}; + std::pmr::memory_resource* resource{}; + void (*invoke_function)(void*){}; + void (*destroy_function)(void*, std::pmr::memory_resource*){}; + void (*move_function)(void*, void*){}; + bool inline_object{}; +}; +inline Task_Stats task_stats() noexcept { + Task_Stats result; + result.sizeof_task = sizeof(Task); + result.inline_size = Task::Inline_Size; +#if RENDERIVE_MEMORY_DOMAIN_STATS + result.total_count = task_detail::total_count.load(std::memory_order_relaxed); + result.inline_count = task_detail::inline_count.load(std::memory_order_relaxed); + result.heap_count = task_detail::heap_count.load(std::memory_order_relaxed); + result.heap_bytes = task_detail::heap_bytes.load(std::memory_order_relaxed); +#endif + return result; +} +} diff --git a/Renderive/base/Tool.h b/Renderive/base/Tool.h index 8f6d80f..9c3bc88 100644 --- a/Renderive/base/Tool.h +++ b/Renderive/base/Tool.h @@ -50,7 +50,7 @@ inline QString get_value_from_string(const QString& value) { } template static QWidget* create_edit_widget_list(const QString& button_text, - const std::function &)>& click, const QVector& init_values) { + const std::function &)>& click, const std::vector& init_values) { auto container = new QWidget; auto layout = new QHBoxLayout(container); layout->setAlignment(Qt::AlignLeft); @@ -66,7 +66,7 @@ static QWidget* create_edit_widget_list(const QString& button_text, confirm->setMinimumWidth(QFontMetrics(confirm->font()).horizontalAdvance(button_text) + 8); confirm->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); layout->addWidget(confirm); - QVector lineEditList; + std::vector lineEditList; for (const T& init_value : init_values) { QString init_value_str = get_init_value(init_value); auto cur_edit = new QLineEdit(init_value_str); @@ -78,13 +78,13 @@ static QWidget* create_edit_widget_list(const QString& button_text, cur_edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); int w = qMax((int)((double)width * 1.4) + 16, 50); cur_edit->setMinimumWidth(w); - lineEditList.append(cur_edit); + lineEditList.push_back(cur_edit); layout->addWidget(cur_edit); } QObject::connect(confirm, &QPushButton::clicked, [lineEditList, click]() { - QVector values; + std::vector values; for (QLineEdit* line_edit : lineEditList) { - values.append(get_value_from_string(line_edit->text())); + values.push_back(get_value_from_string(line_edit->text())); } click(values); }); @@ -93,12 +93,12 @@ static QWidget* create_edit_widget_list(const QString& button_text, template static QWidget* create_edit_widget(const QString& button_text, const std::function& click, const T& init_values) { - return create_edit_widget_list(button_text, [click](const QVector& data) { - click(data.first()); - }, QVector{init_values}); + return create_edit_widget_list(button_text, [click](const std::vector& data) { + click(data.front()); + }, std::vector{init_values}); } static QWidget* create_range(Axis* axis, const QString& text = "更改轴范围") { - return create_edit_widget_list(text, [axis](const QVector& values) { + return create_edit_widget_list(text, [axis](const std::vector& values) { axis->set_coord_range(Range{values[0], values[1]}); }, {axis->coord_range().origin, axis->coord_range().target}); @@ -198,7 +198,14 @@ static QLabel* create_label(const QString& text, int h = 50, const QColor& color return l; } } // namespace renderive -static QVector bw(const QVector& widgets, const QString& name) { +template +static std::vector concat_widgets(const Vectors&... vectors) { + std::vector result; + result.reserve((vectors.size() + ... + 0)); + (result.insert(result.end(), vectors.begin(), vectors.end()), ...); + return result; +} +static std::vector bw(const std::vector& widgets, const QString& name) { if (widgets.empty()) return {}; auto ret = new QWidget; @@ -282,13 +289,13 @@ static QWidget* create_image_interpolation_combo(renderive::Waterfall* waterfall layout->addWidget(combo); return container; } -static QWidget* create_control(renderive::Abs_Plot* plot, const QVector& widgets = {}) { +static QWidget* create_control(renderive::Abs_Plot* plot, const std::vector& widgets = {}) { auto control = new QWidget; auto l = new QVBoxLayout(control); l->setAlignment(Qt::AlignTop); l->setContentsMargins({0, 0, 0, 0}); l->setSpacing(0); - QVector ws = { + std::vector ws = { renderive::create_toggle_button("暂停渲染", "开始渲染", [plot]() { plot->is_rendering() ? plot->pause_render() : plot->start_render(); }, plot->is_rendering()), @@ -299,7 +306,7 @@ static QWidget* create_control(renderive::Abs_Plot* plot, const QVectorset_background_color(color); }, plot->background_color()), create_plot_render_schedule_combo(plot), - renderive::create_edit_widget_list("渲染检查FPS", [plot](QVector data) { + renderive::create_edit_widget_list("渲染检查FPS", [plot](std::vector data) { if (data[0] < 0) { plot->pause_render(); } @@ -307,34 +314,34 @@ static QWidget* create_control(renderive::Abs_Plot* plot, const QVectorstart_render(data[0]); } }, {30}), - renderive::create_edit_widget_list("最大渲染FPS", [plot](QVector data) { + renderive::create_edit_widget_list("最大渲染FPS", [plot](std::vector data) { plot->set_max_render_fps(data[0]); }, {plot->get_max_render_fps() > 0.0 ? plot->get_max_render_fps() : plot->get_render_check_fps()}), }; - for (QWidget* cur : bw(ws, "plot基础") + widgets) { + for (QWidget* cur : concat_widgets(bw(ws, "plot基础"), widgets)) { l->addWidget(cur); } return control; } -static QVector cw(renderive::Abs_Axis* it) { +static std::vector cw(renderive::Abs_Axis* it) { if (it == nullptr) return {}; - auto t = QVector{ + auto t = std::vector{ renderive::create_button("反转轴整体方向", [it]() { it->set_orientation(it->orientation() == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal); }), - renderive::create_edit_widget_list("主刻度值", [it](const QVector& values) { + renderive::create_edit_widget_list("主刻度值", [it](const std::vector& values) { it->set_tick_length(values[0]); }, {it->tick_length()}), - renderive::create_edit_widget_list("子刻度值", [it](const QVector& values) { + renderive::create_edit_widget_list("子刻度值", [it](const std::vector& values) { it->set_sub_tick_length(values[0]); }, {it->sub_tick_length()}), renderive::create_select_color("更改轴颜色", [it](const QColor& color) { it->set_color(color); }, it->color()), - renderive::create_edit_widget_list("设置单位", [it](const QVector& values) { + renderive::create_edit_widget_list("设置单位", [it](const std::vector& values) { it->set_unit_text(values[0]); }, {it->unit_text()}), renderive::create_select_color("更改UnitText颜色", [it](const QColor& color) { @@ -343,35 +350,35 @@ static QVector cw(renderive::Abs_Axis* it) { renderive::create_select_color("更改UnitText背景色", [it](const QColor& color) { it->set_unit_text_background_brush(QBrush(color)); }, it->color()), - renderive::create_edit_widget_list("更改刻度斜度", [it](const QVector& values) { + renderive::create_edit_widget_list("更改刻度斜度", [it](const std::vector& values) { it->set_rotate(values[0]); }, {it->rotate()}), }; return t; } -static QVector cw(renderive::Axis* it) { +static std::vector cw(renderive::Axis* it) { if (it == nullptr) return {}; - auto t = QVector{ + auto t = std::vector{ renderive::create_range(it), }; - return cw(dynamic_cast(it)) + t; + return concat_widgets(cw(dynamic_cast(it)), t); } -static QVector cw(renderive::Frequency_Axis* it) { +static std::vector cw(renderive::Frequency_Axis* it) { if (it == nullptr) return {}; - auto t = QVector{}; - return cw(dynamic_cast(it)) + t; + auto t = std::vector{}; + return concat_widgets(cw(dynamic_cast(it)), t); } -static QVector cw(renderive::Time_Axis* it, int num_pre_second = -1) { +static std::vector cw(renderive::Time_Axis* it, int num_pre_second = -1) { if (it == nullptr) return {}; - auto t = QVector{ - renderive::create_edit_widget_list("时间格式", [it](QVector data) { + auto t = std::vector{ + renderive::create_edit_widget_list("时间格式", [it](std::vector data) { it->set_time_format(data[0]); }, {it->time_format()}), - renderive::create_edit_widget_list("单位间隔pixel", [it](QVector data) { + renderive::create_edit_widget_list("单位间隔pixel", [it](std::vector data) { it->set_tick_pixel_space(data[0]); }, {it->tick_pixel_space()}), @@ -382,7 +389,7 @@ static QVector cw(renderive::Time_Axis* it, int num_pre_second = -1) { QObject::connect(timer, &QTimer::timeout, [it]() { it->give_data(QTime::currentTime()); }); - t.append(renderive::create_edit_widget_list("推数据Timer(次/s)", [timer](QVector data) { + t.push_back(renderive::create_edit_widget_list("推数据Timer(次/s)", [timer](std::vector data) { if (data[0] < 0) { timer->stop(); } @@ -391,12 +398,14 @@ static QVector cw(renderive::Time_Axis* it, int num_pre_second = -1) { } }, {timer->interval() > 0 ? 1000.0 / timer->interval() : -1})); - return t + cw(dynamic_cast(it)); + std::vector axis_widgets = cw(dynamic_cast(it)); + t.insert(t.end(), axis_widgets.begin(), axis_widgets.end()); + return t; } -static QVector cw(renderive::Multi_Select_Rect* it) { +static std::vector cw(renderive::Multi_Select_Rect* it) { if (it == nullptr) return {}; - auto t = QVector{ + auto t = std::vector{ renderive::create_select_color("更改填充色", [it](const QColor& color) { it->set_rect_brush(QBrush(color)); }, it->rect_brush().color()), @@ -414,10 +423,10 @@ static QVector cw(renderive::Multi_Select_Rect* it) { return t; } template -static QVector cw(renderive::Hover_Info_Renderable* it) { +static std::vector cw(renderive::Hover_Info_Renderable* it) { if (it == nullptr) return {}; - auto t = QVector{ + auto t = std::vector{ renderive::create_label("悬浮信息"), renderive::create_toggle_button("启动悬浮信息", "停止悬浮信息", [it]() { it->set_use_hover_info(!it->use_hover_info()); @@ -435,23 +444,23 @@ static QVector cw(renderive::Hover_Info_Renderable* it) { }; return t; } -static QVector cw(renderive::Waterfall* it, int num_pre_second = -1) { +static std::vector cw(renderive::Waterfall* it, int num_pre_second = -1) { if (it == nullptr) return {}; auto timer = new QTimer(it->plot); if (num_pre_second > 0) timer->start(static_cast(1000.0 / static_cast(num_pre_second))); QObject::connect(timer, &QTimer::timeout, [it]() { - QVector d = renderive::get_data(it->power_range(), it->frequency_point_size()); + std::vector d = renderive::get_data(it->power_range(), it->frequency_point_size()); int tick = it->time_axis()->give_data(QTime::currentTime()); it->give_data(tick, d); }); - auto t = QVector{ + auto t = std::vector{ renderive::create_toggle_button("关闭可见区裁剪", "开启可见区裁剪", [it]() { it->set_visible_range_only(!it->visible_range_only()); }, it->visible_range_only()), create_image_interpolation_combo(it), - renderive::create_edit_widget_list("推数据Timer(次/s)", [timer](QVector data) { + renderive::create_edit_widget_list("推数据Timer(次/s)", [timer](std::vector data) { if (data[0] < 0) { timer->stop(); } @@ -460,9 +469,11 @@ static QVector cw(renderive::Waterfall* it, int num_pre_second = -1) { } }, {timer->interval() > 0 ? 1000.0 / timer->interval() : -1}), }; - return t + cw(static_cast*>(it)); + std::vector hover_widgets = cw(static_cast*>(it)); + t.insert(t.end(), hover_widgets.begin(), hover_widgets.end()); + return t; } -static QVector cw(renderive::Audio_Frequency* it, int num_pre_second = -1) { +static std::vector cw(renderive::Audio_Frequency* it, int num_pre_second = -1) { if (it == nullptr) return {}; auto timer = new QTimer(it->plot); @@ -472,8 +483,8 @@ static QVector cw(renderive::Audio_Frequency* it, int num_pre_second = int tick = it->time_axis()->give_data(QTime::currentTime()); it->give_data(tick, renderive::get_data({0, 20})); }); - auto t = QVector{ - renderive::create_edit_widget_list("推数据Timer(次/s)", [timer](QVector data) { + auto t = std::vector{ + renderive::create_edit_widget_list("推数据Timer(次/s)", [timer](std::vector data) { if (data[0] < 0) { timer->stop(); } @@ -485,7 +496,7 @@ static QVector cw(renderive::Audio_Frequency* it, int num_pre_second = }; return t; } -static QVector cw(renderive::Planisphere* it, int num_pre_second = -1) { +static std::vector cw(renderive::Planisphere* it, int num_pre_second = -1) { if (it == nullptr) return {}; auto timer = new QTimer(it->plot); @@ -498,8 +509,8 @@ static QVector cw(renderive::Planisphere* it, int num_pre_second = -1) }); if (num_pre_second > 0) timer->start(static_cast(1000.0 / static_cast(num_pre_second))); - auto t = QVector{ - renderive::create_edit_widget_list("推数据Timer(次/s)", [timer](QVector data) { + auto t = std::vector{ + renderive::create_edit_widget_list("推数据Timer(次/s)", [timer](std::vector data) { if (data[0] < 0) { timer->stop(); } @@ -508,7 +519,7 @@ static QVector cw(renderive::Planisphere* it, int num_pre_second = -1) } }, {timer->interval() > 0 ? 1000.0 / timer->interval() : -1}), - renderive::create_edit_widget_list("设置持续时间(ms)", [it](QVector data) { + renderive::create_edit_widget_list("设置持续时间(ms)", [it](std::vector data) { it->set_continue_millisecond(data[0]); }, {it->continue_millisecond()}), @@ -518,10 +529,10 @@ static QVector cw(renderive::Planisphere* it, int num_pre_second = -1) renderive::create_select_color("更改锚点颜色", [it](const QColor& color) { it->set_anchor_color(color); }, it->anchor_color()), - renderive::create_edit_widget_list("I范围", [it](QVector data) { + renderive::create_edit_widget_list("I范围", [it](std::vector data) { it->set_i_range({data[0], data[1]}); }, {it->i_range().origin, it->i_range().target}), - renderive::create_edit_widget_list("Q范围", [it](QVector data) { + renderive::create_edit_widget_list("Q范围", [it](std::vector data) { it->set_q_range({data[0], data[1]}); }, {it->q_range().origin, it->q_range().target}), renderive::create_button("设置到轴中心", [it]() { @@ -530,12 +541,12 @@ static QVector cw(renderive::Planisphere* it, int num_pre_second = -1) }; return t; } -static QVector cw(renderive::Afterglow* it, int num_pre_second = -1) { +static std::vector cw(renderive::Afterglow* it, int num_pre_second = -1) { if (it == nullptr) return {}; static auto create_base_data = [it]() { renderive::Range r = it->power_range(); - QVector ret(it->frequency_point_size()); + std::vector ret(it->frequency_point_size()); ret[0] = get_data(r); for (int i = 1; i < it->frequency_point_size(); ++i) { double rate; @@ -550,12 +561,12 @@ static QVector cw(renderive::Afterglow* it, int num_pre_second = -1) { } return ret; }; - static QVector base_data = create_base_data(); + static std::vector base_data = create_base_data(); auto timer = new QTimer(it->plot); QObject::connect(timer, &QTimer::timeout, [it]() { renderive::Range r = it->power_range(); int n = base_data.size(); - QVector cur_data(n); + std::vector cur_data(n); for (int i = 0; i < n; ++i) { double rate = renderive::get_data({0.8, 1.2}); cur_data[i] = renderive::clamp(base_data[i] * rate, r.origin, r.target); @@ -564,8 +575,8 @@ static QVector cw(renderive::Afterglow* it, int num_pre_second = -1) { }); if (num_pre_second > 0) timer->start(static_cast(1000.0 / static_cast(num_pre_second))); - auto t = QVector{ - renderive::create_edit_widget_list("推数据Timer(次/s)", [timer](QVector data) { + auto t = std::vector{ + renderive::create_edit_widget_list("推数据Timer(次/s)", [timer](std::vector data) { if (data[0] < 0) { timer->stop(); } @@ -574,11 +585,11 @@ static QVector cw(renderive::Afterglow* it, int num_pre_second = -1) { } }, {timer->interval() > 0 ? 1000.0 / timer->interval() : -1}), - renderive::create_edit_widget_list("频率范围", [it](QVector data) { + renderive::create_edit_widget_list("频率范围", [it](std::vector data) { it->set_frequency_range({data[0], data[1]}); }, {it->frequency_range().origin, it->frequency_range().target}), - renderive::create_edit_widget_list("功率概率范围", [it](QVector data) { + renderive::create_edit_widget_list("功率概率范围", [it](std::vector data) { it->set_power_range({data[0], data[1]}); }, {it->power_range().origin, it->power_range().target}), @@ -588,7 +599,7 @@ static QVector cw(renderive::Afterglow* it, int num_pre_second = -1) { }; return t; } -static QVector cw(renderive::Spectrum* it, int num_pre_second = -1) { +static std::vector cw(renderive::Spectrum* it, int num_pre_second = -1) { if (it == nullptr) return {}; auto timer = new QTimer(it->plot); @@ -597,12 +608,12 @@ static QVector cw(renderive::Spectrum* it, int num_pre_second = -1) { QObject::connect(timer, &QTimer::timeout, [it]() { it->give_data(renderive::get_data(it->power_axis()->coord_range(), it->frequency_point_size())); }); - auto t = QVector{ + auto t = std::vector{ renderive::create_toggle_button("关闭可见区裁剪", "开启可见区裁剪", [it]() { it->set_visible_range_only(!it->visible_range_only()); }, it->visible_range_only()), create_line_interpolation_combo(it), - renderive::create_edit_widget_list("推数据Timer(次/s)", [timer](QVector data) { + renderive::create_edit_widget_list("推数据Timer(次/s)", [timer](std::vector data) { if (data[0] < 0) { timer->stop(); } @@ -610,21 +621,21 @@ static QVector cw(renderive::Spectrum* it, int num_pre_second = -1) { timer->start((int)(1000.0 / data[0])); } }, {timer->interval() > 0 ? 1000.0 / timer->interval() : -1}), - renderive::create_edit_widget_list("频率范围", [it](QVector data) { + renderive::create_edit_widget_list("频率范围", [it](std::vector data) { it->set_frequency_range({data[0], data[1]}); }, {it->frequency_range().origin, it->frequency_range().target}), - renderive::create_edit_widget_list("设置频率点数", [it](QVector data) { + renderive::create_edit_widget_list("设置频率点数", [it](std::vector data) { it->set_frequency_point_size(data[0]); }, {it->frequency_point_size()}), renderive::create_label("sweepRect"), renderive::create_toggle_button("停止sweepRect", "启动sweepRect", [it]() { it->set_use_sweep_frequency_rect(!it->use_sweep_frequency_rect()); }, it->use_sweep_frequency_rect()), - renderive::create_edit_widget_list("sweepRect 范围", [it](QVector data) { + renderive::create_edit_widget_list("sweepRect 范围", [it](std::vector data) { if (!it->use_sweep_frequency_rect()) it->set_use_sweep_frequency_rect(true); it->set_sweep_frequency_range({data[0], data[1]}); }, {it->sweep_frequency_range().origin, it->sweep_frequency_range().target}), - renderive::create_edit_widget_list("中频", [it](QVector data) { + renderive::create_edit_widget_list("中频", [it](std::vector data) { if (!it->use_sweep_frequency_rect()) it->set_use_sweep_frequency_rect(true); it->set_middle_sweep_frequency(data[0]); }, {it->middle_sweep_frequency()}), @@ -644,25 +655,25 @@ static QVector cw(renderive::Spectrum* it, int num_pre_second = -1) { renderive::create_toggle_button("停止最小值marker", "启动最小值marker", [it]() { it->set_use_min_marker(!it->use_min_marker()); }, it->use_min_marker()), - renderive::create_edit_widget_list("添加自定义marker", [it](QVector data) { + renderive::create_edit_widget_list("添加自定义marker", [it](std::vector data) { it->add_custom_marker(data[0]); }, {0}), - renderive::create_edit_widget_list("添加自定义Linemarker", [it](QVector data) { + renderive::create_edit_widget_list("添加自定义Linemarker", [it](std::vector data) { it->add_custom_line_marker(data[0]); }, {0}), - renderive::create_edit_widget_list("删除marker", [it](QVector data) { + renderive::create_edit_widget_list("删除marker", [it](std::vector data) { it->remove_custom_marker(data[0]); }, {0}), - renderive::create_edit_widget_list("清楚所有marker", [it](QVector data) { + renderive::create_edit_widget_list("清楚所有marker", [it](std::vector data) { it->clear_all_custom_marker(); }, {}), - renderive::create_edit_widget_list("选择当前marker", [it](QVector data) { + renderive::create_edit_widget_list("选择当前marker", [it](std::vector data) { it->set_selected_line_marker(data[0]); }, {0}), - renderive::create_edit_widget_list("选择下一个marker", [it](QVector data) { + renderive::create_edit_widget_list("选择下一个marker", [it](std::vector data) { it->select_next(); }, {}), - renderive::create_edit_widget_list("选择上一个marker", [it](QVector data) { + renderive::create_edit_widget_list("选择上一个marker", [it](std::vector data) { it->select_previous(); }, {}), renderive::create_label("当前线"), @@ -701,9 +712,9 @@ static QVector cw(renderive::Spectrum* it, int num_pre_second = -1) { it->set_min_brush(c); }, it->min_brush().color()), }; - return cw(static_cast*>(it)) + t; + return concat_widgets(cw(static_cast*>(it)), t); } -static QVector cw(renderive::Sweep_Frequency* it, int num_pre_second = -1) { +static std::vector cw(renderive::Sweep_Frequency* it, int num_pre_second = -1) { if (it == nullptr) return {}; auto timer = new QTimer(it->plot); @@ -712,11 +723,11 @@ static QVector cw(renderive::Sweep_Frequency* it, int num_pre_second = QObject::connect(timer, &QTimer::timeout, [it]() { it->give_data(renderive::get_data(it->power_axis()->coord_range(), it->block_frequency_point_size())); }); - auto t = QVector{ + auto t = std::vector{ renderive::create_toggle_button("关闭可见区裁剪", "开启可见区裁剪", [it]() { it->set_visible_range_only(!it->visible_range_only()); }, it->visible_range_only()), - create_line_interpolation_combo(it), renderive::create_edit_widget_list("推数据Timer(次/s)", [timer](QVector data) { + create_line_interpolation_combo(it), renderive::create_edit_widget_list("推数据Timer(次/s)", [timer](std::vector data) { if (data[0] < 0) { timer->stop(); } @@ -732,13 +743,13 @@ static QVector cw(renderive::Sweep_Frequency* it, int num_pre_second = p.setColor(color); it->set_cur_frequency_pen(p); }, it->cur_frequency_pen().color()), - renderive::create_edit_widget_list("频率范围", [it](const QVector& data) { + renderive::create_edit_widget_list("频率范围", [it](const std::vector& data) { it->set_frequency_range({data[0], data[1]}); }, {it->frequency_range().origin, it->frequency_range().target}) }; return t; } -static QMenu* create_menu(renderive::Abs_Plot* plot, const QVector& widgets) { +static QMenu* create_menu(renderive::Abs_Plot* plot, const std::vector& widgets) { auto menu = new QMenu(plot); menu->setObjectName("memu"); // menu->setStyle(nullptr); @@ -756,7 +767,7 @@ static QMenu* create_menu(renderive::Abs_Plot* plot, const QVector& wi menu->addAction(scrollAction); return menu; } -static QMenu* create_menu_normal(QWidget* w, const QVector& widgets) { +static QMenu* create_menu_normal(QWidget* w, const std::vector& widgets) { auto menu = new QMenu(w); menu->setObjectName("memu_normal"); // menu->setStyle(nullptr); diff --git a/Renderive/base/global.cpp b/Renderive/base/global.cpp index d32277d..a1b74dc 100644 --- a/Renderive/base/global.cpp +++ b/Renderive/base/global.cpp @@ -2,6 +2,7 @@ #include #include #include "../architecture/Plot_p.h" +#include "Memory.h" #include "asio.hpp" namespace renderive { struct Render_Scheduler_Private { @@ -10,7 +11,7 @@ struct Render_Scheduler_Private { std::unique_ptr cpu_pool; std::thread scheduler_thread; std::thread::id scheduler_thread_id; - QList plots; + std::vector plots; std::atomic_bool running{false}; std::atomic_bool cpu_joined{true}; }; @@ -23,6 +24,7 @@ Render_Scheduler::~Render_Scheduler() { shutdown(); } void Render_Scheduler::start() { + memory_resource(); if (d->running.exchange(true, std::memory_order_acq_rel)) return; d->io_context.restart(); @@ -65,10 +67,10 @@ void Render_Scheduler::shutdown() { d->scheduler_thread.join(); d->cpu_pool.reset(); } -void Render_Scheduler::post(std::function task) { +void Render_Scheduler::post(Task task) { asio::post(d->io_context, std::move(task)); } -void Render_Scheduler::post_cpu(std::function task) { +void Render_Scheduler::post_cpu(Task task) { if (d->cpu_pool) { asio::post(*d->cpu_pool, std::move(task)); } @@ -107,7 +109,7 @@ void Render_Scheduler::join_cpu_pool() { if (d->cpu_pool && !d->cpu_joined.exchange(true, std::memory_order_acq_rel)) d->cpu_pool->join(); } -void Render_Scheduler::post_and_wait(std::function task) { +void Render_Scheduler::post_and_wait(Task task) { if (is_scheduler_thread() || !d->running.load(std::memory_order_acquire)) { task(); return; @@ -115,7 +117,7 @@ void Render_Scheduler::post_and_wait(std::function task) { std::mutex mutex; std::condition_variable cv; bool done = false; - post([&task, &mutex, &cv, &done]() { + post([task = std::move(task), &mutex, &cv, &done]() mutable { task(); { std::lock_guard lock(mutex); @@ -129,22 +131,21 @@ void Render_Scheduler::post_and_wait(std::function task) { }); } void Render_Scheduler::register_plot_on_scheduler(Abs_Plot* plot) { - if (!d->plots.contains(plot)) - d->plots.append(plot); + if (std::find(d->plots.begin(), d->plots.end(), plot) == d->plots.end()) + d->plots.push_back(plot); if (d->running.load(std::memory_order_acquire)) initialize_plot_on_scheduler(plot); } void Render_Scheduler::remove_plot_on_scheduler(Abs_Plot* plot) { - d->plots.removeAll(plot); + d->plots.erase(std::remove(d->plots.begin(), d->plots.end(), plot), d->plots.end()); Plot_Private* data = plot->d; data->render_enabled.store(false, std::memory_order_release); - if (data->render_timer) { + if (data->render_timer) data->render_timer->cancel(); - data->render_timer.reset(); - } if (data->submit_timer) { data->submit_timer->cancel(); - data->submit_timer.reset(); + data->submit_timer_target_ns = 0; + data->render_submit_pending.store(false, std::memory_order_release); } } void Render_Scheduler::shutdown_on_scheduler() { @@ -152,13 +153,12 @@ void Render_Scheduler::shutdown_on_scheduler() { Plot_Private* data = plot->d; data->destroying.store(true, std::memory_order_release); data->render_enabled.store(false, std::memory_order_release); - if (data->render_timer) { + if (data->render_timer) data->render_timer->cancel(); - data->render_timer.reset(); - } if (data->submit_timer) { data->submit_timer->cancel(); - data->submit_timer.reset(); + data->submit_timer_target_ns = 0; + data->render_submit_pending.store(false, std::memory_order_release); } } d->plots.clear(); diff --git a/Renderive/base/global.h b/Renderive/base/global.h index f1ba9a6..80d658a 100644 --- a/Renderive/base/global.h +++ b/Renderive/base/global.h @@ -1,5 +1,6 @@ #pragma once #include "Renderive/architecture/global.h" +#include "Task.h" #include "psc_global_include/Singleton.hpp" extern int qInitResources_resource(); namespace asio { @@ -14,8 +15,8 @@ public: ~Render_Scheduler(); void start(); void shutdown(); - void post(std::function task); - void post_cpu(std::function task); + void post(Task task); + void post_cpu(Task task); void register_plot(Abs_Plot* plot); void remove_plot(Abs_Plot* plot); [[nodiscard]] bool is_scheduler_thread() const; @@ -23,7 +24,7 @@ public: asio::io_context& io_context(); private: void join_cpu_pool(); - void post_and_wait(std::function task); + void post_and_wait(Task task); void register_plot_on_scheduler(Abs_Plot* plot); void remove_plot_on_scheduler(Abs_Plot* plot); void shutdown_on_scheduler(); @@ -36,7 +37,7 @@ public: int interval = 10; Render_Scheduler& render_scheduler(); void start_render_scheduler(); - QVector color_map = get_turbo_color_gradient(); + std::vector color_map = get_turbo_color_gradient(); private: Render_Scheduler render_scheduler_value; }; diff --git a/Renderive/export.h b/Renderive/export.h index ce5c0e2..2b1ec16 100644 --- a/Renderive/export.h +++ b/Renderive/export.h @@ -2,6 +2,7 @@ #include "Axis/export.h" #include "Generate_Mock_Data.h" #include "architecture/export.h" +#include "base/Memory.h" #include "plottable/export.h" #include "primitive/export.h" struct Screen_Info { diff --git a/Renderive/plottable/Afterglow.cpp b/Renderive/plottable/Afterglow.cpp index 113de51..05c5895 100644 --- a/Renderive/plottable/Afterglow.cpp +++ b/Renderive/plottable/Afterglow.cpp @@ -1,5 +1,7 @@ #include #include "Afterglow_p.h" +#include "../base/Frame_Memory.h" +#include "../base/Memory.h" namespace renderive { using Afterglow_Binding = Renderable_Binding; Afterglow::Afterglow() { @@ -8,13 +10,13 @@ Afterglow::Afterglow() { Afterglow_Private* Afterglow::d() { return Afterglow_Binding::d(this); } -Render_Data* Afterglow::create_render_data() { +Render_Object_Owner Afterglow::create_render_data() { return Afterglow_Binding::create_render_data(); } -Render_State* Afterglow::create_render_state() { +Render_Object_Owner Afterglow::create_render_state() { return Afterglow_Binding::create_render_state(); } -Input_Data* Afterglow::create_input_data() { +Render_Object_Owner Afterglow::create_input_data() { return Afterglow_Binding::create_input_data(); } std::shared_ptr Afterglow::frequency_axis(SRC src) { @@ -22,16 +24,12 @@ std::shared_ptr Afterglow::frequency_axis(SRC src) { } void Afterglow::set_frequency_axis(std::shared_ptr value) { d()->set_state_value(&Afterglow_Render_State::frequency_axis, std::weak_ptr(value)); - if (auto image = image_item.lock()) - image->set_x_axis(value); } std::shared_ptr Afterglow::power_axis(SRC src) { return d()->state_value(&Afterglow_Render_State::power_axis, src).lock(); } void Afterglow::set_power_axis(std::shared_ptr value) { d()->set_state_value(&Afterglow_Render_State::power_axis, std::weak_ptr(value)); - if (auto image = image_item.lock()) - image->set_y_axis(value); } Range Afterglow::frequency_range(SRC src) { return d()->state_value(&Afterglow_Render_State::frequency_range, src); @@ -40,8 +38,6 @@ void Afterglow::set_frequency_range(Range value) { if (value.length() == 0.0) return; d()->set_state_value(&Afterglow_Render_State::frequency_range, std::move(value)); - if (auto image = image_item.lock()) - image->set_x_range(frequency_range()); } Range Afterglow::power_range(SRC src) { return d()->state_value(&Afterglow_Render_State::power_range, src); @@ -50,8 +46,6 @@ void Afterglow::set_power_range(Range value) { if (value.size() == 0.0) return; d()->set_state_value(&Afterglow_Render_State::power_range, std::move(value)); - if (auto image = image_item.lock()) - image->set_y_range(power_range()); auto bar = color_bar_item.lock(); if (bar) bar->set_range(power_range()); @@ -116,11 +110,11 @@ Afterglow::Builder::Builder(std::shared_ptr parent, std::shared_ptr< init(parent, frequency_axis, power_axis); } std::shared_ptr Afterglow::Builder::build() { - std::shared_ptr < Afterglow > ret(new Afterglow()); + auto ret = renderive::make_shared(); set(ret.get()); return ret; } -void Afterglow::give_data(const QVector& power_range_data) { +void Afterglow::give_data(std::span power_range_data) { if (!ok()) return; Afterglow_Private* pd = d(); @@ -131,14 +125,25 @@ void Afterglow::give_data(const QVector& power_range_data) { Render_Input_Lease input(pd); input->data_list.push(power_range_data, Afterglow_Input_Data::Max_Pending_Frame_Count); } -void Afterglow_Private::push_data(const QVector& power_range_data, const Afterglow_Render_State* s) { +void Afterglow::give_data(std::pmr::vector&& power_range_data) { + if (!ok()) + return; + Afterglow_Private* pd = d(); + Render_State_Lease state(pd); + int frequency_point_size = state->frequency_point_size; + if (power_range_data.size() != frequency_point_size) + return; + Render_Input_Lease input(pd); + input->data_list.push(std::move(power_range_data), Afterglow_Input_Data::Max_Pending_Frame_Count); +} +void Afterglow_Private::push_data(std::span power_range_data, const Afterglow_Render_State* s) { if (power_range_data.size() != s->frequency_point_size || s->init_strong_value <= 0 || s->power_range.size() == 0.0 || s->frequency_point_size <= 0 || s->power_point_size <= 0) return; - QVector index_list(s->frequency_point_size); + std::pmr::vector index_list(s->frequency_point_size, frame_memory_resource()); double rate = (double)s->power_point_size / s->power_range.size(); for (int i = 0; i < s->frequency_point_size; ++i) { int line_index = (int)((power_range_data[i] - s->power_range.origin) * rate); diff --git a/Renderive/plottable/Afterglow.h b/Renderive/plottable/Afterglow.h index 8797bc6..779a285 100644 --- a/Renderive/plottable/Afterglow.h +++ b/Renderive/plottable/Afterglow.h @@ -1,8 +1,13 @@ #pragma once +#include +#include +#include +#include +#include +#include "../base/Memory.h" #include "global.h" #include "../primitive/Color_Bar.h" #include "../primitive/Color_Map.h" -#include "../primitive/Raster_Image.h" namespace renderive { class Frequency_Axis; class Afterglow_Plot; @@ -12,9 +17,9 @@ class LIB_DECL Afterglow : public Renderable { public: /// 返回私有渲染数据。 Afterglow_Private* d(); - Render_Data* create_render_data() override; - Render_State* create_render_state() override; - Input_Data* create_input_data() override; + Render_Object_Owner create_render_data() override; + Render_Object_Owner create_render_state() override; + Render_Object_Owner create_input_data() override; Afterglow(); PROP(std::shared_ptr, frequency_axis) PROP(std::shared_ptr, power_axis) @@ -29,12 +34,19 @@ public: PROP(Color_Map, color_map) std::shared_ptr color_bar() const; /// 写入一帧功率数据。 - void give_data(const QVector& power_range_data); + void give_data(std::span power_range_data); + void give_data(std::pmr::vector&& power_range_data); + template + void write_data(std::size_t count, Writer&& writer) { + std::pmr::vector values(memory_resource(Memory_Domain::Afterglow)); + values.resize(count); + std::forward(writer)(std::span(values.data(), values.size())); + give_data(std::move(values)); + } template struct Builder_T; struct Builder; std::weak_ptr color_bar_item; - std::weak_ptr image_item; }; struct Afterglow_Prop { std::weak_ptr frequency_axis{}; diff --git a/Renderive/plottable/Afterglow_p.h b/Renderive/plottable/Afterglow_p.h index 61e4777..3d382f0 100644 --- a/Renderive/plottable/Afterglow_p.h +++ b/Renderive/plottable/Afterglow_p.h @@ -1,10 +1,13 @@ #pragma once #include #include +#include +#include #include "../Axis/Frequency_Axis.h" #include "../Axis/Frequency_Axis_p.h" #include "../architecture/Bounded_Input_Buffer.h" #include "../architecture/Plot_p.h" +#include "../base/Memory.h" #include "../base/global.h" #include "../primitive/Raster_Image.h" #include "Afterglow.h" @@ -45,9 +48,9 @@ struct Afterglow_Private : Typed_Render_Data cached_power_data; - QVector old_cache_power_data; - QVector merged_power_data; + std::pmr::vector cached_power_data{memory_resource(Memory_Domain::Afterglow)}; + std::pmr::vector old_cache_power_data{memory_resource(Memory_Domain::Afterglow)}; + std::pmr::vector merged_power_data{memory_resource(Memory_Domain::Afterglow)}; [[nodiscard]] int strength_value(const Afterglow_Render_State* s) const { if (s->attenuation_rate == 1.0) return std::numeric_limits::max(); @@ -58,22 +61,46 @@ struct Afterglow_Private : Typed_Render_Data& power_range_data, const Afterglow_Render_State* s); + void push_data(std::span power_range_data, const Afterglow_Render_State* s); + void draw_image(QPainter* painter, const Afterglow_Render_State* s) { + auto frequency_axis = s->frequency_axis.lock(); + auto power_axis = s->power_axis.lock(); + if (!frequency_axis || !power_axis || image.is_null()) + return; + if (s->frequency_range.length() == 0.0 || s->power_range.length() == 0.0) + return; + double x = frequency_axis->coord_to_pixel(s->frequency_range.origin, SRC::Render); + double y = power_axis->coord_to_pixel(s->power_range.origin, SRC::Render); + double w = frequency_axis->coord_to_pixel(s->frequency_range.target, SRC::Render) - x; + double h = power_axis->coord_to_pixel(s->power_range.target, SRC::Render) - y; + Range x_axis_range = frequency_axis->coord_range(SRC::Render); + Range y_axis_range = power_axis->coord_range(SRC::Render); + double hr = ((x_axis_range.origin < x_axis_range.target) ^ (s->frequency_range.origin < s->frequency_range.target)) ? -1.0 : 1.0; + double vr = ((y_axis_range.origin < y_axis_range.target) ^ (s->power_range.origin < s->power_range.target)) ? -1.0 : 1.0; + painter->save(); + painter->scale(hr, vr); + image.draw(painter, QRectF(hr * x, vr * y, hr * w, vr * h)); + painter->restore(); + } void draw(QPainter* painter, const Plot_Render_Snapshot& snapshot) override { - Q_UNUSED(painter) Afterglow_Render_State* s = render_state(); + draw_image(painter, s); Performance_Shower* shower = snapshot.performance_shower.get(); if (shower) { shower->set_performance_line("col_count, row_count", Performance_Line(QString("col_count:%1, row_count:%2").arg(s->frequency_point_size).arg(s->power_point_size))); } } + std::size_t estimated_frame_memory(const Plot_Render_Snapshot&) override { + Afterglow_Render_State* s = render_state(); + return static_cast(std::max(0, s->frequency_point_size)) * sizeof(int) + 8 * 1024; + } void prepare_data(const Plot_Render_Snapshot& snapshot) override { sync_state_pipeline(); Afterglow_Render_State* s = render_state(); @@ -94,7 +121,7 @@ struct Afterglow_Private : Typed_Render_Datadata_list.read_all([this, s, input](const QVector& data) { + input->data_list.read_all([this, s, input](const auto& data) { if (data.size() != s->frequency_point_size) { input->data_list.record_consumer_dropped(); return; @@ -105,8 +132,6 @@ struct Afterglow_Private : Typed_Render_Datafrequency_point_size, s->power_point_size, s->color_map); cache_image_dirty = false; - if (auto image_item = q()->image_item.lock()) - image_item->give_image(image.image(), false); } clear_render_input(); } @@ -144,13 +169,6 @@ void Afterglow::Builder_T::set(Afterglow* ret) { sc->buffer_size = qMax(1, sc->frequency_point_size * sc->power_point_size); sc->attenuation_rate = std::clamp(sc->attenuation_rate, 0.0, 1.0); sc->init_strong_value = qMax(1, sc->init_strong_value); - auto frequency_axis = static_cast(this)->frequency_axis.lock(); - auto power_axis = static_cast(this)->power_axis.lock(); - if (frequency_axis && power_axis) { - auto image = Raster_Image::Builder(owner, frequency_axis, power_axis).set_x_range(sc->frequency_range).set_y_range(sc->power_range).build(); - image->object_name = "AfterglowRasterImage"; - ret->image_item = image; - } if (static_cast(this)->color_bar_enabled) { auto bar = Color_Bar::Builder(owner).set_color_map(sc->color_map).set_range(sc->power_range).set_name("Afterglow").build(); ret->color_bar_item = bar; diff --git a/Renderive/plottable/Audio_Frequency.cpp b/Renderive/plottable/Audio_Frequency.cpp index 63f5393..d5b261f 100644 --- a/Renderive/plottable/Audio_Frequency.cpp +++ b/Renderive/plottable/Audio_Frequency.cpp @@ -1,5 +1,6 @@ #include "../Axis/Time_Axis.h" #include "Audio_Frequency_p.h" +#include "../base/Memory.h" namespace renderive { using Audio_Frequency_Binding = Renderable_Binding; Audio_Frequency::Audio_Frequency() { @@ -8,13 +9,13 @@ Audio_Frequency::Audio_Frequency() { Audio_Frequency_Private* Audio_Frequency::d() { return Audio_Frequency_Binding::d(this); } -Render_Data* Audio_Frequency::create_render_data() { +Render_Object_Owner Audio_Frequency::create_render_data() { return Audio_Frequency_Binding::create_render_data(); } -Render_State* Audio_Frequency::create_render_state() { +Render_Object_Owner Audio_Frequency::create_render_state() { return Audio_Frequency_Binding::create_render_state(); } -Input_Data* Audio_Frequency::create_input_data() { +Render_Object_Owner Audio_Frequency::create_input_data() { return Audio_Frequency_Binding::create_input_data(); } int Audio_Frequency::time_point_size(SRC src) { @@ -69,7 +70,7 @@ Audio_Frequency::Builder::Builder(std::shared_ptr parent, std::share this->value_axis = value_axis; } std::shared_ptr Audio_Frequency::Builder::build() { - std::shared_ptr < Audio_Frequency > ret(new Audio_Frequency()); + auto ret = renderive::make_shared(); ret->init(parent); Audio_Frequency_Private* pd = ret->d(); Audio_Frequency_Render_State* sc = reinterpret_cast(pd->state(State_Edit)); diff --git a/Renderive/plottable/Audio_Frequency.h b/Renderive/plottable/Audio_Frequency.h index 55b6f08..c4ea770 100644 --- a/Renderive/plottable/Audio_Frequency.h +++ b/Renderive/plottable/Audio_Frequency.h @@ -9,9 +9,9 @@ class LIB_DECL Audio_Frequency : public Renderable { public: /// 返回私有渲染数据。 Audio_Frequency_Private* d(); - Render_Data* create_render_data() override; - Render_State* create_render_state() override; - Input_Data* create_input_data() override; + Render_Object_Owner create_render_data() override; + Render_Object_Owner create_render_state() override; + Render_Object_Owner create_input_data() override; Audio_Frequency(); /// 写入一个 tick 和对应数值。 void give_data(int tick, double data); diff --git a/Renderive/plottable/Audio_Frequency_p.h b/Renderive/plottable/Audio_Frequency_p.h index 57f30c6..69466be 100644 --- a/Renderive/plottable/Audio_Frequency_p.h +++ b/Renderive/plottable/Audio_Frequency_p.h @@ -1,9 +1,12 @@ #pragma once +#include +#include #include "../Axis/Axis_p.h" #include "../Axis/Time_Axis_p.h" #include "../architecture/Bounded_Input_Buffer.h" #include "../architecture/Plot_p.h" #include "../architecture/Renderable.h" +#include "../base/Memory.h" #include "../primitive/Curve.h" #include "Audio_Frequency.h" #include "Performance_Shower_p.h" @@ -36,8 +39,8 @@ struct Audio_Frequency_Input_Data : Input_Data { struct Audio_Frequency_Render_State : Render_State, Audio_Frequency_Prop {}; struct Audio_Frequency_Private : Typed_Render_Data { friend class Audio_Frequency; - Psc::StreamRingBuffer_ST buffer; - std::vector power_snapshot; + Psc::StreamRingBuffer_ST buffer{memory_resource(Memory_Domain::Audio)}; + std::pmr::vector power_snapshot{memory_resource(Memory_Domain::Audio)}; int data_count{}; int point_count{}; void resize_power_buffer(int count) { @@ -78,7 +81,7 @@ struct Audio_Frequency_Private : Typed_Render_Datavalue_axis.lock(); if (time_axis && value_axis) { int start = (int)time_axis->start_coord(); - QVector points = Curve_Utils::create_points(data_count, [this, time_axis, start](int i) { + std::pmr::vector points = Curve_Utils::create_points(data_count, [this, time_axis, start](int i) { double* power = power_snapshot.data() + data_count - 1 - i; int index = time_axis->d()->time_ticker.start_coord_to_end_coord ? start + i : start + time_axis->d()->time_ticker.time_point_size - i; return QPointF(index, *power); diff --git a/Renderive/plottable/Hover_Info.cpp b/Renderive/plottable/Hover_Info.cpp index 86a28ce..f0f1dab 100644 --- a/Renderive/plottable/Hover_Info.cpp +++ b/Renderive/plottable/Hover_Info.cpp @@ -6,7 +6,7 @@ void Hover_Info_Render_State::draw_hover(QPainter* painter, Abs_Axis* h, Abs_Axi auto that = dynamic_cast(this); renderable = dynamic_cast(that->d_ptr->q_ptr); } - QVector lines = renderable->create_hover_string(h, v, pos); + std::vector lines = renderable->create_hover_string(h, v, pos); QFontMetrics fm(hover_info_font); int width = std::numeric_limits::min(); for (QString& s : lines) @@ -26,7 +26,7 @@ bool Hover_Info_Render_State::hover_ok(Renderable* able) const { auto tmp = dynamic_cast(able); return use_hover_info && hover_info_active && tmp->hover_test(hover_info_pos); } -QVector Hover_Info_Renderable_Interface::create_hover_string(Abs_Axis* h, Abs_Axis* v, QPoint pos) { +std::vector Hover_Info_Renderable_Interface::create_hover_string(Abs_Axis* h, Abs_Axis* v, QPoint pos) { double tick1 = h->pixel_to_coord(pos.x(), SRC::Render); double tick2 = v->pixel_to_coord(pos.y(), SRC::Render); QString line1 = h->get_tick_label(tick1, SRC::Render) + h->unit_text(); diff --git a/Renderive/plottable/Hover_Info.h b/Renderive/plottable/Hover_Info.h index 7e4f1bb..40e2452 100644 --- a/Renderive/plottable/Hover_Info.h +++ b/Renderive/plottable/Hover_Info.h @@ -21,7 +21,7 @@ struct LIB_DECL Hover_Info_Render_State { bool hover_ok(Renderable* able) const; }; struct LIB_DECL Hover_Info_Renderable_Interface { - virtual QVector create_hover_string(Abs_Axis* h, Abs_Axis* v, QPoint pos); + virtual std::vector create_hover_string(Abs_Axis* h, Abs_Axis* v, QPoint pos); virtual ~Hover_Info_Renderable_Interface() = default; virtual void draw_hover(QPainter* painter, Abs_Axis* h, Abs_Axis* v) { return hover_state(SRC::Render)->draw_hover(painter, h, v); diff --git a/Renderive/plottable/Interpolation_p.h b/Renderive/plottable/Interpolation_p.h index a8f52bc..b6d4f67 100644 --- a/Renderive/plottable/Interpolation_p.h +++ b/Renderive/plottable/Interpolation_p.h @@ -1,10 +1,12 @@ #pragma once #include #include -#include #include #include +#include +#include #include "../Axis/Abs_Axis.h" +#include "../base/Frame_Memory.h" #include "Interpolation.h" namespace renderive { struct Line_Sample_Window { @@ -85,11 +87,11 @@ double interpolate_line_value(double index, int count, Line_Interpolation_Mode m return left_value; } template -QVector create_line_points(Abs_Axis* x_axis, Abs_Axis* y_axis, const Range& data_range, int total_count, int available_count, bool visible_range_only, Line_Interpolation_Mode mode, Value_At value_at) { +std::pmr::vector create_line_points(Abs_Axis* x_axis, Abs_Axis* y_axis, const Range& data_range, int total_count, int available_count, bool visible_range_only, Line_Interpolation_Mode mode, Value_At value_at) { Line_Sample_Window window = line_sample_window(x_axis, data_range, total_count, available_count, visible_range_only); if (!window) return {}; - QVector points(window.sample_count); + std::pmr::vector points(window.sample_count, frame_memory_resource()); double denominator = static_cast(std::max(window.sample_count - 1, 1)); for (int i = 0; i < window.sample_count; ++i) { double index = window.first_index + (window.last_index - window.first_index) * static_cast(i) / denominator; diff --git a/Renderive/plottable/Multi_Select_Rect.cpp b/Renderive/plottable/Multi_Select_Rect.cpp index cad9e0c..adf0e51 100644 --- a/Renderive/plottable/Multi_Select_Rect.cpp +++ b/Renderive/plottable/Multi_Select_Rect.cpp @@ -1,4 +1,5 @@ #include "Multi_Select_Rect_p.h" +#include "../base/Memory.h" namespace renderive { using Multi_Select_Rect_Binding = Renderable_Binding; Multi_Select_Rect::Multi_Select_Rect() { @@ -7,13 +8,13 @@ Multi_Select_Rect::Multi_Select_Rect() { Multi_Select_Rect_Private* Multi_Select_Rect::d() { return Multi_Select_Rect_Binding::d(this); } -Render_Data* Multi_Select_Rect::create_render_data() { +Render_Object_Owner Multi_Select_Rect::create_render_data() { return Multi_Select_Rect_Binding::create_render_data(); } -Render_State* Multi_Select_Rect::create_render_state() { +Render_Object_Owner Multi_Select_Rect::create_render_state() { return Multi_Select_Rect_Binding::create_render_state(); } -Input_Data* Multi_Select_Rect::create_input_data() { +Render_Object_Owner Multi_Select_Rect::create_input_data() { return Multi_Select_Rect_Binding::create_input_data(); } Multi_Select_Rect::Builder::Builder(std::shared_ptr parent, std::shared_ptr key_axis, std::shared_ptr value_axis) { @@ -27,7 +28,7 @@ Multi_Select_Rect::Builder::Builder(std::shared_ptr parent, std::sha this->value_axis = value_axis; } std::shared_ptr Multi_Select_Rect::Builder::build() { - std::shared_ptr < Multi_Select_Rect > ret(new Multi_Select_Rect()); + auto ret = renderive::make_shared(); ret->init(parent); Multi_Select_Rect_Private* pd = ret->d(); Multi_Select_Rect_Render_State* sc = reinterpret_cast(pd->state(State_Edit)); diff --git a/Renderive/plottable/Multi_Select_Rect.h b/Renderive/plottable/Multi_Select_Rect.h index 92dc329..2562f71 100644 --- a/Renderive/plottable/Multi_Select_Rect.h +++ b/Renderive/plottable/Multi_Select_Rect.h @@ -7,9 +7,9 @@ class LIB_DECL Multi_Select_Rect : public Renderable { public: /// 返回私有渲染数据。 Multi_Select_Rect_Private* d(); - Render_Data* create_render_data() override; - Render_State* create_render_state() override; - Input_Data* create_input_data() override; + Render_Object_Owner create_render_data() override; + Render_Object_Owner create_render_state() override; + Render_Object_Owner create_input_data() override; Multi_Select_Rect(); public: /// 设置水平坐标轴。 diff --git a/Renderive/plottable/Multi_Select_Rect_p.h b/Renderive/plottable/Multi_Select_Rect_p.h index 10807a9..2b23f75 100644 --- a/Renderive/plottable/Multi_Select_Rect_p.h +++ b/Renderive/plottable/Multi_Select_Rect_p.h @@ -4,7 +4,7 @@ #include "Multi_Select_Rect.h" namespace renderive { struct Multi_Select_Rect_Render_State : renderive::Render_State { - QVector rects; + std::vector rects; QFont font; QPen border_pen, font_pen = QPen(Qt::white); QBrush rect_brush = QColor(0, 0, 255, 50); @@ -29,7 +29,7 @@ struct Multi_Select_Rect_Private : renderive::Typed_Render_Datavertical_axis.lock(); if (!horizontal_axis || !vertical_axis) return; - QVector& rect_list = edit->rects; + std::vector& rect_list = edit->rects; if (!(QApplication::keyboardModifiers() & Qt::ControlModifier)) { rect_list.clear(); } @@ -38,13 +38,13 @@ struct Multi_Select_Rect_Private : renderive::Typed_Render_Data= 0; i--) { auto& rect = rect_list[i]; if (qFuzzyCompare(rect.width(), 0.0) || qFuzzyCompare(rect.height(), 0.0)) { - rect_list.removeAt(i); + rect_list.erase(rect_list.begin() + i); } } } double x = horizontal_axis->pixel_to_coord(event->pos().x(), SRC::Render); double y = vertical_axis->pixel_to_coord(event->pos().y(), SRC::Render); - rect_list.append(QRectF(QPointF(x, y), QPointF(x, y))); + rect_list.push_back(QRectF(QPointF(x, y), QPointF(x, y))); active = true; } void mouseMoveEvent(QMouseEvent* event) override { @@ -57,11 +57,11 @@ struct Multi_Select_Rect_Private : renderive::Typed_Render_Datavertical_axis.lock(); if (!horizontal_axis || !vertical_axis) return; - QVector& rect_list = edit->rects; + std::vector& rect_list = edit->rects; if (rect_list.empty()) return; - rect_list.last().setBottomRight(event->pos()); - QRectF& r = rect_list.last(); + rect_list.back().setBottomRight(event->pos()); + QRectF& r = rect_list.back(); r.setRight(horizontal_axis->pixel_to_coord(r.right(), SRC::Render)); r.setBottom(vertical_axis->pixel_to_coord(r.bottom(), SRC::Render)); } @@ -71,10 +71,10 @@ struct Multi_Select_Rect_Private : renderive::Typed_Render_Data edit(this); active = false; - QVector& rect_list = edit->rects; + std::vector& rect_list = edit->rects; if (rect_list.empty()) return; - rect_list.removeLast(); + rect_list.pop_back(); } void keyReleaseEvent(QKeyEvent* event) override {} void draw(QPainter* painter, const Plot_Render_Snapshot&) override { @@ -85,7 +85,7 @@ struct Multi_Select_Rect_Private : renderive::Typed_Render_Datavertical_axis.lock(); if (!horizontal_axis || !vertical_axis) return; - QList draw_rect_list; + std::vector draw_rect_list; for (QRectF r : s->rects) { if (qFuzzyCompare(r.width(), 0.0) || qFuzzyCompare(r.height(), 0.0)) continue; @@ -93,7 +93,7 @@ struct Multi_Select_Rect_Private : renderive::Typed_Render_Datacoord_to_pixel(r.y(), SRC::Render)); r.setRight(horizontal_axis->coord_to_pixel(r.right(), SRC::Render)); r.setBottom(vertical_axis->coord_to_pixel(r.bottom(), SRC::Render)); - draw_rect_list.append(r); + draw_rect_list.push_back(r); } painter->save(); QFontMetrics xfm(horizontal_axis->unit_text_font()); diff --git a/Renderive/plottable/Performance_Shower.cpp b/Renderive/plottable/Performance_Shower.cpp index 15b094d..beb180d 100644 --- a/Renderive/plottable/Performance_Shower.cpp +++ b/Renderive/plottable/Performance_Shower.cpp @@ -1,5 +1,123 @@ #include "../plottable/Performance_Shower_p.h" +#include +#include +#include +#include +#include "Core/spdlog/export.h" namespace renderive { +namespace { +static double non_negative(double value) { + return value > 0.0 ? value : 0.0; +} +static double ratio_percent(double value, double total) { + return total > 0.0 ? value * 100.0 / total : 0.0; +} +struct Frame_Phase_Breakdown { + double data_ms{}; + double painter_ms{}; + double queue_ms{}; + double lock_wait_ms{}; + double measured_ms{}; + double other_ms{}; + double phase_total_ms{}; + double necessary_ms{}; + double unnecessary_ms{}; +}; +static Frame_Phase_Breakdown frame_phase_breakdown(const Frame_Lifecycle_Sample& sample) { + Frame_Phase_Breakdown value; + value.data_ms = sample.prepare_data_ms + sample.image_prepare_ms; + value.painter_ms = sample.painter_draw_ms + sample.painter_end_ms + sample.paint_draw_image_ms; + value.queue_ms = sample.request_to_submit_ms + sample.cpu_queue_wait_ms + sample.update_to_paint_ms; + value.lock_wait_ms = sample.color_wait_ms + sample.paint_consume_ms + sample.publish_color_ms; + value.measured_ms = value.data_ms + value.painter_ms + value.queue_ms + value.lock_wait_ms + sample.stats_log_ms; + value.phase_total_ms = sample.frame_total_ms > value.measured_ms ? sample.frame_total_ms : value.measured_ms; + value.other_ms = non_negative(value.phase_total_ms - value.measured_ms); + value.necessary_ms = value.data_ms + value.painter_ms; + value.unnecessary_ms = non_negative(value.phase_total_ms - value.necessary_ms); + return value; +} +class Performance_Frame_Log { +public: + void write(const Frame_Lifecycle_Sample& sample) { + if (!enabled()) + return; + Frame_Phase_Breakdown phase = frame_phase_breakdown(sample); + std::ostringstream stream; + stream << std::setprecision(9) + << static_cast(sample.plot_id) << ',' + << static_cast(sample.version) << ',' + << sample.frame_total_ms << ',' + << phase.phase_total_ms << ',' + << phase.necessary_ms << ',' + << phase.unnecessary_ms << ',' + << phase.data_ms << ',' + << phase.painter_ms << ',' + << phase.queue_ms << ',' + << phase.lock_wait_ms << ',' + << phase.other_ms << ',' + << ratio_percent(phase.necessary_ms, phase.phase_total_ms) << ',' + << ratio_percent(phase.unnecessary_ms, phase.phase_total_ms) << ',' + << ratio_percent(phase.other_ms, phase.phase_total_ms) << ',' + << ratio_percent(phase.data_ms, phase.phase_total_ms) << ',' + << ratio_percent(phase.painter_ms, phase.phase_total_ms) << ',' + << ratio_percent(phase.queue_ms, phase.phase_total_ms) << ',' + << ratio_percent(phase.lock_wait_ms, phase.phase_total_ms) << ',' + << ratio_percent(sample.stats_log_ms, phase.phase_total_ms) << ',' + << sample.request_to_submit_ms << ',' + << sample.prepare_data_ms << ',' + << sample.renderable_prepare_ms << ',' + << sample.color_wait_ms << ',' + << sample.cpu_queue_wait_ms << ',' + << sample.render_color_ms << ',' + << sample.image_prepare_ms << ',' + << sample.painter_draw_ms << ',' + << sample.painter_end_ms << ',' + << sample.publish_color_ms << ',' + << sample.update_to_paint_ms << ',' + << sample.paint_event_ms << ',' + << sample.paint_consume_ms << ',' + << sample.paint_draw_image_ms << ',' + << sample.stats_log_ms << ',' + << sample.renderable_count << ',' + << sample.visible_renderable_count << ',' + << sample.direct_draw_count << ',' + << sample.local_cache_count; + csv_log(configured_path_utf8(), header(), stream.str()); + } +private: + bool enabled() const { + static bool value = []() { + bool ok = false; + int env_value = qEnvironmentVariableIntValue("RENDERIVE_PERFORMANCE_LOG", &ok); + return ok ? env_value != 0 : true; + }(); + return value; + } + std::string configured_path_utf8() const { + static std::string value = []() { + QString path = configured_path_qstring(); + QByteArray bytes = path.toUtf8(); + return std::string(bytes.constData(), static_cast(bytes.size())); + }(); + return value; + } + static QString configured_path_qstring() { + QString value = qEnvironmentVariable("RENDERIVE_PERFORMANCE_LOG_PATH"); + if (!value.isEmpty()) + return value; + QCoreApplication* app = QCoreApplication::instance(); + QString dir = app ? app->applicationDirPath() : QDir::currentPath(); + return QDir(dir).filePath("renderive_performance_log.csv"); + } + std::string header() const { + return "plot_id,version,frame_total_ms,phase_total_ms,necessary_ms,unnecessary_ms,data_ms,painter_ms,queue_ms,lock_wait_ms,other_ms,necessary_ratio,unnecessary_ratio,other_ratio,data_ratio,painter_ratio,queue_ratio,lock_wait_ratio,stats_log_ratio,request_to_submit_ms,prepare_data_ms,renderable_prepare_ms,color_wait_ms,cpu_queue_wait_ms,render_color_ms,image_prepare_ms,painter_draw_ms,painter_end_ms,publish_color_ms,update_to_paint_ms,paint_event_ms,paint_consume_ms,paint_draw_image_ms,stats_log_ms,renderable_count,visible_renderable_count,direct_draw_count,local_cache_count"; + } +}; +Performance_Frame_Log& frame_log() { + static Performance_Frame_Log log; + return log; +} +} using Performance_Shower_Binding = Renderable_Binding; Performance_Shower::Performance_Shower() { Performance_Shower_Binding::init(this, "Performance_Shower"); @@ -7,13 +125,13 @@ Performance_Shower::Performance_Shower() { Performance_Shower_Private* Performance_Shower::d() { return Performance_Shower_Binding::d(this); } -Render_Data* Performance_Shower::create_render_data() { +Render_Object_Owner Performance_Shower::create_render_data() { return Performance_Shower_Binding::create_render_data(); } -Render_State* Performance_Shower::create_render_state() { +Render_Object_Owner Performance_Shower::create_render_state() { return Performance_Shower_Binding::create_render_state(); } -Input_Data* Performance_Shower::create_input_data() { +Render_Object_Owner Performance_Shower::create_input_data() { return Performance_Shower_Binding::create_input_data(); } void Performance_Shower::setEnabled(bool enabled) { @@ -77,6 +195,14 @@ void Performance_Shower::record_duration(const QString& scope, const QString& na event.number = value; append_event(event); } +void log_frame_lifecycle_sample(const Frame_Lifecycle_Sample& sample) { + frame_log().write(sample); +} +void Performance_Shower::log_frame_sample(const Frame_Lifecycle_Sample& sample) { + if (!enabled()) + return; + log_frame_lifecycle_sample(sample); +} void Performance_Shower::set_view_filter(const QString& name) { set_view_filter(name, {}); } @@ -98,7 +224,7 @@ void Performance_Shower::append_event(const Performance_Event& event) { return; auto pd = d(); Render_Input_Lease input(pd, false); - input->events.append(event); + input->push(event); notify_observed_update(event.type == Performance_Event_Type::Clear || event.type == Performance_Event_Type::Select_View || event.type == Performance_Event_Type::Scroll_View); } void Performance_Shower::notify_observed_update(bool force) { diff --git a/Renderive/plottable/Performance_Shower_p.h b/Renderive/plottable/Performance_Shower_p.h index ff1cdf8..5a039a6 100644 --- a/Renderive/plottable/Performance_Shower_p.h +++ b/Renderive/plottable/Performance_Shower_p.h @@ -1,11 +1,15 @@ #pragma once -#include +#include #include #include #include +#include +#include #include "../architecture/Renderable.h" +#include "../base/Memory.h" #include "Core/Statistics/Statistics.h" namespace renderive { +void log_frame_lifecycle_sample(const Frame_Lifecycle_Sample& sample); inline QString performance_align_text(const QString& text, int& width) { width = qMax(width, static_cast(text.size())); return QString("%1").arg(text, width); @@ -31,8 +35,8 @@ struct Performance_Line { } QString data; int place_holder_num{}; - QVector values; - QVector value_widths; + std::vector values; + std::vector value_widths; Psc::Value_Statistics statistics; void update(double value) { statistics.update(value); @@ -83,6 +87,17 @@ struct Performance_Event { Performance_Line line; double number{}; }; +struct Performance_Event_Key { + QString scope; + QString key; + bool operator<(const Performance_Event_Key& other) const { + if (scope < other.scope) + return true; + if (other.scope < scope) + return false; + return key < other.key; + } +}; struct Performance_Metric { Performance_Metric_Type type = Performance_Metric_Type::State; Psc::Value_Statistics value; @@ -91,7 +106,7 @@ struct Performance_Metric { QString text; double gauge{}; std::uint64_t count{}; - QVector widths; + std::vector widths; int& width_at(int index) { if (widths.size() <= index) widths.resize(index + 1); @@ -140,9 +155,31 @@ struct Performance_Shower_Render_State : Render_State { QColor scrollbar_handle = QColor(120, 120, 120); }; struct Performance_Shower_Input_Data : Input_Data { - QVector events; + std::pmr::vector events{memory_resource(Memory_Domain::Other)}; + std::pmr::map pending_values{memory_resource(Memory_Domain::Other)}; + void push(const Performance_Event& event) { + if (event.type == Performance_Event_Type::Clear) { + events.clear(); + pending_values.clear(); + events.push_back(event); + return; + } + if (event.type == Performance_Event_Type::Gauge || event.type == Performance_Event_Type::State) { + pending_values[{event.scope, event.key}] = event; + return; + } + events.push_back(event); + } + template + void read_all(Handler handler) { + for (const Performance_Event& event : events) + handler(event); + for (const auto& item : pending_values) + handler(item.second); + } void clear() override { events.clear(); + pending_values.clear(); } }; struct Performance_Shower_Private; @@ -150,9 +187,9 @@ class Performance_Shower : public Renderable { public: Performance_Shower(); Performance_Shower_Private* d(); - Render_Data* create_render_data() override; - Render_State* create_render_state() override; - Input_Data* create_input_data() override; + Render_Object_Owner create_render_data() override; + Render_Object_Owner create_render_state() override; + Render_Object_Owner create_input_data() override; void setEnabled(bool enabled); bool enabled() const; void refresh_counter(); @@ -164,6 +201,7 @@ public: void set_gauge(const QString& scope, const QString& name, double value); void record_cost(const QString& key, double value); void record_duration(const QString& scope, const QString& name, double value); + void log_frame_sample(const Frame_Lifecycle_Sample& sample); void set_view_filter(const QString& name); void set_view_filter(const QString& group, const QString& name); void scroll_view(int lines); @@ -175,12 +213,12 @@ private: }; struct Performance_Shower_Private : Typed_Render_Data { friend class Performance_Shower; - QMap> scopes; - QVector all_info; - QVector group_items; - QVector target_items; - QVector group_rects; - QVector target_rects; + std::map> scopes; + std::vector all_info; + std::vector group_items; + std::vector target_items; + std::vector group_rects; + std::vector target_rects; QRect panel_rect; int group_sidebar_width{}; int target_sidebar_width{}; @@ -194,9 +232,9 @@ struct Performance_Shower_Private : Typed_Render_Dataevents) { + input->read_all([this](const Performance_Event& event) { apply_event(event); - } + }); clear_render_input(); rebuild_lines(); } @@ -224,7 +262,7 @@ struct Performance_Shower_Private : Typed_Render_Datascroll_view(steps); } void draw(QPainter* painter, const Plot_Render_Snapshot&) override { - if (!q()->enabled() || group_items.isEmpty()) + if (!q()->enabled() || group_items.empty()) return; auto s = render_state(); painter->save(); @@ -234,8 +272,8 @@ struct Performance_Shower_Private : Typed_Render_Data(std::max(group_items.size(), target_items.size())); + int line_count = std::max(static_cast(all_info.size()), side_line_count); int max_height = painter->device()->height(); int sidebar_width = group_sidebar_width + target_sidebar_width; int content_x = sidebar_width + s->column_gap; @@ -243,7 +281,7 @@ struct Performance_Shower_Private : Typed_Render_Datatop_margin + s->bottom_margin; int height = qMin(full_height, max_height); int visible_lines = qMax(1, (height - s->top_margin - s->bottom_margin) / fm.lineSpacing()); - max_scroll_line = qMax(0, all_info.size() - visible_lines); + max_scroll_line = std::max(0, static_cast(all_info.size()) - visible_lines); scroll_line = qBound(0, scroll_line, max_scroll_line); QRect rect(0, 0, width, height); QRect group_rect(0, 0, group_sidebar_width, height); @@ -260,14 +298,14 @@ struct Performance_Shower_Private : Typed_Render_Datatop_margin + i * fm.lineSpacing(), group_sidebar_width, fm.lineSpacing()); - group_rects.append(item_rect); + group_rects.push_back(item_rect); if (group_items[i] == selected_group) painter->fillRect(item_rect, s->selected_background); painter->drawText(QPointF(item_rect.left() + s->left_margin, item_rect.top() + fm.lineSpacing()), group_items[i]); } for (int i = 0; i < target_items.size() && i < visible_lines; ++i) { QRect item_rect(target_rect.left(), s->top_margin + i * fm.lineSpacing(), target_sidebar_width, fm.lineSpacing()); - target_rects.append(item_rect); + target_rects.push_back(item_rect); if (target_items[i] == selected_target) painter->fillRect(item_rect, s->selected_background); painter->drawText(QPointF(item_rect.left() + s->left_margin, item_rect.top() + fm.lineSpacing()), target_items[i]); @@ -277,7 +315,7 @@ struct Performance_Shower_Private : Typed_Render_Datarestore(); } QRect pixel_bounds(const QSize& plot_size) override { - if (!q()->enabled() || group_items.isEmpty()) + if (!q()->enabled() || group_items.empty()) return {}; auto s = render_state(); QFontMetrics fm(s->font); @@ -285,8 +323,8 @@ struct Performance_Shower_Private : Typed_Render_Data(std::max(group_items.size(), target_items.size())); + int line_count = std::max(static_cast(all_info.size()), side_line_count); int sidebar_width = group_sidebar_width + target_sidebar_width; int width = sidebar_width + s->column_gap + content_width; int full_height = fm.lineSpacing() * line_count + s->top_margin + s->bottom_margin; @@ -396,59 +434,58 @@ struct Performance_Shower_Private : Typed_Render_Data object_items; - QVector old_group_items = group_items; - QVector old_target_items = target_items; + std::set object_items; + std::vector old_group_items = group_items; + std::vector old_target_items = target_items; QString old_selected_target = selected_target; - auto renderable_scope = scopes.constFind("Renderable"); + auto renderable_scope = scopes.find("Renderable"); if (renderable_scope != scopes.cend()) { - for (auto metric_it = renderable_scope.value().cbegin(); metric_it != renderable_scope.value().cend(); ++metric_it) { - QString object_name = object_name_from_metric(metric_it.key()); + for (auto metric_it = renderable_scope->second.cbegin(); metric_it != renderable_scope->second.cend(); ++metric_it) { + QString object_name = object_name_from_metric(metric_it->first); if (!object_name.isEmpty()) object_items.insert(object_name); } } - auto buffer_scope = scopes.constFind("Buffer"); + auto buffer_scope = scopes.find("Buffer"); if (buffer_scope != scopes.cend()) { - for (auto metric_it = buffer_scope.value().cbegin(); metric_it != buffer_scope.value().cend(); ++metric_it) { - if (!metric_it.key().startsWith("Color.")) { - QString object_name = object_name_from_metric(metric_it.key()); + for (auto metric_it = buffer_scope->second.cbegin(); metric_it != buffer_scope->second.cend(); ++metric_it) { + if (!metric_it->first.startsWith("Color.")) { + QString object_name = object_name_from_metric(metric_it->first); if (!object_name.isEmpty()) object_items.insert(object_name); } } } group_items.clear(); - group_items.append("Plot"); - QVector object_names = object_items.values().toVector(); - std::sort(object_names.begin(), object_names.end()); + group_items.push_back("Plot"); + std::vector object_names(object_items.begin(), object_items.end()); for (const QString& item : object_names) - group_items.append(item); - if (!group_items.contains(selected_group)) + group_items.push_back(item); + if (std::find(group_items.begin(), group_items.end(), selected_group) == group_items.end()) selected_group = "Plot"; if (selected_group == "Plot") { target_items.clear(); if (scopes.contains("Pipeline")) - target_items.append("Pipeline"); + target_items.push_back("Pipeline"); if (scopes.contains("General")) - target_items.append("General"); + target_items.push_back("General"); if (has_color_buffer_metrics()) - target_items.append("Color"); + target_items.push_back("Color"); } else { target_items.clear(); if (has_renderable_metrics(selected_group)) - target_items.append("Renderable"); + target_items.push_back("Renderable"); if (has_object_buffer_metrics(selected_group, "State")) - target_items.append("State"); + target_items.push_back("State"); if (has_object_buffer_metrics(selected_group, "Input")) - target_items.append("Input"); + target_items.push_back("Input"); if (has_object_buffer_metrics(selected_group, "Pixel")) - target_items.append("Pixel"); + target_items.push_back("Pixel"); } - if (target_items.isEmpty()) - target_items.append("None"); - if (selected_target.isEmpty() || !target_items.contains(selected_target)) + if (target_items.empty()) + target_items.push_back("None"); + if (selected_target.isEmpty() || std::find(target_items.begin(), target_items.end(), selected_target) == target_items.end()) selected_target = target_items.front(); if (old_selected_target != selected_target) { scroll_line = 0; @@ -459,32 +496,32 @@ struct Performance_Shower_Private : Typed_Render_Datasecond.cbegin(); metric_it != buffer_scope->second.cend(); ++metric_it) { + if (metric_it->first.startsWith("Color.")) return true; } return false; } bool has_renderable_metrics(const QString& object_name) const { - auto renderable_scope = scopes.constFind("Renderable"); + auto renderable_scope = scopes.find("Renderable"); if (renderable_scope == scopes.cend()) return false; - for (auto metric_it = renderable_scope.value().cbegin(); metric_it != renderable_scope.value().cend(); ++metric_it) { - if (object_name_from_metric(metric_it.key()) == object_name) + for (auto metric_it = renderable_scope->second.cbegin(); metric_it != renderable_scope->second.cend(); ++metric_it) { + if (object_name_from_metric(metric_it->first) == object_name) return true; } return false; } bool has_object_buffer_metrics(const QString& object_name, const QString& buffer_name) const { - auto buffer_scope = scopes.constFind("Buffer"); + auto buffer_scope = scopes.find("Buffer"); if (buffer_scope == scopes.cend()) return false; QString prefix = object_name + "." + buffer_name + "."; - for (auto metric_it = buffer_scope.value().cbegin(); metric_it != buffer_scope.value().cend(); ++metric_it) { - if (metric_it.key().startsWith(prefix)) + for (auto metric_it = buffer_scope->second.cbegin(); metric_it != buffer_scope->second.cend(); ++metric_it) { + if (metric_it->first.startsWith(prefix)) return true; } return false; @@ -512,11 +549,11 @@ struct Performance_Shower_Private : Typed_Render_Data lines; + std::vector lines; for (auto scope_it = scopes.begin(); scope_it != scopes.end(); ++scope_it) { - for (auto metric_it = scope_it.value().begin(); metric_it != scope_it.value().end(); ++metric_it) { - if (metric_visible(scope_it.key(), metric_it.key())) { - lines.append(metric_it.value().to_string(display_metric_name(metric_it.key()))); + for (auto metric_it = scope_it->second.begin(); metric_it != scope_it->second.end(); ++metric_it) { + if (metric_visible(scope_it->first, metric_it->first)) { + lines.push_back(metric_it->second.to_string(display_metric_name(metric_it->first))); } } } diff --git a/Renderive/plottable/Planisphere.cpp b/Renderive/plottable/Planisphere.cpp index 83f154a..f2c848b 100644 --- a/Renderive/plottable/Planisphere.cpp +++ b/Renderive/plottable/Planisphere.cpp @@ -1,5 +1,6 @@ #include #include "../Axis/Abs_Axis_p.h" +#include "../base/Memory.h" #include "../primitive/Grid.h" #include "Planisphere_p.h" namespace renderive { @@ -10,13 +11,13 @@ Planisphere::Planisphere() { Planisphere_Private* Planisphere::d() { return Planisphere_Binding::d(this); } -Render_Data* Planisphere::create_render_data() { +Render_Object_Owner Planisphere::create_render_data() { return Planisphere_Binding::create_render_data(); } -Render_State* Planisphere::create_render_state() { +Render_Object_Owner Planisphere::create_render_state() { return Planisphere_Binding::create_render_state(); } -Input_Data* Planisphere::create_input_data() { +Render_Object_Owner Planisphere::create_input_data() { return Planisphere_Binding::create_input_data(); } std::shared_ptr Planisphere::i_axis(SRC src) { @@ -108,7 +109,7 @@ Planisphere::Builder::Builder(std::shared_ptr parent, std::shared_pt this->q_axis = q_axis; } std::shared_ptr Planisphere::Builder::build() { - std::shared_ptr < Planisphere > ret(new Planisphere()); + auto ret = renderive::make_shared(); ret->init(parent); Planisphere_Private* pd = ret->d(); Planisphere_Render_State* sc = reinterpret_cast(pd->state(State_Edit)); diff --git a/Renderive/plottable/Planisphere.h b/Renderive/plottable/Planisphere.h index 53ecde2..9d742c1 100644 --- a/Renderive/plottable/Planisphere.h +++ b/Renderive/plottable/Planisphere.h @@ -14,9 +14,9 @@ class LIB_DECL Planisphere : public Renderable { public: /// 返回私有渲染数据。 Planisphere_Private* d(); - Render_Data* create_render_data() override; - Render_State* create_render_state() override; - Input_Data* create_input_data() override; + Render_Object_Owner create_render_data() override; + Render_Object_Owner create_render_state() override; + Render_Object_Owner create_input_data() override; Planisphere(); /// 写入一个 I/Q 点。 void give_data(QPointF pos); diff --git a/Renderive/plottable/Planisphere_p.h b/Renderive/plottable/Planisphere_p.h index 3a29d82..38bf4e7 100644 --- a/Renderive/plottable/Planisphere_p.h +++ b/Renderive/plottable/Planisphere_p.h @@ -1,9 +1,13 @@ #pragma once #include #include +#include +#include #include "../Axis/Axis.h" #include "../architecture/Bounded_Input_Buffer.h" #include "../architecture/Plot_p.h" +#include "../base/Frame_Memory.h" +#include "../base/Memory.h" #include "Planisphere.h" namespace renderive { struct Planisphere_Data { @@ -56,9 +60,9 @@ struct Planisphere_Input_Data : Input_Data { struct Planisphere_Render_State : Render_State, Planisphere_Prop {}; struct Planisphere_Private : Typed_Render_Data { friend class Planisphere; - std::list data_list; - QVector anchor_points(const Planisphere_Render_State* s) const { - QVector points; + std::pmr::list data_list{memory_resource(Memory_Domain::Planisphere)}; + std::pmr::vector anchor_points(const Planisphere_Render_State* s) const { + std::pmr::vector points(frame_memory_resource()); if (s->type == Planisphere_Type::Psk8) { double x_space = s->i_range.length() / 6.0; double y_space = s->q_range.length() / 6.0; @@ -98,15 +102,15 @@ struct Planisphere_Private : Typed_Render_Data points; - QVector colors; + std::pmr::vector points(frame_memory_resource()); + std::pmr::vector colors(frame_memory_resource()); points.reserve(static_cast(data_list.size())); colors.reserve(static_cast(data_list.size())); for (const Planisphere_Data& data : data_list) { QColor color = s->point_color; color.setAlphaF(color.alphaF() * data.remain_times / data.max_total_times); - points.append(data.pos); - colors.append(color); + points.push_back(data.pos); + colors.push_back(color); } if (auto point_set = q()->point_set.lock()) point_set->give_colored_points(points, colors, false); diff --git a/Renderive/plottable/Spectrum.cpp b/Renderive/plottable/Spectrum.cpp index 2171f5d..2db1a38 100644 --- a/Renderive/plottable/Spectrum.cpp +++ b/Renderive/plottable/Spectrum.cpp @@ -1,19 +1,12 @@ #include #include +#include "../base/Frame_Memory.h" +#include "../base/Memory.h" #include "../primitive/Curve.h" #include "../primitive/Label.h" #include "../primitive/Marker.h" #include "Spectrum_p.h" namespace renderive { -static Curve_Sample_Mode spectrum_curve_sample_mode(Spectrum_Sample_Mode mode) { - switch (mode) { - case Spectrum_Sample_Mode::Source: - return Curve_Sample_Mode::Source; - case Spectrum_Sample_Mode::Pixel_Peak_Bucket: - return Curve_Sample_Mode::Pixel_Peak_Bucket; - } - return Curve_Sample_Mode::Source; -} using Spectrum_Binding = Renderable_Binding; Spectrum::Spectrum() { Spectrum_Binding::init(this, "Spectrum"); @@ -21,13 +14,13 @@ Spectrum::Spectrum() { Spectrum_Private* Spectrum::d() { return Spectrum_Binding::d(this); } -Render_Data* Spectrum::create_render_data() { +Render_Object_Owner Spectrum::create_render_data() { return Spectrum_Binding::create_render_data(); } -Render_State* Spectrum::create_render_state() { +Render_Object_Owner Spectrum::create_render_state() { return Spectrum_Binding::create_render_state(); } -Input_Data* Spectrum::create_input_data() { +Render_Object_Owner Spectrum::create_input_data() { return Spectrum_Binding::create_input_data(); } std::shared_ptr Spectrum::frequency_axis(SRC src) { @@ -35,12 +28,6 @@ std::shared_ptr Spectrum::frequency_axis(SRC src) { } void Spectrum::set_frequency_axis(std::shared_ptr value) { d()->set_state_value(&Spectrum_Render_State::frequency_axis, std::weak_ptr(value)); - if (auto curve = cur_curve.lock()) - curve->set_x_axis(value); - if (auto curve = max_curve.lock()) - curve->set_x_axis(value); - if (auto curve = min_curve.lock()) - curve->set_x_axis(value); if (auto item = marker_item.lock()) item->set_x_axis(value); if (auto item = label_item.lock()) @@ -51,12 +38,6 @@ std::shared_ptr Spectrum::power_axis(SRC src) { } void Spectrum::set_power_axis(std::shared_ptr value) { d()->set_state_value(&Spectrum_Render_State::power_axis, std::weak_ptr(value)); - if (auto curve = cur_curve.lock()) - curve->set_y_axis(value); - if (auto curve = max_curve.lock()) - curve->set_y_axis(value); - if (auto curve = min_curve.lock()) - curve->set_y_axis(value); if (auto item = marker_item.lock()) item->set_y_axis(value); if (auto item = label_item.lock()) @@ -87,16 +68,12 @@ bool Spectrum::use_max_line(SRC src) { } void Spectrum::set_use_max_line(bool value) { d()->set_state_value(&Spectrum_Render_State::use_max_line, value); - if (auto curve = max_curve.lock()) - curve->visible = value; } bool Spectrum::use_min_line(SRC src) { return d()->state_value(&Spectrum_Render_State::use_min_line, src); } void Spectrum::set_use_min_line(bool value) { d()->set_state_value(&Spectrum_Render_State::use_min_line, value); - if (auto curve = min_curve.lock()) - curve->visible = value; } bool Spectrum::use_max_marker(SRC src) { return d()->state_value(&Spectrum_Render_State::use_max_marker, src); @@ -121,84 +98,53 @@ bool Spectrum::visible_range_only(SRC src) { } void Spectrum::set_visible_range_only(bool value) { d()->set_state_value(&Spectrum_Render_State::visible_range_only, value); - if (auto curve = cur_curve.lock()) - curve->set_visible_range_only(value); - if (auto curve = max_curve.lock()) - curve->set_visible_range_only(value); - if (auto curve = min_curve.lock()) - curve->set_visible_range_only(value); } Line_Interpolation_Mode Spectrum::interpolation_mode(SRC src) { return d()->state_value(&Spectrum_Render_State::interpolation_mode, src); } void Spectrum::set_interpolation_mode(Line_Interpolation_Mode value) { d()->set_state_value(&Spectrum_Render_State::interpolation_mode, value); - if (auto curve = cur_curve.lock()) - curve->set_interpolation_mode(value); - if (auto curve = max_curve.lock()) - curve->set_interpolation_mode(value); - if (auto curve = min_curve.lock()) - curve->set_interpolation_mode(value); } Spectrum_Sample_Mode Spectrum::sample_mode(SRC src) { return d()->state_value(&Spectrum_Render_State::sample_mode, src); } void Spectrum::set_sample_mode(Spectrum_Sample_Mode value) { d()->set_state_value(&Spectrum_Render_State::sample_mode, value); - Curve_Sample_Mode curve_mode = spectrum_curve_sample_mode(value); - if (auto curve = cur_curve.lock()) - curve->set_sample_mode(curve_mode); - if (auto curve = max_curve.lock()) - curve->set_sample_mode(curve_mode); - if (auto curve = min_curve.lock()) - curve->set_sample_mode(curve_mode); } QBrush Spectrum::max_brush(SRC src) { return d()->state_value(&Spectrum_Render_State::max_brush, src); } void Spectrum::set_max_brush(QBrush value) { - if (auto curve = max_curve.lock()) - curve->set_fill_brush(value); d()->set_state_value(&Spectrum_Render_State::max_brush, std::move(value)); } QBrush Spectrum::cur_brush(SRC src) { return d()->state_value(&Spectrum_Render_State::cur_brush, src); } void Spectrum::set_cur_brush(QBrush value) { - if (auto curve = cur_curve.lock()) - curve->set_fill_brush(value); d()->set_state_value(&Spectrum_Render_State::cur_brush, std::move(value)); } QBrush Spectrum::min_brush(SRC src) { return d()->state_value(&Spectrum_Render_State::min_brush, src); } void Spectrum::set_min_brush(QBrush value) { - if (auto curve = min_curve.lock()) - curve->set_fill_brush(value); d()->set_state_value(&Spectrum_Render_State::min_brush, std::move(value)); } QPen Spectrum::max_pen(SRC src) { return d()->state_value(&Spectrum_Render_State::max_pen, src); } void Spectrum::set_max_pen(QPen value) { - if (auto curve = max_curve.lock()) - curve->set_pen(value); d()->set_state_value(&Spectrum_Render_State::max_pen, std::move(value)); } QPen Spectrum::cur_pen(SRC src) { return d()->state_value(&Spectrum_Render_State::cur_pen, src); } void Spectrum::set_cur_pen(QPen value) { - if (auto curve = cur_curve.lock()) - curve->set_pen(value); d()->set_state_value(&Spectrum_Render_State::cur_pen, std::move(value)); } QPen Spectrum::min_pen(SRC src) { return d()->state_value(&Spectrum_Render_State::min_pen, src); } void Spectrum::set_min_pen(QPen value) { - if (auto curve = min_curve.lock()) - curve->set_pen(value); d()->set_state_value(&Spectrum_Render_State::min_pen, std::move(value)); } QPen Spectrum::select_marker_pen(SRC src) { @@ -234,12 +180,6 @@ void Spectrum::set_frequency_range(Range frequency_range) { return; Render_Edit_Lease edit(d()); edit->frequency_range = frequency_range; - if (auto curve = cur_curve.lock()) - curve->set_data_range(frequency_range); - if (auto curve = max_curve.lock()) - curve->set_data_range(frequency_range); - if (auto curve = min_curve.lock()) - curve->set_data_range(frequency_range); } Spectrum::Builder::Builder(std::shared_ptr parent, std::shared_ptr frequency_axis, std::shared_ptr power_axis) { ASSERT(parent, "Spectrum build error! parent == nullptr"); @@ -252,7 +192,7 @@ Spectrum::Builder::Builder(std::shared_ptr parent, std::shared_ptrpower_axis = power_axis; } std::shared_ptr Spectrum::Builder::build() { - std::shared_ptr < Spectrum > ret(new Spectrum); + auto ret = renderive::make_shared(); ret->init(parent); Spectrum_Private* pd = ret->d(); Spectrum_Render_State* sc = reinterpret_cast(pd->state(State_Edit)); @@ -285,26 +225,14 @@ std::shared_ptr Spectrum::Builder::build() { sc->frequency_range = {0.0, 1.0}; if (sc->sweep_frequency_range.length() == 0.0) sc->sweep_frequency_range = {0.0, 1.0}; - ret->set_child_render_order(Renderable_Child_Render_Order::Children_Before_Self); + ret->set_child_render_order(Renderable_Child_Render_Order::Self_Before_Children); auto frequency_axis = this->frequency_axis.lock(); auto power_axis = this->power_axis.lock(); if (frequency_axis && power_axis) { - Curve_Sample_Mode curve_sample_mode = spectrum_curve_sample_mode(sc->sample_mode); - auto max_curve = Curve::Builder(ret, frequency_axis, power_axis).set_data_range(sc->frequency_range).set_pen(sc->max_pen).set_fill_brush(sc->max_brush).set_visible_range_only(sc->visible_range_only).set_interpolation_mode(sc->interpolation_mode).set_sample_mode(curve_sample_mode).build(); - auto cur_curve = Curve::Builder(ret, frequency_axis, power_axis).set_data_range(sc->frequency_range).set_pen(sc->cur_pen).set_fill_brush(sc->cur_brush).set_visible_range_only(sc->visible_range_only).set_interpolation_mode(sc->interpolation_mode).set_sample_mode(curve_sample_mode).build(); - auto min_curve = Curve::Builder(ret, frequency_axis, power_axis).set_data_range(sc->frequency_range).set_pen(sc->min_pen).set_fill_brush(sc->min_brush).set_visible_range_only(sc->visible_range_only).set_interpolation_mode(sc->interpolation_mode).set_sample_mode(curve_sample_mode).build(); auto marker_item = Marker::Builder(ret, frequency_axis, power_axis).build(); auto label_item = Label::Builder(ret, frequency_axis, power_axis).build(); - max_curve->object_name = "SpectrumMaxCurve"; - cur_curve->object_name = "SpectrumCurCurve"; - min_curve->object_name = "SpectrumMinCurve"; marker_item->object_name = "SpectrumMarker"; label_item->object_name = "SpectrumLabel"; - max_curve->visible = sc->use_max_line; - min_curve->visible = sc->use_min_line; - ret->max_curve = max_curve; - ret->cur_curve = cur_curve; - ret->min_curve = min_curve; ret->marker_item = marker_item; ret->label_item = label_item; } @@ -340,20 +268,17 @@ void Spectrum_Private::update_frequency_point_size(int frequency_point_size, Ran frequency[i] = cur; } } -void Spectrum_Private::set_frequency_data(const QVector& line_data, const QVector* max_data, const QVector* min_data, const Spectrum_Render_State* s) { +void Spectrum_Private::set_frequency_data(std::pmr::vector& line_data, const std::pmr::vector* max_data, const std::pmr::vector* min_data, const Spectrum_Render_State* s) { if (s->frequency_point_size <= 0) return; if (line_data.size() != s->frequency_point_size) return; if ((max_data && max_data->size() != s->frequency_point_size) || (min_data && min_data->size() != s->frequency_point_size)) return; - cur_powers = line_data; - cur_powers.detach(); + cur_powers.swap(line_data); if (!has_power_data) { - max_powers = line_data; - max_powers.detach(); - min_powers = line_data; - min_powers.detach(); + max_powers.assign(cur_powers.begin(), cur_powers.end()); + min_powers.assign(cur_powers.begin(), cur_powers.end()); has_power_data = true; } double min = std::numeric_limits::max(); @@ -396,16 +321,40 @@ double Spectrum_Private::get_y(double x, bool& ok, Line_Interpolation_Mode mode) return cur_powers.at(i); }); } +void Spectrum_Private::draw_power_curve(QPainter* painter, Spectrum_Render_State* s, const std::pmr::vector& powers, const QPen& pen, const QBrush& brush) { + auto frequency_axis = s->frequency_axis.lock(); + auto power_axis = s->power_axis.lock(); + if (!frequency_axis || !power_axis) + return; + if (powers.size() < 2 || s->frequency_range.length() == 0.0) + return; + std::pmr::vector points(frame_memory_resource()); + int point_count = static_cast(powers.size()); + if (s->sample_mode == Spectrum_Sample_Mode::Pixel_Peak_Bucket) { + points = Curve_Utils::create_peak_bucket_points(frequency_axis.get(), power_axis.get(), s->frequency_range, point_count, point_count, s->visible_range_only, [&powers](int i) { + return powers.at(i); + }); + } + else { + points = Curve_Utils::create_sampled_points(frequency_axis.get(), power_axis.get(), s->frequency_range, point_count, point_count, s->visible_range_only, s->interpolation_mode, [&powers](int i) { + return powers.at(i); + }); + } + painter->save(); + Curve_Utils::draw_polyline(painter, points, pen); + Curve_Utils::draw_fill_to_y(painter, points, power_axis->coord_to_pixel(power_axis->coord_range(SRC::Render).target, SRC::Render), brush); + painter->restore(); +} void Spectrum_Private::update_marker_items(const Spectrum_Render_State* s) { auto frequency_axis = s->frequency_axis.lock(); auto power_axis = s->power_axis.lock(); - QVector marker_items; - QVector label_items; + std::pmr::vector marker_items(memory_resource(Memory_Domain::Other)); + std::pmr::vector label_items(memory_resource(Memory_Domain::Other)); if (!frequency_axis || !power_axis) { if (auto marker_item = q()->marker_item.lock()) - marker_item->give_items(marker_items, false); + marker_item->give_items(std::move(marker_items), false); if (auto label_item = q()->label_item.lock()) - label_item->give_items(label_items, false); + label_item->give_items(std::move(label_items), false); return; } auto append_marker = [&](double frequency, double power, bool drawLine, bool selected) { @@ -415,7 +364,7 @@ void Spectrum_Private::update_marker_items(const Spectrum_Render_State* s) { marker.radius = 2.0; marker.pen = pen; marker.mode = drawLine ? Marker_Mode::Vertical_Line : Marker_Mode::Point; - marker_items.append(marker); + marker_items.push_back(marker); QString frequency_text = QString::number(frequency, 'f', 2) + frequency_axis->unit_text(); QString power_text = QString::number(power, 'f', 2) + power_axis->unit_text(); Label_Item frequency_label; @@ -424,11 +373,11 @@ void Spectrum_Private::update_marker_items(const Spectrum_Render_State* s) { frequency_label.text = frequency_text; frequency_label.alignment = Qt::AlignHCenter | Qt::AlignBottom; frequency_label.text_pen = pen; - label_items.append(frequency_label); + label_items.push_back(frequency_label); Label_Item power_label = frequency_label; power_label.offset = QPointF(0.0, -14.0); power_label.text = power_text; - label_items.append(power_label); + label_items.push_back(power_label); }; if (s->use_max_marker) append_marker(max_marker_key, max_marker_value, false, false); @@ -442,9 +391,9 @@ void Spectrum_Private::update_marker_items(const Spectrum_Render_State* s) { append_marker(custom_marker.frequency, value, custom_marker.draw_line, i == s->cur_select_index); } if (auto marker_item = q()->marker_item.lock()) - marker_item->give_items(marker_items, false); + marker_item->give_items(std::move(marker_items), false); if (auto label_item = q()->label_item.lock()) - label_item->give_items(label_items, false); + label_item->give_items(std::move(label_items), false); } void Spectrum_Private::draw_spectrum(QPainter* painter, Spectrum_Render_State* s) { if (s->use_sweep_frequency_rect) { @@ -457,7 +406,12 @@ void Spectrum_Private::draw(QPainter* painter, const Plot_Render_Snapshot& snaps if (shower) { shower->set_performance_line("frequency_point_size", Performance_Line(QString("frequency_point_size:%1").arg(s->frequency_point_size))); } + draw_power_curve(painter, s, cur_powers, s->cur_pen, s->cur_brush); draw_spectrum(painter, s); + if (s->use_max_line) + draw_power_curve(painter, s, max_powers, s->max_pen, s->max_brush); + if (s->use_min_line) + draw_power_curve(painter, s, min_powers, s->min_pen, s->min_brush); if (q()->hover_ok(q())) { auto frequency_axis = s->frequency_axis.lock(); auto power_axis = s->power_axis.lock(); @@ -465,7 +419,15 @@ void Spectrum_Private::draw(QPainter* painter, const Plot_Render_Snapshot& snaps q()->draw_hover(painter, frequency_axis.get(), power_axis.get()); } } -void Spectrum::give_data(const QVector& line_data) { +std::size_t Spectrum_Private::estimated_frame_memory(const Plot_Render_Snapshot& snapshot) { + const Spectrum_Render_State* s = render_state(); + int line_count = 1 + (s->use_max_line ? 1 : 0) + (s->use_min_line ? 1 : 0); + int point_count = static_cast(cur_powers.size()); + if (s->sample_mode == Spectrum_Sample_Mode::Pixel_Peak_Bucket) + point_count = std::min(point_count, snapshot.size.width() * 4 + 4); + return static_cast(line_count) * static_cast(std::max(0, point_count)) * sizeof(QPointF) + 32 * 1024; +} +void Spectrum::give_data(std::span line_data) { if (!ok()) return; Spectrum_Private* pd = d(); @@ -476,19 +438,72 @@ void Spectrum::give_data(const QVector& line_data) { Render_Input_Lease input(pd); input->push(line_data, state->use_max_line, state->use_min_line); } +void Spectrum::give_data(std::pmr::vector&& line_data) { + if (!ok()) + return; + Spectrum_Private* pd = d(); + Render_State_Lease state(pd); + int frequency_point_size = state->frequency_point_size; + if (line_data.size() != frequency_point_size) + return; + Render_Input_Lease input(pd); + input->push(std::move(line_data), state->use_max_line, state->use_min_line); +} +Update_Ticket Spectrum::submit_data(std::span line_data, const Submit_Options&) { + auto update_state = make_update_state(); + if (!ok()) { + update_state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); + return Update_Ticket(update_state); + } + Spectrum_Private* pd = d(); + Render_State_Lease state(pd); + int frequency_point_size = state->frequency_point_size; + if (line_data.size() != frequency_point_size) { + update_state->complete_all(std::make_error_code(std::errc::invalid_argument), Update_Outcome::Cancelled); + return Update_Ticket(update_state); + } + { + Render_Input_Lease input(pd); + input->push(line_data, state->use_max_line, state->use_min_line); + pd->set_input_completion(input.lease, update_state); + } + update_state->complete_until(Update_Stage::Input_Released); + return Update_Ticket(update_state); +} +Update_Ticket Spectrum::submit_data(std::pmr::vector&& line_data, const Submit_Options&) { + auto update_state = make_update_state(); + if (!ok()) { + update_state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled); + return Update_Ticket(update_state); + } + Spectrum_Private* pd = d(); + Render_State_Lease state(pd); + int frequency_point_size = state->frequency_point_size; + if (line_data.size() != frequency_point_size) { + update_state->complete_all(std::make_error_code(std::errc::invalid_argument), Update_Outcome::Cancelled); + return Update_Ticket(update_state); + } + { + Render_Input_Lease input(pd); + input->push(std::move(line_data), state->use_max_line, state->use_min_line); + pd->set_input_completion(input.lease, update_state); + } + update_state->complete_until(Update_Stage::Input_Released); + return Update_Ticket(update_state); +} void Spectrum::add_custom_marker(double frequency) { Render_Edit_Lease edit(d()); - edit->marker_list.append(Spectrum_Marker{frequency, false}); + edit->marker_list.push_back(Spectrum_Marker{frequency, false}); } void Spectrum::add_custom_line_marker(double frequency) { Render_Edit_Lease edit(d()); - edit->marker_list.append(Spectrum_Marker{frequency, true}); + edit->marker_list.push_back(Spectrum_Marker{frequency, true}); } void Spectrum::remove_custom_marker(double frequency) { Render_Edit_Lease edit(d()); - for (Spectrum_Marker& marker : edit->marker_list) { - if (qFuzzyCompare(marker.frequency, frequency)) { - edit->marker_list.removeOne(marker); + for (auto marker = edit->marker_list.begin(); marker != edit->marker_list.end(); ++marker) { + if (qFuzzyCompare(marker->frequency, frequency)) { + edit->marker_list.erase(marker); break; } } @@ -498,7 +513,7 @@ void Spectrum::remove_current_marker(SRC src) { Render_Edit_Lease edit(d()); if (edit->cur_select_index < 0 || edit->cur_select_index >= edit->marker_list.size()) return; - edit->marker_list.removeAt(edit->cur_select_index); + edit->marker_list.erase(edit->marker_list.begin() + edit->cur_select_index); edit->cur_select_index = -1; } void Spectrum::clear_all_custom_marker(SRC src) { @@ -514,7 +529,7 @@ double Spectrum::get_mark_frequency(int mark_index, SRC src) { } void Spectrum::set_marker_frequency(int marker_index, double frequency) { Render_Edit_Lease edit(d()); - QVector& markers = edit->marker_list; + auto& markers = edit->marker_list; if (marker_index < 0 || marker_index >= markers.size()) return; markers[marker_index].frequency = frequency; diff --git a/Renderive/plottable/Spectrum.h b/Renderive/plottable/Spectrum.h index 2fea2c0..0249c56 100644 --- a/Renderive/plottable/Spectrum.h +++ b/Renderive/plottable/Spectrum.h @@ -1,7 +1,13 @@ #pragma once +#include +#include +#include +#include +#include +#include "../architecture/Update_Completion.h" +#include "../base/Memory.h" #include "Hover_Info.h" #include "Interpolation.h" -#include "../primitive/Curve.h" namespace renderive { struct Spectrum_Private; class Label; @@ -15,9 +21,9 @@ class LIB_DECL Spectrum : public Renderable, public Hover_Info_Renderable, frequency_axis) PROP(std::shared_ptr, power_axis); @@ -44,7 +50,17 @@ public: PROP(QPen, middle_frequency_pen); PROP(QBrush, sweep_rect_brush); /// 写入一帧频谱线数据。 - void give_data(const QVector& line_data); + void give_data(std::span line_data); + void give_data(std::pmr::vector&& line_data); + Update_Ticket submit_data(std::span line_data, const Submit_Options& options = {}); + Update_Ticket submit_data(std::pmr::vector&& line_data, const Submit_Options& options = {}); + template + void write_data(std::size_t count, Writer&& writer) { + std::pmr::vector values(memory_resource(Memory_Domain::Spectrum)); + values.resize(count); + std::forward(writer)(std::span(values.data(), values.size())); + give_data(std::move(values)); + } /// 查询指定频率位置的功率值。 double get_power(double frequency, bool& ok, SRC = SRC::Edit); /// 添加自定义频率标记。 @@ -76,9 +92,6 @@ public: /// 设置当前选中标记频率。 void set_current_marker_frequency(double frequency); struct Builder; - std::weak_ptr cur_curve; - std::weak_ptr max_curve; - std::weak_ptr min_curve; std::weak_ptr marker_item; std::weak_ptr