修复若干bug

This commit is contained in:
2026-08-02 12:48:37 +08:00
parent 63b97c1bb2
commit 6d000f43b4
21 changed files with 495 additions and 233 deletions
+2
View File
@@ -153,6 +153,7 @@ struct Render_Data {
state_control.unmark_use(lease);
}
void clear_state_buffers() {
state_control.close();
for (std::size_t i = 0; i < state_buffers.size(); ++i) {
state_buffer_owners[i].reset();
state_buffers[i] = nullptr;
@@ -185,6 +186,7 @@ struct Render_Data {
clear_input_role(Input_Render);
}
void clear_input_buffers() {
input_control.close();
cancel_pending_completions();
for (std::size_t i = 0; i < input_buffers.size(); ++i) {
input_buffer_owners[i].reset();
+21 -20
View File
@@ -35,7 +35,14 @@ struct Typed_Render_Data_Base : Base {
template <typename Field, typename Source_State>
requires std::derived_from<State, Source_State>
Field state_value(Field Source_State::* field, State_Role src) {
return state_data(src)->*field;
auto record = this->state_control.acquire_role(static_cast<std::uint8_t>(src));
if (record.result != Triple_Buffer_Result::Success)
return {};
Triple_Buffer_Lease lease = record.lease;
auto state = state_data_by_index(lease.index);
Field result = state->*field;
this->state_control.unmark_use(lease);
return result;
}
template <typename Field, typename Source_State>
requires std::derived_from<State, Source_State>
@@ -55,7 +62,7 @@ struct Typed_Render_Data_Base : Base {
template <typename Field, typename Source_State>
requires std::derived_from<State, Source_State>
void set_state_value(Field Source_State::* field, Field value) {
auto record = this->state_control.try_acquire_role(State_Edit);
auto record = this->state_control.acquire_role(State_Edit);
if (record.result != Triple_Buffer_Result::Success)
return;
auto lease = record.lease;
@@ -139,15 +146,11 @@ struct Renderable_Binding {
template <Render_Data_Type Data, Render_State_Type State>
struct Render_State_Lease : Immovable {
explicit Render_State_Lease(Data* data) : data(data) {
auto record = data->state_control.try_acquire_role(State_Edit);
auto record = data->state_control.acquire_role(State_Edit);
if (record.result == Triple_Buffer_Result::Success) {
lease = record.lease;
state = reinterpret_cast<State*>(data->state_by_index(lease.index));
}
else {
fallback_owner = make_render_object<State>();
state = fallback_owner.get<State>();
}
}
~Render_State_Lease() {
if (lease)
@@ -159,23 +162,21 @@ struct Render_State_Lease : Immovable {
State& operator*() {
return *state;
}
explicit operator bool() const {
return state && lease;
}
Data* data{};
Triple_Buffer_Lease lease{};
State* state{};
Render_Object_Owner fallback_owner;
};
template <Render_Data_Type Data, Render_State_Type State>
struct Render_Edit_Lease : Immovable {
explicit Render_Edit_Lease(Data* data) : data(data) {
auto record = data->state_control.try_acquire_role(State_Edit);
auto record = data->state_control.acquire_role(State_Edit);
if (record.result == Triple_Buffer_Result::Success) {
lease = record.lease;
state = reinterpret_cast<State*>(data->state_by_index(lease.index));
}
else {
fallback_owner = make_render_object<State>();
state = fallback_owner.get<State>();
}
}
~Render_Edit_Lease() {
if (!lease)
@@ -192,23 +193,21 @@ struct Render_Edit_Lease : Immovable {
State& operator*() {
return *state;
}
explicit operator bool() const {
return state && lease;
}
Data* data{};
Triple_Buffer_Lease lease{};
State* state{};
Render_Object_Owner fallback_owner;
};
template <Render_Data_Type Data, Input_Data_Type Input>
struct Render_Input_Lease : Immovable {
explicit Render_Input_Lease(Data* data, bool notify_render = true) : data(data), notify_render(notify_render) {
auto record = data->input_control.try_acquire_role(Input_Edit);
auto record = data->input_control.acquire_role(Input_Edit);
if (record.result == Triple_Buffer_Result::Success) {
lease = record.lease;
input = reinterpret_cast<Input*>(data->input_data_by_index(lease.index));
}
else {
fallback_owner = make_render_object<Input>();
input = fallback_owner.get<Input>();
}
}
~Render_Input_Lease() {
if (!lease)
@@ -224,10 +223,12 @@ struct Render_Input_Lease : Immovable {
Input& operator*() {
return *input;
}
explicit operator bool() const {
return input && lease;
}
Data* data{};
bool notify_render{};
Triple_Buffer_Lease lease{};
Input* input{};
Render_Object_Owner fallback_owner;
};
} // namespace renderive
+2 -8
View File
@@ -10,7 +10,6 @@ Renderable_Cache_Mode Renderable::get_cache_mode() const {
}
void Renderable::set_cache_mode(Renderable_Cache_Mode mode) {
cache_mode.store(mode, std::memory_order_release);
cache_dirty.store(true, std::memory_order_release);
mark_render_dirty();
}
bool Renderable::attached_to_plot() const {
@@ -237,7 +236,7 @@ void Renderable::render_cached(Canvas& canvas, const Plot_Render_Snapshot& snaps
std::uint64_t edit_version = cache_edit_version.load(std::memory_order_acquire);
std::string parent_object_name = cache_parent_object_name();
int tree_depth = cache_tree_depth();
bool ready = !cache_dirty.load(std::memory_order_acquire) && cache_ready_version.load(std::memory_order_acquire) == edit_version && cache_image.size() == Size{bounds.width, bounds.height} && cache_bounds.x == bounds.x && cache_bounds.y == bounds.y && cache_bounds.width == bounds.width && cache_bounds.height == bounds.height;
bool ready = cache_ready_version.load(std::memory_order_acquire) == edit_version && cache_image.size() == Size{bounds.width, bounds.height} && cache_bounds.x == bounds.x && cache_bounds.y == bounds.y && cache_bounds.width == bounds.width && cache_bounds.height == bounds.height;
if (ready) {
if (snapshot.frame_metadata)
snapshot.frame_metadata->record_renderable_cache_hit(object_name_, parent_object_name, tree_depth);
@@ -300,7 +299,7 @@ void Renderable::render_cached_tree_node(Canvas& canvas, const Plot_Render_Snaps
return;
const Renderable_Tree_Node& node = tree[index];
std::uint64_t edit_version = cache_edit_version.load(std::memory_order_acquire);
bool ready = !cache_dirty.load(std::memory_order_acquire) && cache_ready_version.load(std::memory_order_acquire) == edit_version && cache_image.size() == Size{bounds.width, bounds.height} && cache_bounds.x == bounds.x && cache_bounds.y == bounds.y && cache_bounds.width == bounds.width && cache_bounds.height == bounds.height;
bool ready = cache_ready_version.load(std::memory_order_acquire) == edit_version && cache_image.size() == Size{bounds.width, bounds.height} && cache_bounds.x == bounds.x && cache_bounds.y == bounds.y && cache_bounds.width == bounds.width && cache_bounds.height == bounds.height;
if (ready) {
if (snapshot.frame_metadata)
snapshot.frame_metadata->record_renderable_cache_hit(object_name_, node.cache_parent_object_name, node.cache_tree_depth);
@@ -337,8 +336,6 @@ void Renderable::render_cache_image_tree_node(const Rect& bounds, const Plot_Ren
cache_bounds = bounds;
this->cache_image = std::move(temp_image);
cache_ready_version.store(version, std::memory_order_release);
bool dirty = cache_edit_version.load(std::memory_order_acquire) != version;
cache_dirty.store(dirty, std::memory_order_release);
}
void Renderable::render_cache_image(const Rect& bounds, const Plot_Render_Snapshot& snapshot, std::uint64_t version) {
if (get_retiring())
@@ -354,8 +351,6 @@ void Renderable::render_cache_image(const Rect& bounds, const Plot_Render_Snapsh
cache_bounds = bounds;
this->cache_image = std::move(temp_image);
cache_ready_version.store(version, std::memory_order_release);
bool dirty = cache_edit_version.load(std::memory_order_acquire) != version;
cache_dirty.store(dirty, std::memory_order_release);
}
void Renderable::render_self(Canvas& canvas, const Plot_Render_Snapshot& snapshot) {
render_self(canvas, snapshot, nullptr);
@@ -484,7 +479,6 @@ void Renderable::mark_render_dirty() {
auto context = plot_context.lock();
if (!context || context->destroying.load(std::memory_order_acquire))
return;
cache_dirty.store(true, std::memory_order_release);
cache_edit_version.fetch_add(1, std::memory_order_acq_rel);
if (auto parent = parent_renderable.lock()) {
parent->mark_render_dirty();
-1
View File
@@ -107,7 +107,6 @@ private:
mutable std::mutex children_mutex;
std::weak_ptr<Plot_Render_Context> plot_context;
std::atomic<Renderable_Cache_Mode> cache_mode{Renderable_Cache_Mode::Direct};
std::atomic_bool cache_dirty{true};
std::atomic<std::uint64_t> cache_edit_version{1};
std::atomic<std::uint64_t> cache_ready_version{0};
Image cache_image;
+124 -25
View File
@@ -2,71 +2,86 @@
#include <array>
#include <atomic>
#include <cstdint>
namespace renderive {
static constexpr std::uint8_t triple_buffer_invalid_index = 255;
/// 三缓冲操作结果。
enum class Triple_Buffer_Result : std::uint8_t {
/// 操作成功。
Success,
/// 目标物理缓冲正在被占用。
Busy,
/// 角色映射在操作期间发生变化。
State_Changed,
/// 交换条件拒绝,不代表错误。
Condition_Failed
Condition_Failed,
Cancelled
};
/// 三个逻辑角色到三个物理缓冲的当前映射。
struct Triple_Buffer_View {
std::array<std::uint8_t, 3> index{};
/// 返回指定角色当前对应的物理缓冲索引。
std::uint8_t operator[](std::uint8_t role) const {
return index[role];
}
};
/// 一次物理缓冲占用租约。
struct Triple_Buffer_Lease {
std::uint8_t index = triple_buffer_invalid_index;
/// 判断租约是否持有有效物理缓冲。
explicit operator bool() const {
return index != triple_buffer_invalid_index;
}
};
struct Triple_Buffer_Mark_Record {
Triple_Buffer_Result result = Triple_Buffer_Result::Condition_Failed;
Triple_Buffer_Lease lease{};
std::uint8_t busy_index = triple_buffer_invalid_index;
};
struct Triple_Buffer_Swap_Record {
Triple_Buffer_Result result = Triple_Buffer_Result::Condition_Failed;
Triple_Buffer_View old_view{};
std::uint8_t busy_index = triple_buffer_invalid_index;
};
struct alignas(64) Triple_Buffer_Busy {
std::atomic_bool busy{false};
};
class Triple_Role_Buffer_Control {
public:
/// 设置性能统计写入目标。
/// 重置角色映射和全部 busy 标记。
void reset() {
closed.store(false, std::memory_order_release);
slot_state.store(0, std::memory_order_release);
for (auto& item : buffer_busy) {
item.busy.store(false, std::memory_order_release);
item.busy.notify_all();
}
wake_generation.fetch_add(1, std::memory_order_acq_rel);
wake_generation.notify_all();
slot_state.notify_all();
}
void close() {
closed.store(true, std::memory_order_release);
for (auto& item : buffer_busy) {
item.busy.store(false, std::memory_order_release);
item.busy.notify_all();
}
wake_generation.fetch_add(1, std::memory_order_acq_rel);
wake_generation.notify_all();
slot_state.notify_all();
}
Triple_Buffer_View read_view() const {
return decode_order(slot_state.load(std::memory_order_acquire));
}
/// 尝试占用指定角色当前对应的物理缓冲。
Triple_Buffer_Mark_Record try_acquire_role(std::uint8_t role) {
if (closed.load(std::memory_order_acquire))
return {Triple_Buffer_Result::Cancelled, {}, triple_buffer_invalid_index};
auto order = slot_state.load(std::memory_order_acquire);
auto view = decode_order(order);
auto index = view[role];
if (!try_mark_index(index)) {
if (!try_mark_index(index))
return {Triple_Buffer_Result::Busy, {}, index};
}
auto cur_order = slot_state.load(std::memory_order_acquire);
if (cur_order != order) {
unmark_index(index);
@@ -74,28 +89,65 @@ public:
}
return {Triple_Buffer_Result::Success, {index}, triple_buffer_invalid_index};
}
/// 释放租约持有的物理缓冲。
void unmark_use(Triple_Buffer_Lease lease) {
unmark_index(lease.index);
Triple_Buffer_Mark_Record acquire_role(std::uint8_t role) {
while (!closed.load(std::memory_order_acquire)) {
auto order = slot_state.load(std::memory_order_acquire);
auto view = decode_order(order);
auto index = view[role];
if (!try_mark_index(index)) {
auto wake = wake_generation.load(std::memory_order_acquire);
if (buffer_busy[index].busy.load(std::memory_order_acquire))
wake_generation.wait(wake, std::memory_order_acquire);
continue;
}
auto cur_order = slot_state.load(std::memory_order_acquire);
if (cur_order != order) {
unmark_index(index);
continue;
}
if (closed.load(std::memory_order_acquire)) {
unmark_index(index);
break;
}
return {Triple_Buffer_Result::Success, {index}, triple_buffer_invalid_index};
}
return {Triple_Buffer_Result::Cancelled, {}, triple_buffer_invalid_index};
}
/// 尝试交换两个角色的物理缓冲映射。
void unmark_use(Triple_Buffer_Lease lease) {
if (lease)
unmark_index(lease.index);
}
template <typename Can_Swap>
Triple_Buffer_Swap_Record try_swap_role(std::uint8_t left_role, std::uint8_t right_role, Can_Swap can_swap) {
std::uint8_t busy_index = triple_buffer_invalid_index;
return try_swap_role_once(left_role, right_role, can_swap, [](const Triple_Buffer_View&, const Triple_Buffer_View&) {}, busy_index);
}
template <typename Can_Swap, typename On_Success>
Triple_Buffer_Swap_Record try_swap_role(std::uint8_t left_role, std::uint8_t right_role, Can_Swap can_swap, On_Success on_success) {
std::uint8_t busy_index = triple_buffer_invalid_index;
return try_swap_role_once(left_role, right_role, can_swap, on_success, busy_index);
}
template <typename Can_Swap, typename On_Success>
Triple_Buffer_Swap_Record try_swap_role_with_left_lease(std::uint8_t left_role, std::uint8_t right_role, Triple_Buffer_Lease left_lease, Can_Swap can_swap, On_Success on_success) {
return try_swap_role_with_left_lease_once(left_role, right_role, left_lease, can_swap, on_success);
}
bool role_busy(std::uint8_t role) const {
auto view = read_view();
return buffer_busy[view[role]].busy.load(std::memory_order_acquire);
}
private:
std::atomic<std::uint8_t> slot_state{0};
std::atomic_bool closed{false};
std::atomic<std::uint64_t> wake_generation{0};
std::array<Triple_Buffer_Busy, 3> buffer_busy{};
static Triple_Buffer_View decode_order(std::uint8_t order) {
switch (order) {
case 0:
@@ -113,15 +165,15 @@ private:
}
return {{0, 1, 2}};
}
static std::uint8_t encode_order(const Triple_Buffer_View& view) {
if (view.index[0] == 0) {
if (view.index[0] == 0)
return view.index[1] == 1 ? 0 : 1;
}
if (view.index[0] == 1) {
if (view.index[0] == 1)
return view.index[1] == 0 ? 2 : 3;
}
return view.index[1] == 0 ? 4 : 5;
}
static std::uint8_t swap_order(std::uint8_t order, std::uint8_t left_role, std::uint8_t right_role) {
auto view = decode_order(order);
auto temp = view.index[left_role];
@@ -129,14 +181,19 @@ private:
view.index[right_role] = temp;
return encode_order(view);
}
bool try_mark_index(std::uint8_t index) {
bool expected = false;
return buffer_busy[index].busy.compare_exchange_strong(expected, true, std::memory_order_acq_rel, std::memory_order_acquire);
}
void unmark_index(std::uint8_t index) {
buffer_busy[index].busy.store(false, std::memory_order_release);
buffer_busy[index].busy.notify_all();
wake_generation.fetch_add(1, std::memory_order_acq_rel);
wake_generation.notify_all();
}
bool try_mark_two_indices(std::uint8_t left_index, std::uint8_t right_index, std::uint8_t& busy_index) {
if (left_index == right_index) {
if (!try_mark_index(left_index)) {
@@ -158,6 +215,7 @@ private:
}
return true;
}
void unmark_two_indices(std::uint8_t left_index, std::uint8_t right_index) {
if (left_index == right_index) {
unmark_index(left_index);
@@ -166,15 +224,17 @@ private:
unmark_index(left_index);
unmark_index(right_index);
}
template <typename Can_Swap, typename On_Success>
Triple_Buffer_Swap_Record try_swap_role_once(std::uint8_t left_role, std::uint8_t right_role, Can_Swap can_swap, On_Success on_success, std::uint8_t& busy_index) {
if (closed.load(std::memory_order_acquire))
return {Triple_Buffer_Result::Cancelled, {}, triple_buffer_invalid_index};
auto old_order = slot_state.load(std::memory_order_acquire);
auto old_view = decode_order(old_order);
auto left_index = old_view[left_role];
auto right_index = old_view[right_role];
if (!try_mark_two_indices(left_index, right_index, busy_index)) {
if (!try_mark_two_indices(left_index, right_index, busy_index))
return {Triple_Buffer_Result::Busy, old_view, busy_index};
}
auto cur_order = slot_state.load(std::memory_order_acquire);
if (cur_order != old_order) {
unmark_two_indices(left_index, right_index);
@@ -193,7 +253,46 @@ private:
on_success(old_view, decode_order(new_order));
unmark_two_indices(left_index, right_index);
slot_state.notify_all();
wake_generation.fetch_add(1, std::memory_order_acq_rel);
wake_generation.notify_all();
return {Triple_Buffer_Result::Success, old_view, triple_buffer_invalid_index};
}
template <typename Can_Swap, typename On_Success>
Triple_Buffer_Swap_Record try_swap_role_with_left_lease_once(std::uint8_t left_role, std::uint8_t right_role, Triple_Buffer_Lease left_lease, Can_Swap can_swap, On_Success on_success) {
if (!left_lease)
return {Triple_Buffer_Result::Condition_Failed, {}, triple_buffer_invalid_index};
if (closed.load(std::memory_order_acquire))
return {Triple_Buffer_Result::Cancelled, {}, triple_buffer_invalid_index};
auto old_order = slot_state.load(std::memory_order_acquire);
auto old_view = decode_order(old_order);
if (old_view[left_role] != left_lease.index)
return {Triple_Buffer_Result::State_Changed, old_view, triple_buffer_invalid_index};
auto right_index = old_view[right_role];
if (!try_mark_index(right_index))
return {Triple_Buffer_Result::Busy, old_view, right_index};
auto cur_order = slot_state.load(std::memory_order_acquire);
if (cur_order != old_order) {
unmark_index(right_index);
return {Triple_Buffer_Result::State_Changed, old_view, triple_buffer_invalid_index};
}
if (!can_swap(old_view)) {
unmark_index(right_index);
return {Triple_Buffer_Result::Condition_Failed, old_view, triple_buffer_invalid_index};
}
auto new_order = swap_order(old_order, left_role, right_role);
auto expected = old_order;
if (!slot_state.compare_exchange_strong(expected, new_order, std::memory_order_acq_rel, std::memory_order_acquire)) {
unmark_index(right_index);
return {Triple_Buffer_Result::State_Changed, old_view, triple_buffer_invalid_index};
}
on_success(old_view, decode_order(new_order));
unmark_index(right_index);
slot_state.notify_all();
wake_generation.fetch_add(1, std::memory_order_acq_rel);
wake_generation.notify_all();
return {Triple_Buffer_Result::Success, old_view, triple_buffer_invalid_index};
}
};
} // namespace renderive
+14
View File
@@ -8,6 +8,7 @@
#include <mutex>
#include <optional>
#include <unordered_set>
#include <vector>
#include <taskflow/taskflow.hpp>
#include <thread>
@@ -183,11 +184,24 @@ public:
void shutdown() {
if (shutting_down.exchange(true, std::memory_order_acq_rel))
return;
std::vector<Render_Executor_Task> cancelled_tasks;
{
std::lock_guard<std::mutex> lock(admission_mutex);
while (!pending_frame_tasks.empty()) {
Pending_Frame_Task pending = std::move(pending_frame_tasks.front());
pending_frame_tasks.pop_front();
cancelled_tasks.push_back(std::move(pending.task));
metrics->queued_frame_jobs.fetch_sub(1, std::memory_order_relaxed);
}
pending_frame_tasks.clear();
queued_frame_plots.clear();
}
for (auto& task : cancelled_tasks) {
if (task.frame_stat)
task.frame_stat->executor_at_finish = snapshot();
if (task.completion)
task.completion();
}
executor.wait_for_all();
}
+1 -1
View File
@@ -9,7 +9,7 @@ void Explicit_Refresh_Strategy::request_render() {
Refresh_Action Explicit_Refresh_Strategy::on_trigger(
Refresh_Trigger,
const Refresh_Runtime_Snapshot& runtime,
const Frame_Lifecycle_Record*) {
Frame_Lifecycle_Record*) {
Refresh_Action action;
if (!manual_request_pending.load(std::memory_order_acquire))
return action;
+1 -1
View File
@@ -12,7 +12,7 @@ private:
[[nodiscard]] Refresh_Action on_trigger(
Refresh_Trigger trigger,
const Refresh_Runtime_Snapshot& runtime,
const Frame_Lifecycle_Record* frame) override;
Frame_Lifecycle_Record* frame) override;
std::atomic_bool manual_request_pending{false};
};
+4 -4
View File
@@ -32,7 +32,7 @@ Refresh_Control_Snapshot Latency_Eager_Refresh_Strategy::feedback_state() const
Refresh_Action Latency_Eager_Refresh_Strategy::on_trigger(
Refresh_Trigger trigger,
const Refresh_Runtime_Snapshot& runtime,
const Frame_Lifecycle_Record* frame) {
Frame_Lifecycle_Record* frame) {
d->feedback.set_max_render_fps(max_render_fps());
if (trigger == Refresh_Trigger::Started)
@@ -42,13 +42,13 @@ Refresh_Action Latency_Eager_Refresh_Strategy::on_trigger(
if (frame) {
switch (trigger) {
case Refresh_Trigger::Render_Finished:
d->feedback.on_render_completed(const_cast<Frame_Lifecycle_Record&>(*frame));
d->feedback.on_render_completed(*frame);
break;
case Refresh_Trigger::Frame_Returned:
if (frame->outcome == Frame_Outcome::Superseded)
d->feedback.on_frame_superseded(const_cast<Frame_Lifecycle_Record&>(*frame));
d->feedback.on_frame_superseded(*frame);
else
d->feedback.on_frame_presented(const_cast<Frame_Lifecycle_Record&>(*frame));
d->feedback.on_frame_presented(*frame);
break;
default:
break;
+1 -1
View File
@@ -19,7 +19,7 @@ private:
[[nodiscard]] Refresh_Action on_trigger(
Refresh_Trigger trigger,
const Refresh_Runtime_Snapshot& runtime,
const Frame_Lifecycle_Record* frame) override;
Frame_Lifecycle_Record* frame) override;
struct Private;
std::unique_ptr<Private> d;
+9 -11
View File
@@ -29,7 +29,7 @@ public:
return rendering.load(std::memory_order_acquire);
}
bool destroying() const {
return destroying_flag.load(std::memory_order_acquire);
return !context || context->destroying.load(std::memory_order_acquire);
}
Refresh_Runtime_Snapshot refresh_runtime_snapshot(std::uint64_t now_ns = steady_now_ns()) const {
return {
@@ -43,7 +43,7 @@ public:
};
}
bool submit_render_once() {
if (!rendering.load(std::memory_order_acquire) || destroying_flag.load(std::memory_order_acquire))
if (!rendering.load(std::memory_order_acquire) || destroying())
return false;
if (!root)
return false;
@@ -189,16 +189,16 @@ public:
if (job->worker_stat)
frame.worker = *job->worker_stat;
bool valid_generation = job->generation == generation.load(std::memory_order_acquire);
bool valid_context = !destroying_flag.load(std::memory_order_acquire) && context && !context->destroying.load(std::memory_order_acquire);
job->frame_lease.reset();
bool valid_context = !destroying();
if (!valid_generation || !valid_context || !frame.render_end_ns) {
job->frame_lease.reset();
frame.outcome = Frame_Outcome::Cancelled;
pipeline.render_job_state = Frame_Job_State::Idle;
supersede_update_states(job->buffer->presented_update_states);
trigger_refresh_strategy(Refresh_Trigger::Render_Finished, &frame);
return;
}
auto publish_result = pipeline.try_publish_rendered_frame(steady_now_ns());
auto publish_result = pipeline.try_publish_rendered_frame(std::move(job->frame_lease), steady_now_ns());
if (publish_result.superseded_frame_record) {
trigger_refresh_strategy(Refresh_Trigger::Frame_Returned, publish_result.superseded_frame_record.get());
collect_frame(publish_result.superseded_frame_record);
@@ -420,7 +420,6 @@ public:
Plot_Frame_Pipeline pipeline;
std::weak_ptr<Presentation_Sink> sink;
std::atomic_bool rendering{false};
std::atomic_bool destroying_flag{false};
std::atomic<std::uint64_t> latest_input_time_ns{0};
std::atomic<std::uint64_t> pending_input_count{0};
bool strategy_started{};
@@ -444,7 +443,6 @@ Plot_Core::Plot_Core(std::unique_ptr<Refresh_Strategy> strategy)
Global::instance()->render_scheduler().register_plot(this);
}
Plot_Core::~Plot_Core() {
d->destroying_flag.store(true, std::memory_order_release);
d->context->destroying.store(true, std::memory_order_release);
d->generation.fetch_add(1, std::memory_order_acq_rel);
Global::instance()->render_scheduler().remove_plot(this);
@@ -512,7 +510,7 @@ void Plot_Core::dispatch_event(const Event& event) {
d->dispatch_event(event);
}
void Plot_Core::mark_render_state_dirty() {
if (d->destroying_flag.load(std::memory_order_acquire))
if (d->destroying())
return;
std::uint64_t now_ns = steady_now_ns();
d->pipeline.advance_edit_version();
@@ -523,14 +521,14 @@ void Plot_Core::mark_render_state_dirty() {
});
}
void Plot_Core::request_manual_refresh() {
if (d->destroying_flag.load(std::memory_order_acquire))
if (d->destroying())
return;
d->post_scheduler_action([data = d]() {
data->trigger_refresh_strategy(Refresh_Trigger::Manual_Request);
});
}
void Plot_Core::start_render() {
if (d->destroying_flag.load(std::memory_order_acquire))
if (d->destroying())
return;
d->rendering.store(true, std::memory_order_release);
d->post_scheduler_action([data = d]() {
@@ -562,7 +560,7 @@ Present_Frame_Lease Plot_Core::begin_present() {
result.is_new_frame = consumed.has_new_frame;
result.update_was_pending = consumed.present_request_was_pending;
if (consumed.lease)
result.lease_ = std::make_unique<Plot_Frame_Lease>(std::move(consumed.lease));
result.lease_.emplace(std::move(consumed.lease));
if (!result.new_frame() && result.request_was_pending()) {
d->post_scheduler_action([data = d]() {
data->trigger_refresh_strategy(Refresh_Trigger::Paint_Finished);
+15 -5
View File
@@ -80,11 +80,13 @@ Plot_Frame_Lease Plot_Frame_Pipeline::try_acquire_render_frame() {
return {frame_by_index(record.lease.index), &frame_control, record.lease};
}
Plot_Frame_Pipeline::Frame_Publish_Result Plot_Frame_Pipeline::try_publish_rendered_frame(std::uint64_t now_ns) {
Plot_Frame_Pipeline::Frame_Publish_Result Plot_Frame_Pipeline::try_publish_rendered_frame(Plot_Frame_Lease rendering_lease, std::uint64_t now_ns) {
Frame_Publish_Result result;
Plot_Frame* rendered = frame_by_role(Frame_Rendering);
if (!rendering_lease)
return result;
Plot_Frame* rendered = rendering_lease.frame;
auto swap = frame_control.try_swap_role(Frame_Rendering, Frame_Middle, [this](const Triple_Buffer_View& view) {
auto swap = frame_control.try_swap_role_with_left_lease(Frame_Rendering, Frame_Middle, rendering_lease.lease, [this](const Triple_Buffer_View& view) {
const Plot_Frame* rendering = frame_by_index(view[Frame_Rendering]);
const Plot_Frame* middle = frame_by_index(view[Frame_Middle]);
return buffer_version_newer(rendering->version.load(std::memory_order_acquire), middle->version.load(std::memory_order_acquire));
@@ -116,6 +118,7 @@ Plot_Frame_Pipeline::Frame_Publish_Result Plot_Frame_Pipeline::try_publish_rende
return result;
}
rendering_lease.reset();
Frame_Publish_Result update_result = request_present(now_ns);
update_result.superseded_frame_record = result.superseded_frame_record;
update_result.dropped_frame_record = result.dropped_frame_record;
@@ -124,9 +127,16 @@ Plot_Frame_Pipeline::Frame_Publish_Result Plot_Frame_Pipeline::try_publish_rende
Plot_Frame_Pipeline::Frame_Publish_Result Plot_Frame_Pipeline::request_present(std::uint64_t now_ns) {
Frame_Publish_Result result;
if (!middle_frame_ready())
auto record = frame_control.try_acquire_role(Frame_Middle);
if (record.result != Triple_Buffer_Result::Success)
return result;
Plot_Frame* middle = frame_by_role(Frame_Middle);
Plot_Frame_Lease middle_lease{frame_by_index(record.lease.index), &frame_control, record.lease};
Plot_Frame* middle = middle_lease.frame;
const Plot_Frame* painting = frame_by_role(Frame_Painting);
if (!buffer_version_newer(middle->version.load(std::memory_order_acquire), painting->version.load(std::memory_order_acquire)))
return result;
if (paint_request_pending.load(std::memory_order_acquire)) {
middle->metadata->update_request_id = pending_present_request_id.load(std::memory_order_acquire);
middle->metadata->update_request_time_ns = pending_present_request_time_ns.load(std::memory_order_acquire);
+1 -1
View File
@@ -63,7 +63,7 @@ public:
[[nodiscard]] Plot_Frame* frame_by_role(std::uint8_t role);
[[nodiscard]] const Plot_Frame* frame_by_role(std::uint8_t role) const;
[[nodiscard]] Plot_Frame_Lease try_acquire_render_frame();
[[nodiscard]] Frame_Publish_Result try_publish_rendered_frame(std::uint64_t now_ns);
[[nodiscard]] Frame_Publish_Result try_publish_rendered_frame(Plot_Frame_Lease rendering_lease, std::uint64_t now_ns);
[[nodiscard]] Frame_Publish_Result request_present(std::uint64_t now_ns);
[[nodiscard]] Frame_Consume_Result try_acquire_paint_frame(std::uint64_t now_ns);
[[nodiscard]] bool middle_frame_ready() const;
+2 -2
View File
@@ -2,7 +2,7 @@
#include "Plot_Frame_Pipeline.h"
#include "../render/Image_View.h"
#include <cstdint>
#include <memory>
#include <optional>
namespace renderive {
class Present_Frame_Lease {
public:
@@ -16,7 +16,7 @@ public:
[[nodiscard]] Image_View image() const;
private:
friend class Plot_Core;
std::unique_ptr<Plot_Frame_Lease> lease_;
std::optional<Plot_Frame_Lease> lease_;
bool is_new_frame{};
bool update_was_pending{};
};
+1 -1
View File
@@ -37,7 +37,7 @@ public:
[[nodiscard]] virtual Refresh_Action on_trigger(
Refresh_Trigger trigger,
const Refresh_Runtime_Snapshot& runtime,
const Frame_Lifecycle_Record* frame) = 0;
Frame_Lifecycle_Record* frame) = 0;
};
} // namespace renderive
+15
View File
@@ -91,6 +91,21 @@ double interpolate_sample_value(double index, int count, Line_Interpolation_Mode
return left_value;
}
template <typename Value_At>
std::pmr::vector<PointF> build_sample_line_points(const Axis_Mapping_2D& mapping, const Range& data_range, int total_count, int available_count, bool visible_range_only, Value_At value_at, std::pmr::memory_resource* resource = frame_memory_resource()) {
Visible_Sample_Window window = compute_visible_sample_window(mapping.domain, data_range, total_count, available_count, visible_range_only);
if (!window)
return {};
int first_source = std::clamp(static_cast<int>(std::ceil(std::min(window.first_index, window.last_index))), 0, available_count - 1);
int last_source = std::clamp(static_cast<int>(std::floor(std::max(window.first_index, window.last_index))), first_source, available_count - 1);
std::pmr::vector<PointF> points(resource);
points.reserve(static_cast<std::size_t>(std::max(0, last_source - first_source + 1)));
for (int i = first_source; i <= last_source; ++i) {
double coord = source_index_to_coordinate(data_range, total_count, i);
points.push_back(mapping.map(coord, value_at(i)));
}
return points;
}
template <typename Value_At>
std::pmr::vector<PointF> build_interpolated_line_points(const Axis_Mapping_2D& mapping, const Range& data_range, int total_count, int available_count, bool visible_range_only, Line_Interpolation_Mode mode, Value_At value_at, std::pmr::memory_resource* resource = frame_memory_resource()) {
Visible_Sample_Window window = compute_visible_sample_window(mapping.domain, data_range, total_count, available_count, visible_range_only);
if (!window)
+77 -29
View File
@@ -99,8 +99,50 @@ struct Performance_Aggregate {
void clear() {
*this = {};
}
static Frame_Lifecycle_Record summarize_frame(const Frame_Lifecycle_Record& frame) {
Frame_Lifecycle_Record summary;
summary.frame_id = frame.frame_id;
summary.input_version = frame.input_version;
summary.core_data_time_ns = frame.core_data_time_ns;
summary.render_request_ns = frame.render_request_ns;
summary.render_role_enter_ns = frame.render_role_enter_ns;
summary.render_begin_ns = frame.render_begin_ns;
summary.prepare_begin_ns = frame.prepare_begin_ns;
summary.prepare_end_ns = frame.prepare_end_ns;
summary.draw_begin_ns = frame.draw_begin_ns;
summary.draw_end_ns = frame.draw_end_ns;
summary.render_end_ns = frame.render_end_ns;
summary.transition_12_ns = frame.transition_12_ns;
summary.update_request_id = frame.update_request_id;
summary.update_request_time_ns = frame.update_request_time_ns;
summary.transition_23_ns = frame.transition_23_ns;
summary.paint_begin_ns = frame.paint_begin_ns;
summary.paint_end_ns = frame.paint_end_ns;
summary.transition_32_ns = frame.transition_32_ns;
summary.wait_21_ns = frame.wait_21_ns;
summary.wait_12_ns = frame.wait_12_ns;
summary.wait_23_ns = frame.wait_23_ns;
summary.wait_32_ns = frame.wait_32_ns;
summary.superseded_time_ns = frame.superseded_time_ns;
summary.outcome = frame.outcome;
summary.refresh = frame.refresh;
summary.worker = frame.worker;
summary.raw_input_count = frame.raw_input_count;
summary.prepared_output_count = frame.prepared_output_count;
summary.renderable_cache_hit_count = frame.renderable_cache_hit_count;
summary.renderable_cache_miss_count = frame.renderable_cache_miss_count;
summary.renderable_cache_rebuild_count = frame.renderable_cache_rebuild_count;
summary.renderable_cache_rebuild_time_ns = frame.renderable_cache_rebuild_time_ns;
summary.renderable_cache_compose_time_ns = frame.renderable_cache_compose_time_ns;
summary.renderable_cache_image_bytes = frame.renderable_cache_image_bytes;
summary.renderable_cache_image_area = frame.renderable_cache_image_area;
summary.pixel_width = frame.pixel_width;
summary.pixel_height = frame.pixel_height;
summary.viewport_version = frame.viewport_version;
return summary;
}
void update(const Frame_Lifecycle_Record& frame) {
latest = frame;
latest = summarize_frame(frame);
has_frame = true;
++observed_frame_count;
std::uint64_t frame_render_time_ns = duration_ns(frame.render_begin_ns, frame.render_end_ns);
@@ -357,7 +399,7 @@ void schedule_performance_metrics_worker(const std::shared_ptr<Performance_Worke
struct Performance_Overlay_Private {
Performance_Aggregate aggregate;
std::atomic_bool enabled_value{false};
std::shared_ptr<Performance_Worker_State> metrics_worker = std::make_shared<Performance_Worker_State>();
std::shared_ptr<Performance_Worker_State> metrics_worker;
std::vector<std::string> lines;
std::vector<Performance_Line_Layout> render_lines;
std::array<Performance_Field_Layout_State, performance_field_count> field_layout_states{};
@@ -365,7 +407,13 @@ struct Performance_Overlay_Private {
RectF scroll_bar_rect;
RectF scroll_thumb_rect;
double content_width{};
explicit Performance_Overlay_Private(bool create_worker = true) {
if (create_worker)
metrics_worker = std::make_shared<Performance_Worker_State>();
}
~Performance_Overlay_Private() {
if (!metrics_worker)
return;
metrics_worker->cancelled.store(true, std::memory_order_release);
Performance_Frame_Record frame;
while (metrics_worker->metric_queue.try_pop(frame)) {}
@@ -418,18 +466,18 @@ struct Performance_Overlay_Private {
state->worker_active.store(false, std::memory_order_release);
}
static std::shared_ptr<const Performance_Display_Snapshot> build_display_snapshot(Performance_Worker_State& worker_state) {
Performance_Overlay_Private builder;
builder.aggregate = worker_state.aggregate;
builder.field_layout_states = worker_state.field_layout_states;
builder.rebuild_lines();
Performance_Overlay_Private layout(false);
layout.aggregate = worker_state.aggregate;
layout.field_layout_states = worker_state.field_layout_states;
layout.rebuild_lines();
auto snapshot = std::make_shared<Performance_Display_Snapshot>();
snapshot->content_version = worker_state.next_content_version.fetch_add(1, std::memory_order_acq_rel);
Size viewport = current_viewport(worker_state);
snapshot->viewport_size = viewport;
snapshot->viewport_version = current_viewport_version(worker_state);
snapshot->viewport_rect = RectF{0.0, 0.0, static_cast<double>(viewport.width), static_cast<double>(viewport.height)};
if (viewport.empty() || builder.lines.empty()) {
snapshot->lines = builder.stripped_lines();
if (viewport.empty() || layout.lines.empty()) {
snapshot->lines = layout.stripped_lines();
return snapshot;
}
Performance_Overlay_Render_State state;
@@ -446,24 +494,24 @@ struct Performance_Overlay_Private {
metrics = measure_canvas.measure_text("0123456789 ABC \xE4\xB8\xAD\xE6\x96\x87");
if (metrics.line_height() <= 0.0)
metrics = measure_canvas.measure_text("M");
builder.seed_static_field_widths(measure_canvas);
layout.seed_static_field_widths(measure_canvas);
int available_text_width = viewport.width - state.left_margin - state.right_margin - state.scroll_bar_width - state.scroll_bar_margin * 2;
builder.build_render_lines(measure_canvas, static_cast<double>(std::max(1, available_text_width)));
layout.build_render_lines(measure_canvas, static_cast<double>(std::max(1, available_text_width)));
}
worker_state.field_layout_states = builder.field_layout_states;
snapshot->lines = builder.display_lines();
builder.update_content_width(&state, worker_state.maximum_content_width, viewport.width);
worker_state.field_layout_states = layout.field_layout_states;
snapshot->lines = layout.display_lines();
layout.update_content_width(&state, worker_state.maximum_content_width, viewport.width);
double line_height = std::max(1.0, metrics.line_height());
double panel_width = std::min(builder.content_width, static_cast<double>(std::max(0, viewport.width)));
double height = builder.panel_height(line_height, &state, viewport.height);
double panel_width = std::min(layout.content_width, static_cast<double>(std::max(0, viewport.width)));
double height = layout.panel_height(line_height, &state, viewport.height);
if (panel_width <= 0.0 || height <= 0.0)
return snapshot;
double content_height = builder.content_height(line_height, &state);
double content_viewport_height = builder.content_viewport_height(&state, height);
double content_height = layout.content_height(line_height, &state);
double content_viewport_height = layout.content_viewport_height(&state, height);
double max_scroll = std::max(0.0, content_height - content_viewport_height);
double scroll = performance_clamp_scroll_offset(worker_state.scroll_offset.load(std::memory_order_acquire), max_scroll);
worker_state.scroll_offset.store(scroll, std::memory_order_release);
builder.panel_rect = RectF{0.0, 0.0, panel_width, height};
layout.panel_rect = RectF{0.0, 0.0, panel_width, height};
Image image(static_cast<int>(std::ceil(panel_width)), static_cast<int>(std::ceil(height)));
image.fill(Color::transparent());
Canvas canvas(image);
@@ -481,30 +529,30 @@ struct Performance_Overlay_Private {
canvas.set_clip_rect(text_rect);
int first_line = std::max(0, static_cast<int>(std::floor(scroll / line_height)));
double y = static_cast<double>(state.top_margin) - (scroll - static_cast<double>(first_line) * line_height);
for (int i = first_line; i < static_cast<int>(builder.render_lines.size()) && y < text_rect.bottom(); ++i, y += line_height) {
for (int i = first_line; i < static_cast<int>(layout.render_lines.size()) && y < text_rect.bottom(); ++i, y += line_height) {
if (y + line_height < text_rect.y)
continue;
builder.draw_render_line(canvas, builder.render_lines[static_cast<std::size_t>(i)], text_rect.x, y);
layout.draw_render_line(canvas, layout.render_lines[static_cast<std::size_t>(i)], text_rect.x, y);
}
canvas.reset_clip();
builder.update_scroll_bar_rects(&state, max_scroll, scroll, content_viewport_height, content_height);
if (!builder.scroll_bar_rect.empty()) {
layout.update_scroll_bar_rects(&state, max_scroll, scroll, content_viewport_height, content_height);
if (!layout.scroll_bar_rect.empty()) {
canvas.set_brush(Brush{state.scroll_track, Brush_Style::Solid});
canvas.fill_rect(builder.scroll_bar_rect);
canvas.fill_rect(layout.scroll_bar_rect);
canvas.set_brush(Brush{state.scroll_thumb, Brush_Style::Solid});
canvas.fill_rect(builder.scroll_thumb_rect);
canvas.fill_rect(layout.scroll_thumb_rect);
}
canvas.restore();
snapshot->destination_rect = builder.panel_rect;
snapshot->scroll_track_rect = builder.scroll_bar_rect;
snapshot->scroll_thumb_rect = builder.scroll_thumb_rect;
snapshot->destination_rect = layout.panel_rect;
snapshot->scroll_track_rect = layout.scroll_bar_rect;
snapshot->scroll_thumb_rect = layout.scroll_thumb_rect;
snapshot->content_height = content_height;
snapshot->scroll_offset = scroll;
snapshot->max_scroll_offset = max_scroll;
snapshot->line_height = line_height;
snapshot->image = std::move(image);
worker_state.last_scroll_track_rect = builder.scroll_bar_rect;
worker_state.last_scroll_thumb_rect = builder.scroll_thumb_rect;
worker_state.last_scroll_track_rect = layout.scroll_bar_rect;
worker_state.last_scroll_thumb_rect = layout.scroll_thumb_rect;
worker_state.last_content_viewport_height = content_viewport_height;
worker_state.last_line_height = line_height;
worker_state.maximum_scroll_offset = max_scroll;
+19 -79
View File
@@ -93,7 +93,6 @@ struct Waterfall_Image_Cache_Key {
Range power_range{};
Range visible_range{};
Color background;
Waterfall_Row_Update_Policy policy = Waterfall_Row_Update_Policy::All_Pending;
std::uint64_t color_map_hash{};
int width{};
int height{};
@@ -105,7 +104,6 @@ struct Waterfall_Image_Cache_Key {
power_range == other.power_range &&
visible_range == other.visible_range &&
background == other.background &&
policy == other.policy &&
color_map_hash == other.color_map_hash &&
width == other.width &&
height == other.height;
@@ -235,33 +233,15 @@ static Waterfall_Image_Snapshot build_waterfall_image_snapshot(const std::shared
struct Waterfall_Private : Typed_Render_Data<Waterfall, Waterfall_Render_State, Waterfall_Input_Data>, Hit_Testable, Hover_Interactive {
static constexpr std::size_t Row_Queue_Capacity = 256;
Waterfall_Ring_Buffer ring_buffer;
Image image;
std::weak_ptr<Color_Bar> color_bar;
std::optional<Waterfall_Data> pending_latest_row;
std::pmr::vector<Waterfall_Data> pending_image_rows{memory_resource(Memory_Domain::Waterfall)};
std::pmr::vector<std::shared_ptr<Update_State>> pending_image_update_states{memory_resource(Memory_Domain::Update_Completion)};
rigtorp::MPMCQueue<std::shared_ptr<Waterfall_Data>> row_queue;
std::atomic_uint64_t dropped_row_count{0};
std::uint64_t last_line_commit_ns{};
std::uint64_t image_color_map_hash{};
Waterfall_Image_Snapshot image_snapshot;
bool has_image_snapshot{};
Waterfall_Image_Cache_Key submitted_image_key;
bool has_submitted_image_key{};
std::optional<Waterfall_Image_Snapshot> image_snapshot;
std::optional<Waterfall_Image_Cache_Key> submitted_image_key;
std::uint64_t pending_latest_deadline_ns{};
Range m_image_frequency_range{};
Range image_power_range{};
Range image_visible_range{};
Color image_background_color{};
Waterfall_Row_Update_Policy image_update_policy = Waterfall_Row_Update_Policy::All_Pending;
int image_start_tick{};
int image_first_col{};
int image_last_col{};
Range image_draw_frequency_range{};
Range image_draw_time_range{};
RectF image_draw_source_rect{};
bool image_valid{};
bool image_draw_visible{};
Waterfall_Private()
: row_queue(Row_Queue_Capacity) {}
bool select_test(const PointF& pos) override {
@@ -273,7 +253,8 @@ struct Waterfall_Private : Typed_Render_Data<Waterfall, Waterfall_Render_State,
edit->hover_info_active = active;
}
~Waterfall_Private() override {
complete_waterfall_result_states(image_snapshot, std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
if (image_snapshot)
complete_waterfall_result_states(*image_snapshot, std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
std::shared_ptr<Waterfall_Data> row;
while (row_queue.try_pop(row)) {
if (row && row->completion_state)
@@ -351,12 +332,7 @@ struct Waterfall_Private : Typed_Render_Data<Waterfall, Waterfall_Render_State,
return false;
}
ring_buffer.push_data(frequency_data.data(), tick);
if (update_policy != Waterfall_Row_Update_Policy::All_Pending) {
Waterfall_Data row;
row.tick = tick;
row.frequency.swap(frequency_data);
pending_image_rows.push_back(std::move(row));
}
(void)update_policy;
return true;
}
void prepare_data(const Plot_Render_Snapshot& snapshot) override {
@@ -428,23 +404,6 @@ struct Waterfall_Private : Typed_Render_Data<Waterfall, Waterfall_Render_State,
}
update_image_item(snapshot, *timeline);
}
bool write_image_row(int tick, const double* row_data, int first_col, int last_col, int start_tick, int row_count, double value_start_coord, double rate, const Color_Map& color_map, Color background_color) {
int tick_offset = tick - start_tick;
if (tick_offset < 0 || tick_offset >= row_count)
return false;
Pixel* line = image.row(tick_offset);
Pixel bg = premultiply(background_color);
for (int col = 0; col < image.width(); ++col)
line[col] = bg;
for (int col = first_col; col <= last_col; ++col) {
int color_offset = static_cast<int>((row_data[col] - value_start_coord) * rate);
line[col] = color_map.at_offset(color_offset);
}
return true;
}
bool image_cache_match(const Waterfall_Render_State* s, int start_tick, int first_col, int last_col, const Range& h_range, const Range& power_range, Color background_color, std::uint64_t color_map_hash) const {
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 == 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();
auto frequency_axis = s->frequency_axis.lock();
@@ -456,7 +415,7 @@ struct Waterfall_Private : Typed_Render_Data<Waterfall, Waterfall_Render_State,
Range h_range = s->frequency_range;
Range v_range{static_cast<double>(timeline.coordinate_begin), static_cast<double>(timeline.coordinate_begin + timeline.visible_time_point_count)};
if (s->visible_range_only && !intersect_range(s->frequency_range, Axis_Render_Access::coord_range(h_axis), h_range)) {
image_draw_visible = false;
image_snapshot.reset();
return;
}
int col_count = s->frequency_bin_count, row_count = timeline.visible_time_point_count;
@@ -481,15 +440,13 @@ struct Waterfall_Private : Typed_Render_Data<Waterfall, Waterfall_Render_State,
color_scale->range,
h_range,
snapshot.background_color,
s->row_update_policy,
color_map_hash,
col_count,
row_count
};
bool should_submit = !has_submitted_image_key || !(submitted_image_key == key) || !pending_image_update_states.empty();
bool should_submit = !submitted_image_key || !(*submitted_image_key == key) || !pending_image_update_states.empty();
if (should_submit) {
submitted_image_key = key;
has_submitted_image_key = true;
auto request = std::make_shared<Waterfall_Image_Request>();
request->key = key;
request->color_map = color_scale->color_map;
@@ -514,44 +471,27 @@ struct Waterfall_Private : Typed_Render_Data<Waterfall, Waterfall_Render_State,
request->update_states = std::move(pending_image_update_states);
pending_image_update_states = std::pmr::vector<std::shared_ptr<Update_State>>(memory_resource(Memory_Domain::Update_Completion));
image_snapshot = build_waterfall_image_snapshot(request);
has_image_snapshot = true;
image = image_snapshot.image;
image_valid = true;
image_start_tick = image_snapshot.key.start_tick;
image_first_col = image_snapshot.key.first_col;
image_last_col = image_snapshot.key.last_col;
m_image_frequency_range = image_snapshot.key.frequency_range;
image_power_range = image_snapshot.key.power_range;
image_visible_range = image_snapshot.key.visible_range;
image_background_color = image_snapshot.key.background;
image_update_policy = image_snapshot.key.policy;
image_color_map_hash = image_snapshot.key.color_map_hash;
image_draw_frequency_range = image_snapshot.frequency_range;
image_draw_time_range = image_snapshot.time_range;
image_draw_source_rect = image_snapshot.source_rect;
image_draw_visible = true;
}
pending_image_rows.clear();
}
void draw_image(Canvas& canvas, Waterfall_Render_State* s, Abs_Axis* h_axis, Time_Axis* v_axis) {
if (!image_draw_visible || image.empty())
if (!image_snapshot || image_snapshot->image.empty())
return;
if (image_draw_frequency_range.length() == 0.0 || image_draw_time_range.length() == 0.0)
if (image_snapshot->frequency_range.length() == 0.0 || image_snapshot->time_range.length() == 0.0)
return;
if (image_draw_source_rect.width == 0.0 || image_draw_source_rect.height == 0.0)
if (image_snapshot->source_rect.width == 0.0 || image_snapshot->source_rect.height == 0.0)
return;
Axis_Mapping_2D mapping = Axis_Render_Access::mapping(h_axis, v_axis);
Axis_Basis_2D basis = Axis_Basis_2D::from_ranges(mapping, image_draw_frequency_range, image_draw_time_range);
Axis_Basis_2D basis = Axis_Basis_2D::from_ranges(mapping, image_snapshot->frequency_range, image_snapshot->time_range);
canvas.save();
canvas.set_image_interpolation(s->interpolation_mode);
canvas.transform(
basis.domain_vector.x / image_draw_source_rect.width,
basis.domain_vector.y / image_draw_source_rect.width,
basis.value_vector.x / image_draw_source_rect.height,
basis.value_vector.y / image_draw_source_rect.height,
basis.domain_vector.x / image_snapshot->source_rect.width,
basis.domain_vector.y / image_snapshot->source_rect.width,
basis.value_vector.x / image_snapshot->source_rect.height,
basis.value_vector.y / image_snapshot->source_rect.height,
basis.origin.x,
basis.origin.y);
canvas.draw_image(RectF{0.0, 0.0, image_draw_source_rect.width, image_draw_source_rect.height}, image, image_draw_source_rect);
canvas.draw_image(RectF{0.0, 0.0, image_snapshot->source_rect.width, image_snapshot->source_rect.height}, image_snapshot->image, image_snapshot->source_rect);
canvas.restore();
}
void draw(Canvas& canvas, const Plot_Render_Snapshot& snapshot) override {
@@ -560,10 +500,10 @@ struct Waterfall_Private : Typed_Render_Data<Waterfall, Waterfall_Render_State,
auto time_axis = s->time_axis.lock();
if (!frequency_axis || !time_axis)
return;
if (snapshot.frame_update_states && has_image_snapshot) {
for (const auto& state : image_snapshot.update_states)
if (snapshot.frame_update_states && image_snapshot) {
for (const auto& state : image_snapshot->update_states)
snapshot.frame_update_states->push_back(state);
image_snapshot.update_states.clear();
image_snapshot->update_states.clear();
}
Abs_Axis* h_axis = frequency_axis.get();
Time_Axis* v_axis = time_axis.get();
+18 -30
View File
@@ -6,6 +6,7 @@
#include <cmath>
#include <memory>
#include <memory_resource>
#include <optional>
#include <rigtorp/MPMCQueue.h>
#include <system_error>
#include "../base/Memory.h"
@@ -137,19 +138,9 @@ static void build_curve_geometry(Curve_Geometry_Result& result, const Curve_Geom
}, memory_resource(Memory_Domain::Curve));
return;
}
Visible_Sample_Window window = compute_visible_sample_window(request.mapping.domain, request.data_range, static_cast<int>(request.samples.size()), static_cast<int>(request.samples.size()), request.visible_range_only);
if (!window)
return;
points.reserve(static_cast<std::size_t>(std::max(0, window.sample_count)));
double denominator = static_cast<double>(std::max(window.sample_count - 1, 1));
for (int i = 0; i < window.sample_count; ++i) {
double index = window.first_index + (window.last_index - window.first_index) * static_cast<double>(i) / denominator;
double coord = source_index_to_coordinate(request.data_range, static_cast<int>(request.samples.size()), index);
double value = interpolate_sample_value(index, static_cast<int>(request.samples.size()), request.interpolation_mode, [&request](int sample_index) {
return request.samples[static_cast<std::size_t>(sample_index)];
});
points.push_back(request.mapping.map(coord, value));
}
points = Curve_Utils::build_resampled_points(request.mapping, request.data_range, static_cast<int>(request.samples.size()), static_cast<int>(request.samples.size()), request.visible_range_only, request.interpolation_mode, [&request](int i) {
return request.samples[static_cast<std::size_t>(i)];
}, memory_resource(Memory_Domain::Curve));
}
struct Curve_Private : Typed_Render_Data<Curve, Curve_Render_State, Curve_Input_Data> {
static constexpr std::size_t Input_Queue_Capacity = 8;
@@ -159,15 +150,14 @@ struct Curve_Private : Typed_Render_Data<Curve, Curve_Render_State, Curve_Input_
rigtorp::MPMCQueue<std::shared_ptr<Curve_Input_Block>> input_queue;
std::atomic_uint64_t dropped_input_count{0};
std::pmr::vector<std::shared_ptr<Update_State>> pending_geometry_update_states{memory_resource(Memory_Domain::Update_Completion)};
Curve_Geometry_Result geometry_result;
bool has_geometry_result{};
Curve_Geometry_Key submitted_geometry_key;
bool has_submitted_geometry_key{};
std::optional<Curve_Geometry_Result> geometry_result;
std::optional<Curve_Geometry_Key> submitted_geometry_key;
std::uint64_t input_generation{};
Curve_Private()
: input_queue(Input_Queue_Capacity) {}
~Curve_Private() override {
complete_curve_result_states(geometry_result, std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
if (geometry_result)
complete_curve_result_states(*geometry_result, std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
std::shared_ptr<Curve_Input_Block> block;
while (input_queue.try_pop(block)) {
if (block && block->completion_state)
@@ -245,10 +235,9 @@ struct Curve_Private : Typed_Render_Data<Curve, Curve_Render_State, Curve_Input_
key.point_count = data_points.size();
key.domain = curve_axis_key(mapping.domain);
key.value = curve_axis_key(mapping.value);
if (has_submitted_geometry_key && key == submitted_geometry_key)
if (submitted_geometry_key && key == *submitted_geometry_key)
return;
submitted_geometry_key = key;
has_submitted_geometry_key = true;
auto request = std::make_shared<Curve_Geometry_Request>();
request->mode = mode;
request->mapping = mapping;
@@ -260,28 +249,27 @@ struct Curve_Private : Typed_Render_Data<Curve, Curve_Render_State, Curve_Input_
request->data_points = data_points;
request->update_states = std::move(pending_geometry_update_states);
pending_geometry_update_states = std::pmr::vector<std::shared_ptr<Update_State>>(memory_resource(Memory_Domain::Update_Completion));
geometry_result = Curve_Geometry_Result();
geometry_result.mapping = request->mapping;
build_curve_geometry(geometry_result, *request);
geometry_result.update_states = std::move(request->update_states);
has_geometry_result = true;
geometry_result.emplace();
geometry_result->mapping = request->mapping;
build_curve_geometry(*geometry_result, *request);
geometry_result->update_states = std::move(request->update_states);
}
void draw(Canvas& canvas, const Plot_Render_Snapshot& snapshot) override {
Curve_Render_State* s = render_state();
if (!has_geometry_result)
if (!geometry_result)
return;
if (snapshot.frame_update_states) {
for (const auto& state : geometry_result.update_states)
for (const auto& state : geometry_result->update_states)
snapshot.frame_update_states->push_back(state);
geometry_result.update_states.clear();
geometry_result->update_states.clear();
}
const auto& points = geometry_result.points;
const auto& points = geometry_result->points;
if (points.empty())
return;
canvas.save();
canvas.set_antialias(s->antialias);
Curve_Utils::draw_polyline(&canvas, points, s->pen);
Curve_Utils::draw_fill_to_value(&canvas, points, geometry_result.mapping, geometry_result.mapping.value.coord_range.target, s->fill_brush);
Curve_Utils::draw_fill_to_value(&canvas, points, geometry_result->mapping, geometry_result->mapping.value.coord_range.target, s->fill_brush);
canvas.restore();
}
};
+6 -10
View File
@@ -32,11 +32,13 @@ public:
}
template <typename Value_At>
static std::pmr::vector<PointF> build_resampled_points(Abs_Axis* domain_axis, Abs_Axis* value_axis, const Range& sample_domain_range, int source_sample_count, int available_sample_count, bool visible_range_only, Line_Interpolation_Mode mode, Value_At value_at) {
return build_interpolated_line_points(domain_axis, value_axis, sample_domain_range, source_sample_count, available_sample_count, visible_range_only, mode, value_at);
(void)mode;
return build_sample_line_points(Axis_Render_Access::mapping(domain_axis, value_axis), sample_domain_range, source_sample_count, available_sample_count, visible_range_only, value_at, frame_memory_resource());
}
template <typename Value_At>
static std::pmr::vector<PointF> build_resampled_points(const Axis_Mapping_2D& mapping, const Range& sample_domain_range, int source_sample_count, int available_sample_count, bool visible_range_only, Line_Interpolation_Mode mode, Value_At value_at, std::pmr::memory_resource* resource = frame_memory_resource()) {
return build_interpolated_line_points(mapping, sample_domain_range, source_sample_count, available_sample_count, visible_range_only, mode, value_at, resource);
(void)mode;
return build_sample_line_points(mapping, sample_domain_range, source_sample_count, available_sample_count, visible_range_only, value_at, resource);
}
template <typename Value_At>
static std::pmr::vector<PointF> build_pixel_envelope_points(const Axis_Mapping_2D& mapping, const Range& sample_domain_range, int source_sample_count, int available_sample_count, bool visible_range_only, Value_At value_at, std::pmr::memory_resource* resource = frame_memory_resource()) {
@@ -87,14 +89,8 @@ public:
std::array<int, 4> indexes{bucket.first_index, bucket.minimum_index, bucket.maximum_index, bucket.last_index};
std::sort(indexes.begin(), indexes.end());
auto end = std::unique(indexes.begin(), indexes.end());
if (pixel_forward) {
for (auto it = indexes.begin(); it != end; ++it)
append_envelope_point(points, mapping, sample_domain_range, source_sample_count, *it, value_at(*it));
}
else {
for (auto it = std::make_reverse_iterator(end); it != indexes.rend(); ++it)
append_envelope_point(points, mapping, sample_domain_range, source_sample_count, *it, value_at(*it));
}
for (auto it = indexes.begin(); it != end; ++it)
append_envelope_point(points, mapping, sample_domain_range, source_sample_count, *it, value_at(*it));
}
return points;
}