所有权指针明确

This commit is contained in:
2026-08-28 13:33:17 +08:00
parent 0bc281b6b9
commit e07d16ea74
70 changed files with 1387 additions and 960 deletions
+4 -3
View File
@@ -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 {
+4 -2
View File
@@ -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;
}
+79 -38
View File
@@ -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<
+14 -10
View File
@@ -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
View File
@@ -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();
});
}
+3 -2
View File
@@ -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;