Files
Aethera/render_2D/tests/Async_Render_Contract_Test.cpp
T
2026-08-27 16:36:29 +08:00

209 lines
9.4 KiB
C++

#include <render_2D/axis/Axis.hpp>
#include <render_2D/base/Frame_2D.hpp>
#include <render_2D/plottable/Constellation_Diagram.hpp>
#include <render_2D/plottable/Afterglow.hpp>
#include <render_2D/plottable/Spectrum.hpp>
#include <render_2D/plottable/Waterfall.hpp>
#include <render_2D/scene/Render_Scene_2D.hpp>
#include <process.h>
#include <atomic>
#include <cstdlib>
#include <memory>
#include <string_view>
#include <vector>
namespace {
using namespace aethera;
using namespace aethera::render_2d;
template <typename Object, typename... Arguments>
Object* build_object(Arguments&&... arguments) {
typename Object::template Builder<Object> builder(std::forward<Arguments>(arguments)...);
auto result = builder.build();
if (!result) std::_Exit(2);
return std::move(result).value().release();
}
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);
for (int x = 0; x < view.width; ++x)
if (row[x] != 0) return true;
}
return false;
}
}
int main() {
using Frequency = Frequency_Axis;
using Power = Numeric_Axis;
using Time = Time_Axis;
using Spectrum_Object = Spectrum;
using Constellation_Object = Constellation_Diagram;
using Waterfall_Object = Waterfall;
using Afterglow_Object = Afterglow;
using Scene_Object = Render_Scene_2D;
initialize_runtime({.workers = 4});
auto* frequency = build_object<Frequency>();
auto* power = build_object<Power>();
auto* spectrum = build_object<Spectrum_Object>(frequency, power, 4u);
auto* time = build_object<Time>();
auto* waterfall = build_object<Waterfall_Object>(
frequency, time, Plot_Partition_Grid{2, 2});
auto* afterglow = build_object<Afterglow_Object>(
frequency, power, Plot_Partition_Grid{2, 2});
auto* q_axis = build_object<Power>();
auto* constellation = build_object<Constellation_Object>(
power, 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{};
auto* trace_valid = new std::atomic_bool{true};
spectrum->taskflow().add("test.spectrum.render.complete", [spectrum_painted] {
spectrum_painted->store(true, std::memory_order_release);
});
const auto verify_spectrum_precedes_axis =
[spectrum_painted, topology_valid] {
if (!spectrum_painted->load(std::memory_order_acquire))
topology_valid->store(false, std::memory_order_relaxed);
};
frequency->taskflow().add("test.frequency.render.order",
verify_spectrum_precedes_axis);
power->taskflow().add("test.power.render.order",
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);
auto scene_result = scene_builder.build();
if (!scene_result) return 2;
auto* scene = std::move(scene_result).value().release();
frequency->set<&Abs_Axis::Prop::position>(Point_F{20.0, 100.0});
frequency->set<&Abs_Axis::Prop::pixel_length>(120.0);
frequency->set<&Numeric_Axis::Prop::coordinate_range>(
Axis_Range{0.0, 100.0});
power->set<&Abs_Axis::Prop::position>(Point_F{20.0, 100.0});
power->set<&Abs_Axis::Prop::pixel_length>(-80.0);
power->set<&Abs_Axis::Prop::orientation>(Axis_Orientation::vertical);
power->set<&Numeric_Axis::Prop::coordinate_range>(
Axis_Range{-100.0, 0.0});
time->set<&Abs_Axis::Prop::position>(Point_F{20.0, 100.0});
time->set<&Abs_Axis::Prop::pixel_length>(-80.0);
time->set<&Abs_Axis::Prop::orientation>(Axis_Orientation::vertical);
time->set<&Time_Axis::Prop::visible_count>(4);
q_axis->set<&Abs_Axis::Prop::position>(Point_F{20.0, 100.0});
q_axis->set<&Abs_Axis::Prop::pixel_length>(120.0);
q_axis->set<&Numeric_Axis::Prop::coordinate_range>(
Axis_Range{-1.0, 1.0});
spectrum->set<&Spectrum::Prop::frequency_range>(
Axis_Range{0.0, 100.0});
spectrum->pending_buffer<Spectrum_Frame_Tag>() =
Spectrum_Frame{std::vector<Spectrum_Power>(512, -50.0)};
spectrum->mark_dirty<Render_Graph_Tag>();
waterfall->set<&Waterfall::Prop::frequency_range>(
Axis_Range{0.0, 100.0});
waterfall->set<&Waterfall::Prop::power_range>(
Axis_Range{-100.0, 0.0});
const Plot_Time_Tick first_tick = time->append_time({1'000});
const Plot_Time_Tick second_tick = time->append_time({2'000});
waterfall->submit_stream<Waterfall_Stream_Tag>(
std::make_shared<const Waterfall_Row>(Waterfall_Row{
first_tick, std::vector<Plot_Value>(64, -70.0)}));
waterfall->submit_stream<Waterfall_Stream_Tag>(
std::make_shared<const Waterfall_Row>(Waterfall_Row{
second_tick, std::vector<Plot_Value>(64, -30.0)}));
waterfall->mark_dirty<Render_Graph_Tag>();
afterglow->set<&Afterglow::Prop::frequency_range>(
Axis_Range{0.0, 100.0});
afterglow->set<&Afterglow::Prop::power_range>(Axis_Range{-100.0, 0.0});
afterglow->set<&Afterglow::Prop::power_point_size>(32u);
afterglow->submit_stream<Afterglow_Stream_Tag>(
std::make_shared<const std::vector<Plot_Value>>(64, -40.0));
afterglow->mark_dirty<Render_Graph_Tag>();
constellation->set<&Constellation_Diagram::Prop::i_range>(
Axis_Range{-1.0, 1.0});
constellation->set<&Constellation_Diagram::Prop::q_range>(
Axis_Range{-1.0, 1.0});
constellation->submit_stream<Constellation_Stream_Tag>(
Constellation_Point{{-50.0, 0.25}, monotonic_milliseconds()});
constellation->mark_dirty<Render_Graph_Tag>();
scene->set<&Render_Scene_2D::Prop::viewport>(Size{160, 120});
scene->activate_view();
auto* frame = new Frame_2D(Frame_Identity{1, 0});
auto* resized_partition_frame = new Frame_2D(Frame_Identity{2, 0});
frame->request_taskflow_trace();
resized_partition_frame->request_taskflow_trace();
scene->set_frame_retired_callback(
[resized_partition_frame, second_frame_valid, trace_valid](
Frame_2D* retired) {
const auto trace = retired->take_taskflow_trace();
bool spectrum_hold{};
bool afterglow_reduce{};
bool afterglow_color_barrier{};
for (const auto& graph : trace.graphs)
for (const auto& node : graph.nodes) {
spectrum_hold = spectrum_hold ||
node.name == "prepare.hold.partition";
afterglow_reduce = afterglow_reduce ||
node.name == "prepare.normalize";
afterglow_color_barrier = afterglow_color_barrier ||
node.name == "prepare.color.complete";
}
if (!spectrum_hold || !afterglow_reduce ||
!afterglow_color_barrier)
trace_valid->store(false, std::memory_order_relaxed);
if (retired == resized_partition_frame)
std::_Exit(second_frame_valid->load(std::memory_order_relaxed) &&
trace_valid->load(std::memory_order_relaxed)
? 0
: 1);
});
scene->set_frame_callback(
[scene, spectrum, waterfall, afterglow, constellation, frame,
resized_partition_frame, spectrum_painted,
topology_valid, second_frame_valid](Frame_2D* completed) {
const bool valid = completed == frame &&
spectrum->read_state<Spectrum::Base_Tag>().sample_count ==
512 &&
topology_valid->load(std::memory_order_relaxed) &&
contains_color(completed->image());
if (!valid) std::_Exit(1);
spectrum->set<&Spectrum::Prop::partition_count>(2u);
waterfall->set<&Waterfall::Prop::partition_grid>(
Plot_Partition_Grid{3, 2});
afterglow->set<&Afterglow::Prop::partition_grid>(
Plot_Partition_Grid{3, 2});
afterglow->submit_stream<Afterglow_Stream_Tag>(
std::make_shared<const std::vector<Plot_Value>>(64, -20.0));
afterglow->mark_dirty<Render_Graph_Tag>();
constellation->set<
&Constellation_Diagram::Prop::partition_count>(2u);
spectrum_painted->store(false, std::memory_order_relaxed);
scene->set_frame_callback(
[resized_partition_frame, second_frame_valid,
topology_valid](Frame_2D* next) {
const bool resized_valid =
next == resized_partition_frame &&
topology_valid->load(std::memory_order_relaxed) &&
contains_color(next->image());
second_frame_valid->store(resized_valid,
std::memory_order_relaxed);
});
if (!scene->render(resized_partition_frame)) std::_Exit(2);
});
if (!scene->render(frame)) return 2;
/*
* render() 是异步入口。结束提交线程但保持 Taskflow worker 和进程存活,
* 完成回调负责给出测试结果;此处没有轮询、future::get 或条件变量等待。
*/
_endthreadex(3);
}