升级
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;
|
||||
|
||||
Reference in New Issue
Block a user