四个方向 分块渲染
This commit is contained in:
@@ -2,12 +2,26 @@
|
||||
#include "Heatmap_Utils.h"
|
||||
#include "Plottable_Real_Time_Data.h"
|
||||
#include "../render/Blend2D_Cache.h"
|
||||
#include "../renderable/Render_Partition.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <deque>
|
||||
namespace renderive {
|
||||
namespace detail {
|
||||
namespace {
|
||||
using Afterglow_History = Plottable_History_Real_Time_Data<std::vector<double>, std::deque<std::vector<double>>>;
|
||||
struct Afterglow_Render_Frame {
|
||||
Adaptive_Render_Partitioner partitioner;
|
||||
Axis_Raster_Layout layout;
|
||||
std::vector<double> intensity;
|
||||
std::vector<Pixel> pixels;
|
||||
int source_width{};
|
||||
int source_height{};
|
||||
int active_partitions{1};
|
||||
double maximum{1.0};
|
||||
std::size_t work_size{};
|
||||
bool valid{};
|
||||
};
|
||||
}
|
||||
struct Afterglow_Control::Impl {
|
||||
Impl(Afterglow_Control& owner, std::shared_ptr<Frequency_Axis> frequency, std::shared_ptr<Axis> power)
|
||||
@@ -15,6 +29,7 @@ struct Afterglow_Control::Impl {
|
||||
std::shared_ptr<Frequency_Axis> frequency_axis;
|
||||
std::shared_ptr<Axis> power_axis;
|
||||
Afterglow_History history;
|
||||
Afterglow_Render_Frame render_frame;
|
||||
};
|
||||
Afterglow_Control::Afterglow_Control(Plot_Core& plot, const Afterglow_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Axis> power_axis)
|
||||
: Plottable_State(plot, properties), impl_(std::make_unique<Impl>(*this, std::move(frequency_axis), std::move(power_axis))) {}
|
||||
@@ -49,9 +64,47 @@ void Afterglow_Control::append_spectrum(std::pmr::vector<double>&& values) {
|
||||
void Afterglow_Control::publish() {
|
||||
publish_properties();
|
||||
}
|
||||
void Afterglow_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
|
||||
void Afterglow_Control::build_paint_task_graph(Renderable_Task_Graph& graph) {
|
||||
const auto prepare = graph.emplace([this](const Scene_Render_Context&) {
|
||||
prepare_render_frame(render_state_view());
|
||||
}, "prepare afterglow");
|
||||
std::array<Renderable_Task_Graph::Task, maximum_render_partitions> accumulation;
|
||||
std::array<Renderable_Task_Graph::Task, maximum_render_partitions> coloring;
|
||||
for (int index = 0; index < maximum_render_partitions; ++index) {
|
||||
accumulation[static_cast<std::size_t>(index)] = graph.emplace(
|
||||
[this, index](const Scene_Render_Context&) {
|
||||
accumulate_partition(render_state_view(), index);
|
||||
},
|
||||
"accumulate afterglow partition");
|
||||
graph.precede(prepare, accumulation[static_cast<std::size_t>(index)]);
|
||||
}
|
||||
const auto normalize = graph.emplace([this](const Scene_Render_Context&) {
|
||||
normalize_render_frame();
|
||||
}, "normalize afterglow");
|
||||
for (const auto task : accumulation)
|
||||
graph.precede(task, normalize);
|
||||
for (int index = 0; index < maximum_render_partitions; ++index) {
|
||||
coloring[static_cast<std::size_t>(index)] = graph.emplace(
|
||||
[this, index](const Scene_Render_Context&) {
|
||||
color_partition(render_state_view(), index);
|
||||
},
|
||||
"color afterglow partition");
|
||||
graph.precede(normalize, coloring[static_cast<std::size_t>(index)]);
|
||||
}
|
||||
const auto compose = add_paint_task(
|
||||
graph, "compose afterglow",
|
||||
[this](Painter& painter, const Render_State_View& view) { paint(painter, view); });
|
||||
for (const auto task : coloring)
|
||||
graph.precede(task, compose);
|
||||
}
|
||||
|
||||
void Afterglow_Control::prepare_render_frame(const Render_State_View& view) {
|
||||
const auto& state = render_properties(view);
|
||||
const auto& history = view.get(impl_->history);
|
||||
auto& output = impl_->render_frame;
|
||||
output.valid = false;
|
||||
output.work_size = 0;
|
||||
if (history.empty())
|
||||
return;
|
||||
const int width = std::min(state.frequency_point_size.get(), static_cast<int>(history.back().size()));
|
||||
@@ -60,27 +113,83 @@ void Afterglow_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
: std::max(1, static_cast<int>(impl_->power_axis->transform(view).pixel_length));
|
||||
if (width <= 0 || height <= 0)
|
||||
return;
|
||||
std::vector<double> intensity(static_cast<std::size_t>(width) * height);
|
||||
const auto layout = axis_raster_layout(impl_->frequency_axis->transform(view),
|
||||
impl_->power_axis->transform(view),
|
||||
state.frequency_range, state.power_range,
|
||||
width, height);
|
||||
if (!layout.valid())
|
||||
return;
|
||||
output.layout = layout;
|
||||
output.source_width = width;
|
||||
output.source_height = height;
|
||||
output.work_size = static_cast<std::size_t>(width) * height;
|
||||
output.intensity.resize(output.work_size);
|
||||
output.pixels.resize(output.work_size);
|
||||
output.active_partitions =
|
||||
output.partitioner.begin(state.partition_count.get(), output.work_size);
|
||||
output.valid = true;
|
||||
}
|
||||
|
||||
void Afterglow_Control::accumulate_partition(const Render_State_View& view,
|
||||
int partition_index) {
|
||||
auto& output = impl_->render_frame;
|
||||
if (!output.valid || partition_index >= output.active_partitions)
|
||||
return;
|
||||
const auto& state = render_properties(view);
|
||||
const auto& history = view.get(impl_->history);
|
||||
const auto columns = render_partition_range(static_cast<std::size_t>(output.source_width),
|
||||
partition_index, output.active_partitions);
|
||||
for (std::size_t x = columns.first; x < columns.last; ++x)
|
||||
for (int y = 0; y < output.source_height; ++y)
|
||||
output.intensity[static_cast<std::size_t>(y) * output.source_width + x] = 0.0;
|
||||
double weight = 1.0;
|
||||
const double decay = 1.0 - state.attenuation_rate.get();
|
||||
for (auto iterator = history.rbegin(); iterator != history.rend(); ++iterator) {
|
||||
const int count = std::min(width, static_cast<int>(iterator->size()));
|
||||
for (int x = 0; x < count; ++x) {
|
||||
const double normalized = normalized_value((*iterator)[static_cast<std::size_t>(x)], state.power_range);
|
||||
const int y = std::clamp(height - 1 - static_cast<int>(normalized * (height - 1)), 0, height - 1);
|
||||
intensity[static_cast<std::size_t>(y) * width + x] += weight;
|
||||
if (state.interpolate && y + 1 < height)
|
||||
intensity[static_cast<std::size_t>(y + 1) * width + x] += weight * 0.35;
|
||||
const std::size_t count = std::min<std::size_t>(output.source_width, iterator->size());
|
||||
for (std::size_t x = columns.first; x < std::min(columns.last, count); ++x) {
|
||||
const double normalized = normalized_value((*iterator)[x], state.power_range);
|
||||
const int y = std::clamp(static_cast<int>(normalized * (output.source_height - 1)),
|
||||
0, output.source_height - 1);
|
||||
output.intensity[static_cast<std::size_t>(y) * output.source_width + x] += weight;
|
||||
if (state.interpolate && y + 1 < output.source_height)
|
||||
output.intensity[static_cast<std::size_t>(y + 1) * output.source_width + x] += weight * 0.35;
|
||||
}
|
||||
weight *= decay;
|
||||
if (weight < 0.01)
|
||||
break;
|
||||
}
|
||||
const double maximum = std::max(1.0, *std::max_element(intensity.begin(), intensity.end()));
|
||||
std::vector<Pixel> pixels(intensity.size());
|
||||
for (std::size_t index = 0; index < pixels.size(); ++index)
|
||||
pixels[index] = state.color_map.at_normalized(intensity[index] / maximum);
|
||||
painter.heatmap(mapped_rect(impl_->frequency_axis->transform(view), impl_->power_axis->transform(view), state.frequency_range, state.power_range), width, height, pixels, Image_Interpolation_Mode::Bilinear);
|
||||
}
|
||||
|
||||
void Afterglow_Control::normalize_render_frame() {
|
||||
auto& output = impl_->render_frame;
|
||||
if (output.valid)
|
||||
output.maximum = std::max(1.0, *std::max_element(output.intensity.begin(),
|
||||
output.intensity.end()));
|
||||
}
|
||||
|
||||
void Afterglow_Control::color_partition(const Render_State_View& view,
|
||||
int partition_index) {
|
||||
auto& output = impl_->render_frame;
|
||||
if (!output.valid || partition_index >= output.active_partitions)
|
||||
return;
|
||||
const auto& state = render_properties(view);
|
||||
const auto range = render_partition_range(output.work_size, partition_index,
|
||||
output.active_partitions);
|
||||
for (std::size_t cell = range.first; cell < range.last; ++cell) {
|
||||
const int y = static_cast<int>(cell / static_cast<std::size_t>(output.source_width));
|
||||
const int x = static_cast<int>(cell % static_cast<std::size_t>(output.source_width));
|
||||
output.pixels[output.layout.index(x, y, output.source_width, output.source_height)] =
|
||||
state.color_map.at_normalized(output.intensity[cell] / output.maximum);
|
||||
}
|
||||
}
|
||||
|
||||
void Afterglow_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
auto& output = impl_->render_frame;
|
||||
if (!output.valid)
|
||||
return;
|
||||
painter.heatmap(output.layout.target, output.layout.width, output.layout.height,
|
||||
output.pixels, Image_Interpolation_Mode::Bilinear);
|
||||
output.partitioner.finish(output.work_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user