所有权指针明确

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
+23 -20
View File
@@ -17,7 +17,7 @@ using namespace aethera;
using namespace aethera::render_2d;
template <typename Object, typename... Arguments>
Object* build_object(Arguments&&... arguments) {
owner<Object*> build_object(Arguments&&... arguments) {
typename Object::template Builder<Object> builder(std::forward<Arguments>(arguments)...);
auto result = builder.build();
if (!result) std::_Exit(2);
@@ -27,7 +27,7 @@ Object* build_object(Arguments&&... arguments) {
bool contains_color(Image_View view) {
for (int y = 0; y < view.height; ++y) {
const auto* row = reinterpret_cast<const Pixel*>(
view.data + static_cast<std::ptrdiff_t>(y) * view.stride);
view.data.data() + static_cast<std::ptrdiff_t>(y) * view.stride);
for (int x = 0; x < view.width; ++x)
if (row[x] != 0) return true;
}
@@ -48,15 +48,16 @@ int main() {
initialize_runtime({.workers = 4});
auto* frequency = build_object<Frequency>();
auto* power = build_object<Power>();
auto* spectrum = build_object<Spectrum_Object>(frequency, power, 4u);
auto* spectrum = build_object<Spectrum_Object>(
not_null{frequency}, not_null{power}, 4u);
auto* time = build_object<Time>();
auto* waterfall = build_object<Waterfall_Object>(
frequency, time, Plot_Partition_Grid{2, 2});
not_null{frequency}, not_null{time}, Plot_Partition_Grid{2, 2});
auto* afterglow = build_object<Afterglow_Object>(
frequency, power, Plot_Partition_Grid{2, 2});
not_null{frequency}, not_null{power}, Plot_Partition_Grid{2, 2});
auto* q_axis = build_object<Power>();
auto* constellation = build_object<Constellation_Object>(
power, q_axis, 3u);
not_null{power}, not_null{q_axis}, 3u);
auto* spectrum_painted = new std::atomic_bool{};
auto* topology_valid = new std::atomic_bool{true};
auto* second_frame_valid = new std::atomic_bool{};
@@ -75,10 +76,10 @@ int main() {
verify_spectrum_precedes_axis);
typename Scene_Object::template Builder<Scene_Object> scene_builder;
scene_builder.add_renderable(spectrum);
scene_builder.add_renderable(waterfall);
scene_builder.add_renderable(afterglow);
scene_builder.add_renderable(constellation);
scene_builder.add_renderable(not_null{spectrum});
scene_builder.add_renderable(not_null{waterfall});
scene_builder.add_renderable(not_null{afterglow});
scene_builder.add_renderable(not_null{constellation});
auto scene_result = scene_builder.build();
if (!scene_result) return 2;
auto* scene = std::move(scene_result).value().release();
@@ -157,12 +158,13 @@ int main() {
!afterglow_color_barrier)
trace_valid->store(false, std::memory_order_relaxed);
};
scene->set_frame_callback(
if (!scene->render(
frame,
[scene, spectrum, waterfall, afterglow, constellation, frame,
resized_partition_frame, spectrum_painted,
topology_valid, second_frame_valid, trace_valid,
validate_trace](Frame_2D* completed) {
const bool valid = completed == frame &&
validate_trace](not_null<Frame_2D*> completed) {
const bool valid = completed.get() == frame &&
spectrum->read_state<Spectrum::Base_Tag>().sample_count ==
512 &&
topology_valid->load(std::memory_order_relaxed) &&
@@ -181,12 +183,13 @@ int main() {
constellation->set<
&Constellation_Diagram::Prop::partition_count>(2u);
spectrum_painted->store(false, std::memory_order_relaxed);
scene->set_frame_callback(
if (!scene->render(
resized_partition_frame,
[resized_partition_frame, second_frame_valid,
topology_valid, trace_valid,
validate_trace](Frame_2D* next) {
validate_trace](not_null<Frame_2D*> next) {
const bool resized_valid =
next == resized_partition_frame &&
next.get() == resized_partition_frame &&
topology_valid->load(std::memory_order_relaxed) &&
contains_color(next->image());
second_frame_valid->store(resized_valid,
@@ -197,10 +200,10 @@ int main() {
trace_valid->load(std::memory_order_relaxed)
? 0
: 1);
});
if (!scene->render(resized_partition_frame)) std::_Exit(2);
});
if (!scene->render(frame)) return 2;
}))
std::_Exit(2);
}))
return 2;
/*
* render() 是异步入口。结束提交线程但保持 Taskflow worker 和进程存活,
+4 -4
View File
@@ -17,8 +17,8 @@ template <typename Scene>
std::unique_ptr<Frame_2D> render_frame(Scene* scene) {
static std::uint64_t sequence{1};
auto frame = std::make_unique<Frame_2D>(Frame_Identity{sequence++, 0});
scene->set_frame_callback([](Frame_2D*) {});
EXPECT_TRUE(scene->render(frame.get()).has_value());
EXPECT_TRUE(scene->render(
frame.get(), [](not_null<Frame_2D*>) {}).has_value());
return frame;
}
template <typename Scene>
@@ -74,7 +74,7 @@ TEST(render_cache, copy_owns_independent_pixels) {
ASSERT_FALSE(view.empty());
bool contains_color{};
for (int y = 0; y < view.height && !contains_color; ++y) {
const auto* row = reinterpret_cast<const Pixel*>(view.data + static_cast<std::ptrdiff_t>(y) * view.stride);
const auto* row = reinterpret_cast<const Pixel*>(view.data.data() + static_cast<std::ptrdiff_t>(y) * view.stride);
for (int x = 0; x < view.width; ++x) contains_color = contains_color || row[x] != 0;
}
EXPECT_TRUE(contains_color);
@@ -107,7 +107,7 @@ TEST(axis_render, scene_prepares_and_paints_uncached_axis_into_frame) {
ASSERT_FALSE(view.empty());
bool contains_color{};
for (int y = 0; y < view.height && !contains_color; ++y) {
const auto* row = reinterpret_cast<const Pixel*>(view.data + static_cast<std::ptrdiff_t>(y) * view.stride);
const auto* row = reinterpret_cast<const Pixel*>(view.data.data() + static_cast<std::ptrdiff_t>(y) * view.stride);
for (int x = 0; x < view.width; ++x) contains_color = contains_color || row[x] != 0;
}
EXPECT_TRUE(contains_color);
+2 -2
View File
@@ -48,7 +48,7 @@ TEST(framebuffer_view, writes_only_the_requested_external_image_region) {
ASSERT_EQ(view.height, 6);
for (int y = 0; y < view.height; ++y) {
const auto* row = reinterpret_cast<const Pixel*>(
view.data + static_cast<std::ptrdiff_t>(y) * view.stride);
view.data.data() + static_cast<std::ptrdiff_t>(y) * view.stride);
for (int x = 0; x < view.width; ++x) {
const bool inside = x >= 2 && x < 5 && y >= 1 && y < 3;
EXPECT_EQ(row[x] != 0, inside) << "pixel " << x << ',' << y;
@@ -75,7 +75,7 @@ TEST(framebuffer_view, overlapping_views_share_the_same_authoritative_pixels) {
const Image_View view = framebuffer.view();
const auto pixel = [&](int x, int y) {
const auto* row = reinterpret_cast<const Pixel*>(
view.data + static_cast<std::ptrdiff_t>(y) * view.stride);
view.data.data() + static_cast<std::ptrdiff_t>(y) * view.stride);
return row[x];
};
EXPECT_NE(pixel(2, 2), 0u);
@@ -39,16 +39,21 @@ TEST(partition_configuration,
auto power = build_object<Power>();
auto q_axis = build_object<Power>();
auto time = build_object<Time>();
auto spectrum = build_object<Spectrum_Object>(frequency.get(), power.get(),
auto spectrum = build_object<Spectrum_Object>(not_null{frequency.get()},
not_null{power.get()},
6u);
auto trace = build_object<Trace_Object>(time.get(), power.get(), 4u);
auto sweep = build_object<Sweep_Object>(frequency.get(), power.get(), 4u);
auto trace = build_object<Trace_Object>(not_null{time.get()},
not_null{power.get()}, 4u);
auto sweep = build_object<Sweep_Object>(not_null{frequency.get()},
not_null{power.get()}, 4u);
auto afterglow = build_object<Afterglow_Object>(
frequency.get(), power.get(), Plot_Partition_Grid{2, 3});
not_null{frequency.get()}, not_null{power.get()},
Plot_Partition_Grid{2, 3});
auto constellation = build_object<Constellation_Object>(
power.get(), q_axis.get(), 7u);
not_null{power.get()}, not_null{q_axis.get()}, 7u);
auto waterfall = build_object<Waterfall_Object>(
frequency.get(), time.get(), Plot_Partition_Grid{2, 3});
not_null{frequency.get()}, not_null{time.get()},
Plot_Partition_Grid{2, 3});
EXPECT_EQ(spectrum->read_prop<Spectrum::Base_Tag>().partition_count, 6u);
EXPECT_EQ(constellation->read_prop<Constellation_Diagram::Base_Tag>()
@@ -132,9 +137,9 @@ TEST(partition_configuration,
ASSERT_EQ(actual.height, expected.height);
for (int y = 0; y < expected.height; ++y) {
const auto* expected_row = reinterpret_cast<const Pixel*>(
expected.data + static_cast<std::ptrdiff_t>(y) * expected.stride);
expected.data.data() + static_cast<std::ptrdiff_t>(y) * expected.stride);
const auto* actual_row = reinterpret_cast<const Pixel*>(
actual.data + static_cast<std::ptrdiff_t>(y) * actual.stride);
actual.data.data() + static_cast<std::ptrdiff_t>(y) * actual.stride);
for (int x = 0; x < expected.width; ++x)
EXPECT_EQ(actual_row[x], expected_row[x])
<< "pixel mismatch at " << x << ", " << y;
@@ -207,9 +212,9 @@ TEST(partition_configuration,
const Image_View actual = actual_cache.view();
for (int y = 0; y < expected.height; ++y) {
const auto* expected_row = reinterpret_cast<const Pixel*>(
expected.data + static_cast<std::ptrdiff_t>(y) * expected.stride);
expected.data.data() + static_cast<std::ptrdiff_t>(y) * expected.stride);
const auto* actual_row = reinterpret_cast<const Pixel*>(
actual.data + static_cast<std::ptrdiff_t>(y) * actual.stride);
actual.data.data() + static_cast<std::ptrdiff_t>(y) * actual.stride);
for (int x = 0; x < expected.width; ++x)
EXPECT_EQ(actual_row[x], expected_row[x])
<< "tile seam mismatch at " << x << ", " << y;
+19 -11
View File
@@ -19,7 +19,7 @@ std::unique_ptr<Object> build_object(Args&&... args) {
template <typename Scene, typename... Renderables>
std::unique_ptr<Scene> build_scene(Renderables*... renderables) {
typename Scene::template Builder<Scene> builder;
(builder.add_renderable(renderables), ...);
(builder.add_renderable(not_null{renderables}), ...);
auto result = builder.build();
if (!result) std::terminate();
return std::move(result).value();
@@ -35,8 +35,8 @@ template <typename Scene>
void render_once(Scene* scene) {
static std::uint64_t sequence{1};
Frame_2D frame{Frame_Identity{sequence++, 0}};
scene->set_frame_callback([](Frame_2D*) {});
EXPECT_TRUE(scene->render(&frame).has_value());
EXPECT_TRUE(scene->render(
&frame, [](not_null<Frame_2D*>) {}).has_value());
}
}
@@ -60,8 +60,10 @@ TEST(plottable_migration, curve_plots_share_partitioned_rendering) {
frequency->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{0.0, 100.0});
power->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{-100.0, 0.0});
value->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{0.0, 100.0});
auto trace = build_object<Trace>(time.get(), value.get(), 2u);
auto sweep = build_object<Sweep>(frequency.get(), power.get(), 2u);
auto trace = build_object<Trace>(not_null{time.get()},
not_null{value.get()}, 2u);
auto sweep = build_object<Sweep>(not_null{frequency.get()},
not_null{power.get()}, 2u);
trace->submit_stream<Frequency_Trace_Stream_Tag>(
Frequency_Trace_Sample{time->append_time({1'000}), 10.0});
trace->submit_stream<Frequency_Trace_Stream_Tag>(
@@ -120,9 +122,11 @@ TEST(plottable_migration, raster_plots_share_partitioned_color_blocks) {
configure_axis(time.get(), Axis_Orientation::vertical, {20.0, 100.0}, -80.0, canvas);
frequency->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{0.0, 100.0});
power->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{-100.0, 0.0});
auto glow = build_object<Glow>(frequency.get(), power.get(),
auto glow = build_object<Glow>(not_null{frequency.get()},
not_null{power.get()},
Plot_Partition_Grid{2, 2});
auto waterfall = build_object<Fall>(frequency.get(), time.get(),
auto waterfall = build_object<Fall>(not_null{frequency.get()},
not_null{time.get()},
Plot_Partition_Grid{2, 2});
glow->set<&Afterglow::Prop::frequency_range>(Axis_Range{0.0, 100.0});
glow->set<&Afterglow::Prop::power_range>(Axis_Range{-100.0, 0.0});
@@ -172,8 +176,10 @@ TEST(plottable_migration, direct_overlay_and_constellation_build_and_render) {
configure_axis(vertical.get(), Axis_Orientation::vertical, {20.0, 100.0}, -80.0, canvas);
horizontal->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{-1.0, 1.0});
vertical->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{-1.0, 1.0});
auto diagram = build_object<Diagram>(horizontal.get(), vertical.get(), 2u);
auto overlay = build_object<Overlay>(horizontal.get(), vertical.get());
auto diagram = build_object<Diagram>(not_null{horizontal.get()},
not_null{vertical.get()}, 2u);
auto overlay = build_object<Overlay>(not_null{horizontal.get()},
not_null{vertical.get()});
diagram->set<&Constellation_Diagram::Prop::i_range>(Axis_Range{-1.0, 1.0});
diagram->set<&Constellation_Diagram::Prop::q_range>(Axis_Range{-1.0, 1.0});
diagram->submit_stream<Constellation_Stream_Tag>(
@@ -199,7 +205,8 @@ TEST(selection_overlay, control_extends_selection_and_plain_click_clears_it) {
configure_axis(vertical.get(), Axis_Orientation::vertical, {20.0, 100.0}, -80.0, canvas);
horizontal->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{0.0, 100.0});
vertical->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{0.0, 100.0});
auto overlay = build_object<Overlay>(horizontal.get(), vertical.get());
auto overlay = build_object<Overlay>(not_null{horizontal.get()},
not_null{vertical.get()});
auto scene = build_scene<Scene>(overlay.get());
scene->set<&Render_Scene_2D::Prop::viewport>(canvas);
scene->activate_view();
@@ -246,7 +253,8 @@ TEST(selection_overlay, time_axis_selection_keeps_axis_coordinates_while_window_
time->append_time({1'000});
time->append_time({2'000});
time->append_time({3'000});
auto overlay = build_object<Overlay>(time.get(), value.get());
auto overlay = build_object<Overlay>(not_null{time.get()},
not_null{value.get()});
auto scene = build_scene<Scene>(overlay.get());
scene->set<&Render_Scene_2D::Prop::viewport>(canvas);
scene->activate_view();
+13 -9
View File
@@ -15,14 +15,14 @@ std::unique_ptr<Object> build_object(Args&&... args) {
template <typename Scene, typename... Renderables>
std::unique_ptr<Scene> build_scene(Renderables*... renderables) {
typename Scene::template Builder<Scene> builder;
(builder.add_renderable(renderables), ...);
(builder.add_renderable(not_null{renderables}), ...);
auto result = builder.build();
if (!result) std::terminate();
return std::move(result).value();
}
bool contains_color(Image_View view) {
for (int y = 0; y < view.height; ++y) {
const auto* row = reinterpret_cast<const Pixel*>(view.data + static_cast<std::ptrdiff_t>(y) * view.stride);
const auto* row = reinterpret_cast<const Pixel*>(view.data.data() + static_cast<std::ptrdiff_t>(y) * view.stride);
for (int x = 0; x < view.width; ++x)
if (row[x] != 0) return true;
}
@@ -31,7 +31,7 @@ bool contains_color(Image_View view) {
std::uint64_t image_hash(Image_View view) {
std::uint64_t value{1469598103934665603ULL};
for (int y = 0; y < view.height; ++y) {
const auto* row = reinterpret_cast<const std::byte*>(view.data) +
const auto* row = view.data.data() +
static_cast<std::ptrdiff_t>(y) * view.stride;
for (std::ptrdiff_t index = 0;
index < static_cast<std::ptrdiff_t>(view.width * sizeof(Pixel));
@@ -46,8 +46,8 @@ template <typename Scene>
std::unique_ptr<Frame_2D> render_frame(Scene* scene) {
static std::uint64_t sequence{1};
auto frame = std::make_unique<Frame_2D>(Frame_Identity{sequence++, 0});
scene->set_frame_callback([](Frame_2D*) {});
EXPECT_TRUE(scene->render(frame.get()).has_value());
EXPECT_TRUE(scene->render(
frame.get(), [](not_null<Frame_2D*>) {}).has_value());
return frame;
}
}
@@ -58,7 +58,8 @@ TEST(spectrum_data, publishes_samples_through_tagged_frame_buffer) {
using Scene_Object = Render_Scene_2D;
auto frequency = build_object<Frequency>();
auto power = build_object<Power>();
auto spectrum = build_object<Object>(frequency.get(), power.get());
auto spectrum = build_object<Object>(not_null{frequency.get()},
not_null{power.get()});
spectrum->set<&Spectrum::Prop::frequency_range>(Axis_Range{0.0, 100.0});
const double samples[]{-100.0, -50.0, 0.0};
spectrum->pending_buffer<Spectrum_Frame_Tag>() =
@@ -75,7 +76,8 @@ TEST(spectrum_markers, uses_tagged_properties_as_the_only_marker_interface) {
using Scene_Object = Render_Scene_2D;
auto frequency = build_object<Frequency>();
auto power = build_object<Power>();
auto spectrum = build_object<Object>(frequency.get(), power.get());
auto spectrum = build_object<Object>(not_null{frequency.get()},
not_null{power.get()});
spectrum->set<&Spectrum::Prop::custom_markers>(
std::vector<Spectrum_Frequency>{10.0, 20.0});
spectrum->advance();
@@ -110,7 +112,8 @@ TEST(render_scene_2d, composites_axes_and_spectrum_into_final_frame) {
initialize_runtime({.workers = 2});
auto frequency = build_object<Frequency>();
auto power = build_object<Power>();
auto spectrum = build_object<Spectrum_Object>(frequency.get(), power.get(), 3u);
auto spectrum = build_object<Spectrum_Object>(not_null{frequency.get()},
not_null{power.get()}, 3u);
auto scene = build_scene<Scene_Object>(spectrum.get());
const Size canvas{160, 120};
frequency->set<&Abs_Axis::Prop::position>(Point_F{20.0, 100.0});
@@ -188,7 +191,8 @@ TEST(spectrum_data, repeatedly_publishes_new_samples_during_long_running_render)
initialize_runtime({.workers = 4});
auto frequency = build_object<Frequency>();
auto power = build_object<Power>();
auto spectrum = build_object<Spectrum_Object>(frequency.get(), power.get());
auto spectrum = build_object<Spectrum_Object>(not_null{frequency.get()},
not_null{power.get()});
auto scene = build_scene<Scene_Object>(spectrum.get());
frequency->set<&Abs_Axis::Prop::position>(Point_F{20.0, 100.0});
frequency->set<&Abs_Axis::Prop::pixel_length>(120.0);