仍有bug
This commit is contained in:
@@ -4,7 +4,10 @@ Render_Scene_2D::Private::~Private() = default;
|
||||
bool Render_Scene_2D::State::operator==(const State&) const = default;
|
||||
bool Render_Scene_2D::Prop::operator==(const Prop&) const = default;
|
||||
|
||||
Render_Scene_2D::Render_Result Render_Scene_2D::render(Frame_2D* frame) { return static_cast<Private&>(*d).dispatch->render(this, frame); }
|
||||
std::expected<void, Render_Scene_2D::Render_Result>
|
||||
Render_Scene_2D::render(Frame_2D* frame) {
|
||||
return static_cast<Private&>(*d).dispatch->render(this, frame);
|
||||
}
|
||||
void Render_Scene_2D::set_frame_callback(Frame_Callback callback) { static_cast<Private&>(*d).dispatch->set_frame_callback(this, std::move(callback)); }
|
||||
tf::Taskflow& Render_Scene_2D::completion_taskflow() {
|
||||
return static_cast<Private&>(*d).completion_graph;
|
||||
@@ -15,4 +18,7 @@ void Render_Scene_2D::activate_view() {
|
||||
void Render_Scene_2D::deactivate_view() {
|
||||
static_cast<Private&>(*d).dispatch->set_active(this, false);
|
||||
}
|
||||
void Render_Scene_2D::reset_frame_statistics() {
|
||||
static_cast<Private&>(*d).dispatch->reset_statistics(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include "../base/Renderable_2D.hpp"
|
||||
#include "../render/Blend2D_Cache.hpp"
|
||||
#include <scene.hpp>
|
||||
#include <frame_statistics.hpp>
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
namespace aethera::render_2d {
|
||||
@@ -17,6 +19,7 @@ struct Render_Scene_2D : Def<Render_Scene_2D, Scene,
|
||||
bool operator==(const Prop&) const;
|
||||
};
|
||||
struct State : Prev_State {
|
||||
Frame_Statistics_State frame_statistics{};
|
||||
bool operator==(const State&) const;
|
||||
};
|
||||
/* 完整声明、合成顺序和 CRTP 分派见 Render_Scene_2D.ipp。 */
|
||||
@@ -33,10 +36,10 @@ struct Render_Scene_2D : Def<Render_Scene_2D, Scene,
|
||||
private:
|
||||
std::vector<std::function<std::expected<void, Dependency_Graph_Error>(Object*)>> attachments{}; /* 仅在 build 期间绑定已构造 Renderable。 */
|
||||
};
|
||||
enum class Render_Result { completed, frame_in_flight, view_inactive, empty_viewport };
|
||||
enum class Render_Result { frame_in_flight, view_inactive, empty_viewport };
|
||||
using Frame_Callback = std::function<void(Frame_2D*)>;
|
||||
/* 向调用方拥有的帧合成一次;必须先安装回调。回调返回前的并发请求返回 frame_in_flight。 */
|
||||
[[nodiscard]] Render_Result render(Frame_2D* frame);
|
||||
[[nodiscard]] std::expected<void, Render_Result> render(Frame_2D* frame);
|
||||
/* 安装完成帧回调;回调收到对应 render(frame) 的对象,返回时 Scene 才释放下一帧准入。 */
|
||||
void set_frame_callback(Frame_Callback callback);
|
||||
/*
|
||||
@@ -50,6 +53,7 @@ struct Render_Scene_2D : Def<Render_Scene_2D, Scene,
|
||||
void activate_view();
|
||||
/* 停止后续 render 调用,不清除最后一帧。 */
|
||||
void deactivate_view();
|
||||
void reset_frame_statistics();
|
||||
};
|
||||
}
|
||||
#include "Render_Scene_2D.ipp"
|
||||
|
||||
@@ -34,7 +34,7 @@ std::expected<std::unique_ptr<Object>, Dependency_Graph_Error> Render_Scene_2D::
|
||||
return scene;
|
||||
}
|
||||
struct Render_Scene_2D::Private : Prev_Private {
|
||||
using Render_Run = Render_Result (*)(Root*, Frame_2D*);
|
||||
using Render_Run = std::expected<void, Render_Result> (*)(Root*, Frame_2D*);
|
||||
using Callback_Run = void (*)(Root*, Frame_Callback);
|
||||
using Active_Run = void (*)(Root*, bool);
|
||||
struct Paint_Node {
|
||||
@@ -54,6 +54,7 @@ struct Render_Scene_2D::Private : Prev_Private {
|
||||
Renderable_2D_Base::Private* private_data{}; /* 候选对象的二维能力层;仅在本次 Prepare 分发期间有效。 */
|
||||
};
|
||||
struct Dispatch {
|
||||
void (*reset_statistics)(Root*);
|
||||
Render_Run render; /* 向外部帧执行最终 Scene 并合成颜色层。 */
|
||||
Callback_Run set_frame_callback; /* 安装最终完成帧回调。 */
|
||||
Active_Run set_active; /* 修改最终 Scene 的视图活动状态。 */
|
||||
@@ -62,6 +63,7 @@ struct Render_Scene_2D::Private : Prev_Private {
|
||||
std::mutex render_mutex{}; /* 只保护完成回调和单帧准入。 */
|
||||
bool frame_in_flight{}; /* render 准入到完成回调返回的唯一状态源。 */
|
||||
Frame_Callback frame_callback{}; /* 合成完成后的唯一像素发布出口。 */
|
||||
Frame_Statistics_Accumulator frame_statistics{}; /* Scene 内部增量计算;State 只发布定长统计结果。 */
|
||||
tf::Taskflow completion_graph{}; /* 最终像素完成后、发布回调前执行的外部续写图。 */
|
||||
std::unique_ptr<tf::Taskflow> paint_taskflow{}; /* 仅由二维 Paint 图构建的执行图。 */
|
||||
~Private();
|
||||
@@ -81,7 +83,9 @@ struct Render_Scene_2D::Private : Prev_Private {
|
||||
template <Attached Object>
|
||||
void dispatch_events(Object* object, Size viewport,
|
||||
std::uint64_t frame_sequence);
|
||||
template <Attached Object> [[nodiscard]] Render_Result render(Object* object, Frame_2D* frame);
|
||||
template <Attached Object>
|
||||
[[nodiscard]] std::expected<void, Render_Result> render(Object* object,
|
||||
Frame_2D* frame);
|
||||
template <Attached Object>
|
||||
[[nodiscard]] static const Dispatch& dispatch_for();
|
||||
/* CRTP 覆盖:Builder 挂接最终 Private 后安装二维 Scene 的无虚函数业务分派。 */
|
||||
@@ -297,14 +301,16 @@ void Render_Scene_2D::Private::process(Object* object, Callback&& callback)
|
||||
std::invoke(std::forward<Callback>(callback));
|
||||
}
|
||||
template <Attached Object>
|
||||
Render_Scene_2D::Render_Result Render_Scene_2D::Private::render(Object* object, Frame_2D* frame) {
|
||||
std::expected<void, Render_Scene_2D::Render_Result>
|
||||
Render_Scene_2D::Private::render(Object* object, Frame_2D* frame) {
|
||||
static_cast<void>(detail::Frame_2D_Access::render_target(frame));
|
||||
Frame_Callback callback;
|
||||
{
|
||||
std::lock_guard lock(render_mutex);
|
||||
if (!frame_callback)
|
||||
throw std::logic_error("Render_Scene_2D requires a frame callback before render");
|
||||
if (frame_in_flight) return Render_Result::frame_in_flight;
|
||||
if (frame_in_flight)
|
||||
return std::unexpected(Render_Result::frame_in_flight);
|
||||
frame_in_flight = true;
|
||||
callback = frame_callback;
|
||||
}
|
||||
@@ -331,10 +337,18 @@ Render_Scene_2D::Render_Result Render_Scene_2D::Private::render(Object* object,
|
||||
frame->mark(Frame_Trace_Marker::callback_started);
|
||||
callback(frame);
|
||||
frame->mark(Frame_Trace_Marker::callback_finished);
|
||||
frame->mark(Frame_Trace_Marker::frame_ready);
|
||||
auto& private_data = static_cast<typename Object::Private&>(*this);
|
||||
auto& scene_state = static_cast<State&>(*private_data.state.pending);
|
||||
scene_state.frame_statistics = frame_statistics.submit(
|
||||
*frame, Frame_Dimension::two_dimensional);
|
||||
private_data.state.advance();
|
||||
object->template notify_state<Render_Scene_2D::Base_Tag>();
|
||||
});
|
||||
if (completed) return Render_Result::completed;
|
||||
if (completed) return {};
|
||||
const auto& prop = object->template read_prop<Render_Scene_2D::Base_Tag>();
|
||||
return prop.view_active ? Render_Result::empty_viewport : Render_Result::view_inactive;
|
||||
return std::unexpected(prop.view_active ? Render_Result::empty_viewport
|
||||
: Render_Result::view_inactive);
|
||||
}
|
||||
template <Attached Object>
|
||||
void Render_Scene_2D::Private::dispatch_events(Object* object, Size viewport,
|
||||
@@ -367,6 +381,17 @@ void Render_Scene_2D::Private::dispatch_events(Object* object, Size viewport,
|
||||
template <Attached Object>
|
||||
const Render_Scene_2D::Private::Dispatch& Render_Scene_2D::Private::dispatch_for() {
|
||||
static const Dispatch value{
|
||||
[](Root* root) {
|
||||
auto* object = static_cast<Object*>(root);
|
||||
auto& data = static_cast<typename Object::Private&>(*object->d);
|
||||
object->template publish_state<Render_Scene_2D::Base_Tag,
|
||||
&State::frame_statistics>(
|
||||
[&data](State_Access<typename Object::State> states) {
|
||||
data.frame_statistics.reset();
|
||||
states.template get<Render_Scene_2D::Base_Tag>()
|
||||
.frame_statistics = {};
|
||||
});
|
||||
},
|
||||
[](Root* root, Frame_2D* frame) {
|
||||
auto* object = static_cast<Object*>(root);
|
||||
return static_cast<typename Object::Private&>(*object->d).render(object, frame);
|
||||
|
||||
@@ -18,7 +18,7 @@ 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_EQ(scene->render(frame.get()), Render_Scene_2D::Render_Result::completed);
|
||||
EXPECT_TRUE(scene->render(frame.get()).has_value());
|
||||
return frame;
|
||||
}
|
||||
template <typename Scene>
|
||||
|
||||
@@ -36,7 +36,7 @@ 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_EQ(scene->render(&frame), Render_Scene_2D::Render_Result::completed);
|
||||
EXPECT_TRUE(scene->render(&frame).has_value());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,12 +28,26 @@ bool contains_color(Image_View view) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
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) +
|
||||
static_cast<std::ptrdiff_t>(y) * view.stride;
|
||||
for (std::ptrdiff_t index = 0;
|
||||
index < static_cast<std::ptrdiff_t>(view.width * sizeof(Pixel));
|
||||
++index) {
|
||||
value ^= static_cast<std::uint8_t>(row[index]);
|
||||
value *= 1099511628211ULL;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
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_EQ(scene->render(frame.get()), Render_Scene_2D::Render_Result::completed);
|
||||
EXPECT_TRUE(scene->render(frame.get()).has_value());
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
@@ -182,3 +196,45 @@ TEST(render_scene_2d, composites_axes_and_spectrum_into_final_frame) {
|
||||
EXPECT_EQ(resized_frame.height, resized_canvas.height);
|
||||
EXPECT_TRUE(contains_color(resized_frame));
|
||||
}
|
||||
|
||||
TEST(spectrum_data, repeatedly_publishes_new_samples_during_long_running_render) {
|
||||
using Frequency = Impl<Frequency_Axis>;
|
||||
using Power = Impl<Numeric_Axis>;
|
||||
using Spectrum_Object = Impl<Spectrum>;
|
||||
using Scene_Object = Impl<Render_Scene_2D>;
|
||||
initialize_runtime(4);
|
||||
auto frequency = build_object<Frequency>();
|
||||
auto power = build_object<Power>();
|
||||
auto spectrum = build_object<Spectrum_Object>(frequency.get(), 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);
|
||||
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});
|
||||
spectrum->set<&Spectrum::Prop::frequency_range>(Axis_Range{0.0, 100.0});
|
||||
scene->set<&Render_Scene_2D::Prop::viewport>(Size{160, 120});
|
||||
scene->set<&Render_Scene_2D::Prop::background>(Color::transparent());
|
||||
scene->activate_view();
|
||||
|
||||
std::uint64_t previous_hash{};
|
||||
std::size_t changed_frames{};
|
||||
for (std::size_t frame_index = 0; frame_index < 720; ++frame_index) {
|
||||
std::vector<Spectrum_Power> samples(128, -95.0);
|
||||
const std::size_t peak = 8 + frame_index % 112;
|
||||
samples[peak] = -15.0;
|
||||
spectrum->pending_buffer<Spectrum_Frame_Tag>() =
|
||||
Spectrum_Frame{std::move(samples)};
|
||||
spectrum->mark_dirty<Prepare_Data_Tag>();
|
||||
auto frame = render_frame(scene.get());
|
||||
const auto hash = image_hash(frame->image());
|
||||
if (frame_index != 0 && hash != previous_hash) ++changed_frames;
|
||||
previous_hash = hash;
|
||||
EXPECT_TRUE(spectrum->read_state<Renderable::Base_Tag>().prepare_executed);
|
||||
EXPECT_TRUE(spectrum->read_state<Renderable::Base_Tag>().paint_executed);
|
||||
EXPECT_EQ(spectrum->read_state<Spectrum::Base_Tag>().sample_count, 128u);
|
||||
}
|
||||
EXPECT_GT(changed_frames, 700u);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user