升级
This commit is contained in:
@@ -66,9 +66,22 @@ void Afterglow_Control::publish() {
|
||||
|
||||
void Afterglow_Control::build_paint_task_graph(Renderable_Task_Graph& graph) {
|
||||
auto& output = impl_->render_frame;
|
||||
const auto view = render_state_view();
|
||||
const auto& state = render_properties(view);
|
||||
const auto& history = view.get(impl_->history);
|
||||
const int width = history.empty()
|
||||
? 0
|
||||
: std::min(state.frequency_point_size.get(),
|
||||
static_cast<int>(history.back().size()));
|
||||
const int height = state.power_point_size.get() > 0
|
||||
? state.power_point_size.get()
|
||||
: std::max(1, static_cast<int>(impl_->power_axis->transform(view).pixel_length));
|
||||
const std::size_t work_size = width > 0 && height > 0
|
||||
? static_cast<std::size_t>(width) * static_cast<std::size_t>(height)
|
||||
: 0;
|
||||
const int partition_count = output.partitioner.graph_partition_count(
|
||||
render_properties(render_state_view()).partition_count.get(),
|
||||
static_cast<int>(Scene_Base::task_executor_worker_count()));
|
||||
state.partition_mode, state.partition_count.get(),
|
||||
static_cast<int>(Scene_Base::task_executor_worker_count()), work_size, 4096);
|
||||
const auto prepare = graph.emplace([this, partition_count](const Scene_Render_Context&) {
|
||||
prepare_render_frame(render_state_view(), partition_count);
|
||||
}, "prepare afterglow");
|
||||
@@ -99,11 +112,15 @@ void Afterglow_Control::build_paint_task_graph(Renderable_Task_Graph& graph) {
|
||||
graph.precede(normalize, task);
|
||||
coloring.push_back(task);
|
||||
}
|
||||
const auto compose = add_paint_task(
|
||||
graph, "compose afterglow",
|
||||
[this](Painter& painter, const Render_State_View& view) { paint(painter, view); });
|
||||
const auto paint_image = add_paint_task(
|
||||
graph, "paint afterglow shared image",
|
||||
[this](Painter& painter, const Render_State_View& frame_view,
|
||||
const Scene_Render_Context& context) {
|
||||
paint_render_frame(painter, frame_view,
|
||||
context.frame_control_state.next_refresh_interval_ns);
|
||||
});
|
||||
for (const auto task : coloring)
|
||||
graph.precede(task, compose);
|
||||
graph.precede(task, paint_image);
|
||||
}
|
||||
|
||||
void Afterglow_Control::prepare_render_frame(const Render_State_View& view,
|
||||
@@ -191,7 +208,9 @@ void Afterglow_Control::color_partition(const Render_State_View& view,
|
||||
}
|
||||
}
|
||||
|
||||
void Afterglow_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
void Afterglow_Control::paint_render_frame(Painter& painter,
|
||||
const Render_State_View& view,
|
||||
std::uint64_t frame_interval_ns) {
|
||||
auto& output = impl_->render_frame;
|
||||
if (!output.valid)
|
||||
return;
|
||||
@@ -199,10 +218,18 @@ void Afterglow_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
output.pixels, Image_Interpolation_Mode::Bilinear);
|
||||
const auto& state = render_properties(view);
|
||||
if (output.partitioner.finish(
|
||||
state.partition_count.get(), output.active_partitions,
|
||||
state.partition_mode, output.active_partitions, frame_interval_ns,
|
||||
static_cast<int>(Scene_Base::task_executor_worker_count()),
|
||||
output.work_size))
|
||||
output.work_size, 4096))
|
||||
task_graph_changed();
|
||||
}
|
||||
|
||||
void Afterglow_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
prepare_render_frame(view, 1);
|
||||
accumulate_partition(view, 0);
|
||||
normalize_render_frame();
|
||||
color_partition(view, 0);
|
||||
paint_render_frame(painter, view, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "Plottable.h"
|
||||
#include "../axis/Axis.h"
|
||||
#include "../renderable/Render_Partition.h"
|
||||
#include <memory_resource>
|
||||
#include <span>
|
||||
namespace renderive {
|
||||
@@ -9,7 +10,8 @@ struct Afterglow_Properties {
|
||||
Range power_range{0.0, 10.0};
|
||||
Nonnegative_Count frequency_point_size;
|
||||
Nonnegative_Count power_point_size;
|
||||
Nonnegative_Count partition_count;
|
||||
Render_Partition_Mode partition_mode{Render_Partition_Mode::Automatic};
|
||||
Positive_Count partition_count;
|
||||
bool interpolate = true;
|
||||
Unit_Interval attenuation_rate{0.2};
|
||||
Color_Map color_map;
|
||||
@@ -38,6 +40,8 @@ private:
|
||||
void accumulate_partition(const Render_State_View& state, int partition_index);
|
||||
void normalize_render_frame();
|
||||
void color_partition(const Render_State_View& state, int partition_index);
|
||||
void paint_render_frame(Painter& painter, const Render_State_View& state,
|
||||
std::uint64_t frame_interval_ns);
|
||||
void publish() override;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
template <auto Member, Property_Member_Assignable<Properties, Member> Value>
|
||||
Plottable_State& set(Value&& value) {
|
||||
Base::template set<Member>(std::forward<Value>(value));
|
||||
if constexpr (requires { &Properties::partition_count; }) {
|
||||
if constexpr (requires { &Properties::partition_count; &Properties::partition_mode; }) {
|
||||
if constexpr (std::same_as<decltype(Member),
|
||||
decltype(&Properties::partition_count)>) {
|
||||
if constexpr (Member == &Properties::partition_count) {
|
||||
@@ -68,6 +68,13 @@ public:
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
if constexpr (std::same_as<decltype(Member),
|
||||
decltype(&Properties::partition_mode)>) {
|
||||
if constexpr (Member == &Properties::partition_mode) {
|
||||
this->task_graph_changed();
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
}
|
||||
this->changed();
|
||||
return *this;
|
||||
|
||||
@@ -24,7 +24,6 @@ struct Spectrum_Interaction {
|
||||
using Spectrum_Interaction_State = Double_State_Strategy<Spectrum_Interaction_Base, Spectrum_Interaction>;
|
||||
struct Spectrum_Render_Frame {
|
||||
Adaptive_Render_Partitioner partitioner;
|
||||
std::vector<Blend2D_Color_Cache> layers;
|
||||
int active_partitions{1};
|
||||
std::size_t work_size{};
|
||||
bool valid{};
|
||||
@@ -244,34 +243,45 @@ void Spectrum_Control::publish() {
|
||||
|
||||
void Spectrum_Control::build_paint_task_graph(Renderable_Task_Graph& graph) {
|
||||
auto& output = impl_->render_frame;
|
||||
const auto view = render_state_view();
|
||||
const auto& state = render_properties(view);
|
||||
const auto& published_frame = view.get(impl_->frame);
|
||||
const std::size_t work_size = published_frame ? published_frame->samples.size() : 0;
|
||||
const int partition_count = output.partitioner.graph_partition_count(
|
||||
render_properties(render_state_view()).partition_count.get(),
|
||||
static_cast<int>(Scene_Base::task_executor_worker_count()));
|
||||
output.layers.resize(static_cast<std::size_t>(partition_count));
|
||||
const auto prepare = graph.emplace([this, partition_count](const Scene_Render_Context&) {
|
||||
prepare_render_frame(render_state_view(), partition_count);
|
||||
}, "prepare spectrum");
|
||||
state.partition_mode, state.partition_count.get(),
|
||||
static_cast<int>(Scene_Base::task_executor_worker_count()), work_size, 128);
|
||||
const auto prepare = add_paint_task(
|
||||
graph, "prepare spectrum and paint background",
|
||||
[this, partition_count](Painter& painter, const Render_State_View& frame_view,
|
||||
const Scene_Render_Context&) {
|
||||
prepare_render_frame(frame_view, partition_count);
|
||||
paint_background(painter, frame_view);
|
||||
});
|
||||
std::vector<Renderable_Task_Graph::Task> partitions;
|
||||
partitions.reserve(static_cast<std::size_t>(partition_count));
|
||||
for (int index = 0; index < partition_count; ++index) {
|
||||
const auto partition = graph.emplace(
|
||||
[this, index](const Scene_Render_Context&) {
|
||||
render_partition(render_state_view(), index);
|
||||
},
|
||||
"paint spectrum partition " + std::to_string(index + 1));
|
||||
const auto partition = add_paint_task(
|
||||
graph, "paint spectrum partition " + std::to_string(index + 1),
|
||||
[this, index](Painter& painter, const Render_State_View& frame_view,
|
||||
const Scene_Render_Context&) {
|
||||
render_partition(painter, frame_view, index);
|
||||
});
|
||||
graph.precede(prepare, partition);
|
||||
partitions.push_back(partition);
|
||||
}
|
||||
const auto compose = add_paint_task(
|
||||
graph, "compose spectrum",
|
||||
[this](Painter& painter, const Render_State_View& view) { paint(painter, view); });
|
||||
const auto overlay = add_paint_task(
|
||||
graph, "paint spectrum overlay",
|
||||
[this](Painter& painter, const Render_State_View& frame_view,
|
||||
const Scene_Render_Context& context) {
|
||||
paint_overlay(painter, frame_view,
|
||||
context.frame_control_state.next_refresh_interval_ns);
|
||||
});
|
||||
for (const auto task : partitions)
|
||||
graph.precede(task, compose);
|
||||
graph.precede(task, overlay);
|
||||
}
|
||||
|
||||
void Spectrum_Control::prepare_render_frame(const Render_State_View& view,
|
||||
int graph_partition_count) {
|
||||
const auto& state = render_properties(view);
|
||||
const auto& published_frame = view.get(impl_->frame);
|
||||
auto& output = impl_->render_frame;
|
||||
const Axis_Transform frequency_axis = impl_->frequency_axis->transform(view);
|
||||
@@ -280,11 +290,9 @@ void Spectrum_Control::prepare_render_frame(const Render_State_View& view,
|
||||
output.work_size = published_frame ? published_frame->samples.size() : 0;
|
||||
output.active_partitions = output.partitioner.begin(graph_partition_count,
|
||||
output.work_size);
|
||||
for (int index = 0; index < output.active_partitions; ++index)
|
||||
output.layers[static_cast<std::size_t>(index)].clear();
|
||||
}
|
||||
|
||||
void Spectrum_Control::render_partition(const Render_State_View& view,
|
||||
void Spectrum_Control::render_partition(Painter& painter, const Render_State_View& view,
|
||||
int partition_index) {
|
||||
auto& output = impl_->render_frame;
|
||||
if (!output.valid || partition_index >= output.active_partitions)
|
||||
@@ -301,9 +309,6 @@ void Spectrum_Control::render_partition(const Render_State_View& view,
|
||||
if (current.values.empty())
|
||||
return;
|
||||
|
||||
Painter painter(output.layers[static_cast<std::size_t>(partition_index)], viewport_size());
|
||||
if (!painter)
|
||||
return;
|
||||
painter.clip(mapped_rect(frequency_axis, power_axis, current.clip_domain,
|
||||
power_axis.coordinate_range));
|
||||
if (state.max_hold_visible) {
|
||||
@@ -325,7 +330,24 @@ void Spectrum_Control::render_partition(const Render_State_View& view,
|
||||
state.current_pen, state.current_brush);
|
||||
}
|
||||
|
||||
void Spectrum_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
void Spectrum_Control::paint_background(Painter& painter, const Render_State_View& view) {
|
||||
const auto& state = render_properties(view);
|
||||
const Axis_Transform frequency_axis = impl_->frequency_axis->transform(view);
|
||||
const Axis_Transform power_axis = impl_->power_axis->transform(view);
|
||||
const RectF content = mapped_rect(frequency_axis, power_axis,
|
||||
frequency_axis.coordinate_range,
|
||||
power_axis.coordinate_range);
|
||||
if (content.empty())
|
||||
return;
|
||||
if(state.sweep_region_visible) {
|
||||
painter.rect(mapped_rect(frequency_axis, power_axis, state.sweep_frequency_range,
|
||||
power_axis.coordinate_range),
|
||||
Pen{.style = Line_Style::None}, state.sweep_region_brush);
|
||||
}
|
||||
}
|
||||
|
||||
void Spectrum_Control::paint_overlay(Painter& painter, const Render_State_View& view,
|
||||
std::uint64_t frame_interval_ns) {
|
||||
const auto& state = render_properties(view);
|
||||
const auto& published_frame = view.get(impl_->frame);
|
||||
const Spectrum_Frame empty_frame;
|
||||
@@ -339,13 +361,6 @@ void Spectrum_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
if (content.empty())
|
||||
return;
|
||||
auto& output = impl_->render_frame;
|
||||
if(state.sweep_region_visible) {
|
||||
painter.rect(mapped_rect(frequency_axis, power_axis, state.sweep_frequency_range,
|
||||
power_axis.coordinate_range),
|
||||
Pen{.style = Line_Style::None}, state.sweep_region_brush);
|
||||
}
|
||||
for (int index = 0; output.valid && index < output.active_partitions; ++index)
|
||||
painter.composite(output.layers[static_cast<std::size_t>(index)]);
|
||||
if(state.middle_frequency_pen.enabled()) {
|
||||
painter.line(mapped_point(frequency_axis, state.center_frequency, power_axis,
|
||||
power_axis.coordinate_range.origin),
|
||||
@@ -388,10 +403,17 @@ void Spectrum_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
}
|
||||
}
|
||||
if (output.partitioner.finish(
|
||||
state.partition_count.get(), output.active_partitions,
|
||||
state.partition_mode, output.active_partitions, frame_interval_ns,
|
||||
static_cast<int>(Scene_Base::task_executor_worker_count()),
|
||||
output.work_size))
|
||||
output.work_size, 128))
|
||||
task_graph_changed();
|
||||
}
|
||||
|
||||
void Spectrum_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
prepare_render_frame(view, 1);
|
||||
paint_background(painter, view);
|
||||
render_partition(painter, view, 0);
|
||||
paint_overlay(painter, view, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
#include "Hover_Tooltip.h"
|
||||
#include "Plottable.h"
|
||||
#include "../axis/Axis.h"
|
||||
#include "../renderable/Render_Partition.h"
|
||||
#include <memory_resource>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
namespace renderive {
|
||||
struct Spectrum_Properties : Hover_Tooltip_Properties {
|
||||
Nonnegative_Count frequency_point_size;
|
||||
Nonnegative_Count partition_count;
|
||||
Render_Partition_Mode partition_mode{Render_Partition_Mode::Automatic};
|
||||
Positive_Count partition_count;
|
||||
Range frequency_range{};
|
||||
double center_frequency = 50.0;
|
||||
Range sweep_frequency_range{40.0, 60.0};
|
||||
@@ -66,7 +68,11 @@ private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
void prepare_render_frame(const Render_State_View& state, int graph_partition_count);
|
||||
void render_partition(const Render_State_View& state, int partition_index);
|
||||
void render_partition(Painter& painter, const Render_State_View& state,
|
||||
int partition_index);
|
||||
void paint_background(Painter& painter, const Render_State_View& state);
|
||||
void paint_overlay(Painter& painter, const Render_State_View& state,
|
||||
std::uint64_t frame_interval_ns);
|
||||
void publish() override;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -98,9 +98,23 @@ void Waterfall_Control::publish() {
|
||||
|
||||
void Waterfall_Control::build_paint_task_graph(Renderable_Task_Graph& graph) {
|
||||
auto& output = impl_->render_frame;
|
||||
const auto view = render_state_view();
|
||||
const auto& state = render_properties(view);
|
||||
const auto& rows = view.get(impl_->rows);
|
||||
std::size_t work_size{};
|
||||
if (!rows.empty()) {
|
||||
const auto shortest = std::min_element(
|
||||
rows.begin(), rows.end(),
|
||||
[](const auto& left, const auto& right) {
|
||||
return left.values.size() < right.values.size();
|
||||
});
|
||||
work_size = static_cast<std::size_t>(std::max(
|
||||
0, std::min(state.frequency_bin_count.get(),
|
||||
static_cast<int>(shortest->values.size())))) * rows.size();
|
||||
}
|
||||
const int partition_count = output.partitioner.graph_partition_count(
|
||||
render_properties(render_state_view()).partition_count.get(),
|
||||
static_cast<int>(Scene_Base::task_executor_worker_count()));
|
||||
state.partition_mode, state.partition_count.get(),
|
||||
static_cast<int>(Scene_Base::task_executor_worker_count()), work_size, 4096);
|
||||
const auto prepare = graph.emplace([this, partition_count](const Scene_Render_Context&) {
|
||||
prepare_render_frame(render_state_view(), partition_count);
|
||||
}, "prepare waterfall");
|
||||
@@ -115,11 +129,15 @@ void Waterfall_Control::build_paint_task_graph(Renderable_Task_Graph& graph) {
|
||||
graph.precede(prepare, partition);
|
||||
partitions.push_back(partition);
|
||||
}
|
||||
const auto compose = add_paint_task(
|
||||
graph, "compose waterfall",
|
||||
[this](Painter& painter, const Render_State_View& view) { paint(painter, view); });
|
||||
const auto paint_image = add_paint_task(
|
||||
graph, "paint waterfall shared image",
|
||||
[this](Painter& painter, const Render_State_View& frame_view,
|
||||
const Scene_Render_Context& context) {
|
||||
paint_render_frame(painter, frame_view,
|
||||
context.frame_control_state.next_refresh_interval_ns);
|
||||
});
|
||||
for (const auto partition : partitions)
|
||||
graph.precede(partition, compose);
|
||||
graph.precede(partition, paint_image);
|
||||
}
|
||||
|
||||
void Waterfall_Control::prepare_render_frame(const Render_State_View& view,
|
||||
@@ -179,7 +197,9 @@ void Waterfall_Control::render_partition(const Render_State_View& view, int part
|
||||
}
|
||||
}
|
||||
|
||||
void Waterfall_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
void Waterfall_Control::paint_render_frame(Painter& painter,
|
||||
const Render_State_View& view,
|
||||
std::uint64_t frame_interval_ns) {
|
||||
const auto& state = render_properties(view);
|
||||
const auto& interaction = view.get(impl_->interaction);
|
||||
auto& output = impl_->render_frame;
|
||||
@@ -188,9 +208,9 @@ void Waterfall_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
painter.heatmap(output.layout.target, output.layout.width, output.layout.height,
|
||||
output.pixels, state.interpolation_mode);
|
||||
if (output.partitioner.finish(
|
||||
state.partition_count.get(), output.active_partitions,
|
||||
state.partition_mode, output.active_partitions, frame_interval_ns,
|
||||
static_cast<int>(Scene_Base::task_executor_worker_count()),
|
||||
output.work_size))
|
||||
output.work_size, 4096))
|
||||
task_graph_changed();
|
||||
if(state.tooltip_enabled && interaction.tooltip.active && output.layout.target.contains(interaction.tooltip.position)) {
|
||||
const Axis_Transform frequency_axis = impl_->frequency_axis->transform(view);
|
||||
@@ -202,5 +222,11 @@ void Waterfall_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
painter.text({box.x + 4.0, box.y + 3.0}, text.str(), state.tooltip_font, state.tooltip_text_pen);
|
||||
}
|
||||
}
|
||||
|
||||
void Waterfall_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
prepare_render_frame(view, 1);
|
||||
render_partition(view, 0);
|
||||
paint_render_frame(painter, view, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "Hover_Tooltip.h"
|
||||
#include "Plottable.h"
|
||||
#include "../axis/Axis.h"
|
||||
#include "../renderable/Render_Partition.h"
|
||||
#include <memory_resource>
|
||||
#include <span>
|
||||
namespace renderive {
|
||||
@@ -9,7 +10,8 @@ struct Waterfall_Properties : Hover_Tooltip_Properties {
|
||||
Range frequency_range{0.0, 10.0};
|
||||
Range power_range{0.0, 10.0};
|
||||
Nonnegative_Count frequency_bin_count;
|
||||
Nonnegative_Count partition_count;
|
||||
Render_Partition_Mode partition_mode{Render_Partition_Mode::Automatic};
|
||||
Positive_Count partition_count;
|
||||
bool visible_range_only{true};
|
||||
Image_Interpolation_Mode interpolation_mode = Image_Interpolation_Mode::Nearest;
|
||||
Color_Map color_map;
|
||||
@@ -42,6 +44,8 @@ private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
void prepare_render_frame(const Render_State_View& state, int graph_partition_count);
|
||||
void paint_render_frame(Painter& painter, const Render_State_View& state,
|
||||
std::uint64_t frame_interval_ns);
|
||||
void render_partition(const Render_State_View& state, int partition_index);
|
||||
void publish() override;
|
||||
};
|
||||
|
||||
@@ -4,8 +4,16 @@
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <cstdint>
|
||||
|
||||
namespace renderive::detail {
|
||||
namespace renderive {
|
||||
|
||||
enum class Render_Partition_Mode : std::uint8_t {
|
||||
Automatic,
|
||||
Fixed
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct Render_Partition_Range {
|
||||
std::size_t first{};
|
||||
@@ -23,14 +31,20 @@ inline Render_Partition_Range render_partition_range(std::size_t size,
|
||||
|
||||
class Adaptive_Render_Partitioner {
|
||||
public:
|
||||
[[nodiscard]] int graph_partition_count(int configured_count,
|
||||
int worker_count) noexcept {
|
||||
if (configured_count > 0)
|
||||
return configured_count;
|
||||
if (automatic_count_ == 0)
|
||||
automatic_count_ = std::max(1, worker_count);
|
||||
automatic_count_ = std::clamp(automatic_count_, 1,
|
||||
std::max(1, worker_count));
|
||||
[[nodiscard]] int graph_partition_count(Render_Partition_Mode mode,
|
||||
int configured_count,
|
||||
int worker_count,
|
||||
std::size_t work_size,
|
||||
std::size_t minimum_partition_size) noexcept {
|
||||
if (mode == Render_Partition_Mode::Fixed)
|
||||
return std::max(1, configured_count);
|
||||
|
||||
const std::size_t granularity = std::max<std::size_t>(1, minimum_partition_size);
|
||||
const int work_limit = static_cast<int>(std::min<std::size_t>(
|
||||
static_cast<std::size_t>(std::numeric_limits<int>::max()),
|
||||
std::max<std::size_t>(1, work_size / granularity)));
|
||||
const int automatic_limit = std::clamp(work_limit, 1, std::max(1, worker_count));
|
||||
automatic_count_ = std::clamp(automatic_count_, 1, automatic_limit);
|
||||
return automatic_count_;
|
||||
}
|
||||
|
||||
@@ -42,29 +56,43 @@ public:
|
||||
return std::clamp(graph_partition_count, 1, useful);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool finish(int configured_count, int active_count,
|
||||
int worker_count, std::size_t work_size) noexcept {
|
||||
if (configured_count != 0)
|
||||
[[nodiscard]] bool finish(Render_Partition_Mode mode, int active_count,
|
||||
std::uint64_t frame_interval_ns, int worker_count,
|
||||
std::size_t work_size,
|
||||
std::size_t minimum_partition_size) noexcept {
|
||||
if (mode != Render_Partition_Mode::Automatic)
|
||||
return false;
|
||||
const auto elapsed =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(Clock::now() - started_at_);
|
||||
constexpr auto target = std::chrono::microseconds(1500);
|
||||
const std::size_t granularity = std::max<std::size_t>(1, minimum_partition_size);
|
||||
const int work_limit = static_cast<int>(std::min<std::size_t>(
|
||||
static_cast<std::size_t>(std::numeric_limits<int>::max()),
|
||||
std::max<std::size_t>(1, work_size / granularity)));
|
||||
const int automatic_limit = std::clamp(work_limit, 1, std::max(1, worker_count));
|
||||
const auto elapsed = Clock::now() - started_at_;
|
||||
const auto elapsed_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed);
|
||||
const auto bottleneck_threshold = frame_interval_ns == 0
|
||||
? std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::microseconds(1500))
|
||||
: std::max(std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::microseconds(750)),
|
||||
std::chrono::nanoseconds(frame_interval_ns / 4));
|
||||
const int previous = automatic_count_;
|
||||
const int maximum = std::max(1, worker_count);
|
||||
if (elapsed > target * 2 && active_count < maximum &&
|
||||
work_size / static_cast<std::size_t>(active_count) >= 512)
|
||||
automatic_count_ = std::min(maximum, active_count + 1);
|
||||
else if (elapsed < target / 2 && active_count > 1)
|
||||
automatic_count_ = active_count - 1;
|
||||
else
|
||||
automatic_count_ = active_count;
|
||||
const auto estimated_serial_cost = elapsed_ns * std::max(1, active_count);
|
||||
if (elapsed_ns > bottleneck_threshold && active_count < automatic_limit) {
|
||||
automatic_count_ = std::min(automatic_limit, active_count * 2);
|
||||
} else if (active_count > 1 &&
|
||||
estimated_serial_cost < bottleneck_threshold * 3 / 5) {
|
||||
automatic_count_ = 1;
|
||||
} else {
|
||||
automatic_count_ = std::clamp(active_count, 1, automatic_limit);
|
||||
}
|
||||
return automatic_count_ != previous;
|
||||
}
|
||||
|
||||
private:
|
||||
using Clock = std::chrono::steady_clock;
|
||||
Clock::time_point started_at_{};
|
||||
int automatic_count_{};
|
||||
int automatic_count_{1};
|
||||
};
|
||||
|
||||
} // namespace renderive::detail
|
||||
} // namespace detail
|
||||
} // namespace renderive
|
||||
|
||||
@@ -60,7 +60,8 @@ void Renderable::build_task_graph(Renderable_Task_Graph& graph) {
|
||||
|
||||
void Renderable::build_paint_task_graph(Renderable_Task_Graph& graph) {
|
||||
add_paint_task(graph, "paint", [this](detail::Painter& painter,
|
||||
const Render_State_View& state) {
|
||||
const Render_State_View& state,
|
||||
const Scene_Render_Context&) {
|
||||
paint(painter, state);
|
||||
});
|
||||
}
|
||||
@@ -78,7 +79,7 @@ Renderable_Task_Graph::Task Renderable::add_paint_task(
|
||||
return;
|
||||
detail::Painter painter(*cache, viewport_size());
|
||||
if (painter)
|
||||
function(painter, render_state_view());
|
||||
function(painter, render_state_view(), context);
|
||||
},
|
||||
std::move(name));
|
||||
}
|
||||
|
||||
@@ -42,7 +42,8 @@ public:
|
||||
|
||||
protected:
|
||||
using Paint_Task_Function =
|
||||
std::function<void(detail::Painter&, const Render_State_View&)>;
|
||||
std::function<void(detail::Painter&, const Render_State_View&,
|
||||
const Scene_Render_Context&)>;
|
||||
|
||||
virtual void paint(detail::Painter& painter, const Render_State_View& state) = 0;
|
||||
virtual void build_paint_task_graph(Renderable_Task_Graph& graph);
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <memory_resource>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
namespace renderive {
|
||||
namespace {
|
||||
@@ -668,12 +670,18 @@ TEST(Renderive_Core2, PartitionedPlotsBuildPropertyDrivenTaskGraphs) {
|
||||
.set_pixel_length(180)
|
||||
.build();
|
||||
const auto spectrum = Spectrum::Builder{}
|
||||
.set<&Spectrum::Properties::partition_mode>(
|
||||
Render_Partition_Mode::Fixed)
|
||||
.set<&Spectrum::Properties::partition_count>(4)
|
||||
.build(root, frequency, power);
|
||||
const auto waterfall = Waterfall::Builder{}
|
||||
.set<&Waterfall::Properties::partition_mode>(
|
||||
Render_Partition_Mode::Fixed)
|
||||
.set<&Waterfall::Properties::partition_count>(4)
|
||||
.build(root, frequency, time);
|
||||
const auto afterglow = Afterglow::Builder{}
|
||||
.set<&Afterglow::Properties::partition_mode>(
|
||||
Render_Partition_Mode::Fixed)
|
||||
.set<&Afterglow::Properties::partition_count>(4)
|
||||
.build(root, frequency, power);
|
||||
|
||||
@@ -692,14 +700,37 @@ TEST(Renderive_Core2, PartitionedPlotsBuildPropertyDrivenTaskGraphs) {
|
||||
EXPECT_EQ(waterfall->task_graph()->nodes().size(), 3u);
|
||||
EXPECT_EQ(afterglow->task_graph()->nodes().size(), 5u);
|
||||
|
||||
spectrum->set<&Spectrum::Properties::partition_count>(0);
|
||||
waterfall->set<&Waterfall::Properties::partition_count>(0);
|
||||
afterglow->set<&Afterglow::Properties::partition_count>(0);
|
||||
spectrum->set<&Spectrum::Properties::partition_mode>(Render_Partition_Mode::Automatic);
|
||||
waterfall->set<&Waterfall::Properties::partition_mode>(Render_Partition_Mode::Automatic);
|
||||
afterglow->set<&Afterglow::Properties::partition_mode>(Render_Partition_Mode::Automatic);
|
||||
spectrum->scene().publish_frame_state();
|
||||
const auto workers = Scene_Base::task_executor_worker_count();
|
||||
EXPECT_EQ(spectrum->task_graph()->nodes().size(), workers + 2u);
|
||||
EXPECT_EQ(waterfall->task_graph()->nodes().size(), workers + 2u);
|
||||
EXPECT_EQ(afterglow->task_graph()->nodes().size(), workers * 2u + 3u);
|
||||
EXPECT_EQ(spectrum->task_graph()->nodes().size(), 3u);
|
||||
EXPECT_EQ(waterfall->task_graph()->nodes().size(), 3u);
|
||||
EXPECT_EQ(afterglow->task_graph()->nodes().size(), 5u);
|
||||
}
|
||||
|
||||
TEST(Renderive_Core2, AutomaticPartitioningSplitsOnlyForMeasuredBottlenecks) {
|
||||
detail::Adaptive_Render_Partitioner partitioner;
|
||||
EXPECT_EQ(partitioner.graph_partition_count(
|
||||
Render_Partition_Mode::Automatic, 8, 8, 4096, 128),
|
||||
1);
|
||||
EXPECT_EQ(partitioner.begin(1, 4096), 1);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2));
|
||||
EXPECT_TRUE(partitioner.finish(Render_Partition_Mode::Automatic, 1, 0,
|
||||
8, 4096, 128));
|
||||
EXPECT_EQ(partitioner.graph_partition_count(
|
||||
Render_Partition_Mode::Automatic, 8, 8, 4096, 128),
|
||||
2);
|
||||
|
||||
EXPECT_EQ(partitioner.begin(2, 4096), 2);
|
||||
EXPECT_TRUE(partitioner.finish(Render_Partition_Mode::Automatic, 2, 0,
|
||||
8, 4096, 128));
|
||||
EXPECT_EQ(partitioner.graph_partition_count(
|
||||
Render_Partition_Mode::Automatic, 8, 8, 4096, 128),
|
||||
1);
|
||||
EXPECT_EQ(partitioner.graph_partition_count(
|
||||
Render_Partition_Mode::Fixed, 7, 2, 1, 4096),
|
||||
7);
|
||||
}
|
||||
TEST(Renderive_Core2, PlottableAxesAreDataDependenciesAndPaintOverlays) {
|
||||
Plot_Core plot;
|
||||
|
||||
@@ -54,10 +54,15 @@ append_glob_source(Renderive_Web_Server_sources "${Renderive_Web_Server_source_d
|
||||
add_executable(Renderive_Web_Server ${Renderive_Web_Server_sources})
|
||||
target_compile_features(Renderive_Web_Server PRIVATE cxx_std_20)
|
||||
target_link_libraries(Renderive_Web_Server PRIVATE Renderive_Web)
|
||||
if (CMAKE_CONFIGURATION_TYPES)
|
||||
set(Renderive_Web_assets_dir "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/webapp_gallery")
|
||||
else ()
|
||||
set(Renderive_Web_assets_dir "${CMAKE_CURRENT_BINARY_DIR}/webapp_gallery")
|
||||
endif ()
|
||||
add_custom_target(Renderive_Web_Assets
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
"${CMAKE_CURRENT_LIST_DIR}/../webapp_gallery"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/webapp_gallery"
|
||||
"${Renderive_Web_assets_dir}"
|
||||
COMMENT "Synchronizing Renderive Web assets"
|
||||
)
|
||||
add_dependencies(Renderive_Web_Server Renderive_Web_Assets)
|
||||
|
||||
@@ -96,7 +96,7 @@ struct Type_Descriptor<renderive::Frame_Observer_Snapshot> {
|
||||
ADMINIVE_FIELD_LABEL(T, task_peak_parallelism, "峰值并行度"),
|
||||
ADMINIVE_FIELD_LABEL(T, task_failed_node_count, "失败节点"),
|
||||
ADMINIVE_FIELD_LABEL(T, task_graph_duration_ns, "任务图耗时"))
|
||||
.label("Kernel Observer");
|
||||
.label("内核观察器");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -114,7 +114,7 @@ struct Type_Descriptor<renderive::web::Gallery_Consumer_Feedback_Snapshot> {
|
||||
ADMINIVE_FIELD_LABEL(T, pixel_interval_ns, "像素响应周期"),
|
||||
ADMINIVE_FIELD_LABEL(T, presentation_interval_ns, "浏览器呈现周期"),
|
||||
ADMINIVE_FIELD_LABEL(T, manual_interval_ns, "手动消费者周期"))
|
||||
.label("Consumer Feedback");
|
||||
.label("消费者反馈");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ adminive::Table_View action_view();
|
||||
namespace adminive {
|
||||
|
||||
template <>
|
||||
struct <renderive::web::gallery_detail::Case_Model> {
|
||||
struct Type_Descriptor<renderive::web::gallery_detail::Case_Model> {
|
||||
static auto get() {
|
||||
using T = renderive::web::gallery_detail::Case_Model;
|
||||
return object<T>("renderive_gallery_case",
|
||||
|
||||
@@ -233,9 +233,9 @@ struct Type_Descriptor<renderive::Range> {
|
||||
static auto get() {
|
||||
using T = renderive::Range;
|
||||
using namespace renderive::web::gallery_adminive;
|
||||
return adminive::object<T>("range", "Range",
|
||||
number<&T::origin>("origin", "Origin"),
|
||||
number<&T::target>("target", "Target"));
|
||||
return adminive::object<T>("range", "范围",
|
||||
number<&T::origin>("origin", "起点"),
|
||||
number<&T::target>("target", "终点"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -244,12 +244,22 @@ struct Type_Descriptor<renderive::Pen> {
|
||||
static auto get() {
|
||||
using T = renderive::Pen;
|
||||
using namespace renderive::web::gallery_adminive;
|
||||
return adminive::object<T>("pen", "Pen",
|
||||
color<&T::color>("color", "Color"),
|
||||
number<&T::width>("width", "Width"),
|
||||
select<&T::style>("style", "Style"),
|
||||
select<&T::cap>("cap", "Cap"),
|
||||
select<&T::join>("join", "Join"));
|
||||
return adminive::object<T>("pen", "画笔",
|
||||
color<&T::color>("color", "颜色"),
|
||||
number<&T::width>("width", "宽度"),
|
||||
select<&T::style>("style", "线型")
|
||||
.enum_label<renderive::Line_Style::None>("无")
|
||||
.enum_label<renderive::Line_Style::Solid>("实线")
|
||||
.enum_label<renderive::Line_Style::Dash>("虚线")
|
||||
.enum_label<renderive::Line_Style::Dot>("点线"),
|
||||
select<&T::cap>("cap", "端点样式")
|
||||
.enum_label<renderive::Line_Cap::Butt>("平头")
|
||||
.enum_label<renderive::Line_Cap::Square>("方头")
|
||||
.enum_label<renderive::Line_Cap::Round>("圆头"),
|
||||
select<&T::join>("join", "连接样式")
|
||||
.enum_label<renderive::Line_Join::Miter>("尖角")
|
||||
.enum_label<renderive::Line_Join::Bevel>("斜角")
|
||||
.enum_label<renderive::Line_Join::Round>("圆角"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -258,9 +268,11 @@ struct Type_Descriptor<renderive::Brush> {
|
||||
static auto get() {
|
||||
using T = renderive::Brush;
|
||||
using namespace renderive::web::gallery_adminive;
|
||||
return adminive::object<T>("brush", "Brush",
|
||||
color<&T::color>("color", "Color"),
|
||||
select<&T::style>("style", "Style"));
|
||||
return adminive::object<T>("brush", "画刷",
|
||||
color<&T::color>("color", "颜色"),
|
||||
select<&T::style>("style", "填充样式")
|
||||
.enum_label<renderive::Brush_Style::None>("无")
|
||||
.enum_label<renderive::Brush_Style::Solid>("纯色"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -269,10 +281,10 @@ struct Type_Descriptor<renderive::Font> {
|
||||
static auto get() {
|
||||
using T = renderive::Font;
|
||||
using namespace renderive::web::gallery_adminive;
|
||||
return adminive::object<T>("font", "Font",
|
||||
number<&T::size>("size", "Size"),
|
||||
number<&T::weight>("weight", "Weight"),
|
||||
boolean<&T::italic>("italic", "Italic"));
|
||||
return adminive::object<T>("font", "字体",
|
||||
number<&T::size>("size", "字号"),
|
||||
number<&T::weight>("weight", "字重"),
|
||||
boolean<&T::italic>("italic", "斜体"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -284,28 +296,32 @@ struct Type_Descriptor<renderive::Font> {
|
||||
using P = renderive::Axis_Properties; \
|
||||
using namespace renderive::web::gallery_adminive; \
|
||||
return adminive::object<T>(Name, Label, \
|
||||
readonly_property<T, &B::x>("x", "X"), \
|
||||
readonly_property<T, &B::y>("y", "Y"), \
|
||||
property_select<T, &B::orientation>("orientation", "Orientation"), \
|
||||
readonly_property<T, &B::pixel_length>("pixel_length", "Pixel length"), \
|
||||
property_number<T, &B::tick_length>("tick_length", "Tick length"), \
|
||||
property_number<T, &B::sub_tick_length>("sub_tick_length", "Sub tick length"), \
|
||||
property_color<T, &B::color>("color", "Color"), \
|
||||
property_select<T, &B::locale>("locale", "Decimal separator"), \
|
||||
property_text<T, &B::unit_text>("unit_text", "Unit text"), \
|
||||
property_object<T, &B::unit_text_font>("unit_text_font", "Unit font"), \
|
||||
property_object<T, &B::unit_text_pen>("unit_text_pen", "Unit pen"), \
|
||||
property_object<T, &B::unit_text_background_brush>("unit_text_background_brush", "Unit background"), \
|
||||
property_number<T, &B::label_rotation_degrees>("label_rotation_degrees", "Label rotation"), \
|
||||
property_object<T, &P::coordinates>("coordinates", "Coordinates"), \
|
||||
property_number<T, &P::precision>("precision", "Precision"), \
|
||||
property_boolean<T, &P::wheel>("wheel", "Wheel zoom"), \
|
||||
property_boolean<T, &P::drag>("drag", "Drag pan")); \
|
||||
readonly_property<T, &B::x>("x", "X 位置"), \
|
||||
readonly_property<T, &B::y>("y", "Y 位置"), \
|
||||
property_select<T, &B::orientation>("orientation", "方向") \
|
||||
.enum_label<renderive::Orientation::Horizontal>("水平") \
|
||||
.enum_label<renderive::Orientation::Vertical>("垂直"), \
|
||||
readonly_property<T, &B::pixel_length>("pixel_length", "像素长度"), \
|
||||
property_number<T, &B::tick_length>("tick_length", "主刻度长度"), \
|
||||
property_number<T, &B::sub_tick_length>("sub_tick_length", "次刻度长度"), \
|
||||
property_color<T, &B::color>("color", "颜色"), \
|
||||
property_select<T, &B::locale>("locale", "小数点符号") \
|
||||
.enum_label<renderive::web::gallery_adminive::Number_Locale_Choice::Point>("点") \
|
||||
.enum_label<renderive::web::gallery_adminive::Number_Locale_Choice::Comma>("逗号"), \
|
||||
property_text<T, &B::unit_text>("unit_text", "单位文本"), \
|
||||
property_object<T, &B::unit_text_font>("unit_text_font", "单位字体"), \
|
||||
property_object<T, &B::unit_text_pen>("unit_text_pen", "单位画笔"), \
|
||||
property_object<T, &B::unit_text_background_brush>("unit_text_background_brush", "单位背景"), \
|
||||
property_number<T, &B::label_rotation_degrees>("label_rotation_degrees", "标签旋转角度"), \
|
||||
property_object<T, &P::coordinates>("coordinates", "坐标范围"), \
|
||||
property_number<T, &P::precision>("precision", "小数位数"), \
|
||||
property_boolean<T, &P::wheel>("wheel", "滚轮缩放"), \
|
||||
property_boolean<T, &P::drag>("drag", "拖拽平移")); \
|
||||
} \
|
||||
}
|
||||
|
||||
RENDERIVE_AXIS_DESCRIPTOR(Gallery_Axis, "axis", "Axis");
|
||||
RENDERIVE_AXIS_DESCRIPTOR(Gallery_Frequency_Axis, "frequency_axis", "Frequency axis");
|
||||
RENDERIVE_AXIS_DESCRIPTOR(Gallery_Axis, "axis", "数值轴");
|
||||
RENDERIVE_AXIS_DESCRIPTOR(Gallery_Frequency_Axis, "frequency_axis", "频率轴");
|
||||
#undef RENDERIVE_AXIS_DESCRIPTOR
|
||||
|
||||
template <>
|
||||
@@ -315,24 +331,28 @@ struct Type_Descriptor<renderive::web::Gallery_Time_Axis> {
|
||||
using B = renderive::Axis_Base_Properties;
|
||||
using P = renderive::Time_Axis_Properties;
|
||||
using namespace renderive::web::gallery_adminive;
|
||||
return adminive::object<T>("time_axis", "Time axis",
|
||||
readonly_property<T, &B::x>("x", "X"),
|
||||
readonly_property<T, &B::y>("y", "Y"),
|
||||
property_select<T, &B::orientation>("orientation", "Orientation"),
|
||||
readonly_property<T, &B::pixel_length>("pixel_length", "Pixel length"),
|
||||
property_number<T, &B::tick_length>("tick_length", "Tick length"),
|
||||
property_number<T, &B::sub_tick_length>("sub_tick_length", "Sub tick length"),
|
||||
property_color<T, &B::color>("color", "Color"),
|
||||
property_select<T, &B::locale>("locale", "Decimal separator"),
|
||||
property_text<T, &B::unit_text>("unit_text", "Unit text"),
|
||||
property_object<T, &B::unit_text_font>("unit_text_font", "Unit font"),
|
||||
property_object<T, &B::unit_text_pen>("unit_text_pen", "Unit pen"),
|
||||
property_object<T, &B::unit_text_background_brush>("unit_text_background_brush", "Unit background"),
|
||||
property_number<T, &B::label_rotation_degrees>("label_rotation_degrees", "Label rotation"),
|
||||
property_number<T, &P::visible_count>("visible_count", "Visible points"),
|
||||
property_number<T, &P::tick_label_spacing_px>("tick_label_spacing_px", "Label spacing"),
|
||||
property_text<T, &P::format>("format", "Time format"),
|
||||
property_boolean<T, &P::newest_at_start>("newest_at_start", "Newest at start"));
|
||||
return adminive::object<T>("time_axis", "时间轴",
|
||||
readonly_property<T, &B::x>("x", "X 位置"),
|
||||
readonly_property<T, &B::y>("y", "Y 位置"),
|
||||
property_select<T, &B::orientation>("orientation", "方向")
|
||||
.enum_label<renderive::Orientation::Horizontal>("水平")
|
||||
.enum_label<renderive::Orientation::Vertical>("垂直"),
|
||||
readonly_property<T, &B::pixel_length>("pixel_length", "像素长度"),
|
||||
property_number<T, &B::tick_length>("tick_length", "主刻度长度"),
|
||||
property_number<T, &B::sub_tick_length>("sub_tick_length", "次刻度长度"),
|
||||
property_color<T, &B::color>("color", "颜色"),
|
||||
property_select<T, &B::locale>("locale", "小数点符号")
|
||||
.enum_label<renderive::web::gallery_adminive::Number_Locale_Choice::Point>("点")
|
||||
.enum_label<renderive::web::gallery_adminive::Number_Locale_Choice::Comma>("逗号"),
|
||||
property_text<T, &B::unit_text>("unit_text", "单位文本"),
|
||||
property_object<T, &B::unit_text_font>("unit_text_font", "单位字体"),
|
||||
property_object<T, &B::unit_text_pen>("unit_text_pen", "单位画笔"),
|
||||
property_object<T, &B::unit_text_background_brush>("unit_text_background_brush", "单位背景"),
|
||||
property_number<T, &B::label_rotation_degrees>("label_rotation_degrees", "标签旋转角度"),
|
||||
property_number<T, &P::visible_count>("visible_count", "可见点数"),
|
||||
property_number<T, &P::tick_label_spacing_px>("tick_label_spacing_px", "标签间距"),
|
||||
property_text<T, &P::format>("format", "时间格式"),
|
||||
property_boolean<T, &P::newest_at_start>("newest_at_start", "最新数据在起点"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -346,86 +366,122 @@ struct Type_Descriptor<renderive::web::Gallery_Time_Axis> {
|
||||
} \
|
||||
}
|
||||
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Spectrum, Spectrum_Properties, "spectrum", "Spectrum",
|
||||
property_number<T, &P::frequency_point_size>("frequency_point_size", "Frequency points"),
|
||||
property_number<T, &P::partition_count>("partition_count", "Render partitions")
|
||||
.description("0 = automatic, 1 = single task, N = N parallel partitions"),
|
||||
property_object<T, &P::frequency_range>("frequency_range", "Frequency range"),
|
||||
property_number<T, &P::center_frequency>("center_frequency", "Center frequency"),
|
||||
property_object<T, &P::sweep_frequency_range>("sweep_frequency_range", "Sweep range"),
|
||||
property_boolean<T, &P::max_hold_visible>("max_hold_visible", "Max hold"),
|
||||
property_boolean<T, &P::min_hold_visible>("min_hold_visible", "Min hold"),
|
||||
property_boolean<T, &P::max_marker_visible>("max_marker_visible", "Max marker"),
|
||||
property_boolean<T, &P::use_min_marker>("use_min_marker", "Use min marker"),
|
||||
property_boolean<T, &P::sweep_region_visible>("sweep_region_visible", "Sweep region"),
|
||||
property_boolean<T, &P::visible_range_only>("visible_range_only", "Visible range only"),
|
||||
property_select<T, &P::interpolation_mode>("interpolation_mode", "Interpolation"),
|
||||
property_object<T, &P::max_brush>("max_brush", "Max brush"),
|
||||
property_object<T, &P::current_brush>("current_brush", "Current brush"),
|
||||
property_object<T, &P::min_brush>("min_brush", "Min brush"),
|
||||
property_object<T, &P::max_pen>("max_pen", "Max pen"),
|
||||
property_object<T, &P::current_pen>("current_pen", "Current pen"),
|
||||
property_object<T, &P::min_pen>("min_pen", "Min pen"),
|
||||
property_object<T, &P::selected_marker_pen>("selected_marker_pen", "Selected marker pen"),
|
||||
property_object<T, &P::marker_pen>("marker_pen", "Marker pen"),
|
||||
property_object<T, &P::middle_frequency_pen>("middle_frequency_pen", "Middle frequency pen"),
|
||||
property_object<T, &P::sweep_region_brush>("sweep_region_brush", "Sweep region brush"),
|
||||
property_boolean<T, &P::tooltip_enabled>("tooltip_enabled", "Tooltip"),
|
||||
property_object<T, &P::tooltip_font>("tooltip_font", "Tooltip font"),
|
||||
property_object<T, &P::tooltip_text_pen>("tooltip_text_pen", "Tooltip text pen"),
|
||||
property_object<T, &P::tooltip_background_brush>("tooltip_background_brush", "Tooltip background"));
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Spectrum, Spectrum_Properties, "spectrum", "实时频谱",
|
||||
property_number<T, &P::frequency_point_size>("frequency_point_size", "频率点数"),
|
||||
property_select<T, &P::partition_mode>("partition_mode", "任务分区模式")
|
||||
.enum_label<renderive::Render_Partition_Mode::Automatic>("自动瓶颈优化")
|
||||
.enum_label<renderive::Render_Partition_Mode::Fixed>("固定分区"),
|
||||
property_number<T, &P::partition_count>("partition_count", "固定分区数")
|
||||
.description("固定模式下的并行任务数;1 表示不分区")
|
||||
.visible_on("${$self.partition_mode == 'Fixed'}"),
|
||||
property_object<T, &P::frequency_range>("frequency_range", "频率范围"),
|
||||
property_number<T, &P::center_frequency>("center_frequency", "中心频率"),
|
||||
property_object<T, &P::sweep_frequency_range>("sweep_frequency_range", "扫频范围"),
|
||||
property_boolean<T, &P::max_hold_visible>("max_hold_visible", "显示最大保持"),
|
||||
property_boolean<T, &P::min_hold_visible>("min_hold_visible", "显示最小保持"),
|
||||
property_boolean<T, &P::max_marker_visible>("max_marker_visible", "显示最大值标记"),
|
||||
property_boolean<T, &P::use_min_marker>("use_min_marker", "显示最小值标记"),
|
||||
property_boolean<T, &P::sweep_region_visible>("sweep_region_visible", "显示扫频区域"),
|
||||
property_boolean<T, &P::visible_range_only>("visible_range_only", "仅绘制可见范围"),
|
||||
property_select<T, &P::interpolation_mode>("interpolation_mode", "曲线插值")
|
||||
.enum_label<renderive::Line_Interpolation_Mode::Nearest_Sample>("最近样本")
|
||||
.enum_label<renderive::Line_Interpolation_Mode::Linear_Value>("数值线性")
|
||||
.enum_label<renderive::Line_Interpolation_Mode::Linear_Power_Domain>("功率域线性")
|
||||
.enum_label<renderive::Line_Interpolation_Mode::Step_Left>("左阶梯")
|
||||
.enum_label<renderive::Line_Interpolation_Mode::Step_Right>("右阶梯")
|
||||
.enum_label<renderive::Line_Interpolation_Mode::Cubic_Value>("三次插值"),
|
||||
property_object<T, &P::max_brush>("max_brush", "最大保持画刷"),
|
||||
property_object<T, &P::current_brush>("current_brush", "当前曲线画刷"),
|
||||
property_object<T, &P::min_brush>("min_brush", "最小保持画刷"),
|
||||
property_object<T, &P::max_pen>("max_pen", "最大保持画笔"),
|
||||
property_object<T, &P::current_pen>("current_pen", "当前曲线画笔"),
|
||||
property_object<T, &P::min_pen>("min_pen", "最小保持画笔"),
|
||||
property_object<T, &P::selected_marker_pen>("selected_marker_pen", "选中标记画笔"),
|
||||
property_object<T, &P::marker_pen>("marker_pen", "标记画笔"),
|
||||
property_object<T, &P::middle_frequency_pen>("middle_frequency_pen", "中心频率画笔"),
|
||||
property_object<T, &P::sweep_region_brush>("sweep_region_brush", "扫频区域画刷"),
|
||||
property_boolean<T, &P::tooltip_enabled>("tooltip_enabled", "启用提示框"),
|
||||
property_object<T, &P::tooltip_font>("tooltip_font", "提示框字体"),
|
||||
property_object<T, &P::tooltip_text_pen>("tooltip_text_pen", "提示框文字画笔"),
|
||||
property_object<T, &P::tooltip_background_brush>("tooltip_background_brush", "提示框背景"));
|
||||
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Waterfall, Waterfall_Properties, "waterfall", "Waterfall",
|
||||
property_object<T, &P::frequency_range>("frequency_range", "Frequency range"),
|
||||
property_object<T, &P::power_range>("power_range", "Power range"),
|
||||
property_number<T, &P::frequency_bin_count>("frequency_bin_count", "Frequency bins"),
|
||||
property_number<T, &P::partition_count>("partition_count", "Render partitions")
|
||||
.description("0 = automatic, 1 = single task, N = N parallel partitions"),
|
||||
property_boolean<T, &P::visible_range_only>("visible_range_only", "Visible range only"),
|
||||
property_select<T, &P::interpolation_mode>("interpolation_mode", "Interpolation"),
|
||||
property_select<T, &P::color_map>("color_map", "Color map"),
|
||||
property_boolean<T, &P::tooltip_enabled>("tooltip_enabled", "Tooltip"),
|
||||
property_object<T, &P::tooltip_font>("tooltip_font", "Tooltip font"),
|
||||
property_object<T, &P::tooltip_text_pen>("tooltip_text_pen", "Tooltip text pen"),
|
||||
property_object<T, &P::tooltip_background_brush>("tooltip_background_brush", "Tooltip background"));
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Waterfall, Waterfall_Properties, "waterfall", "频谱瀑布",
|
||||
property_object<T, &P::frequency_range>("frequency_range", "频率范围"),
|
||||
property_object<T, &P::power_range>("power_range", "功率范围"),
|
||||
property_number<T, &P::frequency_bin_count>("frequency_bin_count", "频率格数"),
|
||||
property_select<T, &P::partition_mode>("partition_mode", "任务分区模式")
|
||||
.enum_label<renderive::Render_Partition_Mode::Automatic>("自动瓶颈优化")
|
||||
.enum_label<renderive::Render_Partition_Mode::Fixed>("固定分区"),
|
||||
property_number<T, &P::partition_count>("partition_count", "固定分区数")
|
||||
.description("固定模式下的并行任务数;1 表示不分区")
|
||||
.visible_on("${$self.partition_mode == 'Fixed'}"),
|
||||
property_boolean<T, &P::visible_range_only>("visible_range_only", "仅绘制可见范围"),
|
||||
property_select<T, &P::interpolation_mode>("interpolation_mode", "图像插值")
|
||||
.enum_label<renderive::Image_Interpolation_Mode::Nearest>("最近邻")
|
||||
.enum_label<renderive::Image_Interpolation_Mode::Bilinear>("双线性")
|
||||
.enum_label<renderive::Image_Interpolation_Mode::Bicubic>("双三次"),
|
||||
property_select<T, &P::color_map>("color_map", "色图")
|
||||
.enum_label<renderive::web::gallery_adminive::Color_Map_Choice::Spectrum>("频谱")
|
||||
.enum_label<renderive::web::gallery_adminive::Color_Map_Choice::Ember>("火焰")
|
||||
.enum_label<renderive::web::gallery_adminive::Color_Map_Choice::Grayscale>("灰度"),
|
||||
property_boolean<T, &P::tooltip_enabled>("tooltip_enabled", "启用提示框"),
|
||||
property_object<T, &P::tooltip_font>("tooltip_font", "提示框字体"),
|
||||
property_object<T, &P::tooltip_text_pen>("tooltip_text_pen", "提示框文字画笔"),
|
||||
property_object<T, &P::tooltip_background_brush>("tooltip_background_brush", "提示框背景"));
|
||||
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Afterglow, Afterglow_Properties, "afterglow", "Afterglow",
|
||||
property_object<T, &P::frequency_range>("frequency_range", "Frequency range"),
|
||||
property_object<T, &P::power_range>("power_range", "Power range"),
|
||||
property_number<T, &P::frequency_point_size>("frequency_point_size", "Frequency points"),
|
||||
property_number<T, &P::power_point_size>("power_point_size", "Power points"),
|
||||
property_number<T, &P::partition_count>("partition_count", "Render partitions")
|
||||
.description("0 = automatic, 1 = single task, N = N parallel partitions"),
|
||||
property_boolean<T, &P::interpolate>("interpolate", "Interpolate power"),
|
||||
property_number<T, &P::attenuation_rate>("attenuation_rate", "Attenuation"),
|
||||
property_select<T, &P::color_map>("color_map", "Color map"));
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Afterglow, Afterglow_Properties, "afterglow", "余辉频谱",
|
||||
property_object<T, &P::frequency_range>("frequency_range", "频率范围"),
|
||||
property_object<T, &P::power_range>("power_range", "功率范围"),
|
||||
property_number<T, &P::frequency_point_size>("frequency_point_size", "频率点数"),
|
||||
property_number<T, &P::power_point_size>("power_point_size", "功率点数"),
|
||||
property_select<T, &P::partition_mode>("partition_mode", "任务分区模式")
|
||||
.enum_label<renderive::Render_Partition_Mode::Automatic>("自动瓶颈优化")
|
||||
.enum_label<renderive::Render_Partition_Mode::Fixed>("固定分区"),
|
||||
property_number<T, &P::partition_count>("partition_count", "固定分区数")
|
||||
.description("固定模式下的并行任务数;1 表示不分区")
|
||||
.visible_on("${$self.partition_mode == 'Fixed'}"),
|
||||
property_boolean<T, &P::interpolate>("interpolate", "插值功率点"),
|
||||
property_number<T, &P::attenuation_rate>("attenuation_rate", "衰减率"),
|
||||
property_select<T, &P::color_map>("color_map", "色图")
|
||||
.enum_label<renderive::web::gallery_adminive::Color_Map_Choice::Spectrum>("频谱")
|
||||
.enum_label<renderive::web::gallery_adminive::Color_Map_Choice::Ember>("火焰")
|
||||
.enum_label<renderive::web::gallery_adminive::Color_Map_Choice::Grayscale>("灰度"));
|
||||
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Sweep_Spectrum, Sweep_Spectrum_Properties, "sweep_spectrum", "Sweep spectrum",
|
||||
property_object<T, &P::frequency_range>("frequency_range", "Frequency range"),
|
||||
property_number<T, &P::bins_per_block>("bins_per_block", "Bins per block"),
|
||||
property_number<T, &P::block_count>("block_count", "Block count"),
|
||||
property_object<T, &P::pen>("pen", "Sweep pen"),
|
||||
property_object<T, &P::current_frequency_pen>("current_frequency_pen", "Current frequency pen"),
|
||||
property_boolean<T, &P::visible_range_only>("visible_range_only", "Visible range only"),
|
||||
property_select<T, &P::interpolation_mode>("interpolation_mode", "Interpolation"));
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Sweep_Spectrum, Sweep_Spectrum_Properties, "sweep_spectrum", "扫频频谱",
|
||||
property_object<T, &P::frequency_range>("frequency_range", "频率范围"),
|
||||
property_number<T, &P::bins_per_block>("bins_per_block", "每块频率格数"),
|
||||
property_number<T, &P::block_count>("block_count", "扫频块数"),
|
||||
property_object<T, &P::pen>("pen", "扫频曲线画笔"),
|
||||
property_object<T, &P::current_frequency_pen>("current_frequency_pen", "当前频率画笔"),
|
||||
property_boolean<T, &P::visible_range_only>("visible_range_only", "仅绘制可见范围"),
|
||||
property_select<T, &P::interpolation_mode>("interpolation_mode", "曲线插值")
|
||||
.enum_label<renderive::Line_Interpolation_Mode::Nearest_Sample>("最近样本")
|
||||
.enum_label<renderive::Line_Interpolation_Mode::Linear_Value>("数值线性")
|
||||
.enum_label<renderive::Line_Interpolation_Mode::Linear_Power_Domain>("功率域线性")
|
||||
.enum_label<renderive::Line_Interpolation_Mode::Step_Left>("左阶梯")
|
||||
.enum_label<renderive::Line_Interpolation_Mode::Step_Right>("右阶梯")
|
||||
.enum_label<renderive::Line_Interpolation_Mode::Cubic_Value>("三次插值"));
|
||||
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Frequency_Trace, Frequency_Trace_Properties, "frequency_trace", "Frequency trace",
|
||||
property_object<T, &P::pen>("pen", "Trace pen"));
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Frequency_Trace, Frequency_Trace_Properties, "frequency_trace", "频率轨迹",
|
||||
property_object<T, &P::pen>("pen", "轨迹画笔"));
|
||||
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Selection_Rectangle_Overlay, Selection_Rectangle_Overlay_Properties, "selection_overlay", "Selection overlay",
|
||||
property_object<T, &P::label_font>("label_font", "Label font"),
|
||||
property_object<T, &P::label_pen>("label_pen", "Label pen"),
|
||||
property_object<T, &P::selection_brush>("selection_brush", "Selection brush"),
|
||||
property_object<T, &P::selection_border_pen>("selection_border_pen", "Selection border"));
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Selection_Rectangle_Overlay, Selection_Rectangle_Overlay_Properties, "selection_overlay", "框选区域",
|
||||
property_object<T, &P::label_font>("label_font", "标签字体"),
|
||||
property_object<T, &P::label_pen>("label_pen", "标签画笔"),
|
||||
property_object<T, &P::selection_brush>("selection_brush", "选区画刷"),
|
||||
property_object<T, &P::selection_border_pen>("selection_border_pen", "选区边框画笔"));
|
||||
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Constellation_Diagram, Constellation_Diagram_Properties, "constellation", "Constellation diagram",
|
||||
property_object<T, &P::i_range>("i_range", "I range"),
|
||||
property_object<T, &P::q_range>("q_range", "Q range"),
|
||||
property_color<T, &P::point_color>("point_color", "Point color"),
|
||||
property_color<T, &P::anchor_color>("anchor_color", "Anchor color"),
|
||||
property_number<T, &P::point_lifetime_ms>("point_lifetime_ms", "Point lifetime"),
|
||||
property_select<T, &P::type>("type", "Constellation"),
|
||||
property_number<T, &P::phase_offset_radians>("phase_offset_radians", "Phase offset"));
|
||||
RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Constellation_Diagram, Constellation_Diagram_Properties, "constellation", "星座图",
|
||||
property_object<T, &P::i_range>("i_range", "I 轴范围"),
|
||||
property_object<T, &P::q_range>("q_range", "Q 轴范围"),
|
||||
property_color<T, &P::point_color>("point_color", "点颜色"),
|
||||
property_color<T, &P::anchor_color>("anchor_color", "锚点颜色"),
|
||||
property_number<T, &P::point_lifetime_ms>("point_lifetime_ms", "点保留时间"),
|
||||
property_select<T, &P::type>("type", "调制类型")
|
||||
.enum_label<renderive::Constellation_Diagram_Type::Psk4>("4 PSK")
|
||||
.enum_label<renderive::Constellation_Diagram_Type::Psk8>("8 PSK")
|
||||
.enum_label<renderive::Constellation_Diagram_Type::Psk16>("16 PSK"),
|
||||
property_number<T, &P::phase_offset_radians>("phase_offset_radians", "相位偏移"));
|
||||
|
||||
#undef RENDERIVE_GALLERY_DESCRIPTOR
|
||||
|
||||
@@ -437,7 +493,7 @@ constexpr auto axis_metadata() {
|
||||
return structive::type_metadata(
|
||||
gallery_action("axis_probe", "读取坐标映射与刻度",
|
||||
"coord_to_pixel / pixel_to_coord / tick_step / sub_tick_count / tick_label",
|
||||
"Axis"));
|
||||
"坐标轴"));
|
||||
}
|
||||
|
||||
} // namespace renderive::web::gallery_action_registry
|
||||
@@ -477,61 +533,61 @@ struct Type_Descriptor<renderive::web::Gallery_Time_Axis> {
|
||||
return object<T>(type_metadata(
|
||||
gallery_action("axis_probe", "读取坐标映射与刻度",
|
||||
"coord_to_pixel / pixel_to_coord / tick_step / sub_tick_count / tick_label",
|
||||
"Axis"),
|
||||
"坐标轴"),
|
||||
gallery_action("append_time", "追加时间点",
|
||||
"Time_Axis::append_time / tick_to_time", "Time_Axis")));
|
||||
"Time_Axis::append_time / tick_to_time", "时间轴")));
|
||||
}
|
||||
};
|
||||
|
||||
RENDERIVE_TYPE_ACTIONS(Gallery_Spectrum,
|
||||
gallery_action("push_samples", "推送一帧样本", "Spectrum::update_samples", "Spectrum"),
|
||||
gallery_action("power_at", "查询指定频率功率", "Spectrum::power_at", "Spectrum",
|
||||
gallery_action("push_samples", "推送一帧样本", "Spectrum::update_samples", "频谱"),
|
||||
gallery_action("power_at", "查询指定频率功率", "Spectrum::power_at", "频谱",
|
||||
{}, "number", "频率", 98e6),
|
||||
gallery_action("add_marker", "添加点 Marker", "Spectrum::add_custom_marker", "Marker",
|
||||
gallery_action("add_marker", "添加点标记", "Spectrum::add_custom_marker", "标记",
|
||||
{}, "number", "频率", 96e6),
|
||||
gallery_action("add_line_marker", "添加线 Marker", "Spectrum::add_custom_line_marker", "Marker",
|
||||
gallery_action("add_line_marker", "添加线标记", "Spectrum::add_custom_line_marker", "标记",
|
||||
{}, "number", "频率", 100e6),
|
||||
gallery_action("remove_marker", "按频率删除 Marker", "Spectrum::remove_custom_marker", "Marker",
|
||||
gallery_action("remove_marker", "按频率删除标记", "Spectrum::remove_custom_marker", "标记",
|
||||
{}, "number", "频率", 96e6),
|
||||
gallery_action("remove_selected_marker", "删除选中 Marker",
|
||||
"Spectrum::remove_selected_marker", "Marker"),
|
||||
gallery_action("clear_markers", "清空 Marker", "Spectrum::clear_custom_markers", "Marker"),
|
||||
gallery_action("select_marker", "选择 Marker 索引", "Spectrum::set_selected_marker_index", "Marker",
|
||||
gallery_action("remove_selected_marker", "删除选中标记",
|
||||
"Spectrum::remove_selected_marker", "标记"),
|
||||
gallery_action("clear_markers", "清空标记", "Spectrum::clear_custom_markers", "标记"),
|
||||
gallery_action("select_marker", "选择标记索引", "Spectrum::set_selected_marker_index", "标记",
|
||||
{}, "number", "索引", 0.0),
|
||||
gallery_action("select_next_marker", "选择下一个 Marker", "Spectrum::select_next_marker", "Marker"),
|
||||
gallery_action("select_previous_marker", "选择上一个 Marker", "Spectrum::select_previous_marker", "Marker"),
|
||||
gallery_action("clear_marker_selection", "清除 Marker 选择",
|
||||
"Spectrum::clear_marker_selection", "Marker"),
|
||||
gallery_action("set_marker_frequency", "修改选中 Marker 频率",
|
||||
"Spectrum::set_marker_frequency / set_current_marker_frequency", "Marker",
|
||||
gallery_action("select_next_marker", "选择下一个标记", "Spectrum::select_next_marker", "标记"),
|
||||
gallery_action("select_previous_marker", "选择上一个标记", "Spectrum::select_previous_marker", "标记"),
|
||||
gallery_action("clear_marker_selection", "清除标记选择",
|
||||
"Spectrum::clear_marker_selection", "标记"),
|
||||
gallery_action("set_marker_frequency", "修改选中标记频率",
|
||||
"Spectrum::set_marker_frequency / set_current_marker_frequency", "标记",
|
||||
{}, "number", "频率", 99e6));
|
||||
|
||||
RENDERIVE_TYPE_ACTIONS(Gallery_Waterfall,
|
||||
gallery_action("append_row", "追加一行", "Waterfall::append_row", "Waterfall"),
|
||||
gallery_action("append_row", "追加一行", "Waterfall::append_row", "频谱瀑布"),
|
||||
gallery_action("append_tick_row", "按 tick 追加一行",
|
||||
"Time_Axis::append_time / Waterfall::append_row(int, span)", "Waterfall"));
|
||||
"Time_Axis::append_time / Waterfall::append_row(int, span)", "频谱瀑布"));
|
||||
|
||||
RENDERIVE_TYPE_ACTIONS(Gallery_Afterglow,
|
||||
gallery_action("append_spectrum", "追加一帧频谱", "Afterglow::append_spectrum", "Afterglow"));
|
||||
gallery_action("append_spectrum", "追加一帧频谱", "Afterglow::append_spectrum", "余辉频谱"));
|
||||
|
||||
RENDERIVE_TYPE_ACTIONS(Gallery_Sweep_Spectrum,
|
||||
gallery_action("append_block", "追加一个扫频块", "Sweep_Spectrum::append_block", "Sweep Spectrum"));
|
||||
gallery_action("append_block", "追加一个扫频块", "Sweep_Spectrum::append_block", "扫频频谱"));
|
||||
|
||||
RENDERIVE_TYPE_ACTIONS(Gallery_Frequency_Trace,
|
||||
gallery_action("append_sample", "追加一个样本", "Frequency_Trace::append_sample", "Frequency Trace"),
|
||||
gallery_action("append_sample", "追加一个样本", "Frequency_Trace::append_sample", "频率轨迹"),
|
||||
gallery_action("append_tick_sample", "按 tick 追加样本",
|
||||
"Time_Axis::append_time / Frequency_Trace::append_sample(int, double)",
|
||||
"Frequency Trace"));
|
||||
"频率轨迹"));
|
||||
|
||||
RENDERIVE_TYPE_ACTIONS(Gallery_Selection_Rectangle_Overlay,
|
||||
gallery_action("clear_selection", "清空框选区域",
|
||||
"Selection_Rectangle_Overlay::clear_selected_regions", "Selection Overlay"));
|
||||
"Selection_Rectangle_Overlay::clear_selected_regions", "框选区域"));
|
||||
|
||||
RENDERIVE_TYPE_ACTIONS(Gallery_Constellation_Diagram,
|
||||
gallery_action("append_points", "追加一组 IQ 点",
|
||||
"Constellation_Diagram::append_point", "Constellation"),
|
||||
"Constellation_Diagram::append_point", "星座图"),
|
||||
gallery_action("fit_square", "按轴拟合正方形",
|
||||
"Constellation_Diagram::fit_square_to_axes", "Constellation"));
|
||||
"Constellation_Diagram::fit_square_to_axes", "星座图"));
|
||||
|
||||
#undef RENDERIVE_TYPE_ACTIONS
|
||||
|
||||
|
||||
@@ -29,12 +29,12 @@ struct Type_Descriptor<renderive::web::Gallery_Feedback_Policy> {
|
||||
static auto get() {
|
||||
using T = renderive::web::Gallery_Feedback_Policy;
|
||||
using namespace renderive::web::gallery_adminive;
|
||||
return adminive::object<T>("feedback_policy", "Consumer feedback",
|
||||
boolean<&T::enabled>("enabled", "Enabled"),
|
||||
boolean<&T::pixel>("pixel", "Pixel feedback"),
|
||||
boolean<&T::presentation>("presentation", "Presentation feedback"),
|
||||
boolean<&T::manual>("manual", "Manual feedback"),
|
||||
number<&T::manual_fps>("manual_fps", "Manual FPS"));
|
||||
return adminive::object<T>("feedback_policy", "消费者反馈",
|
||||
boolean<&T::enabled>("enabled", "启用反馈"),
|
||||
boolean<&T::pixel>("pixel", "像素响应反馈"),
|
||||
boolean<&T::presentation>("presentation", "浏览器呈现反馈"),
|
||||
boolean<&T::manual>("manual", "手动反馈"),
|
||||
number<&T::manual_fps>("manual_fps", "手动反馈帧率"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -44,16 +44,16 @@ struct Type_Descriptor<renderive::web::Gallery_Session_Control> {
|
||||
using T = renderive::web::Gallery_Session_Control;
|
||||
using namespace renderive::web::gallery_adminive;
|
||||
const auto dirty = [](T& value) { value.plot_.notify_model_dirty(); };
|
||||
return adminive::object<T>("scene", "Scene",
|
||||
return adminive::object<T>("scene", "场景",
|
||||
callback_property<T, renderive::Color>(
|
||||
"background_color", "Background", Field_Control::color,
|
||||
"background_color", "背景颜色", Field_Control::color,
|
||||
[](const T& value) { return value.plot_.background_color(); },
|
||||
[dirty](T& value, renderive::Color color) {
|
||||
value.plot_.set_background_color(color);
|
||||
dirty(value);
|
||||
}),
|
||||
callback_property<T, bool>(
|
||||
"performance_overlay", "Performance overlay", Field_Control::boolean,
|
||||
"performance_overlay", "性能叠加层", Field_Control::boolean,
|
||||
[](const T& value) {
|
||||
const auto overlay = value.plot_.performance_overlay();
|
||||
return overlay && overlay->enabled();
|
||||
@@ -63,28 +63,30 @@ struct Type_Descriptor<renderive::web::Gallery_Session_Control> {
|
||||
dirty(value);
|
||||
}),
|
||||
callback_property<T, bool>(
|
||||
"renderable_visible", "Renderable visible", Field_Control::boolean,
|
||||
"renderable_visible", "显示渲染元素", Field_Control::boolean,
|
||||
[](const T& value) { return value.renderable_.is_visible(); },
|
||||
[dirty](T& value, bool visible) {
|
||||
value.renderable_.set_visible(visible);
|
||||
dirty(value);
|
||||
}),
|
||||
callback_property<T, renderive::Renderable_Cache_Mode>(
|
||||
"cache_mode", "Cache mode", Field_Control::select,
|
||||
"cache_mode", "缓存模式", Field_Control::select,
|
||||
[](const T& value) { return value.renderable_.get_cache_mode(); },
|
||||
[dirty](T& value, renderive::Renderable_Cache_Mode mode) {
|
||||
value.renderable_.set_cache_mode(mode);
|
||||
dirty(value);
|
||||
}),
|
||||
})
|
||||
.enum_label<renderive::Renderable_Cache_Mode::Direct>("直接绘制")
|
||||
.enum_label<renderive::Renderable_Cache_Mode::Local_Pixel>("本地像素缓存"),
|
||||
callback_property<T, std::string>(
|
||||
"object_name", "Object name", Field_Control::text,
|
||||
"object_name", "对象名称", Field_Control::text,
|
||||
[](const T& value) { return value.renderable_.object_name(); },
|
||||
[dirty](T& value, std::string name) {
|
||||
value.renderable_.set_object_name(std::move(name));
|
||||
dirty(value);
|
||||
}),
|
||||
callback_property<T, bool>(
|
||||
"frequency_limit_enabled", "Frequency limit", Field_Control::boolean,
|
||||
"frequency_limit_enabled", "启用帧率限制", Field_Control::boolean,
|
||||
[](const T& value) { return value.plot_.max_render_fps() > 0.0; },
|
||||
[dirty](T& value, bool enabled) {
|
||||
if (enabled) {
|
||||
@@ -96,7 +98,7 @@ struct Type_Descriptor<renderive::web::Gallery_Session_Control> {
|
||||
dirty(value);
|
||||
}),
|
||||
callback_property<T, double>(
|
||||
"max_render_fps", "Maximum render FPS", Field_Control::number,
|
||||
"max_render_fps", "最大渲染帧率", Field_Control::number,
|
||||
[](const T& value) {
|
||||
const double fps = value.plot_.max_render_fps();
|
||||
return fps > 0.0 ? fps : 30.0;
|
||||
@@ -107,7 +109,7 @@ struct Type_Descriptor<renderive::web::Gallery_Session_Control> {
|
||||
dirty(value);
|
||||
}),
|
||||
callback_property<T, renderive::web::Gallery_Feedback_Policy>(
|
||||
"feedback", "Consumer feedback", Field_Control::automatic,
|
||||
"feedback", "消费者反馈", Field_Control::automatic,
|
||||
[](const T& value) { return value.feedback_; },
|
||||
[dirty](T& value, renderive::web::Gallery_Feedback_Policy feedback) {
|
||||
value.feedback_ = std::move(feedback);
|
||||
@@ -129,11 +131,11 @@ struct Type_Descriptor<renderive::web::Gallery_Session_Control> {
|
||||
using T = renderive::web::Gallery_Session_Control;
|
||||
using renderive::web::gallery_action;
|
||||
return object<T>(type_metadata(
|
||||
gallery_action("reset", "恢复本图默认值", "Gallery_Scene::rebuild", "Session"),
|
||||
gallery_action("reset", "恢复本图默认值", "Gallery_Scene::rebuild", "场景"),
|
||||
gallery_action("toggle_view", "切换 View 生命周期",
|
||||
"Plot_Core::activate_view / deactivate_view", "Session"),
|
||||
"Plot_Core::activate_view / deactivate_view", "场景"),
|
||||
gallery_action("read_data_shape", "读取数据形状",
|
||||
"Renderable data query / observer telemetry", "Observer")));
|
||||
"Renderable data query / observer telemetry", "观察器")));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -276,6 +276,8 @@ TEST(RenderiveWebGallery, ObserverTelemetryAndDashboardUseAdminiveDescriptors) {
|
||||
const auto& observer_descriptor = observer_contract.at("descriptor");
|
||||
const auto& feedback_descriptor =
|
||||
observer_contract.at("consumer_feedback_descriptor");
|
||||
EXPECT_EQ(observer_descriptor.at("label"), "内核观察器");
|
||||
EXPECT_EQ(feedback_descriptor.at("label"), "消费者反馈");
|
||||
|
||||
Gallery_Plot_Session session;
|
||||
const auto opened = response_json(session.handle(gallery_request(
|
||||
@@ -356,16 +358,26 @@ TEST(RenderiveWebGallery, AdminiveResourcesPatchTheRealRenderableState) {
|
||||
spectrum.at("descriptor").at("fields").end(),
|
||||
[](const auto& field) { return field.at("name") == "partition_count"; });
|
||||
ASSERT_NE(partition_field, spectrum.at("descriptor").at("fields").end());
|
||||
EXPECT_EQ(partition_field->at("presentation").at("label"), "固定分区数");
|
||||
EXPECT_EQ(partition_field->at("presentation").at("description"),
|
||||
"0 = automatic, 1 = single task, N = N parallel partitions");
|
||||
"固定模式下的并行任务数;1 表示不分区");
|
||||
|
||||
const auto partition_mode_field = std::find_if(
|
||||
spectrum.at("descriptor").at("fields").begin(),
|
||||
spectrum.at("descriptor").at("fields").end(),
|
||||
[](const auto& field) { return field.at("name") == "partition_mode"; });
|
||||
ASSERT_NE(partition_mode_field, spectrum.at("descriptor").at("fields").end());
|
||||
EXPECT_EQ(partition_mode_field->at("presentation").at("label"), "任务分区模式");
|
||||
EXPECT_EQ(spectrum.at("descriptor").at("label"), "实时频谱");
|
||||
|
||||
response = response_json(session.handle(gallery_request(
|
||||
Gallery_Request_Kind::Patch,
|
||||
R"({"category":"event","type":"gallery_patch","target":"spectrum","patch":{"frequency_point_size":1024,"partition_count":4,"frequency_range":{"origin":90000000},"current_pen":{"color":"#ff8844"},"interpolation_mode":"Cubic_Value"}})")));
|
||||
R"({"category":"event","type":"gallery_patch","target":"spectrum","patch":{"frequency_point_size":1024,"partition_mode":"Fixed","partition_count":4,"frequency_range":{"origin":90000000},"current_pen":{"color":"#ff8844"},"interpolation_mode":"Cubic_Value"}})")));
|
||||
ASSERT_EQ(response.at("type"), "case_state");
|
||||
const auto& updated = find_resource(response, "spectrum").at("data");
|
||||
EXPECT_EQ(updated.at("frequency_point_size"), 1024);
|
||||
EXPECT_EQ(updated.at("partition_count"), 4);
|
||||
EXPECT_EQ(updated.at("partition_mode"), "Fixed");
|
||||
EXPECT_EQ(updated.at("frequency_range").at("origin"), 90'000'000);
|
||||
EXPECT_EQ(updated.at("current_pen").at("color"), "#ff8844");
|
||||
EXPECT_EQ(updated.at("interpolation_mode"), "Cubic_Value");
|
||||
@@ -394,7 +406,9 @@ TEST(RenderiveWebGallery, PartitionControlRebuildsTheObservedTaskGraph) {
|
||||
return invoke_action(session, "mode_render").at("telemetry").at("kernel_observer");
|
||||
};
|
||||
|
||||
ASSERT_EQ(patch_controls(session, {{"partition_count", 1}}, "spectrum").at("type"),
|
||||
ASSERT_EQ(patch_controls(session,
|
||||
{{"partition_mode", "Fixed"}, {"partition_count", 1}},
|
||||
"spectrum").at("type"),
|
||||
"case_state");
|
||||
const auto single = render();
|
||||
EXPECT_EQ(single.at("task_execution_state"), "Completed");
|
||||
@@ -448,7 +462,8 @@ TEST(RenderiveWebGallery, AxisSwapAndEveryPlotDirectionAreEditableAndRenderable)
|
||||
{{"orientation", "Horizontal"},
|
||||
{"coordinates", {{"origin", -20.0}, {"target", -120.0}}}});
|
||||
ASSERT_EQ(state.at("type"), "case_state");
|
||||
state = patch(session, case_id, {{"partition_count", 4}});
|
||||
state = patch(session, case_id,
|
||||
{{"partition_mode", "Fixed"}, {"partition_count", 4}});
|
||||
ASSERT_EQ(state.at("type"), "case_state");
|
||||
EXPECT_EQ(resource_data(state, "frequency_axis").at("orientation"), "Vertical");
|
||||
EXPECT_EQ(resource_data(state, "value_axis").at("orientation"), "Horizontal");
|
||||
@@ -467,7 +482,8 @@ TEST(RenderiveWebGallery, AxisSwapAndEveryPlotDirectionAreEditableAndRenderable)
|
||||
state = patch(waterfall, "time_axis",
|
||||
{{"orientation", "Horizontal"}, {"newest_at_start", true}});
|
||||
ASSERT_EQ(state.at("type"), "case_state");
|
||||
state = patch(waterfall, "waterfall", {{"partition_count", 4}});
|
||||
state = patch(waterfall, "waterfall",
|
||||
{{"partition_mode", "Fixed"}, {"partition_count", 4}});
|
||||
ASSERT_EQ(state.at("type"), "case_state");
|
||||
EXPECT_EQ(resource_data(state, "frequency_axis").at("orientation"), "Vertical");
|
||||
EXPECT_EQ(resource_data(state, "time_axis").at("orientation"), "Horizontal");
|
||||
|
||||
+12
-4
@@ -58,12 +58,18 @@ function grouped(items) {
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
function adminiveFieldVisible(expression, data) {
|
||||
if (!expression) return true;
|
||||
const equality = expression.match(/^\$\{\$self\.([A-Za-z_][A-Za-z0-9_]*) == '([^']*)'\}$/);
|
||||
return equality ? String(data?.[equality[1]]) === equality[2] : true;
|
||||
}
|
||||
function adminiveControls(resource) {
|
||||
const resources = resource?.resources || (resource ? [resource] : []);
|
||||
const controls = [];
|
||||
const visit = (fields, data, target, group, path = [], labels = []) => {
|
||||
for (const field of fields || []) {
|
||||
const presentation = field.presentation || {};
|
||||
if (!adminiveFieldVisible(presentation.visible_on, data)) continue;
|
||||
const fieldPath = [...path, field.name];
|
||||
const fieldLabels = [...labels, presentation.label || field.name];
|
||||
if (field.children?.length) {
|
||||
@@ -76,7 +82,7 @@ function adminiveControls(resource) {
|
||||
target,
|
||||
path: fieldPath,
|
||||
label: fieldLabels.join(" / "),
|
||||
api: presentation.description || fieldPath.join("."),
|
||||
api: fieldPath.join("."),
|
||||
description: presentation.description || "",
|
||||
group,
|
||||
input: presentation.control === "automatic" ? "text" : presentation.control || "text",
|
||||
@@ -637,7 +643,7 @@ function renderControl(item) {
|
||||
const row = document.createElement("div"); row.className = "control-row";
|
||||
const copy = document.createElement("div"); copy.className = "control-copy";
|
||||
const label = document.createElement("label"); label.textContent = item.label;
|
||||
const api = document.createElement("code"); api.textContent = item.api; api.title = item.api; copy.append(label, api);
|
||||
copy.append(label);
|
||||
let input;
|
||||
if (item.input === "select") {
|
||||
input = document.createElement("select");
|
||||
@@ -648,10 +654,12 @@ function renderControl(item) {
|
||||
if (item.input === "boolean") input.checked = Boolean(item.value); else input.value = item.value;
|
||||
if (item.input === "number") { input.min = item.minimum; input.max = item.maximum; input.step = item.step || "any"; }
|
||||
}
|
||||
input.className = "control-input"; input.title = item.description || item.api;
|
||||
input.className = "control-input";
|
||||
input.setAttribute("aria-label", item.label);
|
||||
input.title = item.description || item.label;
|
||||
const submit = value => {
|
||||
card.send("gallery_patch", {target: item.target, patch: nestedPatch(item.path, value)});
|
||||
elements.menuStatus.textContent = `提交 ${item.api} · 等待后端回读`;
|
||||
elements.menuStatus.textContent = `提交“${item.label}”并等待后端回读`;
|
||||
};
|
||||
if (item.input === "number") {
|
||||
let committedValue = input.value;
|
||||
|
||||
Reference in New Issue
Block a user