所有权指针明确
This commit is contained in:
@@ -36,11 +36,12 @@ std::span<const Plot_Definition> gallery_plot_definitions() noexcept {
|
||||
return definitions;
|
||||
}
|
||||
|
||||
const Plot_Definition* find_gallery_plot_definition(
|
||||
std::string_view id) noexcept {
|
||||
std::optional<not_null<const Plot_Definition*>>
|
||||
find_gallery_plot_definition(std::string_view id) noexcept {
|
||||
const auto found = std::ranges::find(definitions, id,
|
||||
&Plot_Definition::id);
|
||||
return found == definitions.end() ? nullptr : &*found;
|
||||
if (found == definitions.end()) return std::nullopt;
|
||||
return not_null{&*found};
|
||||
}
|
||||
|
||||
std::string_view plot_dimension_name(Plot_Dimension dimension) noexcept {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include "Gallery_Plots_2D.hpp"
|
||||
#include "Gallery_Plots_3D.hpp"
|
||||
#include <optional>
|
||||
#include <ownership.hpp>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
@@ -18,8 +20,8 @@ struct Plot_Definition {
|
||||
|
||||
[[nodiscard]] std::span<const Plot_Definition>
|
||||
gallery_plot_definitions() noexcept;
|
||||
[[nodiscard]] const Plot_Definition* find_gallery_plot_definition(
|
||||
std::string_view id) noexcept;
|
||||
[[nodiscard]] std::optional<not_null<const Plot_Definition*>>
|
||||
find_gallery_plot_definition(std::string_view id) noexcept;
|
||||
[[nodiscard]] std::string_view plot_dimension_name(
|
||||
Plot_Dimension dimension) noexcept;
|
||||
}
|
||||
|
||||
@@ -508,16 +508,19 @@ std::unique_ptr<Time_Axis_Object> make_time_axis(
|
||||
return std::move(result).value();
|
||||
}
|
||||
template <Axis_Object Horizontal_Axis, Axis_Object Vertical_Axis>
|
||||
std::unique_ptr<Selection_Object> selection_overlay(Horizontal_Axis* horizontal_axis, Vertical_Axis* vertical_axis) {
|
||||
std::unique_ptr<Selection_Object> selection_overlay(
|
||||
not_null<Horizontal_Axis*> horizontal_axis,
|
||||
not_null<Vertical_Axis*> vertical_axis) {
|
||||
auto result = Selection_Object::Builder<Selection_Object>(horizontal_axis, vertical_axis).build();
|
||||
if (!result) throw std::logic_error("selection overlay axes form an invalid dependency graph");
|
||||
return std::move(result).value();
|
||||
}
|
||||
template <typename... Axes>
|
||||
void resize_axes(Scene_2D* scene, Size viewport, Axes*... axes) {
|
||||
void resize_axes(not_null<Scene_2D*> scene, Size viewport,
|
||||
not_null<Axes*>... axes) {
|
||||
const Size previous_viewport = scene->template read_prop<Render_Scene_2D::Base_Tag>().viewport;
|
||||
if (previous_viewport == viewport || previous_viewport.empty()) return;
|
||||
const auto resize_axis = [&](auto* axis) {
|
||||
const auto resize_axis = [&](auto axis) {
|
||||
const auto layout = axis->template read_prop<Abs_Axis::Base_Tag>();
|
||||
const double horizontal_scale = static_cast<double>(viewport.width) / previous_viewport.width;
|
||||
const double vertical_scale = static_cast<double>(viewport.height) / previous_viewport.height;
|
||||
@@ -549,7 +552,10 @@ std::shared_ptr<Plot> make_axes_plot() {
|
||||
.add_dependency_node<Render_Graph_Tag>(numeric.get())
|
||||
.add_dependency_node<Render_Graph_Tag>(time.get())
|
||||
.build();
|
||||
auto update = [scene = scene.get(), frequency = frequency.get(), numeric = numeric.get(), time = time.get()](const Plot_Render_Tick& event, bool) {
|
||||
auto update = [scene = not_null{scene.get()},
|
||||
frequency = not_null{frequency.get()},
|
||||
numeric = not_null{numeric.get()},
|
||||
time = not_null{time.get()}](const Plot_Render_Tick& event, bool) {
|
||||
resize_axes(scene, {static_cast<int>(event.width), static_cast<int>(event.height)}, frequency, numeric, time);
|
||||
constexpr double day_milliseconds = 86'400'000.0;
|
||||
time->append_time(Time_Of_Day{
|
||||
@@ -570,7 +576,9 @@ std::shared_ptr<Plot> make_axes_plot() {
|
||||
generator_number_field("value_min", "数值下界", "数值轴可见坐标下界。", -100.0, -1'000'000'000.0, 1'000'000'000.0),
|
||||
generator_number_field("value_max", "数值上界", "数值轴可见坐标上界,必须大于下界。", 0.0, -1'000'000'000.0, 1'000'000'000.0)
|
||||
});
|
||||
auto generate = [frequency = frequency.get(), numeric = numeric.get(), time = time.get()](const Json& input) {
|
||||
auto generate = [frequency = not_null{frequency.get()},
|
||||
numeric = not_null{numeric.get()},
|
||||
time = not_null{time.get()}](const Json& input) {
|
||||
try {
|
||||
const auto sample_count = generator_count(input, "time_sample_count");
|
||||
const auto time_step = generator_count(input, "time_step_ms");
|
||||
@@ -604,20 +612,25 @@ std::shared_ptr<Plot> make_spectrum_plot() {
|
||||
constexpr Size canvas{720, 420};
|
||||
auto frequency = make_frequency_axis();
|
||||
auto vertical = make_numeric_axis(Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-110.0, 0.0});
|
||||
auto spectrum = *Spectrum::Builder<Spectrum>(frequency.get(), vertical.get(), 4u)
|
||||
auto spectrum = *Spectrum::Builder<Spectrum>(
|
||||
not_null{frequency.get()}, not_null{vertical.get()}, 4u)
|
||||
.set(&Spectrum::Prop::frequency_range, Axis_Range{0.0, 100.0})
|
||||
.set(&Spectrum::Prop::max_hold_visible, true)
|
||||
.build();
|
||||
auto selection = selection_overlay(frequency.get(), vertical.get());
|
||||
auto selection = selection_overlay(
|
||||
not_null{frequency.get()}, not_null{vertical.get()});
|
||||
auto scene = *Scene_2D::Builder<Scene_2D>()
|
||||
.set(&Render_Scene_2D::Prop::viewport, canvas)
|
||||
.set(&Render_Scene_2D::Prop::background, Color{7, 13, 24, 255})
|
||||
.set(&Render_Scene_2D::Prop::view_active, true)
|
||||
.add_renderable(spectrum.get())
|
||||
.add_renderable(selection.get())
|
||||
.add_renderable(not_null{spectrum.get()})
|
||||
.add_renderable(not_null{selection.get()})
|
||||
.add_dependency<Render_Graph_Tag>(selection.get(), spectrum.get())
|
||||
.build();
|
||||
auto update = [scene = scene.get(), raw = spectrum.get(), frequency = frequency.get(), vertical = vertical.get()](const Plot_Render_Tick& event, bool demo_data) {
|
||||
auto update = [scene = not_null{scene.get()},
|
||||
raw = not_null{spectrum.get()},
|
||||
frequency = not_null{frequency.get()},
|
||||
vertical = not_null{vertical.get()}](const Plot_Render_Tick& event, bool demo_data) {
|
||||
resize_axes(scene, {static_cast<int>(event.width), static_cast<int>(event.height)}, frequency, vertical);
|
||||
if (!demo_data) return;
|
||||
std::array < double, 256 > samples{};
|
||||
@@ -667,17 +680,22 @@ std::shared_ptr<Plot> make_frequency_trace_plot() {
|
||||
Axis_Orientation::horizontal, {64.0, 370.0}, 620.0);
|
||||
auto vertical = make_numeric_axis(
|
||||
Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-1.2, 1.2});
|
||||
auto trace = *Frequency_Trace::Builder<Frequency_Trace>(time.get(), vertical.get(), 4u).build();
|
||||
auto selection = selection_overlay(time.get(), vertical.get());
|
||||
auto trace = *Frequency_Trace::Builder<Frequency_Trace>(
|
||||
not_null{time.get()}, not_null{vertical.get()}, 4u).build();
|
||||
auto selection = selection_overlay(
|
||||
not_null{time.get()}, not_null{vertical.get()});
|
||||
auto scene = *Scene_2D::Builder<Scene_2D>{}
|
||||
.set(&Render_Scene_2D::Prop::viewport, canvas)
|
||||
.set(&Render_Scene_2D::Prop::background, Color{7, 13, 24, 255})
|
||||
.set(&Render_Scene_2D::Prop::view_active, true)
|
||||
.add_renderable(trace.get())
|
||||
.add_renderable(selection.get())
|
||||
.add_renderable(not_null{trace.get()})
|
||||
.add_renderable(not_null{selection.get()})
|
||||
.add_dependency<Render_Graph_Tag>(selection.get(), trace.get())
|
||||
.build();
|
||||
auto update = [scene = scene.get(), raw = trace.get(), time = time.get(), vertical = vertical.get()](const Plot_Render_Tick& event, bool demo_data) {
|
||||
auto update = [scene = not_null{scene.get()},
|
||||
raw = not_null{trace.get()},
|
||||
time = not_null{time.get()},
|
||||
vertical = not_null{vertical.get()}](const Plot_Render_Tick& event, bool demo_data) {
|
||||
resize_axes(scene, {static_cast<int>(event.width), static_cast<int>(event.height)}, time, vertical);
|
||||
if (!demo_data) return;
|
||||
constexpr double day_milliseconds = 86'400'000.0;
|
||||
@@ -702,21 +720,26 @@ std::shared_ptr<Plot> make_sweep_spectrum_plot() {
|
||||
auto frequency = make_frequency_axis();
|
||||
auto vertical = make_numeric_axis(
|
||||
Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-110.0, 0.0});
|
||||
auto sweep = *Sweep_Spectrum::Builder<Sweep_Spectrum>(frequency.get(), vertical.get(), 4u)
|
||||
auto sweep = *Sweep_Spectrum::Builder<Sweep_Spectrum>(
|
||||
not_null{frequency.get()}, not_null{vertical.get()}, 4u)
|
||||
.set(&Sweep_Spectrum::Prop::frequency_range, Axis_Range{0.0, 100.0})
|
||||
.set(&Sweep_Spectrum::Prop::bins_per_block, std::size_t{8})
|
||||
.set(&Sweep_Spectrum::Prop::block_count, std::size_t{64})
|
||||
.build();
|
||||
auto selection = selection_overlay(frequency.get(), vertical.get());
|
||||
auto selection = selection_overlay(
|
||||
not_null{frequency.get()}, not_null{vertical.get()});
|
||||
auto scene = *Scene_2D::Builder<Scene_2D>{}
|
||||
.set(&Render_Scene_2D::Prop::viewport, canvas)
|
||||
.set(&Render_Scene_2D::Prop::background, Color{7, 13, 24, 255})
|
||||
.set(&Render_Scene_2D::Prop::view_active, true)
|
||||
.add_renderable(sweep.get())
|
||||
.add_renderable(selection.get())
|
||||
.add_renderable(not_null{sweep.get()})
|
||||
.add_renderable(not_null{selection.get()})
|
||||
.add_dependency<Render_Graph_Tag>(selection.get(), sweep.get())
|
||||
.build();
|
||||
auto update = [scene = scene.get(), raw = sweep.get(), frequency = frequency.get(), vertical = vertical.get()](const Plot_Render_Tick& event, bool demo_data) {
|
||||
auto update = [scene = not_null{scene.get()},
|
||||
raw = not_null{sweep.get()},
|
||||
frequency = not_null{frequency.get()},
|
||||
vertical = not_null{vertical.get()}](const Plot_Render_Tick& event, bool demo_data) {
|
||||
resize_axes(scene, {static_cast<int>(event.width), static_cast<int>(event.height)}, frequency, vertical);
|
||||
if (!demo_data) return;
|
||||
const auto& state = raw->template read_prop<Sweep_Spectrum::Base_Tag>();
|
||||
@@ -751,21 +774,26 @@ std::shared_ptr<Plot> make_afterglow_plot() {
|
||||
auto vertical = make_numeric_axis(
|
||||
Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-110.0, 0.0});
|
||||
auto afterglow = *Afterglow::Builder<Afterglow>(
|
||||
frequency.get(), vertical.get(), Plot_Partition_Grid{4, 3})
|
||||
not_null{frequency.get()}, not_null{vertical.get()},
|
||||
Plot_Partition_Grid{4, 3})
|
||||
.set(&Afterglow::Prop::frequency_range, Axis_Range{0.0, 100.0})
|
||||
.set(&Afterglow::Prop::power_range, Axis_Range{-110.0, 0.0})
|
||||
.set(&Afterglow::Prop::power_point_size, 96)
|
||||
.build();
|
||||
auto selection = selection_overlay(frequency.get(), vertical.get());
|
||||
auto selection = selection_overlay(
|
||||
not_null{frequency.get()}, not_null{vertical.get()});
|
||||
auto scene = *Scene_2D::Builder<Scene_2D>{}
|
||||
.set(&Render_Scene_2D::Prop::viewport, canvas)
|
||||
.set(&Render_Scene_2D::Prop::background, Color{7, 13, 24, 255})
|
||||
.set(&Render_Scene_2D::Prop::view_active, true)
|
||||
.add_renderable(afterglow.get())
|
||||
.add_renderable(selection.get())
|
||||
.add_renderable(not_null{afterglow.get()})
|
||||
.add_renderable(not_null{selection.get()})
|
||||
.add_dependency<Render_Graph_Tag>(selection.get(), afterglow.get())
|
||||
.build();
|
||||
auto update = [scene = scene.get(), raw = afterglow.get(), frequency = frequency.get(), vertical = vertical.get()](const Plot_Render_Tick& event, bool demo_data) {
|
||||
auto update = [scene = not_null{scene.get()},
|
||||
raw = not_null{afterglow.get()},
|
||||
frequency = not_null{frequency.get()},
|
||||
vertical = not_null{vertical.get()}](const Plot_Render_Tick& event, bool demo_data) {
|
||||
resize_axes(scene, {static_cast<int>(event.width), static_cast<int>(event.height)}, frequency, vertical);
|
||||
if (!demo_data) return;
|
||||
std::array < double, 192 > values{};
|
||||
@@ -795,20 +823,25 @@ std::shared_ptr<Plot> make_waterfall_plot() {
|
||||
auto time = make_time_axis(
|
||||
Axis_Orientation::vertical, {64.0, 370.0}, -320.0);
|
||||
auto waterfall = *Waterfall::Builder<Waterfall>(
|
||||
frequency.get(), time.get(), Plot_Partition_Grid{4, 3})
|
||||
not_null{frequency.get()}, not_null{time.get()},
|
||||
Plot_Partition_Grid{4, 3})
|
||||
.set(&Waterfall::Prop::frequency_range, Axis_Range{0.0, 100.0})
|
||||
.set(&Waterfall::Prop::power_range, Axis_Range{-110.0, 0.0})
|
||||
.build();
|
||||
auto selection = selection_overlay(frequency.get(), time.get());
|
||||
auto selection = selection_overlay(
|
||||
not_null{frequency.get()}, not_null{time.get()});
|
||||
auto scene = *Scene_2D::Builder<Scene_2D>{}
|
||||
.set(&Render_Scene_2D::Prop::viewport, canvas)
|
||||
.set(&Render_Scene_2D::Prop::background, Color{7, 13, 24, 255})
|
||||
.set(&Render_Scene_2D::Prop::view_active, true)
|
||||
.add_renderable(waterfall.get())
|
||||
.add_renderable(selection.get())
|
||||
.add_renderable(not_null{waterfall.get()})
|
||||
.add_renderable(not_null{selection.get()})
|
||||
.add_dependency<Render_Graph_Tag>(selection.get(), waterfall.get())
|
||||
.build();
|
||||
auto update = [scene = scene.get(), raw = waterfall.get(), frequency = frequency.get(), time = time.get()](const Plot_Render_Tick& event, bool demo_data) {
|
||||
auto update = [scene = not_null{scene.get()},
|
||||
raw = not_null{waterfall.get()},
|
||||
frequency = not_null{frequency.get()},
|
||||
time = not_null{time.get()}](const Plot_Render_Tick& event, bool demo_data) {
|
||||
resize_axes(scene, {static_cast<int>(event.width), static_cast<int>(event.height)}, frequency, time);
|
||||
if (!demo_data) return;
|
||||
constexpr double day_milliseconds = 86'400'000.0;
|
||||
@@ -849,20 +882,25 @@ std::shared_ptr<Plot> make_constellation_plot() {
|
||||
auto vertical = make_numeric_axis(
|
||||
Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-1.2, 1.2});
|
||||
auto constellation = *Constellation_Diagram::Builder<Constellation_Diagram>(
|
||||
horizontal.get(), vertical.get(), 4u)
|
||||
not_null{horizontal.get()},
|
||||
not_null{vertical.get()}, 4u)
|
||||
.set(&Constellation_Diagram::Prop::i_range, Axis_Range{-1.2, 1.2})
|
||||
.set(&Constellation_Diagram::Prop::q_range, Axis_Range{-1.2, 1.2})
|
||||
.build();
|
||||
auto selection = selection_overlay(horizontal.get(), vertical.get());
|
||||
auto selection = selection_overlay(
|
||||
not_null{horizontal.get()}, not_null{vertical.get()});
|
||||
auto scene = *Scene_2D::Builder<Scene_2D>{}
|
||||
.set(&Render_Scene_2D::Prop::viewport, canvas)
|
||||
.set(&Render_Scene_2D::Prop::background, Color{7, 13, 24, 255})
|
||||
.set(&Render_Scene_2D::Prop::view_active, true)
|
||||
.add_renderable(constellation.get())
|
||||
.add_renderable(selection.get())
|
||||
.add_renderable(not_null{constellation.get()})
|
||||
.add_renderable(not_null{selection.get()})
|
||||
.add_dependency<Render_Graph_Tag>(selection.get(), constellation.get())
|
||||
.build();
|
||||
auto update = [scene = scene.get(), raw = constellation.get(), horizontal = horizontal.get(), vertical = vertical.get()](const Plot_Render_Tick& event, bool demo_data) {
|
||||
auto update = [scene = not_null{scene.get()},
|
||||
raw = not_null{constellation.get()},
|
||||
horizontal = not_null{horizontal.get()},
|
||||
vertical = not_null{vertical.get()}](const Plot_Render_Tick& event, bool demo_data) {
|
||||
resize_axes(scene, {static_cast<int>(event.width), static_cast<int>(event.height)}, horizontal, vertical);
|
||||
if (!demo_data) return;
|
||||
const auto& state = raw->template read_prop<Constellation_Diagram::Base_Tag>();
|
||||
@@ -904,14 +942,17 @@ std::shared_ptr<Plot> make_selection_overlay_plot() {
|
||||
Axis_Orientation::horizontal, {64.0, 370.0}, 620.0, {0.0, 100.0});
|
||||
auto vertical = make_numeric_axis(
|
||||
Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {0.0, 100.0});
|
||||
auto selection = *Selection_Rectangle_Overlay::Builder<Selection_Rectangle_Overlay>(horizontal.get(), vertical.get()).build();
|
||||
auto selection = *Selection_Rectangle_Overlay::Builder<Selection_Rectangle_Overlay>(
|
||||
not_null{horizontal.get()}, not_null{vertical.get()}).build();
|
||||
auto scene = *Scene_2D::Builder<Scene_2D>{}
|
||||
.set(&Render_Scene_2D::Prop::viewport, canvas)
|
||||
.set(&Render_Scene_2D::Prop::background, Color{7, 13, 24, 255})
|
||||
.set(&Render_Scene_2D::Prop::view_active, true)
|
||||
.add_renderable(selection.get())
|
||||
.add_renderable(not_null{selection.get()})
|
||||
.build();
|
||||
auto update = [scene = scene.get(), horizontal = horizontal.get(), vertical = vertical.get()](const Plot_Render_Tick& event, bool) {
|
||||
auto update = [scene = not_null{scene.get()},
|
||||
horizontal = not_null{horizontal.get()},
|
||||
vertical = not_null{vertical.get()}](const Plot_Render_Tick& event, bool) {
|
||||
resize_axes(scene, {static_cast<int>(event.width), static_cast<int>(event.height)}, horizontal, vertical);
|
||||
};
|
||||
auto view = make_scene_view<
|
||||
|
||||
@@ -437,11 +437,15 @@ public:
|
||||
}
|
||||
void update(const Plot_Render_Tick& request) override {
|
||||
if constexpr (requires(Data_Generator& generator, Visual_Object& visual,
|
||||
Axes_Object& axes, Marker_Object* markers,
|
||||
Axes_Object& axes,
|
||||
std::optional<not_null<Marker_Object*>> markers,
|
||||
const Plot_Render_Tick& frame) {
|
||||
generator.update(visual, axes, markers, frame);
|
||||
}) {
|
||||
data_generator_.update(*visual_, *axes_, markers_.get(), request);
|
||||
const auto markers = markers_
|
||||
? std::optional{not_null{markers_.get()}}
|
||||
: std::nullopt;
|
||||
data_generator_.update(*visual_, *axes_, markers, request);
|
||||
}
|
||||
}
|
||||
private:
|
||||
@@ -494,13 +498,13 @@ std::shared_ptr<Plot> make_visual_plot(std::string label,
|
||||
auto camera = std::move(camera_result).value();
|
||||
auto axes = std::move(axes_result).value();
|
||||
auto scene_builder = Scene_3D::Builder<Scene_3D>{};
|
||||
scene_builder.add_camera(camera.get())
|
||||
.add_axes(axes.get())
|
||||
.add_renderable(visual.get())
|
||||
scene_builder.add_camera(not_null{camera.get()})
|
||||
.add_axes(not_null{axes.get()})
|
||||
.add_renderable(not_null{visual.get()})
|
||||
.set(&Render_Scene_3D::Prop::viewport, Extent{720, 420})
|
||||
.set(&Render_Scene_3D::Prop::clear_color, Linear_Color{0.018F, 0.027F, 0.047F, 1.0F})
|
||||
.set(&Render_Scene_3D::Prop::view_active, true);
|
||||
if (markers) scene_builder.add_renderable(markers.get());
|
||||
if (markers) scene_builder.add_renderable(not_null{markers.get()});
|
||||
auto scene_result = scene_builder.build();
|
||||
if (!scene_result) throw std::logic_error("3D Gallery scene dependency graph is invalid");
|
||||
auto scene = std::move(scene_result).value();
|
||||
@@ -744,7 +748,7 @@ struct Spectrogram_Data_Generator {
|
||||
}
|
||||
}
|
||||
void update(Mesh_Visual& visual, Axes_3D&,
|
||||
Marker_Visual* markers,
|
||||
std::optional<not_null<Marker_Visual*>> markers,
|
||||
const Plot_Render_Tick& request) {
|
||||
if (!parameters.animation_enabled ||
|
||||
request.sequence % parameters.update_every_n_frames != 0)
|
||||
@@ -752,15 +756,15 @@ struct Spectrogram_Data_Generator {
|
||||
const double animation_seconds = request.time_milliseconds / 1000.0 * parameters.animation_speed;
|
||||
auto mesh = spectrogram_mesh(parameters, animation_seconds);
|
||||
visual.template set<&Mesh_Visual::Prop::items>(std::move(mesh));
|
||||
if (markers == nullptr) return;
|
||||
auto items = markers->template read_prop<Marker_Visual::Base_Tag>().items;
|
||||
if (!markers) return;
|
||||
auto items = (*markers)->template read_prop<Marker_Visual::Base_Tag>().items;
|
||||
for (auto& marker : items) {
|
||||
const float time = std::clamp((marker.position.x + 1.0F) * 0.5F, 0.0F, 1.0F);
|
||||
const float frequency = std::clamp((marker.position.y + 1.0F) * 0.5F, 0.0F, 1.0F);
|
||||
marker.position.z = -1.0F + 2.0F * spectrogram_level(
|
||||
time, frequency, animation_seconds, parameters.ridge_count);
|
||||
}
|
||||
markers->template set<&Marker_Visual::Prop::items>(std::move(items));
|
||||
(*markers)->template set<&Marker_Visual::Prop::items>(std::move(items));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+69
-63
@@ -637,11 +637,11 @@ struct Plot::Private {
|
||||
void refresh_schedule();
|
||||
void clock_tick(const Plot_Render_Tick& tick);
|
||||
void render_frame(Plot_Render_Tick tick);
|
||||
void publish_completed_frame(Render_Frame* completed);
|
||||
void consume_completed_frame(Render_Frame* frame);
|
||||
void retire_completed_frame(Render_Frame* frame);
|
||||
void publish_completed_frame(not_null<Render_Frame*> completed);
|
||||
void consume_completed_frame(not_null<Render_Frame*> frame);
|
||||
void retire_completed_frame(not_null<Render_Frame*> frame);
|
||||
void consume_retired_frames();
|
||||
void finalize_retired_frame(Render_Frame* frame);
|
||||
void finalize_retired_frame(not_null<Render_Frame*> frame);
|
||||
void attach_completion(std::unique_ptr<Task_Graph> completion);
|
||||
[[nodiscard]] bool mark_taskflow_trace(Render_Frame& frame);
|
||||
[[nodiscard]] bool mark_post_publish_taskflow_trace();
|
||||
@@ -1095,7 +1095,20 @@ void Plot::Private::render_frame(Plot_Render_Tick tick) {
|
||||
record_plot_measurements(output);
|
||||
(*scene_2d)->set<&Render_Scene_2D::Prop::viewport>(
|
||||
Size{static_cast<int>(tick.width), static_cast<int>(tick.height)});
|
||||
const auto result = (*scene_2d)->render(&output);
|
||||
const auto result = (*scene_2d)->render(
|
||||
&output,
|
||||
[weak = lifetime](not_null<Frame_2D*> frame) {
|
||||
if (auto owner = weak.lock()) {
|
||||
try {
|
||||
owner->d->publish_completed_frame(frame);
|
||||
owner->d->consume_completed_frame(frame);
|
||||
owner->d->retire_completed_frame(frame);
|
||||
}
|
||||
catch (...) {
|
||||
owner->d->fail(std::current_exception());
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!result) {
|
||||
with_frame_policy([](auto& policy) {
|
||||
policy.record_scene_rejection();
|
||||
@@ -1147,24 +1160,25 @@ void Plot::Private::render_frame(Plot_Render_Tick tick) {
|
||||
}
|
||||
|
||||
|
||||
void Plot::Private::publish_completed_frame(Render_Frame* completed) {
|
||||
if (!completed)
|
||||
throw std::invalid_argument("Plot received a null completed frame");
|
||||
Managed_Frame* managed{};
|
||||
void Plot::Private::publish_completed_frame(
|
||||
not_null<Render_Frame*> completed) {
|
||||
std::optional<not_null<Managed_Frame*>> managed;
|
||||
for (auto& slot : frame_slots) {
|
||||
auto* frame = std::visit(
|
||||
[](const auto& value) -> Render_Frame* { return value.get(); },
|
||||
const auto frame = std::visit(
|
||||
[](const auto& value) -> not_null<Render_Frame*> {
|
||||
return not_null{value.get()};
|
||||
},
|
||||
slot.frame);
|
||||
if (frame != completed) continue;
|
||||
if (frame.get() != completed.get()) continue;
|
||||
managed = &slot;
|
||||
break;
|
||||
}
|
||||
if (!managed)
|
||||
throw std::logic_error("completed frame has no owning Plot policy slot");
|
||||
if (managed->state.load(std::memory_order_acquire) !=
|
||||
if ((*managed)->state.load(std::memory_order_acquire) !=
|
||||
Frame_State::rendering)
|
||||
throw std::logic_error("completed Plot frame is not rendering");
|
||||
auto* frame = completed;
|
||||
const auto frame = completed;
|
||||
|
||||
const auto& pacing = pacing_state();
|
||||
const auto identity = frame->identity();
|
||||
@@ -1174,7 +1188,7 @@ void Plot::Private::publish_completed_frame(Render_Frame* completed) {
|
||||
std::uint32_t width{};
|
||||
std::uint32_t height{};
|
||||
if (auto *frame_2d =
|
||||
std::get_if<std::unique_ptr<Frame_2D>>(&managed->frame)) {
|
||||
std::get_if<std::unique_ptr<Frame_2D>>(&(*managed)->frame)) {
|
||||
pixel_layout = Plot_Pixel_Layout::bgra8;
|
||||
const auto image = (*frame_2d)->image();
|
||||
width = static_cast<std::uint32_t>(image.width);
|
||||
@@ -1187,7 +1201,7 @@ void Plot::Private::publish_completed_frame(Render_Frame* completed) {
|
||||
height = static_cast<std::uint32_t>(output.height);
|
||||
}
|
||||
} else {
|
||||
auto &frame_3d = std::get<std::unique_ptr<Frame_3D>>(managed->frame);
|
||||
auto &frame_3d = std::get<std::unique_ptr<Frame_3D>>((*managed)->frame);
|
||||
rendered_identity = frame_3d->rendered_identity();
|
||||
const auto extent = frame_3d->extent();
|
||||
width = extent.width;
|
||||
@@ -1197,7 +1211,7 @@ void Plot::Private::publish_completed_frame(Render_Frame* completed) {
|
||||
}
|
||||
|
||||
auto pixels = std::make_shared<const Plot_Pixel_Frame>(Plot_Pixel_Frame{
|
||||
std::move(pixel_storage), pixel_layout, managed->presentation_time,
|
||||
std::move(pixel_storage), pixel_layout, (*managed)->presentation_time,
|
||||
identity.sequence, identity.correlation_id, rendered_identity.sequence,
|
||||
rendered_identity.correlation_id, width, height});
|
||||
|
||||
@@ -1211,21 +1225,21 @@ void Plot::Private::publish_completed_frame(Render_Frame* completed) {
|
||||
std::chrono::steady_clock::now() - publish_started)
|
||||
.count())));
|
||||
auto expected = Frame_State::rendering;
|
||||
if (!managed->state.compare_exchange_strong(
|
||||
if (!(*managed)->state.compare_exchange_strong(
|
||||
expected, Frame_State::consuming, std::memory_order_acq_rel,
|
||||
std::memory_order_acquire))
|
||||
throw std::logic_error("Plot frame left rendering before pixel publish");
|
||||
}
|
||||
|
||||
void Plot::Private::consume_completed_frame(Render_Frame* frame) {
|
||||
if (!frame)
|
||||
throw std::invalid_argument("Plot received a null completed frame");
|
||||
Managed_Frame* managed{};
|
||||
void Plot::Private::consume_completed_frame(not_null<Render_Frame*> frame) {
|
||||
std::optional<not_null<Managed_Frame*>> managed;
|
||||
for (auto& slot : frame_slots) {
|
||||
auto* address = std::visit(
|
||||
[](const auto& value) -> Render_Frame* { return value.get(); },
|
||||
const auto address = std::visit(
|
||||
[](const auto& value) -> not_null<Render_Frame*> {
|
||||
return not_null{value.get()};
|
||||
},
|
||||
slot.frame);
|
||||
if (address != frame) continue;
|
||||
if (address.get() != frame.get()) continue;
|
||||
if (slot.state.load(std::memory_order_acquire) != Frame_State::consuming)
|
||||
throw std::logic_error("completed Plot frame was not published");
|
||||
managed = &slot;
|
||||
@@ -1239,7 +1253,7 @@ void Plot::Private::consume_completed_frame(Render_Frame* frame) {
|
||||
* 释放 Plot 的 view/update 门,并立刻唤醒 busy 期间保留的 latest tick。
|
||||
* 之后外接 DAG 仍在当前 Frame trace 内执行,但不会阻塞下一帧渲染。
|
||||
*/
|
||||
if (std::holds_alternative<std::unique_ptr<Frame_2D>>(managed->frame))
|
||||
if (std::holds_alternative<std::unique_ptr<Frame_2D>>((*managed)->frame))
|
||||
release_render_admission(lifetime);
|
||||
if (post_publish_graph.empty()) return;
|
||||
|
||||
@@ -1321,15 +1335,15 @@ void Plot::Private::consume_retired_frames() {
|
||||
}
|
||||
}
|
||||
|
||||
void Plot::Private::retire_completed_frame(Render_Frame* frame) {
|
||||
if (!frame)
|
||||
throw std::invalid_argument("Plot received a null retired frame");
|
||||
Managed_Frame* managed{};
|
||||
void Plot::Private::retire_completed_frame(not_null<Render_Frame*> frame) {
|
||||
std::optional<not_null<Managed_Frame*>> managed;
|
||||
for (auto& slot : frame_slots) {
|
||||
auto* address = std::visit(
|
||||
[](const auto& value) -> Render_Frame* { return value.get(); },
|
||||
const auto address = std::visit(
|
||||
[](const auto& value) -> not_null<Render_Frame*> {
|
||||
return not_null{value.get()};
|
||||
},
|
||||
slot.frame);
|
||||
if (address != frame) continue;
|
||||
if (address.get() != frame.get()) continue;
|
||||
managed = &slot;
|
||||
break;
|
||||
}
|
||||
@@ -1338,20 +1352,22 @@ void Plot::Private::retire_completed_frame(Render_Frame* frame) {
|
||||
|
||||
auto* head = retired_frames.load(std::memory_order_relaxed);
|
||||
do {
|
||||
managed->retired_next.store(head, std::memory_order_relaxed);
|
||||
(*managed)->retired_next.store(head, std::memory_order_relaxed);
|
||||
} while (!retired_frames.compare_exchange_weak(
|
||||
head, managed, std::memory_order_release,
|
||||
head, managed->get(), std::memory_order_release,
|
||||
std::memory_order_relaxed));
|
||||
arm_tick_consumer(lifetime);
|
||||
}
|
||||
|
||||
void Plot::Private::finalize_retired_frame(Render_Frame* frame) {
|
||||
Managed_Frame* managed{};
|
||||
void Plot::Private::finalize_retired_frame(not_null<Render_Frame*> frame) {
|
||||
std::optional<not_null<Managed_Frame*>> managed;
|
||||
for (auto& slot : frame_slots) {
|
||||
auto* address = std::visit(
|
||||
[](const auto& value) -> Render_Frame* { return value.get(); },
|
||||
const auto address = std::visit(
|
||||
[](const auto& value) -> not_null<Render_Frame*> {
|
||||
return not_null{value.get()};
|
||||
},
|
||||
slot.frame);
|
||||
if (address == frame) {
|
||||
if (address.get() == frame.get()) {
|
||||
managed = &slot;
|
||||
break;
|
||||
}
|
||||
@@ -1360,11 +1376,11 @@ void Plot::Private::finalize_retired_frame(Render_Frame* frame) {
|
||||
throw std::logic_error("retired frame has no owned Plot slot");
|
||||
|
||||
const auto completion_latency =
|
||||
managed->submitted_at.time_since_epoch().count() == 0
|
||||
(*managed)->submitted_at.time_since_epoch().count() == 0
|
||||
? std::chrono::steady_clock::duration::zero()
|
||||
: std::chrono::steady_clock::now() - managed->submitted_at;
|
||||
: std::chrono::steady_clock::now() - (*managed)->submitted_at;
|
||||
std::optional<Datoviz_Frame_Observation> datoviz_observation;
|
||||
if (auto* frame_3d = dynamic_cast<Frame_3D*>(frame)) {
|
||||
if (auto* frame_3d = dynamic_cast<Frame_3D*>(frame.get())) {
|
||||
datoviz_observation = frame_3d->take_datoviz_observation();
|
||||
if (!datoviz_observation)
|
||||
throw std::logic_error(
|
||||
@@ -1384,8 +1400,8 @@ void Plot::Private::finalize_retired_frame(Render_Frame* frame) {
|
||||
completed_frame_statistics.reset();
|
||||
applied_statistics_generation = statistics_generation_value;
|
||||
}
|
||||
managed->statistics = completed_frame_statistics.submit(*frame);
|
||||
managed->statistics_generation = statistics_generation_value;
|
||||
(*managed)->statistics = completed_frame_statistics.submit(*frame);
|
||||
(*managed)->statistics_generation = statistics_generation_value;
|
||||
|
||||
std::optional<Taskflow_Frame_Trace> captured_trace;
|
||||
nlohmann::json captured_components;
|
||||
@@ -1414,11 +1430,11 @@ void Plot::Private::finalize_retired_frame(Render_Frame* frame) {
|
||||
}
|
||||
|
||||
auto expected = Frame_State::consuming;
|
||||
if (!managed->state.compare_exchange_strong(
|
||||
if (!(*managed)->state.compare_exchange_strong(
|
||||
expected, Frame_State::available, std::memory_order_acq_rel,
|
||||
std::memory_order_acquire))
|
||||
throw std::logic_error("retired Plot frame is not consuming");
|
||||
latest_statistics_frame.store(managed, std::memory_order_release);
|
||||
latest_statistics_frame.store(managed->get(), std::memory_order_release);
|
||||
|
||||
/* 物理槽是唯一背压原因;归还任意槽后只解除一次耗尽状态。 */
|
||||
auto admission = Render_Admission_State::frame_slots_exhausted;
|
||||
@@ -1483,22 +1499,12 @@ void Plot::ensure_started() {
|
||||
.source = Frame_Request_Source::periodic});
|
||||
}
|
||||
});
|
||||
/* Scene 回调只表示借用结束;Frame 的发布、统计和复用始终由本帧策略决定。 */
|
||||
if (auto* scene = std::get_if<std::unique_ptr<Scene_2D>>(&d->scene)) {
|
||||
(*scene)->set_frame_callback([weak](Frame_2D* frame) {
|
||||
if (auto owner = weak.lock()) {
|
||||
try {
|
||||
owner->d->publish_completed_frame(frame);
|
||||
owner->d->consume_completed_frame(frame);
|
||||
owner->d->retire_completed_frame(frame);
|
||||
}
|
||||
catch (...) { owner->d->fail(std::current_exception()); }
|
||||
}
|
||||
});
|
||||
} else {
|
||||
/* 3D Scene 的提交/完成回调只表示借用阶段变化;Frame 的发布、
|
||||
* 统计和复用始终由本帧策略决定。2D 回调与每次 render 直接绑定。 */
|
||||
if (std::holds_alternative<std::unique_ptr<Scene_3D>>(d->scene)) {
|
||||
auto& scene_3d = std::get<std::unique_ptr<Scene_3D>>(d->scene);
|
||||
scene_3d->set_submitted_frame_callback(
|
||||
[weak](Frame_3D*, bool) {
|
||||
[weak](not_null<Frame_3D*>, bool) {
|
||||
if (auto owner = weak.lock()) {
|
||||
try {
|
||||
owner->d->release_render_admission(weak);
|
||||
@@ -1507,7 +1513,7 @@ void Plot::ensure_started() {
|
||||
}
|
||||
});
|
||||
scene_3d->set_frame_callback(
|
||||
[weak](Frame_3D* frame) {
|
||||
[weak](not_null<Frame_3D*> frame) {
|
||||
if (auto owner = weak.lock()) {
|
||||
try {
|
||||
owner->d->publish_completed_frame(frame);
|
||||
@@ -1518,7 +1524,7 @@ void Plot::ensure_started() {
|
||||
}
|
||||
});
|
||||
}
|
||||
/* callback 必须先于周期时钟安装,避免首帧在初始化窗口进入 Scene。 */
|
||||
/* 3D callback 必须先于周期时钟安装,避免首帧在初始化窗口进入 Scene。 */
|
||||
d->refresh_schedule();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <mcp/core/Render_Types.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <ownership.hpp>
|
||||
#include <render_2D/plottable/Plottables.hpp>
|
||||
#include <render_3D/Render_3D.hpp>
|
||||
#include <deque>
|
||||
@@ -43,13 +44,13 @@ public:
|
||||
explicit Renderable_Adapter(Object& value) : object(&value) {}
|
||||
void identify(std::string_view id) {
|
||||
if constexpr (std::derived_from<Object, aethera::Renderable>)
|
||||
double_buffer::detail::Internal_Access::layer<aethera::Renderable>(object)
|
||||
double_buffer::detail::Internal_Access::layer<aethera::Renderable>(object.get())
|
||||
.diagnostic_component_id = id;
|
||||
}
|
||||
private:
|
||||
template <typename Adapter, typename Field>
|
||||
friend struct Renderable_Field_Accessor;
|
||||
Object* object;
|
||||
not_null<Object*> object;
|
||||
};
|
||||
template <typename Adapter, typename Field>
|
||||
struct Renderable_Field_Accessor;
|
||||
|
||||
Reference in New Issue
Block a user