270 lines
10 KiB
C++
270 lines
10 KiB
C++
#include "Renderable.h"
|
|
|
|
#include "../plot/Plot_Core.h"
|
|
#include "../render/Blend2D_Cache.h"
|
|
|
|
#include <renderive/scene/base/Scene_Render_Context.hpp>
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
namespace renderive {
|
|
|
|
Renderable_Observation Renderable_Observer::observation() const noexcept {
|
|
std::lock_guard lock(mutex_);
|
|
auto result = observation_;
|
|
const auto render = render_duration_statistics_.snapshot();
|
|
const auto task = task_duration_statistics_.snapshot();
|
|
const auto nanoseconds = [](double value) {
|
|
return static_cast<std::uint64_t>(std::max(0.0, std::round(value)));
|
|
};
|
|
result.render_duration_sample_count = render.sample_count;
|
|
result.render_duration_average_ns = nanoseconds(render.average);
|
|
result.render_duration_deviation_ns = nanoseconds(render.deviation);
|
|
result.render_duration_p50_ns = nanoseconds(render.p50);
|
|
result.render_duration_p95_ns = nanoseconds(render.p95);
|
|
result.render_duration_p99_ns = nanoseconds(render.p99);
|
|
result.task_duration_sample_count = task.sample_count;
|
|
result.task_duration_average_ns = nanoseconds(task.average);
|
|
result.task_duration_deviation_ns = nanoseconds(task.deviation);
|
|
result.task_duration_p50_ns = nanoseconds(task.p50);
|
|
result.task_duration_p95_ns = nanoseconds(task.p95);
|
|
result.task_duration_p99_ns = nanoseconds(task.p99);
|
|
return result;
|
|
}
|
|
|
|
void Renderable_Observer::observe_state(Renderable_Observer_Event event,
|
|
std::uint64_t time_ns,
|
|
std::uint64_t cache_update_count,
|
|
std::uint64_t publish_count) noexcept {
|
|
std::lock_guard lock(mutex_);
|
|
observation_.event = event;
|
|
observation_.event_time_ns = time_ns;
|
|
observation_.cache_update_count = cache_update_count;
|
|
observation_.publish_count = publish_count;
|
|
}
|
|
|
|
void Renderable_Observer::observe_task(
|
|
const Renderable_Task_Observation& task) noexcept {
|
|
std::lock_guard lock(mutex_);
|
|
if (task.event == Renderable_Task_Observation_Event::started) {
|
|
if (active_render_sequence_ != task.render_sequence) {
|
|
active_render_sequence_ = task.render_sequence;
|
|
active_render_started_ns_ = task.time_ns;
|
|
active_task_count_ = task.task_count;
|
|
completed_task_count_ = 0;
|
|
running_task_count_ = 0;
|
|
active_peak_parallelism_ = 0;
|
|
active_render_failed_ = false;
|
|
}
|
|
++running_task_count_;
|
|
++observation_.task_execution_count;
|
|
active_peak_parallelism_ =
|
|
std::max(active_peak_parallelism_, running_task_count_);
|
|
if (!active_render_failed_) {
|
|
observation_.event = Renderable_Observer_Event::Render_Started;
|
|
observation_.event_time_ns = task.time_ns;
|
|
}
|
|
observation_.last_render_sequence = task.render_sequence;
|
|
return;
|
|
}
|
|
|
|
if (active_render_sequence_ != task.render_sequence)
|
|
return;
|
|
if (running_task_count_ != 0)
|
|
--running_task_count_;
|
|
++completed_task_count_;
|
|
observation_.last_task_duration_ns = task.task_duration_ns;
|
|
observation_.total_task_duration_ns += task.task_duration_ns;
|
|
observation_.maximum_task_duration_ns =
|
|
std::max(observation_.maximum_task_duration_ns, task.task_duration_ns);
|
|
task_duration_statistics_.add(static_cast<double>(task.task_duration_ns));
|
|
|
|
const auto render_duration =
|
|
task.time_ns >= active_render_started_ns_
|
|
? task.time_ns - active_render_started_ns_
|
|
: 0;
|
|
const auto extend_failed_render_duration = [&] {
|
|
if (render_duration > observation_.last_render_duration_ns) {
|
|
observation_.total_render_duration_ns +=
|
|
render_duration - observation_.last_render_duration_ns;
|
|
observation_.last_render_duration_ns = render_duration;
|
|
render_duration_statistics_.replace_latest(
|
|
static_cast<double>(render_duration));
|
|
observation_.maximum_render_duration_ns =
|
|
std::max(observation_.maximum_render_duration_ns, render_duration);
|
|
}
|
|
};
|
|
if (task.event == Renderable_Task_Observation_Event::failed) {
|
|
++observation_.failed_task_count;
|
|
if (!active_render_failed_) {
|
|
active_render_failed_ = true;
|
|
++observation_.failed_render_count;
|
|
observation_.event = Renderable_Observer_Event::Render_Failed;
|
|
observation_.event_time_ns = task.time_ns;
|
|
observation_.last_render_duration_ns = render_duration;
|
|
observation_.total_render_duration_ns += render_duration;
|
|
render_duration_statistics_.add(static_cast<double>(render_duration));
|
|
observation_.maximum_render_duration_ns =
|
|
std::max(observation_.maximum_render_duration_ns, render_duration);
|
|
observation_.last_task_count = active_task_count_;
|
|
observation_.peak_parallelism = active_peak_parallelism_;
|
|
} else
|
|
extend_failed_render_duration();
|
|
return;
|
|
}
|
|
|
|
if (active_render_failed_) {
|
|
extend_failed_render_duration();
|
|
return;
|
|
}
|
|
|
|
if (completed_task_count_ == active_task_count_) {
|
|
++observation_.successful_render_count;
|
|
observation_.event = Renderable_Observer_Event::Render_Completed;
|
|
observation_.event_time_ns = task.time_ns;
|
|
observation_.last_render_duration_ns = render_duration;
|
|
observation_.total_render_duration_ns += render_duration;
|
|
render_duration_statistics_.add(static_cast<double>(render_duration));
|
|
observation_.maximum_render_duration_ns =
|
|
std::max(observation_.maximum_render_duration_ns, render_duration);
|
|
observation_.last_task_count = active_task_count_;
|
|
observation_.peak_parallelism = active_peak_parallelism_;
|
|
}
|
|
}
|
|
|
|
void Renderable_Observer::reset_performance() noexcept {
|
|
std::lock_guard lock(mutex_);
|
|
Renderable_Observation reset;
|
|
if (observation_.event == Renderable_Observer_Event::Cache_Updated ||
|
|
observation_.event == Renderable_Observer_Event::Published) {
|
|
reset.event = observation_.event;
|
|
reset.event_time_ns = observation_.event_time_ns;
|
|
}
|
|
reset.cache_update_count = observation_.cache_update_count;
|
|
reset.publish_count = observation_.publish_count;
|
|
observation_ = reset;
|
|
active_render_sequence_ = 0;
|
|
active_render_started_ns_ = 0;
|
|
active_task_count_ = 0;
|
|
completed_task_count_ = 0;
|
|
running_task_count_ = 0;
|
|
active_peak_parallelism_ = 0;
|
|
active_render_failed_ = false;
|
|
render_duration_statistics_.clear();
|
|
task_duration_statistics_.clear();
|
|
}
|
|
|
|
Renderable::Renderable(Plot_Core& plot, bool cache_enabled)
|
|
: ::Renderable_Base(plot.kernel_scene(), {.cache_enabled = cache_enabled}), plot_(plot) {}
|
|
|
|
Renderable::~Renderable() = default;
|
|
|
|
Renderable_Cache_Mode Renderable::get_cache_mode() const noexcept {
|
|
return configuration().cache_enabled
|
|
? Renderable_Cache_Mode::Local_Pixel
|
|
: Renderable_Cache_Mode::Direct;
|
|
}
|
|
|
|
void Renderable::set_cache_mode(Renderable_Cache_Mode mode) {
|
|
plot_.set_renderable_cache(*this, mode);
|
|
}
|
|
|
|
std::string Renderable::object_name() const {
|
|
std::lock_guard lock(metadata_mutex_);
|
|
return object_name_;
|
|
}
|
|
|
|
void Renderable::set_object_name(std::string name) {
|
|
std::lock_guard lock(metadata_mutex_);
|
|
object_name_ = std::move(name);
|
|
}
|
|
|
|
bool Renderable::is_visible() const noexcept {
|
|
return visible_.load(std::memory_order_acquire);
|
|
}
|
|
|
|
void Renderable::set_visible(bool visible) {
|
|
if (visible_.exchange(visible, std::memory_order_acq_rel) != visible)
|
|
changed();
|
|
}
|
|
|
|
Renderable_Observation Renderable::observation() const noexcept {
|
|
return observer_.observation();
|
|
}
|
|
|
|
void Renderable::observe_state(Renderable_Observer_Event event,
|
|
std::uint64_t time_ns,
|
|
std::uint64_t cache_update_count,
|
|
std::uint64_t publish_count) noexcept {
|
|
observer_.observe_state(event, time_ns, cache_update_count, publish_count);
|
|
}
|
|
|
|
void Renderable::observe_task(
|
|
const Renderable_Task_Observation& observation) noexcept {
|
|
observer_.observe_task(observation);
|
|
}
|
|
|
|
void Renderable::reset_task_observation() noexcept {
|
|
observer_.reset_performance();
|
|
}
|
|
|
|
void Renderable::render(const ::Scene_Render_Context& context) {
|
|
if (!is_visible() || context.color_cache == nullptr)
|
|
return;
|
|
auto* cache = dynamic_cast<detail::Blend2D_Color_Cache*>(context.color_cache);
|
|
if (!cache)
|
|
return;
|
|
detail::Painter painter(*cache, viewport_size());
|
|
if (painter) {
|
|
const auto state = render_state_view();
|
|
paint(painter, state);
|
|
}
|
|
}
|
|
|
|
void Renderable::build_task_graph(Renderable_Task_Graph& graph) {
|
|
build_paint_task_graph(graph);
|
|
}
|
|
|
|
void Renderable::build_paint_task_graph(Renderable_Task_Graph& graph) {
|
|
add_paint_task(graph, "绘制", [this](detail::Painter& painter,
|
|
const Render_State_View& state,
|
|
const Scene_Render_Context&) {
|
|
paint(painter, state);
|
|
});
|
|
}
|
|
|
|
Renderable_Task_Graph::Task Renderable::add_paint_task(
|
|
Renderable_Task_Graph& graph,
|
|
std::string name,
|
|
Paint_Task_Function function) {
|
|
return graph.emplace(
|
|
[this, function = std::move(function)](const Scene_Render_Context& context) {
|
|
if (!is_visible() || context.color_cache == nullptr)
|
|
return;
|
|
auto* cache = dynamic_cast<detail::Blend2D_Color_Cache*>(context.color_cache);
|
|
if (!cache)
|
|
return;
|
|
detail::Painter painter(*cache, viewport_size());
|
|
if (painter)
|
|
function(painter, render_state_view(), context);
|
|
},
|
|
std::move(name));
|
|
}
|
|
|
|
void Renderable::changed() noexcept {
|
|
invalidate_cache();
|
|
plot_.notify_model_dirty();
|
|
}
|
|
|
|
void Renderable::task_graph_changed() noexcept {
|
|
rebuild_task_graph();
|
|
plot_.notify_model_dirty();
|
|
}
|
|
|
|
Size Renderable::viewport_size() const noexcept {
|
|
return plot_.viewport_size();
|
|
}
|
|
|
|
} // namespace renderive
|