diff --git a/Renderive/Axis/Frequency_Axis_p.h b/Renderive/Axis/Frequency_Axis_p.h index 351c98d..1c6ec12 100644 --- a/Renderive/Axis/Frequency_Axis_p.h +++ b/Renderive/Axis/Frequency_Axis_p.h @@ -6,69 +6,53 @@ namespace renderive { struct Frequency_Axis_Input_Data : Axis_Input_Data {}; struct Frequency_Axis_Render_State : Axis_Render_State {}; struct Frequency_Axis_Private : Typed_Render_Data_Base { - enum X_Unit_Type { - Unknown = -1, - Hz = 0, - KHz = 3, - MHz = 6, - GHz = 9 - } x_unit_type = Unknown; - static QString get_x_unit_type_string(X_Unit_Type unit_type) { - switch (unit_type) { - case Hz: - return "Hz"; - case KHz: - return "KHz"; - case MHz: - return "MHz"; - case GHz: - return "GHz"; - default: - return "Unknown"; - } - } std::vector create_label_vector(const std::vector& ticks, SRC src) override { - if (!ticks.empty()) { - double range = ticks.back() - ticks.front(); - X_Unit_Type before_unit_type = x_unit_type; - if (range >= 1e9) { - x_unit_type = GHz; - } - else if (range >= 1e6) { - x_unit_type = MHz; - } - else if (range >= 1e3) { - x_unit_type = KHz; - } - else { - x_unit_type = Hz; - } - if (before_unit_type != x_unit_type) { - render_state()->unit_text = "频率/" + get_x_unit_type_string(x_unit_type); - } - } + const int exponent = unit_exponent(ticks); + if (!ticks.empty()) + render_state()->unit_text = "Frequency/" + unit_text(exponent); std::vector result; result.reserve(ticks.size()); - for (const double& tick_coord : ticks) { - result.push_back(get_true_value(get_tick_label(tick_coord, src))); - } + for (const double& tick_coord : ticks) + result.push_back(get_true_value(get_tick_label(tick_coord, src), exponent)); return result; } - QString get_true_value(QString value) { + static int unit_exponent(const std::vector& ticks) { + if (ticks.empty()) + return 0; + const double range = qAbs(ticks.back() - ticks.front()); + if (range >= 1e9) + return 9; + if (range >= 1e6) + return 6; + if (range >= 1e3) + return 3; + return 0; + } + static QString unit_text(int exponent) { + switch (exponent) { + case 0: + return "Hz"; + case 3: + return "kHz"; + case 6: + return "MHz"; + case 9: + return "GHz"; + } + return "Hz"; + } + QString get_true_value(QString value, int exponent) { value.replace(",", ""); - int move_num = x_unit_type; Abs_Axis_Render_State* s = render_state(); int precision = s->number_precision; int dot_index = value.indexOf('.'); - if (dot_index == -1) { + if (dot_index == -1) dot_index = value.size(); - } QString number = value.left(dot_index) + value.mid(dot_index + 1); - int after_dot_index = dot_index - move_num; + int after_dot_index = dot_index - exponent; if (after_dot_index < 1) { - for (int i = 0; i < 1 - after_dot_index; ++i) { + for (int i = 0; i < 1 - after_dot_index; ++i) number.prepend('0'); - } after_dot_index = 1; } number.insert(after_dot_index, '.'); diff --git a/Renderive/architecture/Bounded_Input_Buffer.h b/Renderive/architecture/Bounded_Input_Buffer.h index 4e6fea4..bac345e 100644 --- a/Renderive/architecture/Bounded_Input_Buffer.h +++ b/Renderive/architecture/Bounded_Input_Buffer.h @@ -34,7 +34,6 @@ public: void clear() { while (buffer.skip_one()) {} } - void record_consumer_dropped() {} private: void configure(std::size_t max_packet_size, std::size_t max_packet_count) { max_packet_count = std::max(1, max_packet_count); @@ -74,7 +73,6 @@ public: void clear() { row_count = 0; } - void record_consumer_dropped() {} private: static constexpr std::size_t Storage_Count = 64; std::pmr::vector& row_at(std::size_t index) { diff --git a/Renderive/architecture/Frame_Scheduler.cpp b/Renderive/architecture/Frame_Scheduler.cpp index c690b76..6a00ed4 100644 --- a/Renderive/architecture/Frame_Scheduler.cpp +++ b/Renderive/architecture/Frame_Scheduler.cpp @@ -112,7 +112,7 @@ void Frame_Feedback_Controller::on_frame_superseded(Frame_Lifecycle_Record& fram write_state(frame); } void Frame_Feedback_Controller::on_consumer_underrun(std::uint64_t, bool input_pending) { - ++state_value.repeated_frame_count; + ++state_value.reused_front_frame_count; state_value.consumer_underrun_ratio = smooth_ratio(state_value.consumer_underrun_ratio, 1.0); state_value.input_pending_ratio = smooth_ratio(state_value.input_pending_ratio, input_pending ? 1.0 : 0.0); update_control(false, false, input_pending); @@ -139,7 +139,7 @@ void Frame_Feedback_Controller::update_control(bool ready_occupied, bool consume bool overproducing = state_value.superseded_ready_ratio > 0.03 || (backlog && ready_waiting) || (ready_occupied && consumer_busy); update_effective_interval(overproducing); state_value.display_efficiency = state_value.produced_frame_count ? static_cast(state_value.consumed_frame_count) / static_cast(state_value.produced_frame_count) : 0.0; - std::uint64_t display_events = state_value.consumed_frame_count + state_value.repeated_frame_count; + std::uint64_t display_events = state_value.consumed_frame_count + state_value.reused_front_frame_count; state_value.unique_consumed_ratio = display_events ? static_cast(state_value.consumed_frame_count) / static_cast(display_events) : 1.0; } Bottleneck_State Frame_Feedback_Controller::classify_bottleneck(bool ready_occupied, bool consumer_busy, bool input_pending) const { diff --git a/Renderive/architecture/Frame_Scheduler.h b/Renderive/architecture/Frame_Scheduler.h index 71ec0c5..927bacc 100644 --- a/Renderive/architecture/Frame_Scheduler.h +++ b/Renderive/architecture/Frame_Scheduler.h @@ -58,7 +58,7 @@ struct Frame_Feedback_Snapshot { std::uint64_t produced_frame_count{}; std::uint64_t consumed_frame_count{}; std::uint64_t superseded_frame_count{}; - std::uint64_t repeated_frame_count{}; + std::uint64_t reused_front_frame_count{}; }; // 用于每一个 renderable 上报自己的信息 struct Renderable_Cache_Frame_Stat { @@ -93,7 +93,6 @@ struct Renderable_Frame_Stat { struct Frame_Lifecycle_Record { std::uint64_t frame_id{}; std::uint64_t input_version{}; - std::uint64_t frame_slot_generation{}; std::uint64_t core_data_time{}; std::uint64_t render_request_time{}; std::uint64_t render_queue_enter_time{}; diff --git a/Renderive/architecture/Render_Config.h b/Renderive/architecture/Render_Config.h index 9b4ebf5..ede37f0 100644 --- a/Renderive/architecture/Render_Config.h +++ b/Renderive/architecture/Render_Config.h @@ -1,45 +1,75 @@ #pragma once #include namespace renderive { -// 系统受限状态 +/// @brief Current limiting factor inferred by frame feedback control. enum class Bottleneck_State : std::uint8_t { - Unknown, // 未知受限状态 - Input_Limited, // 输入受限 - Producer_Limited, // 生产者受限 - Consumer_Limited, // 消费者受限 - Manual_Limited, // 人工帧率受限 + /// @brief Not enough samples are available to classify the bottleneck. + Unknown, + /// @brief New input arrives slower than the render and presentation path can run. + Input_Limited, + /// @brief CPU render production is the limiting stage. + Producer_Limited, + /// @brief Frontend consumption or presentation is the limiting stage. + Consumer_Limited, + /// @brief A user configured maximum FPS is currently limiting rendering. + Manual_Limited, + /// @brief Input, render, and presentation cadence are currently balanced. Balanced }; -enum class Frame_State : std::uint8_t { +/// @brief Physical frame-buffer slot state inside the scheduler. +enum class Frame_Buffer_State : std::uint8_t { + /// @brief Slot is available for a producer. Free, + /// @brief Slot is being rendered into by the producer. Producing, + /// @brief Slot contains a completed frame that can be consumed. Ready, + /// @brief Slot is being presented by the frontend consumer. Consuming }; +/// @brief Lifecycle result for a produced frame. enum class Frame_Outcome : std::uint8_t { + /// @brief Frame was produced and published as a ready frame. Produced, + /// @brief Frame was consumed by the frontend. Consumed, + /// @brief Frame was replaced by a newer ready frame before presentation. Superseded, - Cancelled, - Repeated + /// @brief Frame was cancelled due to shutdown, destruction, or an error path. + Cancelled }; +/// @brief Rendering cache strategy for a renderable subtree. enum class Renderable_Cache_Mode : std::uint8_t { - Direct, // 不启用缓存,直接绘制 - Local_Pixel // 启用缓存 + /// @brief Draw directly into the target painter each frame. + Direct, + /// @brief Rebuild a local image cache when state changes, then compose it. + Local_Pixel }; +/// @brief Internal state triple-buffer role. enum State_Role : std::uint8_t { + /// @brief Editable configuration state. State_Edit, + /// @brief Committed state ready for the render thread. State_Ready, + /// @brief State currently used by the render thread. State_Render }; +/// @brief Internal input triple-buffer role. enum Input_Role : std::uint8_t { + /// @brief Input buffer receiving caller submissions. Input_Edit, + /// @brief Input buffer ready for render-thread consumption. Input_Ready, + /// @brief Input buffer currently consumed by the render thread. Input_Render }; +/// @brief Internal color-frame triple-buffer role. enum Color_Role : std::uint8_t { + /// @brief Frame currently owned by the frontend. Color_Front, + /// @brief Frame ready to be presented. Color_Ready, + /// @brief Frame currently rendered by the producer. Color_Render }; inline bool buffer_version_newer(std::uint64_t a, std::uint64_t b) { diff --git a/Renderive/architecture/Render_Pipeline.cpp b/Renderive/architecture/Render_Pipeline.cpp index c147b72..99f15e0 100644 --- a/Renderive/architecture/Render_Pipeline.cpp +++ b/Renderive/architecture/Render_Pipeline.cpp @@ -40,14 +40,14 @@ Renderable::Render_Pipeline::Frame_Publish_Result Renderable::Render_Pipeline::p Frame* render = color_by_role(Color_Render); Frame* old_ready = color_by_role(Color_Ready); const Frame* front = color_by_role(Color_Front); - bool replace_ready = old_ready->state.load(std::memory_order_acquire) == Frame_State::Ready && buffer_version_newer(old_ready->version.load(std::memory_order_acquire), front->version.load(std::memory_order_acquire)); + bool replace_ready = old_ready->state.load(std::memory_order_acquire) == Frame_Buffer_State::Ready && buffer_version_newer(old_ready->version.load(std::memory_order_acquire), front->version.load(std::memory_order_acquire)); if (paint_request_pending.load(std::memory_order_acquire)) { render->metadata.update_request_id = pending_update_request_id.load(std::memory_order_acquire); render->metadata.update_request_time = pending_update_request_time.load(std::memory_order_acquire); } render->metadata.publish_time = now_ns; render->metadata.frame_outcome = Frame_Outcome::Produced; - render->state.store(Frame_State::Ready, std::memory_order_release); + render->state.store(Frame_Buffer_State::Ready, std::memory_order_release); auto swap = color_control.wait_swap_role(Color_Ready, Color_Render, [this](const Triple_Buffer_View& view) { const Frame* render_buffer = color_by_index(view[Color_Render]); const Frame* ready_buffer = color_by_index(view[Color_Ready]); @@ -63,7 +63,7 @@ Renderable::Render_Pipeline::Frame_Publish_Result Renderable::Render_Pipeline::r Frame_Publish_Result result; Frame* ready = color_by_role(Color_Ready); const Frame* front = color_by_role(Color_Front); - if (ready->state.load(std::memory_order_acquire) != Frame_State::Ready) + if (ready->state.load(std::memory_order_acquire) != Frame_Buffer_State::Ready) return result; if (!buffer_version_newer(ready->version.load(std::memory_order_acquire), front->version.load(std::memory_order_acquire))) return result; @@ -89,22 +89,22 @@ Renderable::Render_Pipeline::Frame_Consume_Result Renderable::Render_Pipeline::c auto can_swap = [this](const Triple_Buffer_View& view) { const Frame* ready = color_by_index(view[Color_Ready]); const Frame* front = color_by_index(view[Color_Front]); - return ready->state.load(std::memory_order_acquire) == Frame_State::Ready && buffer_version_newer(ready->version.load(std::memory_order_acquire), front->version.load(std::memory_order_acquire)); + return ready->state.load(std::memory_order_acquire) == Frame_Buffer_State::Ready && buffer_version_newer(ready->version.load(std::memory_order_acquire), front->version.load(std::memory_order_acquire)); }; Triple_Buffer_Swap_Record swap = color_control.try_swap_role(Color_Front, Color_Ready, can_swap); result.new_frame = swap.result == Triple_Buffer_Result::Success; result.lease = try_front_color(); if (result.new_frame && result.lease) { - result.lease.frame->state.store(Frame_State::Consuming, std::memory_order_release); + result.lease.frame->state.store(Frame_Buffer_State::Consuming, std::memory_order_release); } return result; } bool Renderable::Render_Pipeline::has_ready_frame() const { const Frame* ready = color_by_role(Color_Ready); const Frame* front = color_by_role(Color_Front); - return ready->state.load(std::memory_order_acquire) == Frame_State::Ready && buffer_version_newer(ready->version.load(std::memory_order_acquire), front->version.load(std::memory_order_acquire)); + return ready->state.load(std::memory_order_acquire) == Frame_Buffer_State::Ready && buffer_version_newer(ready->version.load(std::memory_order_acquire), front->version.load(std::memory_order_acquire)); } bool Renderable::Render_Pipeline::consumer_busy() const { - return color_by_role(Color_Front)->state.load(std::memory_order_acquire) == Frame_State::Consuming; + return color_by_role(Color_Front)->state.load(std::memory_order_acquire) == Frame_Buffer_State::Consuming; } } // namespace renderive diff --git a/Renderive/architecture/Renderable.h b/Renderive/architecture/Renderable.h index 4641cde..bf47fbb 100644 --- a/Renderive/architecture/Renderable.h +++ b/Renderive/architecture/Renderable.h @@ -1,55 +1,45 @@ -#pragma once +#pragma once #include -#include #include #include #include #include #include -#include "../base/Pmr_QImage_Buffer.h" +#include #include "../base/Object_Semantics.h" +#include "../base/Pmr_QImage_Buffer.h" #include "Frame_Scheduler.h" #include "Render_Data.h" namespace renderive { struct Update_State; -/// 可渲染对象的基础接口和每对象渲染管线。 struct Renderable : std::enable_shared_from_this { - /// 后台渲染任务状态。 enum class Job_State { - /// 没有任务运行或排队。 Idle, - /// 已经进入调度队列。 Queued, - /// CPU 渲染任务正在执行。 Running }; - /// Plot 级颜色三缓冲中的一个物理颜色缓冲。 struct Frame { Frame(); std::atomic version{0}; Pmr_QImage_Buffer image{Memory_Domain::Plot_Frame}; std::pmr::vector> presented_update_states; - std::atomic state{Frame_State::Free}; + std::atomic state{Frame_Buffer_State::Free}; Frame_Lifecycle_Record metadata; }; - /// 颜色缓冲占用租约,析构时自动释放底层 busy 标记。 struct Frame_Lease : Move_Only { Frame_Lease() = default; Frame_Lease(Frame* buffer, Triple_Role_Buffer_Control* control, Triple_Buffer_Lease lease); Frame_Lease(Frame_Lease&& other) noexcept; Frame_Lease& operator=(Frame_Lease&& other) noexcept; ~Frame_Lease(); - /// 提前释放当前租约。 void reset(); Frame* frame{}; Triple_Role_Buffer_Control* control{}; Triple_Buffer_Lease lease{}; - /// 判断当前租约是否持有有效颜色缓冲。 explicit operator bool() const { return frame && lease; } }; - /// 单个 Plot 的颜色三缓冲管线。 struct Render_Pipeline { struct Frame_Publish_Result { QRegion region; @@ -73,9 +63,7 @@ struct Renderable : std::enable_shared_from_this { std::atomic pending_update_request_time{0}; std::uint64_t next_update_request_id{}; Render_Pipeline(); - /// 标记 GUI/edit 状态已经变化。 void mark_edit_state(); - /// 判断是否存在 render 未消费的新状态。 bool has_new_state() const; Frame* color_by_index(std::uint8_t index); const Frame* color_by_index(std::uint8_t index) const; @@ -89,34 +77,25 @@ struct Renderable : std::enable_shared_from_this { bool has_ready_frame() const; bool consumer_busy() const; }; - /// 创建派生对象的私有渲染数据。 virtual Render_Object_Owner create_render_data() { return make_render_object(); } - /// 创建派生对象的 State 缓冲实体。 virtual Render_Object_Owner create_render_state() { return make_render_object(); } - /// 创建派生对象的 Input 缓冲实体。 virtual Render_Object_Owner create_input_data() { return make_render_object(); } - /// 把对象挂接到 parent renderable。 virtual void init(const std::shared_ptr& parent); - /// 初始化 Plot 根节点。 virtual void init_root(Abs_Plot* plot); void add_child(const std::shared_ptr& child); void remove_child(const std::shared_ptr& child); std::vector> children_snapshot() const; std::vector> hit_renderables(const QPointF& pos); void render(QPainter* painter, const Plot_Render_Snapshot& snapshot); - /// 绘制已准备好的 Render_Data。 virtual void draw(QPainter* painter, const Plot_Render_Snapshot& snapshot); - /// 在 CPU render 阶段准备渲染数据。 virtual void prepare_data(const Plot_Render_Snapshot& snapshot); - /// 判断指定 Plot 坐标是否命中当前对象。 virtual bool select_test(const QPointF& pos); - /// 转发 Qt 事件给私有渲染数据。 virtual void plot_event(QEvent* event); virtual void mousePressEvent(QMouseEvent* event); virtual void mouseMoveEvent(QMouseEvent* event); @@ -127,16 +106,12 @@ struct Renderable : std::enable_shared_from_this { virtual void first_resize_event(QResizeEvent* event); virtual void resizeEvent(QResizeEvent* event); virtual void wheelEvent(QWheelEvent* event); - /// 按三缓冲版本号环绕语义判断 a 是否比 b 新。 - /// 当前 Renderable 输出缓存策略。 Renderable_Cache_Mode get_cache_mode() const; void set_cache_mode(Renderable_Cache_Mode mode); QString cache_parent_object_name() const; int cache_tree_depth() const; QString render_parent_object_name() const; int render_tree_depth() const; - /// 标记 State 缓冲有新版本。 - /// 请求父 Plot 调度一次渲染。 void mark_render_dirty(); QString object_name; Render_Object_Owner d_owner; diff --git a/Renderive/architecture/Update_Completion.cpp b/Renderive/architecture/Update_Completion.cpp index c8aae1d..78bff80 100644 --- a/Renderive/architecture/Update_Completion.cpp +++ b/Renderive/architecture/Update_Completion.cpp @@ -3,8 +3,19 @@ namespace renderive { namespace { std::atomic next_update_id{1}; -int stage_index(Update_Stage stage) noexcept { - return static_cast(stage); +int stage_index(Update_Stage stage) { + switch (stage) { + case Update_Stage::Input_Released: + return 0; + case Update_Stage::Committed: + return 1; + case Update_Stage::Rendered: + return 2; + case Update_Stage::Presented: + return 3; + } + ASSERT(false, "Invalid Update_Stage"); + return 3; } } Update_State::Stage_Record::Stage_Record(std::pmr::memory_resource* resource) : waiters(resource) {} @@ -28,9 +39,9 @@ Update_State::Stage_Record& Update_State::slot(Update_Stage stage) { case Update_Stage::Rendered: return rendered; case Update_Stage::Presented: - case Update_Stage::Count: return presented; } + ASSERT(false, "Invalid Update_Stage"); return presented; } const Update_State::Stage_Record& Update_State::slot(Update_Stage stage) const { diff --git a/Renderive/architecture/Update_Completion.h b/Renderive/architecture/Update_Completion.h index 15465c4..5bf7907 100644 --- a/Renderive/architecture/Update_Completion.h +++ b/Renderive/architecture/Update_Completion.h @@ -13,16 +13,24 @@ #include "../base/Task.h" #include "asio.hpp" namespace renderive { +/// @brief Lifecycle stage that an asynchronous update ticket can wait for. enum class Update_Stage : std::uint8_t { + /// @brief Input storage no longer references caller-owned data. Input_Released, + /// @brief The update has been committed to a version visible to the render pipeline. Committed, + /// @brief The update has participated in a completed CPU render pass. Rendered, - Presented, - Count + /// @brief A frame containing the update has been presented by the frontend. + Presented }; +/// @brief Final result reported when an update wait operation completes. enum class Update_Outcome : std::uint8_t { + /// @brief The requested lifecycle stage completed normally. Completed, + /// @brief The update was replaced by newer data before completing all stages. Superseded, + /// @brief The update was stopped by cancellation, destruction, or shutdown. Cancelled }; using Update_Id = std::uint64_t; diff --git a/Renderive/base/Memory.cpp b/Renderive/base/Memory.cpp index d9364d0..b0a7433 100644 --- a/Renderive/base/Memory.cpp +++ b/Renderive/base/Memory.cpp @@ -7,6 +7,13 @@ #include namespace renderive { namespace { +constexpr std::size_t memory_domain_count = static_cast(Memory_Domain::Other) + 1; + +std::size_t memory_domain_index(Memory_Domain domain) noexcept { + std::size_t index = static_cast(domain); + return index < memory_domain_count ? index : static_cast(Memory_Domain::Other); +} + void* default_allocate(void*, std::size_t size, std::size_t alignment) { return std::pmr::new_delete_resource()->allocate(size, alignment); } @@ -92,7 +99,7 @@ struct Memory_System { std::atomic small_resource{}; std::atomic_bool initialized{}; bool configured{}; - std::array(Memory_Domain::Count)> domains; + std::array domains; }; Memory_System& memory_system() { static Memory_System value; @@ -172,7 +179,7 @@ Memory_Stats memory_stats() noexcept { return memory_system().upstream.stats(); } Memory_Domain_Stats memory_domain_stats(Memory_Domain domain) noexcept { - return memory_system().domains[static_cast(domain)].stats(); + return memory_system().domains[memory_domain_index(domain)].stats(); } std::pmr::memory_resource* memory_resource() { return small_object_resource(memory_system()); @@ -181,7 +188,7 @@ std::pmr::memory_resource* memory_resource(Memory_Domain domain) { #if RENDERIVE_MEMORY_DOMAIN_STATS Memory_System& system = memory_system(); system.initialized.store(true, std::memory_order_release); - return &system.domains[static_cast(domain)]; + return &system.domains[memory_domain_index(domain)]; #else return domain_resource(domain); #endif diff --git a/Renderive/base/Memory.h b/Renderive/base/Memory.h index fe1fa64..ac7f3bf 100644 --- a/Renderive/base/Memory.h +++ b/Renderive/base/Memory.h @@ -23,26 +23,44 @@ struct Memory_Stats { std::uint64_t upstream_current_bytes{}; std::uint64_t upstream_peak_bytes{}; }; +/// @brief Allocation statistics tag used by Renderive memory resources. enum class Memory_Domain : std::uint8_t { + /// @brief Renderable facade and render-data objects. Renderable, + /// @brief Pending input packet buffers. Input_Buffer, + /// @brief Waterfall data and image working storage. Waterfall, + /// @brief Spectrum data and rendering storage. Spectrum, + /// @brief Per-frame temporary arena overflow storage. Frame_Arena, + /// @brief Generic ring-buffer storage. Ring_Buffer, + /// @brief Image pixel storage. Image, + /// @brief Audio-frequency data and rendering storage. Audio, + /// @brief Afterglow data and rendering storage. Afterglow, + /// @brief Planisphere data and rendering storage. Planisphere, + /// @brief Curve data and rendering storage. Curve, + /// @brief Point-set data and rendering storage. Point_Set, + /// @brief Axis data and rendering storage. Axis, + /// @brief Scheduler task objects. Scheduler_Task, + /// @brief Renderable local cache images. Renderable_Cache_Image, + /// @brief Plot frame images and metadata. Plot_Frame, + /// @brief Update completion state and waiters. Update_Completion, - Other, - Count + /// @brief Fallback tag for allocations without a narrower category. + Other }; struct Memory_Domain_Stats { std::uint64_t allocation_count{}; diff --git a/Renderive/plot/Plot.cpp b/Renderive/plot/Plot.cpp index a8381a9..dac80fa 100644 --- a/Renderive/plot/Plot.cpp +++ b/Renderive/plot/Plot.cpp @@ -94,7 +94,7 @@ Abs_Plot::~Abs_Plot() { } for (auto& color : d->pipeline.color_buffers) { cancel_update_states(color.presented_update_states); - color.state.store(Frame_State::Free, std::memory_order_release); + color.state.store(Frame_Buffer_State::Free, std::memory_order_release); } if (d->root_renderable) d->root_renderable->detach_from_plot_tree(); @@ -220,7 +220,7 @@ void Abs_Plot_Private::paint_event(QPaintEvent* event) { consumed.lease.frame->metadata.paint_end_time = steady_now_ns(); consumed.lease.frame->metadata.consumer_release_time = steady_now_ns(); Frame_Lifecycle_Record frame = consumed.lease.frame->metadata; - consumed.lease.frame->state.store(Frame_State::Free, std::memory_order_release); + consumed.lease.frame->state.store(Frame_Buffer_State::Free, std::memory_order_release); consumed.lease.reset(); add_render_task(); Global::instance()->render_scheduler().post([this, frame = std::move(frame)]() mutable { @@ -270,15 +270,15 @@ bool Abs_Plot_Private::submit_render_once(std::uint64_t render_request_time) { if (!color_lease) return false; Renderable::Frame* color = color_lease.frame; - Frame_State old_state = color->state.load(std::memory_order_acquire); + Frame_Buffer_State old_state = color->state.load(std::memory_order_acquire); bool input_pending = pipeline.has_new_state(); - if (old_state == Frame_State::Ready) { + if (old_state == Frame_Buffer_State::Ready) { color->metadata.superseded_time = steady_now_ns(); on_ready_frame_superseded(color->metadata, pipeline.consumer_busy(), input_pending); collect_returned_frame(color->metadata); supersede_update_states(color->presented_update_states); } - else if (old_state == Frame_State::Producing || old_state == Frame_State::Consuming) { + else if (old_state == Frame_Buffer_State::Producing || old_state == Frame_Buffer_State::Consuming) { color_lease.reset(); return false; } @@ -288,13 +288,12 @@ bool Abs_Plot_Private::submit_render_once(std::uint64_t render_request_time) { std::uint64_t submit_time = steady_now_ns(); frame.frame_id = ++next_frame_id; frame.input_version = pipeline.edit_state_version.load(std::memory_order_acquire); - frame.frame_slot_generation = frame.frame_id; frame.render_request_time = render_request_time; frame.core_data_time = latest_input_time_ns.load(std::memory_order_acquire); frame.raw_input_count = pending_input_count.exchange(0, std::memory_order_acq_rel); frame.pixel_width = static_cast(frame_snapshot->size.width()); frame.pixel_height = static_cast(frame_snapshot->size.height()); - color->state.store(Frame_State::Producing, std::memory_order_release); + color->state.store(Frame_Buffer_State::Producing, std::memory_order_release); pipeline.job_state = Renderable::Job_State::Queued; pipeline.render_state_version = frame.input_version; on_render_submitted(frame, submit_time); @@ -340,7 +339,7 @@ bool Abs_Plot_Private::submit_render_once(std::uint64_t render_request_time) { Renderable::Frame* color = task_context->color_lease.frame; auto cancel_task = [&]() { color->metadata.frame_outcome = Frame_Outcome::Cancelled; - color->state.store(Frame_State::Free, std::memory_order_release); + color->state.store(Frame_Buffer_State::Free, std::memory_order_release); private_data->pipeline.job_state = Renderable::Job_State::Idle; cancel_update_states(task_context->update_states); task_context->color_lease.reset(); @@ -374,7 +373,7 @@ void Abs_Plot_Private::finish_render() { if (destroying.load(std::memory_order_acquire)) return; Renderable::Frame* render_color = pipeline.color_by_role(Color_Render); - if (!render_color || render_color->state.load(std::memory_order_acquire) != Frame_State::Producing) + if (!render_color || render_color->state.load(std::memory_order_acquire) != Frame_Buffer_State::Producing) return; bool ready_occupied = pipeline.has_ready_frame(); bool input_pending = pipeline.has_new_state(); @@ -386,7 +385,7 @@ void Abs_Plot_Private::finish_render() { on_ready_frame_superseded(publish_result.superseded_ready->metadata, pipeline.consumer_busy(), input_pending); collect_returned_frame(publish_result.superseded_ready->metadata); supersede_update_states(publish_result.superseded_ready->presented_update_states); - publish_result.superseded_ready->state.store(Frame_State::Free, std::memory_order_release); + publish_result.superseded_ready->state.store(Frame_Buffer_State::Free, std::memory_order_release); } pipeline.job_state = Renderable::Job_State::Idle; if (publish_result.request_update) diff --git a/Renderive/plottable/Afterglow_p.h b/Renderive/plottable/Afterglow_p.h index e756dcf..c3464a2 100644 --- a/Renderive/plottable/Afterglow_p.h +++ b/Renderive/plottable/Afterglow_p.h @@ -95,7 +95,6 @@ struct Afterglow_Private : Typed_Render_Datadata_list.read_all([this, s, input](const auto& data) { if (data.size() != s->frequency_point_size) { - input->data_list.record_consumer_dropped(); return; } push_data(data, s); diff --git a/Renderive/plottable/Performance_Shower.cpp b/Renderive/plottable/Performance_Shower.cpp index db9a6b4..a28c421 100644 --- a/Renderive/plottable/Performance_Shower.cpp +++ b/Renderive/plottable/Performance_Shower.cpp @@ -84,7 +84,6 @@ public: stream << std::setprecision(9) << frame.frame_id << ',' << frame.input_version << ',' - << frame.frame_slot_generation << ',' << Psc::to_string(frame.frame_outcome) << ',' << Psc::to_string(feedback.bottleneck_state) << ',' << feedback.input_rate_fps << ',' @@ -129,7 +128,7 @@ public: << feedback.produced_frame_count << ',' << feedback.consumed_frame_count << ',' << feedback.superseded_frame_count << ',' - << feedback.repeated_frame_count << ',' + << feedback.reused_front_frame_count << ',' << duration_ms(frame.render_request_time, frame.render_queue_enter_time) << ',' << duration_ms(frame.render_queue_enter_time, frame.render_begin_time) << ',' << duration_ms(frame.prepare_begin_time, frame.prepare_end_time) << ',' diff --git a/Renderive/plottable/Performance_Shower_p.h b/Renderive/plottable/Performance_Shower_p.h index fe20ebe..a8b5fb9 100644 --- a/Renderive/plottable/Performance_Shower_p.h +++ b/Renderive/plottable/Performance_Shower_p.h @@ -209,8 +209,6 @@ struct Frame_Performance_Aggregate { case Frame_Outcome::Cancelled: ++cancelled_outcome_count; break; - case Frame_Outcome::Repeated: - break; } } private: @@ -469,7 +467,6 @@ private: return "被新帧替换"; case Frame_Outcome::Cancelled: return "已取消"; - case Frame_Outcome::Repeated: return "重复旧帧"; } return "未知"; @@ -686,7 +683,7 @@ private: .arg(count_text("produced_frame_count", feedback.produced_frame_count)) .arg(count_text("consumed_frame_count", feedback.consumed_frame_count)) .arg(count_text("superseded_frame_count", feedback.superseded_frame_count)) - .arg(count_text("repeated_frame_count", feedback.repeated_frame_count))); + .arg(count_text("reused_front_frame_count", feedback.reused_front_frame_count))); lines.push_back(QString("版本 最新输入:%1 渲染中:%2 已发布:%3 已显示:%4") .arg(count_text("latest_input_version", feedback.latest_input_version)) .arg(count_text("rendering_input_version", feedback.rendering_input_version)) diff --git a/Renderive/plottable/Planisphere.cpp b/Renderive/plottable/Planisphere.cpp index e6eb294..3f60b0e 100644 --- a/Renderive/plottable/Planisphere.cpp +++ b/Renderive/plottable/Planisphere.cpp @@ -79,6 +79,12 @@ Planisphere_Type Planisphere::type(SRC src) { void Planisphere::set_type(Planisphere_Type value) { d()->set_state_value(&Planisphere_Render_State::type, value); } +double Planisphere::phase_offset_radians(SRC src) { + return d()->state_value(&Planisphere_Render_State::phase_offset_radians, src); +} +void Planisphere::set_phase_offset_radians(double value) { + d()->set_state_value(&Planisphere_Render_State::phase_offset_radians, value); +} void Planisphere::give_data(QPointF pos) { if (!ok()) return; @@ -108,6 +114,7 @@ std::shared_ptr Planisphere::Builder::build() { PROP_R(QColor, anchor_color) PROP_R(int, continue_millisecond) PROP_R(Planisphere_Type, type) + PROP_R(double, phase_offset_radians) if (sc->i_range.length() == 0.0) sc->i_range = {0.0, 1.0}; if (sc->q_range.length() == 0.0) diff --git a/Renderive/plottable/Planisphere.h b/Renderive/plottable/Planisphere.h index 4f794e0..53e0091 100644 --- a/Renderive/plottable/Planisphere.h +++ b/Renderive/plottable/Planisphere.h @@ -3,9 +3,15 @@ #include "../primitive/Point_Set.h" namespace renderive { /// @brief 星座图调制类型。 -enum class Planisphere_Type { +/// @brief PSK constellation phase-state count. +enum class Planisphere_Type : std::uint8_t { /// @brief 8PSK 星座图。 - Psk8 = 8 + /// @brief 4PSK constellation, commonly also called QPSK. + Psk4 = 4, + /// @brief 8PSK constellation. + Psk8 = 8, + /// @brief 16PSK constellation. + Psk16 = 16 }; struct Planisphere_Private; class Grid; @@ -32,6 +38,8 @@ public: PROP(int, continue_millisecond) /// @brief 星座调制类型。 PROP(Planisphere_Type, type) + /// @brief PSK constellation phase offset in radians. + PROP(double, phase_offset_radians) /// @brief 设置坐标轴到星座图中心范围。 void set_to_axis_center(); struct Builder; @@ -50,6 +58,7 @@ struct Planisphere_Prop { QColor anchor_color = Qt::yellow; int continue_millisecond = 1000; Planisphere_Type type = Planisphere_Type::Psk8; + double phase_offset_radians = 0.0; }; /// @brief 星座图构造器。 struct LIB_DECL Planisphere::Builder : Planisphere_Prop { @@ -62,6 +71,7 @@ struct LIB_DECL Planisphere::Builder : Planisphere_Prop { PROP_B(QColor, anchor_color) PROP_B(int, continue_millisecond) PROP_B(Planisphere_Type, type) + PROP_B(double, phase_offset_radians) /// 创建星座图构造器。 Builder(std::shared_ptr parent, std::shared_ptr i_axis, std::shared_ptr q_axis); /// 构建并返回星座图对象。 diff --git a/Renderive/plottable/Planisphere_p.h b/Renderive/plottable/Planisphere_p.h index a345942..a257ded 100644 --- a/Renderive/plottable/Planisphere_p.h +++ b/Renderive/plottable/Planisphere_p.h @@ -1,6 +1,8 @@ #pragma once +#include #include #include +#include #include #include #include "../Axis/Axis.h" @@ -47,20 +49,17 @@ struct Planisphere_Private : Typed_Render_Data data_list{memory_resource(Memory_Domain::Planisphere)}; std::pmr::vector anchor_points(const Planisphere_Render_State* s) const { std::pmr::vector points(frame_memory_resource()); - if (s->type == Planisphere_Type::Psk8) { - double x_space = s->i_range.length() / 6.0; - double y_space = s->q_range.length() / 6.0; - double x_s = s->i_range.origin; - double y_s = s->q_range.origin; - points.reserve(8); - points.push_back({x_s + 2 * x_space, y_s + 1 * y_space}); - points.push_back({x_s + 1 * x_space, y_s + 2 * y_space}); - points.push_back({x_s + 1 * x_space, y_s + 4 * y_space}); - points.push_back({x_s + 2 * x_space, y_s + 5 * y_space}); - points.push_back({x_s + 4 * x_space, y_s + 5 * y_space}); - points.push_back({x_s + 5 * x_space, y_s + 4 * y_space}); - points.push_back({x_s + 5 * x_space, y_s + 2 * y_space}); - points.push_back({x_s + 4 * x_space, y_s + 1 * y_space}); + const unsigned order = static_cast(s->type); + if (order == 0) + return points; + constexpr double pi = 3.141592653589793238462643383279502884; + const double center_x = s->i_range.middle(); + const double center_y = s->q_range.middle(); + const double radius = std::min(s->i_range.size(), s->q_range.size()) / 3.0; + points.reserve(order); + for (unsigned i = 0; i < order; ++i) { + const double angle = s->phase_offset_radians + 2.0 * pi * static_cast(i) / static_cast(order); + points.push_back({center_x + radius * std::cos(angle), center_y + radius * std::sin(angle)}); } return points; } diff --git a/Renderive/plottable/Spectrum.cpp b/Renderive/plottable/Spectrum.cpp index 5294c9e..71adac3 100644 --- a/Renderive/plottable/Spectrum.cpp +++ b/Renderive/plottable/Spectrum.cpp @@ -90,10 +90,10 @@ Line_Interpolation_Mode Spectrum::interpolation_mode(SRC src) { void Spectrum::set_interpolation_mode(Line_Interpolation_Mode value) { d()->set_state_value(&Spectrum_Render_State::interpolation_mode, value); } -Spectrum_Sample_Mode Spectrum::sample_mode(SRC src) { +Line_Sampling_Mode Spectrum::sample_mode(SRC src) { return d()->state_value(&Spectrum_Render_State::sample_mode, src); } -void Spectrum::set_sample_mode(Spectrum_Sample_Mode value) { +void Spectrum::set_sample_mode(Line_Sampling_Mode value) { d()->set_state_value(&Spectrum_Render_State::sample_mode, value); } QBrush Spectrum::max_brush(SRC src) { @@ -194,7 +194,7 @@ std::shared_ptr Spectrum::Builder::build() { PROP_R(bool, use_sweep_frequency_rect); PROP_R(bool, visible_range_only); PROP_R(Line_Interpolation_Mode, interpolation_mode); - PROP_R(Spectrum_Sample_Mode, sample_mode); + PROP_R(Line_Sampling_Mode, sample_mode); PROP_R(QBrush, max_brush); PROP_R(QBrush, cur_brush); PROP_R(QBrush, min_brush); @@ -314,7 +314,7 @@ void Spectrum_Private::draw_power_curve(QPainter* painter, Spectrum_Render_State return; std::pmr::vector points(frame_memory_resource()); int point_count = static_cast(powers.size()); - if (s->sample_mode == Spectrum_Sample_Mode::Pixel_Peak_Bucket) { + if (s->sample_mode == Line_Sampling_Mode::Pixel_Peak_Envelope) { points = Curve_Utils::create_peak_bucket_points(frequency_axis.get(), power_axis.get(), s->frequency_range, point_count, point_count, s->visible_range_only, [&powers](int i) { return powers.at(i); }); @@ -403,7 +403,7 @@ std::size_t Spectrum_Private::estimated_frame_memory(const Plot_Render_Snapshot& const Spectrum_Render_State* s = render_state(); int line_count = 1 + (s->use_max_line ? 1 : 0) + (s->use_min_line ? 1 : 0); int point_count = static_cast(cur_powers.size()); - if (s->sample_mode == Spectrum_Sample_Mode::Pixel_Peak_Bucket) + if (s->sample_mode == Line_Sampling_Mode::Pixel_Peak_Envelope) point_count = std::min(point_count, snapshot.size.width() * 4 + 4); return static_cast(line_count) * static_cast(std::max(0, point_count)) * sizeof(QPointF) + 32 * 1024; } diff --git a/Renderive/plottable/Spectrum.h b/Renderive/plottable/Spectrum.h index be67fee..e66b85d 100644 --- a/Renderive/plottable/Spectrum.h +++ b/Renderive/plottable/Spectrum.h @@ -6,16 +6,13 @@ #include #include "../architecture/Update_Completion.h" #include "../base/Memory.h" +#include "../primitive/Curve.h" #include "Hover_Info.h" #include "Interpolation.h" namespace renderive { struct Spectrum_Private; class Label; class Marker; -enum class Spectrum_Sample_Mode { - Source, - Pixel_Peak_Bucket -}; /// @brief 单条频谱线图元,支持可见范围裁剪和多种线插值策略。 class LIB_DECL Spectrum : public Renderable, public Hover_Info_Renderable { public: @@ -48,7 +45,7 @@ public: /// @brief 折线插值模式。 PROP(Line_Interpolation_Mode, interpolation_mode); /// @brief 采样降采样模式。 - PROP(Spectrum_Sample_Mode, sample_mode); + PROP(Line_Sampling_Mode, sample_mode); /// @brief 最大保持线填充刷。 PROP(QBrush, max_brush); /// @brief 当前频谱线填充刷。 @@ -136,7 +133,7 @@ struct Spectrum_PROP { bool use_sweep_frequency_rect = false; bool visible_range_only = true; Line_Interpolation_Mode interpolation_mode = Line_Interpolation_Mode::Linear_Display; - Spectrum_Sample_Mode sample_mode = Spectrum_Sample_Mode::Source; + Line_Sampling_Mode sample_mode = Line_Sampling_Mode::All_Samples; QBrush max_brush; QBrush cur_brush; QBrush min_brush; @@ -169,7 +166,7 @@ struct LIB_DECL Spectrum::Builder : Spectrum_PROP { PROP_B(bool, use_sweep_frequency_rect); PROP_B(bool, visible_range_only); PROP_B(Line_Interpolation_Mode, interpolation_mode); - PROP_B(Spectrum_Sample_Mode, sample_mode); + PROP_B(Line_Sampling_Mode, sample_mode); PROP_B(QBrush, max_brush); PROP_B(QBrush, cur_brush); PROP_B(QBrush, min_brush); diff --git a/Renderive/plottable/Sweep_Frequency_p.h b/Renderive/plottable/Sweep_Frequency_p.h index 574c788..ed4f836 100644 --- a/Renderive/plottable/Sweep_Frequency_p.h +++ b/Renderive/plottable/Sweep_Frequency_p.h @@ -61,7 +61,6 @@ struct Sweep_Frequency_Private : renderive::Typed_Render_Datadata.read_all([this, d](const auto& data) { if (data.size() != block_frequency_point_size || frequency_list.empty()) { - d->data.record_consumer_dropped(); return; } QPointF* start = &frequency_list[offset * block_frequency_point_size]; diff --git a/Renderive/plottable/Waterfall.cpp b/Renderive/plottable/Waterfall.cpp index 4f17e2d..f84ad76 100644 --- a/Renderive/plottable/Waterfall.cpp +++ b/Renderive/plottable/Waterfall.cpp @@ -52,17 +52,17 @@ Image_Interpolation_Mode Waterfall::interpolation_mode(SRC src) { void Waterfall::set_interpolation_mode(Image_Interpolation_Mode value) { d()->set_state_value(&Waterfall_Render_State::interpolation_mode, value); } -Waterfall_Update_Mode Waterfall::update_mode(SRC src) { - return d()->state_value(&Waterfall_Render_State::update_mode, src); +Waterfall_Row_Update_Policy Waterfall::row_update_policy(SRC src) { + return d()->state_value(&Waterfall_Render_State::row_update_policy, src); } -void Waterfall::set_update_mode(Waterfall_Update_Mode value) { - d()->set_state_value(&Waterfall_Render_State::update_mode, value); +void Waterfall::set_row_update_policy(Waterfall_Row_Update_Policy value) { + d()->set_state_value(&Waterfall_Render_State::row_update_policy, value); } -int Waterfall::update_interval_ms(SRC src) { - return d()->state_value(&Waterfall_Render_State::update_interval_ms, src); +int Waterfall::latest_row_min_interval_ms(SRC src) { + return d()->state_value(&Waterfall_Render_State::latest_row_min_interval_ms, src); } -void Waterfall::set_update_interval_ms(int value) { - d()->set_state_value(&Waterfall_Render_State::update_interval_ms, qMax(1, value)); +void Waterfall::set_latest_row_min_interval_ms(int value) { + d()->set_state_value(&Waterfall_Render_State::latest_row_min_interval_ms, qMax(1, value)); } Color_Map Waterfall::color_map(SRC src) { return d()->state_value(&Waterfall_Render_State::color_map, src); @@ -164,11 +164,11 @@ std::shared_ptr Waterfall::Builder::build() { PROP_R(int, frequency_point_size) PROP_R(bool, visible_range_only) PROP_R(Image_Interpolation_Mode, interpolation_mode) - PROP_R(Waterfall_Update_Mode, update_mode) - PROP_R(int, update_interval_ms) + PROP_R(Waterfall_Row_Update_Policy, row_update_policy) + PROP_R(int, latest_row_min_interval_ms) PROP_R(Color_Map, color_map) sc->frequency_point_size = qMax(1, sc->frequency_point_size); - sc->update_interval_ms = qMax(1, sc->update_interval_ms); + sc->latest_row_min_interval_ms = qMax(1, sc->latest_row_min_interval_ms); if (color_bar_enabled) { auto bar = Color_Bar::Builder(ret).set_color_map(sc->color_map).set_range(sc->power_range).set_name("Waterfall").build(); pd->color_bar = bar; diff --git a/Renderive/plottable/Waterfall.h b/Renderive/plottable/Waterfall.h index 17f1ccf..c0ca275 100644 --- a/Renderive/plottable/Waterfall.h +++ b/Renderive/plottable/Waterfall.h @@ -15,10 +15,14 @@ namespace renderive { struct Waterfall_Private; class Frequency_Axis; class Time_Axis; -enum class Waterfall_Update_Mode { - Batch, - Single_Line, - Timed_Latest +/// @brief Policy for consuming pending waterfall rows. +enum class Waterfall_Row_Update_Policy : std::uint8_t { + /// @brief Consume every currently pending row in submit order. + All_Pending, + /// @brief Consume only the latest pending row and drop older pending rows. + Latest_Only, + /// @brief Consume only the latest row while enforcing a minimum interval between commits. + Rate_Limited_Latest }; /// @brief 瀑布图图元,按时间行保存二维频谱强度。 class LIB_DECL Waterfall : public Renderable, public Hover_Info_Renderable { @@ -60,9 +64,9 @@ public: /// @brief 图像缩放插值模式。 PROP(Image_Interpolation_Mode, interpolation_mode) /// @brief 瀑布图增量更新模式。 - PROP(Waterfall_Update_Mode, update_mode) + PROP(Waterfall_Row_Update_Policy, row_update_policy) /// @brief Timed_Latest 模式下的最小更新间隔。 - PROP(int, update_interval_ms) + PROP(int, latest_row_min_interval_ms) /// @brief 颜色映射表。 PROP(Color_Map, color_map) /// @brief 返回构造时启用的颜色条。 @@ -82,8 +86,8 @@ struct Waterfall_Prop { int frequency_point_size{}; bool visible_range_only = true; Image_Interpolation_Mode interpolation_mode = Image_Interpolation_Mode::Nearest; - Waterfall_Update_Mode update_mode = Waterfall_Update_Mode::Batch; - int update_interval_ms = 10; + Waterfall_Row_Update_Policy row_update_policy = Waterfall_Row_Update_Policy::All_Pending; + int latest_row_min_interval_ms = 10; Color_Map color_map{}; bool color_bar_enabled = false; }; @@ -96,8 +100,8 @@ struct LIB_DECL Waterfall::Builder : Waterfall_Prop { PROP_B(int, frequency_point_size) PROP_B(bool, visible_range_only) PROP_B(Image_Interpolation_Mode, interpolation_mode) - PROP_B(Waterfall_Update_Mode, update_mode) - PROP_B(int, update_interval_ms) + PROP_B(Waterfall_Row_Update_Policy, row_update_policy) + PROP_B(int, latest_row_min_interval_ms) PROP_B(Color_Map, color_map) PROP_B(bool, color_bar_enabled) /// 创建瀑布图构造器。 diff --git a/Renderive/plottable/Waterfall_p.h b/Renderive/plottable/Waterfall_p.h index 94c9236..55ebedf 100644 --- a/Renderive/plottable/Waterfall_p.h +++ b/Renderive/plottable/Waterfall_p.h @@ -54,7 +54,6 @@ struct Waterfall_Input_Data : Input_Data { void clear() override { clear_rows(); } - void record_consumer_dropped() {} private: void clear_rows() { for (std::size_t i = 0; i < row_count; ++i) @@ -132,7 +131,7 @@ struct Waterfall_Private : Typed_Render_Data frequency_data, int tick, int col_count) { if (frequency_data.size() != col_count) { - data->record_consumer_dropped(); return false; } ring_buffer.push_data(frequency_data.data(), tick); return true; } - bool push_owned_row(Waterfall_Input_Data* data, std::pmr::vector& frequency_data, int tick, int col_count, Waterfall_Update_Mode update_mode) { + bool push_owned_row(Waterfall_Input_Data* data, std::pmr::vector& frequency_data, int tick, int col_count, Waterfall_Row_Update_Policy update_policy) { if (frequency_data.size() != col_count) { - data->record_consumer_dropped(); return false; } ring_buffer.push_data(frequency_data.data(), tick); - if (update_mode != Waterfall_Update_Mode::Batch) { + if (update_policy != Waterfall_Row_Update_Policy::All_Pending) { Waterfall_Data row; row.tick = tick; row.frequency.swap(frequency_data); @@ -171,11 +168,6 @@ struct Waterfall_Private : Typed_Render_Datarecord_consumer_dropped(); - } - } void prepare_data(const Plot_Render_Snapshot& snapshot) override { sync_state_pipeline(); Waterfall_Input_Data* data = render_input_data(); @@ -192,44 +184,39 @@ struct Waterfall_Private : Typed_Render_Dataupdate_mode == Waterfall_Update_Mode::Batch) { + switch (s->row_update_policy) { + case Waterfall_Row_Update_Policy::All_Pending: data->read_all([this, data, col_count](int tick, const auto& frequency_data) { push_row(data, frequency_data, tick, col_count); }); - } - else { + break; + case Waterfall_Row_Update_Policy::Latest_Only: + case Waterfall_Row_Update_Policy::Rate_Limited_Latest: { Waterfall_Data latest{}; bool has_latest = false; - int valid_rows = 0; - data->read_all([data, col_count, &latest, &has_latest, &valid_rows](int tick, std::pmr::vector& frequency_data) { + data->read_all([col_count, &latest, &has_latest](int tick, std::pmr::vector& frequency_data) { if (frequency_data.size() != col_count) { - data->record_consumer_dropped(); return; } latest.tick = tick; latest.frequency.swap(frequency_data); has_latest = true; - ++valid_rows; }); if (has_latest) { bool allow_commit = true; std::uint64_t now_ns = steady_now_ns(); - if (s->update_mode == Waterfall_Update_Mode::Timed_Latest && last_line_commit_ns != 0) { - std::uint64_t interval_ns = static_cast(qMax(1, s->update_interval_ms)) * 1000000ull; + if (s->row_update_policy == Waterfall_Row_Update_Policy::Rate_Limited_Latest && last_line_commit_ns != 0) { + std::uint64_t interval_ns = static_cast(qMax(1, s->latest_row_min_interval_ms)) * 1000000ull; allow_commit = now_ns - last_line_commit_ns >= interval_ns; } if (allow_commit) { - if (push_owned_row(data, latest.frequency, latest.tick, col_count, s->update_mode)) { + if (push_owned_row(data, latest.frequency, latest.tick, col_count, s->row_update_policy)) { last_line_commit_ns = now_ns; } - policy_dropped_rows = valid_rows - 1; } - else { - policy_dropped_rows = valid_rows; - } - record_policy_dropped(data, policy_dropped_rows); } + break; + } } clear_render_input(); update_image_item(snapshot, *timeline); @@ -242,7 +229,7 @@ struct Waterfall_Private : Typed_Render_Datafrequency_range && image_power_range == s->power_range && image_visible_range == h_range && image_background_color == background_color && image_update_mode == s->update_mode && image_color_map_hash == color_map_hash; + return image_valid && image_start_tick == start_tick && image_first_col == first_col && image_last_col == last_col && m_image_frequency_range == s->frequency_range && image_power_range == s->power_range && image_visible_range == h_range && image_background_color == background_color && image_update_policy == s->row_update_policy && image_color_map_hash == color_map_hash; } void update_image_item(const Plot_Render_Snapshot& snapshot, const Timeline_Stream_Snapshot& timeline) { Waterfall_Render_State* s = render_state(); @@ -274,7 +261,7 @@ struct Waterfall_Private : Typed_Render_Datacolor_map.hash(); - bool full_rebuild = s->update_mode == Waterfall_Update_Mode::Batch || image_size_changed || !image_cache_match(s, start_tick, first_col, last_col, h_range, snapshot.background_color, color_map_hash); + bool full_rebuild = s->row_update_policy == Waterfall_Row_Update_Policy::All_Pending || image_size_changed || !image_cache_match(s, start_tick, first_col, last_col, h_range, snapshot.background_color, color_map_hash); if (full_rebuild) { image.fill(snapshot.background_color); for (int row = 0; row < buffer_data_count; ++row) { @@ -298,7 +285,7 @@ struct Waterfall_Private : Typed_Render_Datapower_range; image_visible_range = h_range; image_background_color = snapshot.background_color; - image_update_mode = s->update_mode; + image_update_policy = s->row_update_policy; image_color_map_hash = color_map_hash; QRectF source_rect(source_start, 0.0, source_end - source_start, static_cast(row_count)); image_draw_frequency_range = h_range; diff --git a/Renderive/primitive/Curve.cpp b/Renderive/primitive/Curve.cpp index a931ba7..d49d712 100644 --- a/Renderive/primitive/Curve.cpp +++ b/Renderive/primitive/Curve.cpp @@ -72,7 +72,7 @@ struct Curve_Private : Typed_Render_Datadata_range.length() == 0.0) return; - if (s->sample_mode == Curve_Sample_Mode::Pixel_Peak_Bucket) { + if (s->sample_mode == Line_Sampling_Mode::Pixel_Peak_Envelope) { points = Curve_Utils::create_peak_bucket_points(x_axis.get(), y_axis.get(), s->data_range, samples.size(), samples.size(), s->visible_range_only, [this](int i) { return samples.at(i); }); @@ -98,7 +98,7 @@ struct Curve_Private : Typed_Render_Datasample_mode == Curve_Sample_Mode::Pixel_Peak_Bucket) + if (mode == Curve_Input_Mode::Samples && s->sample_mode == Line_Sampling_Mode::Pixel_Peak_Envelope) count = std::min(count, static_cast(snapshot.size.width() * 4 + 4)); return count * sizeof(QPointF) + 16 * 1024; } @@ -211,7 +211,7 @@ RENDERIVE_DEFINE_STATE_PROP(Curve, Curve_Render_State, QBrush, fill_brush) RENDERIVE_DEFINE_STATE_PROP(Curve, Curve_Render_State, bool, visible_range_only) RENDERIVE_DEFINE_STATE_PROP(Curve, Curve_Render_State, bool, antialias) RENDERIVE_DEFINE_STATE_PROP(Curve, Curve_Render_State, Line_Interpolation_Mode, interpolation_mode) -RENDERIVE_DEFINE_STATE_PROP(Curve, Curve_Render_State, Curve_Sample_Mode, sample_mode) +RENDERIVE_DEFINE_STATE_PROP(Curve, Curve_Render_State, Line_Sampling_Mode, sample_mode) Curve::Builder::Builder(std::shared_ptr parent, std::shared_ptr x_axis, std::shared_ptr y_axis) { ASSERT(parent, "Curve build error! parent == nullptr"); ASSERT(x_axis && y_axis, "Curve build error! axis == nullptr"); @@ -235,7 +235,7 @@ std::shared_ptr Curve::Builder::build() { PROP_R(bool, visible_range_only) PROP_R(bool, antialias) PROP_R(Line_Interpolation_Mode, interpolation_mode) - PROP_R(Curve_Sample_Mode, sample_mode) + PROP_R(Line_Sampling_Mode, sample_mode) if (sc->data_range.length() == 0.0) sc->data_range = {0.0, 1.0}; return ret; diff --git a/Renderive/primitive/Curve.h b/Renderive/primitive/Curve.h index bf169e4..1eec7db 100644 --- a/Renderive/primitive/Curve.h +++ b/Renderive/primitive/Curve.h @@ -18,11 +18,14 @@ namespace renderive { struct Curve_Private; /// @brief 曲线采样模式。 -enum class Curve_Sample_Mode : std::uint8_t { +/// @brief Sampling strategy used when converting a continuous sample sequence to screen polyline points. +enum class Line_Sampling_Mode : std::uint8_t { /// @brief 直接按输入数据采样。 - Source, + /// @brief Use every input sample. + All_Samples, /// @brief 按像素峰值桶降采样。 - Pixel_Peak_Bucket + /// @brief Keep a local peak envelope per screen-pixel bucket to limit the number of drawn points. + Pixel_Peak_Envelope }; /// @brief 曲线构造属性。 struct Curve_Prop { @@ -34,7 +37,7 @@ struct Curve_Prop { bool visible_range_only = true; bool antialias = false; Line_Interpolation_Mode interpolation_mode = Line_Interpolation_Mode::Linear_Display; - Curve_Sample_Mode sample_mode = Curve_Sample_Mode::Source; + Line_Sampling_Mode sample_mode = Line_Sampling_Mode::All_Samples; }; /// @brief 峰值桶采样过程中的单桶统计。 struct Curve_Peak_Bucket { @@ -108,7 +111,7 @@ public: /// @brief 折线插值模式。 PROP(Line_Interpolation_Mode, interpolation_mode) /// @brief 曲线采样模式。 - PROP(Curve_Sample_Mode, sample_mode) + PROP(Line_Sampling_Mode, sample_mode) struct Builder; /// @brief 创建曲线图元。 Curve(); @@ -129,7 +132,7 @@ struct LIB_DECL Curve::Builder : Curve_Prop { PROP_B(bool, visible_range_only) PROP_B(bool, antialias) PROP_B(Line_Interpolation_Mode, interpolation_mode) - PROP_B(Curve_Sample_Mode, sample_mode) + PROP_B(Line_Sampling_Mode, sample_mode) /// @brief 创建曲线构造器。 Builder(std::shared_ptr parent, std::shared_ptr x_axis, std::shared_ptr y_axis); /// @brief 构建并返回曲线对象。 diff --git a/tests/Frame_Scheduler_Feedback.cpp b/tests/Frame_Scheduler_Feedback.cpp index c2a6acb..fdf3a32 100644 --- a/tests/Frame_Scheduler_Feedback.cpp +++ b/tests/Frame_Scheduler_Feedback.cpp @@ -85,7 +85,6 @@ private: renderive::Frame_Lifecycle_Record frame; frame.frame_id = config.id * 1000000 + ++next_frame_id; frame.input_version = latest_input_version; - frame.frame_slot_generation = next_frame_id; frame.core_data_time = latest_input_time; frame.render_request_time = latest_input_time; frame.render_queue_enter_time = now; diff --git a/tests/Memory_Data_Path_Stress.cpp b/tests/Memory_Data_Path_Stress.cpp index ff49d4a..70632bc 100644 --- a/tests/Memory_Data_Path_Stress.cpp +++ b/tests/Memory_Data_Path_Stress.cpp @@ -83,17 +83,17 @@ void stress_data_paths() { ASSERT_TRUE(wait_ready(waterfall, time_axis, audio, planisphere)); const int frequency_sizes[] = {64, 512, 8192, 8193, 128}; const int time_sizes[] = {32, 96, 48, 64, 24}; - const renderive::Waterfall_Update_Mode update_modes[] = { - renderive::Waterfall_Update_Mode::Batch, - renderive::Waterfall_Update_Mode::Single_Line, - renderive::Waterfall_Update_Mode::Timed_Latest, - renderive::Waterfall_Update_Mode::Single_Line, - renderive::Waterfall_Update_Mode::Batch + const renderive::Waterfall_Row_Update_Policy update_modes[] = { + renderive::Waterfall_Row_Update_Policy::All_Pending, + renderive::Waterfall_Row_Update_Policy::Latest_Only, + renderive::Waterfall_Row_Update_Policy::Rate_Limited_Latest, + renderive::Waterfall_Row_Update_Policy::Latest_Only, + renderive::Waterfall_Row_Update_Policy::All_Pending }; for (int stage = 0; stage < 5; ++stage) { waterfall->set_frequency_point_size(frequency_sizes[stage]); - waterfall->set_update_mode(update_modes[stage]); - waterfall->set_update_interval_ms(2); + waterfall->set_row_update_policy(update_modes[stage]); + waterfall->set_latest_row_min_interval_ms(2); time_axis->set_time_point_size(time_sizes[stage]); audio->set_time_point_size(time_sizes[stage]); process_events(100); diff --git a/tests/Renderable_Lifecycle_Stress.cpp b/tests/Renderable_Lifecycle_Stress.cpp index 081214f..870bc54 100644 --- a/tests/Renderable_Lifecycle_Stress.cpp +++ b/tests/Renderable_Lifecycle_Stress.cpp @@ -33,7 +33,7 @@ void stress_remove_local_cache_renderable() { auto axis = plot.create_renderable_node(plot.get_root_renderable(), "Axis_Renderable"); auto x_axis = renderive::Frequency_Axis::Builder(axis, Qt::Horizontal).set_pixel_size(620).set_coord_range({0.0, 100.0}).build(); auto y_axis = renderive::Axis::Builder(axis, Qt::Vertical).set_pixel_size(300).set_coord_range({-120.0, 0.0}).build(); - auto spectrum = renderive::Spectrum::Builder(data, x_axis, y_axis).set_frequency_point_size(4096).set_frequency_range({0.0, 100.0}).set_sample_mode(renderive::Spectrum_Sample_Mode::Pixel_Peak_Bucket).build(); + auto spectrum = renderive::Spectrum::Builder(data, x_axis, y_axis).set_frequency_point_size(4096).set_frequency_range({0.0, 100.0}).set_sample_mode(renderive::Line_Sampling_Mode::Pixel_Peak_Envelope).build(); spectrum->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel); plot.start_render(120); for (int i = 0; i < 64; ++i) { @@ -53,7 +53,7 @@ void stress_plot_destroy_with_pending_tasks() { auto axis = plot->create_renderable_node(plot->get_root_renderable(), "Axis_Renderable"); auto x_axis = renderive::Frequency_Axis::Builder(axis, Qt::Horizontal).set_pixel_size(460).set_coord_range({100.0, 0.0}).build(); auto y_axis = renderive::Axis::Builder(axis, Qt::Vertical).set_pixel_size(220).set_coord_range({0.0, -120.0}).build(); - auto spectrum = renderive::Spectrum::Builder(data, x_axis, y_axis).set_frequency_point_size(2048).set_frequency_range({100.0, 0.0}).set_sample_mode(renderive::Spectrum_Sample_Mode::Pixel_Peak_Bucket).build(); + auto spectrum = renderive::Spectrum::Builder(data, x_axis, y_axis).set_frequency_point_size(2048).set_frequency_range({100.0, 0.0}).set_sample_mode(renderive::Line_Sampling_Mode::Pixel_Peak_Envelope).build(); spectrum->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel); plot->start_render(120); for (int frame = 0; frame < 16; ++frame) {