This commit is contained in:
2026-08-12 11:03:16 +08:00
parent 0910b73ea4
commit 898a58b554
18 changed files with 521 additions and 277 deletions
+36 -9
View File
@@ -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);
}
}
}
+5 -1
View File
@@ -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;
};
}
+8 -1
View File
@@ -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;
+55 -33
View File
@@ -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);
}
}
}
+8 -2
View File
@@ -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;
};
}
+35 -9
View File
@@ -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);
}
}
}
+5 -1
View File
@@ -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;
};