This commit is contained in:
2026-08-12 13:46:53 +08:00
parent 898a58b554
commit d7cf626c25
22 changed files with 1639 additions and 292 deletions
+6 -6
View File
@@ -67,7 +67,7 @@ void Afterglow_Control::publish() {
void Afterglow_Control::build_paint_task_graph(Renderable_Task_Graph& graph) {
auto& output = impl_->render_frame;
const auto view = render_state_view();
const auto& state = render_properties(view);
const auto state = properties();
const auto& history = view.get(impl_->history);
const int width = history.empty()
? 0
@@ -84,7 +84,7 @@ void Afterglow_Control::build_paint_task_graph(Renderable_Task_Graph& graph) {
static_cast<int>(Scene_Base::task_executor_worker_count()), work_size, 4096);
const auto prepare = graph.emplace([this, partition_count](const Scene_Render_Context&) {
prepare_render_frame(render_state_view(), partition_count);
}, "prepare afterglow");
}, "准备余晖图");
std::vector<Renderable_Task_Graph::Task> accumulation;
accumulation.reserve(static_cast<std::size_t>(partition_count));
for (int index = 0; index < partition_count; ++index) {
@@ -92,13 +92,13 @@ void Afterglow_Control::build_paint_task_graph(Renderable_Task_Graph& graph) {
[this, index](const Scene_Render_Context&) {
accumulate_partition(render_state_view(), index);
},
"accumulate afterglow partition " + std::to_string(index + 1));
"累积余晖图分区 " + std::to_string(index + 1));
graph.precede(prepare, task);
accumulation.push_back(task);
}
const auto normalize = graph.emplace([this](const Scene_Render_Context&) {
normalize_render_frame();
}, "normalize afterglow");
}, "归一化余晖图");
for (const auto task : accumulation)
graph.precede(task, normalize);
std::vector<Renderable_Task_Graph::Task> coloring;
@@ -108,12 +108,12 @@ void Afterglow_Control::build_paint_task_graph(Renderable_Task_Graph& graph) {
[this, index](const Scene_Render_Context&) {
color_partition(render_state_view(), index);
},
"color afterglow partition " + std::to_string(index + 1));
"着色余晖图分区 " + std::to_string(index + 1));
graph.precede(normalize, task);
coloring.push_back(task);
}
const auto paint_image = add_paint_task(
graph, "paint afterglow shared image",
graph, "绘制余晖图共享图像",
[this](Painter& painter, const Render_State_View& frame_view,
const Scene_Render_Context& context) {
paint_render_frame(painter, frame_view,
+11 -4
View File
@@ -52,11 +52,19 @@ namespace detail {
class Paint_Overlay {};
template <Property_Set Properties_Type>
class Plottable_State : public Double_State_Strategy<Renderable, Properties_Type> {
class Plottable_State
: public Double_State_Strategy<Renderable, Properties_Type, Atomic_Spin_Mutex,
Observer_State<Renderable_State_Observer>> {
public:
using Properties = Properties_Type;
using Base = Double_State_Strategy<Renderable, Properties>;
Plottable_State(Plot_Core& plot, const Properties& properties) : Base(properties, plot) {}
using State_Observer = Observer_State<Renderable_State_Observer>;
using Base = Double_State_Strategy<Renderable, Properties, Atomic_Spin_Mutex,
State_Observer>;
Plottable_State(Plot_Core& plot, const Properties& properties)
: Base(properties,
With_Observer<State_Observer>{
State_Observer(Renderable_State_Observer(this))},
plot) {}
template <auto Member, Property_Member_Assignable<Properties, Member> Value>
Plottable_State& set(Value&& value) {
Base::template set<Member>(std::forward<Value>(value));
@@ -76,7 +84,6 @@ public:
}
}
}
this->changed();
return *this;
}
template <auto Member>
+17 -8
View File
@@ -66,11 +66,20 @@ void draw_curve(Painter& painter, std::span<const double> values, Range domain,
if(brush.enabled()) {
std::vector<PointF> polygon;
polygon.reserve(points.size() + 2);
polygon.push_back(mapped_point(frequency_axis, domain.origin, power_axis,
power_axis.coordinate_range.target));
PointF first_baseline = points.front();
PointF last_baseline = points.back();
const double baseline =
power_axis.coord_to_pixel(power_axis.coordinate_range.target);
if (power_axis.orientation == Orientation::Horizontal) {
first_baseline.x = baseline;
last_baseline.x = baseline;
} else {
first_baseline.y = baseline;
last_baseline.y = baseline;
}
polygon.push_back(first_baseline);
polygon.insert(polygon.end(), points.begin(), points.end());
polygon.push_back(mapped_point(frequency_axis, domain.target, power_axis,
power_axis.coordinate_range.target));
polygon.push_back(last_baseline);
painter.polygon(polygon, Pen{.style = Line_Style::None}, brush);
}
painter.polyline(points, pen);
@@ -244,14 +253,14 @@ void Spectrum_Control::publish() {
void Spectrum_Control::build_paint_task_graph(Renderable_Task_Graph& graph) {
auto& output = impl_->render_frame;
const auto view = render_state_view();
const auto& state = render_properties(view);
const auto state = properties();
const auto& published_frame = view.get(impl_->frame);
const std::size_t work_size = published_frame ? published_frame->samples.size() : 0;
const int partition_count = output.partitioner.graph_partition_count(
state.partition_mode, state.partition_count.get(),
static_cast<int>(Scene_Base::task_executor_worker_count()), work_size, 128);
const auto prepare = add_paint_task(
graph, "prepare spectrum and paint background",
graph, "准备频谱并绘制背景",
[this, partition_count](Painter& painter, const Render_State_View& frame_view,
const Scene_Render_Context&) {
prepare_render_frame(frame_view, partition_count);
@@ -261,7 +270,7 @@ void Spectrum_Control::build_paint_task_graph(Renderable_Task_Graph& graph) {
partitions.reserve(static_cast<std::size_t>(partition_count));
for (int index = 0; index < partition_count; ++index) {
const auto partition = add_paint_task(
graph, "paint spectrum partition " + std::to_string(index + 1),
graph, "绘制频谱分区 " + std::to_string(index + 1),
[this, index](Painter& painter, const Render_State_View& frame_view,
const Scene_Render_Context&) {
render_partition(painter, frame_view, index);
@@ -270,7 +279,7 @@ void Spectrum_Control::build_paint_task_graph(Renderable_Task_Graph& graph) {
partitions.push_back(partition);
}
const auto overlay = add_paint_task(
graph, "paint spectrum overlay",
graph, "绘制频谱覆盖层",
[this](Painter& painter, const Render_State_View& frame_view,
const Scene_Render_Context& context) {
paint_overlay(painter, frame_view,
+4 -4
View File
@@ -99,7 +99,7 @@ void Waterfall_Control::publish() {
void Waterfall_Control::build_paint_task_graph(Renderable_Task_Graph& graph) {
auto& output = impl_->render_frame;
const auto view = render_state_view();
const auto& state = render_properties(view);
const auto state = properties();
const auto& rows = view.get(impl_->rows);
std::size_t work_size{};
if (!rows.empty()) {
@@ -117,7 +117,7 @@ void Waterfall_Control::build_paint_task_graph(Renderable_Task_Graph& graph) {
static_cast<int>(Scene_Base::task_executor_worker_count()), work_size, 4096);
const auto prepare = graph.emplace([this, partition_count](const Scene_Render_Context&) {
prepare_render_frame(render_state_view(), partition_count);
}, "prepare waterfall");
}, "准备瀑布图");
std::vector<Renderable_Task_Graph::Task> partitions;
partitions.reserve(static_cast<std::size_t>(partition_count));
for (int index = 0; index < partition_count; ++index) {
@@ -125,12 +125,12 @@ void Waterfall_Control::build_paint_task_graph(Renderable_Task_Graph& graph) {
[this, index](const Scene_Render_Context&) {
render_partition(render_state_view(), index);
},
"raster waterfall partition " + std::to_string(index + 1));
"光栅化瀑布图分区 " + std::to_string(index + 1));
graph.precede(prepare, partition);
partitions.push_back(partition);
}
const auto paint_image = add_paint_task(
graph, "paint waterfall shared image",
graph, "绘制瀑布图共享图像",
[this](Painter& painter, const Render_State_View& frame_view,
const Scene_Render_Context& context) {
paint_render_frame(painter, frame_view,
+169 -1
View File
@@ -5,8 +5,156 @@
#include <renderive/scene/base/Scene_Render_Context.hpp>
#include <algorithm>
#include <cmath>
namespace renderive {
Renderable_Observation Renderable_Observer::observation() const noexcept {
std::lock_guard lock(mutex_);
auto result = observation_;
const auto render = render_duration_statistics_.snapshot();
const auto task = task_duration_statistics_.snapshot();
const auto nanoseconds = [](double value) {
return static_cast<std::uint64_t>(std::max(0.0, std::round(value)));
};
result.render_duration_sample_count = render.sample_count;
result.render_duration_average_ns = nanoseconds(render.average);
result.render_duration_deviation_ns = nanoseconds(render.deviation);
result.render_duration_p50_ns = nanoseconds(render.p50);
result.render_duration_p95_ns = nanoseconds(render.p95);
result.render_duration_p99_ns = nanoseconds(render.p99);
result.task_duration_sample_count = task.sample_count;
result.task_duration_average_ns = nanoseconds(task.average);
result.task_duration_deviation_ns = nanoseconds(task.deviation);
result.task_duration_p50_ns = nanoseconds(task.p50);
result.task_duration_p95_ns = nanoseconds(task.p95);
result.task_duration_p99_ns = nanoseconds(task.p99);
return result;
}
void Renderable_Observer::observe_state(Renderable_Observer_Event event,
std::uint64_t time_ns,
std::uint64_t cache_update_count,
std::uint64_t publish_count) noexcept {
std::lock_guard lock(mutex_);
observation_.event = event;
observation_.event_time_ns = time_ns;
observation_.cache_update_count = cache_update_count;
observation_.publish_count = publish_count;
}
void Renderable_Observer::observe_task(
const Renderable_Task_Observation& task) noexcept {
std::lock_guard lock(mutex_);
if (task.event == Renderable_Task_Observation_Event::started) {
if (active_render_sequence_ != task.render_sequence) {
active_render_sequence_ = task.render_sequence;
active_render_started_ns_ = task.time_ns;
active_task_count_ = task.task_count;
completed_task_count_ = 0;
running_task_count_ = 0;
active_peak_parallelism_ = 0;
active_render_failed_ = false;
}
++running_task_count_;
++observation_.task_execution_count;
active_peak_parallelism_ =
std::max(active_peak_parallelism_, running_task_count_);
if (!active_render_failed_) {
observation_.event = Renderable_Observer_Event::Render_Started;
observation_.event_time_ns = task.time_ns;
}
observation_.last_render_sequence = task.render_sequence;
return;
}
if (active_render_sequence_ != task.render_sequence)
return;
if (running_task_count_ != 0)
--running_task_count_;
++completed_task_count_;
observation_.last_task_duration_ns = task.task_duration_ns;
observation_.total_task_duration_ns += task.task_duration_ns;
observation_.maximum_task_duration_ns =
std::max(observation_.maximum_task_duration_ns, task.task_duration_ns);
task_duration_statistics_.add(static_cast<double>(task.task_duration_ns));
const auto render_duration =
task.time_ns >= active_render_started_ns_
? task.time_ns - active_render_started_ns_
: 0;
const auto extend_failed_render_duration = [&] {
if (render_duration > observation_.last_render_duration_ns) {
observation_.total_render_duration_ns +=
render_duration - observation_.last_render_duration_ns;
observation_.last_render_duration_ns = render_duration;
render_duration_statistics_.replace_latest(
static_cast<double>(render_duration));
observation_.maximum_render_duration_ns =
std::max(observation_.maximum_render_duration_ns, render_duration);
}
};
if (task.event == Renderable_Task_Observation_Event::failed) {
++observation_.failed_task_count;
if (!active_render_failed_) {
active_render_failed_ = true;
++observation_.failed_render_count;
observation_.event = Renderable_Observer_Event::Render_Failed;
observation_.event_time_ns = task.time_ns;
observation_.last_render_duration_ns = render_duration;
observation_.total_render_duration_ns += render_duration;
render_duration_statistics_.add(static_cast<double>(render_duration));
observation_.maximum_render_duration_ns =
std::max(observation_.maximum_render_duration_ns, render_duration);
observation_.last_task_count = active_task_count_;
observation_.peak_parallelism = active_peak_parallelism_;
} else
extend_failed_render_duration();
return;
}
if (active_render_failed_) {
extend_failed_render_duration();
return;
}
if (completed_task_count_ == active_task_count_) {
++observation_.successful_render_count;
observation_.event = Renderable_Observer_Event::Render_Completed;
observation_.event_time_ns = task.time_ns;
observation_.last_render_duration_ns = render_duration;
observation_.total_render_duration_ns += render_duration;
render_duration_statistics_.add(static_cast<double>(render_duration));
observation_.maximum_render_duration_ns =
std::max(observation_.maximum_render_duration_ns, render_duration);
observation_.last_task_count = active_task_count_;
observation_.peak_parallelism = active_peak_parallelism_;
}
}
void Renderable_Observer::reset_performance() noexcept {
std::lock_guard lock(mutex_);
Renderable_Observation reset;
if (observation_.event == Renderable_Observer_Event::Cache_Updated ||
observation_.event == Renderable_Observer_Event::Published) {
reset.event = observation_.event;
reset.event_time_ns = observation_.event_time_ns;
}
reset.cache_update_count = observation_.cache_update_count;
reset.publish_count = observation_.publish_count;
observation_ = reset;
active_render_sequence_ = 0;
active_render_started_ns_ = 0;
active_task_count_ = 0;
completed_task_count_ = 0;
running_task_count_ = 0;
active_peak_parallelism_ = 0;
active_render_failed_ = false;
render_duration_statistics_.clear();
task_duration_statistics_.clear();
}
Renderable::Renderable(Plot_Core& plot, bool cache_enabled)
: ::Renderable_Base(plot.kernel_scene(), {.cache_enabled = cache_enabled}), plot_(plot) {}
@@ -41,6 +189,26 @@ void Renderable::set_visible(bool visible) {
changed();
}
Renderable_Observation Renderable::observation() const noexcept {
return observer_.observation();
}
void Renderable::observe_state(Renderable_Observer_Event event,
std::uint64_t time_ns,
std::uint64_t cache_update_count,
std::uint64_t publish_count) noexcept {
observer_.observe_state(event, time_ns, cache_update_count, publish_count);
}
void Renderable::observe_task(
const Renderable_Task_Observation& observation) noexcept {
observer_.observe_task(observation);
}
void Renderable::reset_task_observation() noexcept {
observer_.reset_performance();
}
void Renderable::render(const ::Scene_Render_Context& context) {
if (!is_visible() || context.color_cache == nullptr)
return;
@@ -59,7 +227,7 @@ void Renderable::build_task_graph(Renderable_Task_Graph& graph) {
}
void Renderable::build_paint_task_graph(Renderable_Task_Graph& graph) {
add_paint_task(graph, "paint", [this](detail::Painter& painter,
add_paint_task(graph, "绘制", [this](detail::Painter& painter,
const Render_State_View& state,
const Scene_Render_Context&) {
paint(painter, state);
+85 -2
View File
@@ -4,6 +4,8 @@
#include "../event/Event.h"
#include <renderive/renderable/base/Renderable_Base.hpp>
#include <renderive/renderable/base/Renderable_Task_Observer.hpp>
#include <renderive/base/statistics/Rolling_Statistics.hpp>
#include <atomic>
#include <functional>
@@ -24,7 +26,72 @@ public:
virtual void handle_event(const Event& event) = 0;
};
class LIB_DECL Renderable : public ::Renderable_Base {
enum class Renderable_Observer_Event {
None,
Cache_Updated,
Published,
Render_Started,
Render_Completed,
Render_Failed
};
struct Renderable_Observation {
Renderable_Observer_Event event{Renderable_Observer_Event::None};
std::uint64_t event_time_ns{};
std::uint64_t cache_update_count{};
std::uint64_t publish_count{};
std::uint64_t successful_render_count{};
std::uint64_t failed_render_count{};
std::uint64_t last_render_sequence{};
std::uint64_t last_render_duration_ns{};
std::uint64_t total_render_duration_ns{};
std::uint64_t maximum_render_duration_ns{};
std::size_t render_duration_sample_count{};
std::uint64_t render_duration_average_ns{};
std::uint64_t render_duration_deviation_ns{};
std::uint64_t render_duration_p50_ns{};
std::uint64_t render_duration_p95_ns{};
std::uint64_t render_duration_p99_ns{};
std::uint64_t task_execution_count{};
std::uint64_t failed_task_count{};
std::uint64_t last_task_duration_ns{};
std::uint64_t total_task_duration_ns{};
std::uint64_t maximum_task_duration_ns{};
std::size_t task_duration_sample_count{};
std::uint64_t task_duration_average_ns{};
std::uint64_t task_duration_deviation_ns{};
std::uint64_t task_duration_p50_ns{};
std::uint64_t task_duration_p95_ns{};
std::uint64_t task_duration_p99_ns{};
std::size_t last_task_count{};
std::size_t peak_parallelism{};
};
class LIB_DECL Renderable_Observer final {
public:
[[nodiscard]] Renderable_Observation observation() const noexcept;
void observe_state(Renderable_Observer_Event event, std::uint64_t time_ns,
std::uint64_t cache_update_count,
std::uint64_t publish_count) noexcept;
void observe_task(const Renderable_Task_Observation& observation) noexcept;
void reset_performance() noexcept;
private:
mutable std::mutex mutex_;
Renderable_Observation observation_;
std::uint64_t active_render_sequence_{};
std::uint64_t active_render_started_ns_{};
std::size_t active_task_count_{};
std::size_t completed_task_count_{};
std::size_t running_task_count_{};
std::size_t active_peak_parallelism_{};
bool active_render_failed_{};
Rolling_Statistics render_duration_statistics_{256};
Rolling_Statistics task_duration_statistics_{1024};
};
class LIB_DECL Renderable : public ::Renderable_Base,
public ::Renderable_Task_Observer {
public:
explicit Renderable(Plot_Core& plot, bool cache_enabled = true);
~Renderable() override;
@@ -37,8 +104,10 @@ public:
void set_object_name(std::string name);
[[nodiscard]] bool is_visible() const noexcept;
void set_visible(bool visible);
[[nodiscard]] Renderable_Observation observation() const noexcept;
void render(const ::Scene_Render_Context& context) final;
void observe_task(const Renderable_Task_Observation& observation) noexcept final;
protected:
using Paint_Task_Function =
@@ -56,11 +125,16 @@ protected:
private:
void build_task_graph(Renderable_Task_Graph& graph) final;
void reset_task_observation() noexcept final;
void observe_state(Renderable_Observer_Event event, std::uint64_t time_ns,
std::uint64_t cache_update_count,
std::uint64_t publish_count) noexcept;
friend class detail::Renderable_State_Observer;
Plot_Core& plot_;
mutable std::mutex metadata_mutex_;
std::string object_name_;
std::atomic<bool> visible_{true};
Renderable_Observer observer_;
};
namespace detail {
@@ -73,7 +147,16 @@ public:
template <class Observation>
void observe(const Observation& observation) noexcept {
if (observation.event == decltype(observation.event)::cache_updated)
const bool cache_updated =
observation.event == decltype(observation.event)::cache_updated;
renderable_->observe_state(
cache_updated ? Renderable_Observer_Event::Cache_Updated
: Renderable_Observer_Event::Published,
observation.time_ns,
observation.cache_update_count,
observation.publish_count
);
if (cache_updated)
renderable_->changed();
}
@@ -589,6 +589,88 @@ TEST(Renderive_Core2, PlottablePropertiesPublishOnlyAtFrameBoundary) {
EXPECT_EQ(state->state_revision(), 1U);
ASSERT_TRUE(plot.render_prepared_frame());
}
TEST(Renderive_Core2, EveryStatefulRenderableOwnsItsStateObserver) {
Plot_Core plot;
plot.init();
const auto root = plot.root_renderable();
const auto frequency = Frequency_Axis::Builder(root, Orientation::Horizontal).build();
const auto power = Axis::Builder(root, Orientation::Vertical).build();
const auto spectrum = Spectrum::Builder{}.build(root, frequency, power);
ASSERT_TRUE(frequency);
ASSERT_TRUE(power);
ASSERT_TRUE(spectrum);
EXPECT_EQ(frequency->observation().event, Renderable_Observer_Event::None);
EXPECT_EQ(power->observation().event, Renderable_Observer_Event::None);
EXPECT_EQ(spectrum->observation().event, Renderable_Observer_Event::None);
frequency->set<&Axis_Properties::coordinates>(Range{88'000'000.0, 108'000'000.0});
spectrum->set<&Spectrum::Properties::partition_count>(2);
const auto frequency_update = frequency->observation();
const auto spectrum_update = spectrum->observation();
EXPECT_EQ(frequency_update.event, Renderable_Observer_Event::Cache_Updated);
EXPECT_EQ(frequency_update.cache_update_count, 1U);
EXPECT_EQ(spectrum_update.event, Renderable_Observer_Event::Cache_Updated);
EXPECT_EQ(spectrum_update.cache_update_count, 1U);
EXPECT_EQ(power->observation().event, Renderable_Observer_Event::None);
ASSERT_TRUE(plot.prepare_frame());
const auto frequency_publish = frequency->observation();
const auto power_publish = power->observation();
const auto spectrum_publish = spectrum->observation();
EXPECT_EQ(frequency_publish.event, Renderable_Observer_Event::Published);
EXPECT_EQ(power_publish.event, Renderable_Observer_Event::Published);
EXPECT_EQ(spectrum_publish.event, Renderable_Observer_Event::Published);
EXPECT_EQ(frequency_publish.publish_count, 1U);
EXPECT_EQ(power_publish.publish_count, 1U);
EXPECT_EQ(spectrum_publish.publish_count, 1U);
EXPECT_EQ(frequency_publish.cache_update_count, 1U);
EXPECT_EQ(power_publish.cache_update_count, 0U);
EXPECT_EQ(spectrum_publish.cache_update_count, 1U);
ASSERT_TRUE(plot.render_prepared_frame());
const auto frequency_render = frequency->observation();
const auto power_render = power->observation();
const auto spectrum_render = spectrum->observation();
EXPECT_EQ(frequency_render.event, Renderable_Observer_Event::Render_Completed);
EXPECT_EQ(power_render.event, Renderable_Observer_Event::Render_Completed);
EXPECT_EQ(spectrum_render.event, Renderable_Observer_Event::Render_Completed);
EXPECT_GT(frequency_render.successful_render_count, 0U);
EXPECT_GT(power_render.successful_render_count, 0U);
EXPECT_GT(spectrum_render.successful_render_count, 0U);
EXPECT_EQ(spectrum_render.last_task_count, spectrum->task_graph()->nodes().size());
EXPECT_GE(spectrum_render.task_execution_count, spectrum_render.last_task_count);
EXPECT_GE(spectrum_render.peak_parallelism, 1U);
}
TEST(Renderive_Core2, RenderableObserverKeepsFailureTerminalForTheRenderSequence) {
Renderable_Observer observer;
observer.observe_task({Renderable_Task_Observation_Event::started, 100, 7, 2, 0});
observer.observe_task({Renderable_Task_Observation_Event::failed, 180, 7, 2, 80});
observer.observe_task({Renderable_Task_Observation_Event::started, 190, 7, 2, 0});
observer.observe_task({Renderable_Task_Observation_Event::completed, 240, 7, 2, 50});
const auto failed = observer.observation();
EXPECT_EQ(failed.event, Renderable_Observer_Event::Render_Failed);
EXPECT_EQ(failed.failed_render_count, 1U);
EXPECT_EQ(failed.successful_render_count, 0U);
EXPECT_EQ(failed.failed_task_count, 1U);
EXPECT_EQ(failed.task_execution_count, 2U);
EXPECT_EQ(failed.last_task_count, 2U);
EXPECT_EQ(failed.last_render_duration_ns, 140U);
EXPECT_EQ(failed.total_render_duration_ns, 140U);
EXPECT_EQ(failed.render_duration_sample_count, 1U);
EXPECT_EQ(failed.render_duration_p95_ns, 140U);
EXPECT_EQ(failed.render_duration_p99_ns, 140U);
EXPECT_EQ(failed.task_duration_sample_count, 2U);
EXPECT_EQ(failed.task_duration_p95_ns, 80U);
observer.reset_performance();
const auto reset = observer.observation();
EXPECT_EQ(reset.failed_render_count, 0U);
EXPECT_EQ(reset.task_execution_count, 0U);
EXPECT_EQ(reset.render_duration_sample_count, 0U);
EXPECT_EQ(reset.task_duration_sample_count, 0U);
}
TEST(Renderive_Core2, PlottableDataUpdatesReachKernelRealTimeDataStrategy) {
Plot_Core plot;
plot.init();