修复若干bug
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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};
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,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{};
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user