接入3D secene taskflow

This commit is contained in:
2026-08-14 23:38:18 +08:00
parent 9a0601073b
commit 12e46b4c21
31 changed files with 1210 additions and 6116 deletions
+302 -128
View File
@@ -4,9 +4,13 @@
#include "detail/Point_Core.h"
#include "detail/Render_Domain.h"
#include <atomic>
#include <renderive/frame_control/Frame_Control.hpp>
#include <renderive/scene/Scene.hpp>
#include <algorithm>
#include <atomic>
#include <cmath>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <utility>
@@ -28,73 +32,280 @@ void validate(Clear_Color color) {
throw std::invalid_argument("Point_Scene clear color must be in [0, 1]");
}
detail::Input_Command_Type pointer_type(::renderive::Event_Type type) {
switch (type) {
case ::renderive::Event_Type::Pointer_Move:
return detail::Input_Command_Type::Pointer_Move;
case ::renderive::Event_Type::Pointer_Press:
return detail::Input_Command_Type::Pointer_Press;
case ::renderive::Event_Type::Pointer_Release:
return detail::Input_Command_Type::Pointer_Release;
default:
throw std::invalid_argument("Point_Scene expected a pointer event");
}
float datoviz_wheel_step(float pixel_delta, float angle_delta) noexcept {
return angle_delta != 0.0F ? angle_delta / 120.0F
: pixel_delta / 100.0F;
}
float datoviz_wheel_step(float pixel_delta, float angle_delta) {
if (angle_delta != 0.0F)
return angle_delta / 120.0F;
return pixel_delta / 100.0F;
struct Scene_Model {
virtual ~Scene_Model() = default;
virtual void resize(Extent extent) = 0;
virtual void set_clear_color(Clear_Color color) = 0;
virtual void dispatch_pointer(
::renderive::Event_Type type, float x, float y,
::renderive::Mouse_Button button,
::renderive::Keyboard_Modifier modifiers) = 0;
virtual void dispatch_wheel(
float x, float y, float delta_x, float delta_y,
::renderive::Keyboard_Modifier modifiers) = 0;
virtual void dispatch_key(const ::renderive::Key_Event& event) = 0;
[[nodiscard]] virtual bool prepare_frame() = 0;
[[nodiscard]] virtual bool refresh_manual_frame() = 0;
[[nodiscard]] virtual bool discard_pending_frame() = 0;
[[nodiscard]] virtual bool render_prepared_frame() = 0;
[[nodiscard]] virtual bool request_frame() = 0;
[[nodiscard]] virtual std::shared_ptr<const Pixel_Frame> latest_frame()
const = 0;
[[nodiscard]] virtual Frame_Status frame_status() const = 0;
[[nodiscard]] virtual Scene_Base& scene() noexcept = 0;
[[nodiscard]] virtual const Scene_Base& scene() const noexcept = 0;
};
using Manual_Frame = ::Manual_Refresh_Strategy<::Scene3D_Frame_Data>;
using Low_Latency_Frame = ::Low_Latency_Strategy<::Scene3D_Frame_Data>;
using Playback_Frame = ::Flow_Refresh_Strategy<::Scene3D_Frame_Data>;
template <class Strategy, Frame_Mode Mode>
struct Basic_Point_Scene final
: ::Scene3D_Context<Strategy, detail::Scene_State>,
::Scene_Renderer,
Scene_Model {
using Kernel_Scene = ::Scene3D_Context<Strategy, detail::Scene_State>;
using Scene_State_Strategy = typename Kernel_Scene::Scene_State_Strategy;
Basic_Point_Scene(const Scene_Options& options,
std::shared_ptr<Point_Visual> visual)
requires (!std::same_as<Strategy, Low_Latency_Frame>)
: Kernel_Scene() {
initialize(options, std::move(visual));
}
Basic_Point_Scene(const Scene_Options& options,
std::shared_ptr<Point_Visual> visual)
requires std::same_as<Strategy, Low_Latency_Frame>
: Kernel_Scene(Observer_State<>{}, typename Strategy::Configuration{
.frequency_hz = options.maximum_frames_per_second,
.replace_pending_frame = true}) {
initialize(options, std::move(visual));
}
~Basic_Point_Scene() override {
this->shutdown();
render_domain.invoke([this] { backend.reset(); });
}
void initialize(const Scene_Options& options,
std::shared_ptr<Point_Visual> point_visual) {
if (!point_visual)
throw std::invalid_argument("Point_Scene requires a Point_Visual");
visual = std::move(point_visual);
point_id = visual->renderable_id();
this->Scene_State_Strategy::template set<
&detail::Scene_State::viewport>(options.viewport);
this->Scene_State_Strategy::template set<
&detail::Scene_State::clear_color>(options.clear_color);
this->Scene_State_Strategy::template set<
&detail::Scene_State::visual_family>(options.visual_family);
{
auto builder = this->attach_builder();
builder.attach(renderive_Owner<Point_Visual>(visual));
}
const detail::Scene_State initial{
options.viewport, options.clear_color, options.visual_family};
render_domain.invoke([this, &options, &initial] {
backend = std::make_unique<detail::Datoviz_Visual_Backend>(
options.gpu_index, options.validation_enabled, initial);
});
}
void resize(Extent extent) override {
this->Scene_State_Strategy::template set<
&detail::Scene_State::viewport>(extent);
this->invalidate_renderables();
}
void set_clear_color(Clear_Color color) override {
this->Scene_State_Strategy::template set<
&detail::Scene_State::clear_color>(color);
this->notify_model_dirty();
}
void dispatch_pointer(
::renderive::Event_Type type, float x, float y,
::renderive::Mouse_Button button,
::renderive::Keyboard_Modifier modifiers) override {
const Extent viewport = this->Scene_State_Strategy::template get<
&detail::Scene_State::viewport>();
render_domain.invoke([this, type, x, y, button, modifiers, viewport] {
backend->dispatch_pointer(type, x, y, button, modifiers, viewport);
});
this->notify_model_dirty();
}
void dispatch_wheel(
float x, float y, float delta_x, float delta_y,
::renderive::Keyboard_Modifier modifiers) override {
const Extent viewport = this->Scene_State_Strategy::template get<
&detail::Scene_State::viewport>();
render_domain.invoke(
[this, x, y, delta_x, delta_y, modifiers, viewport] {
backend->dispatch_wheel(x, y, delta_x, delta_y, modifiers,
viewport);
});
this->notify_model_dirty();
}
void dispatch_key(const ::renderive::Key_Event& event) override {
render_domain.invoke([this, event] { backend->dispatch_key(event); });
this->notify_model_dirty();
}
[[nodiscard]] bool prepare_frame() override {
auto painter = this->frame_control.acquire_painter();
if (!painter)
return false;
this->publish_frame_state(*painter);
return true;
}
[[nodiscard]] bool refresh_manual_frame() override {
if constexpr (Mode == Frame_Mode::Manual)
return this->frame_control.refresh();
return false;
}
[[nodiscard]] bool discard_pending_frame() override {
if constexpr (Mode == Frame_Mode::Playback)
return false;
else
return this->frame_control.discard_pending_frame();
}
[[nodiscard]] bool render_prepared_frame() override {
auto renderer = this->frame_control.acquire_renderer();
if (!renderer)
return false;
this->render(*renderer);
this->wait_for_render();
return true;
}
[[nodiscard]] bool request_frame() override {
if (!prepare_frame())
return false;
if constexpr (Mode == Frame_Mode::Manual) {
if (!refresh_manual_frame())
return false;
}
return render_prepared_frame();
}
[[nodiscard]] std::shared_ptr<const Pixel_Frame> latest_frame()
const override {
std::lock_guard lock(frame_mutex);
return latest;
}
[[nodiscard]] Frame_Status frame_status() const override {
Frame_Status result;
result.mode = Mode;
if constexpr (Mode == Frame_Mode::Manual) {
const auto state = this->frame_control.state();
result.produced_frame_count = state.prepared_frame_count;
result.consumed_frame_count = state.render_count;
result.dropped_frame_count = state.replaced_prepared_frame_count +
state.discarded_prepared_frame_count;
result.failed_operation_count = state.failed_refresh_count;
result.pending_frame_count = state.pending_frame ? 1U : 0U;
result.latest_sequence = state.render_frame_sequence;
} else if constexpr (Mode == Frame_Mode::Low_Latency) {
const auto state = this->frame_control.state();
const auto counters = this->frame_control.counter_statistics();
result.frequency_hz = state.frequency_hz;
result.produced_frame_count = counters.published_frame_count;
result.consumed_frame_count = counters.render_pass_count;
result.dropped_frame_count = counters.abandoned_frame_count +
counters.manually_discarded_frame_count;
result.failed_operation_count = counters.swap_failure_count;
result.pending_frame_count =
this->frame_control.pending_frame_count();
result.latest_sequence = state.frame_sequence;
result.next_refresh_interval_ns = state.next_refresh_interval_ns;
} else {
const auto state = this->frame_control.state();
result.produced_frame_count = state.enqueued_frame_count;
result.consumed_frame_count = state.rendered_frame_count;
result.failed_operation_count = state.empty_acquire_count;
result.pending_frame_count = state.pending_frame_count;
}
std::lock_guard lock(frame_mutex);
if (latest)
result.latest_sequence =
std::max(result.latest_sequence, latest->sequence);
return result;
}
[[nodiscard]] Scene_Base& scene() noexcept override { return *this; }
[[nodiscard]] const Scene_Base& scene() const noexcept override {
return *this;
}
void render_scene(const Scene_Render_Context& context) override {
const auto prepared = context.prepared<detail::Prepared_Point>(point_id);
if (!prepared)
throw std::logic_error("Point_Visual did not publish prepared data");
const auto& scene_state =
context.frame.scene_state<detail::Scene_State>();
auto frame = render_domain.invoke([this, &scene_state, &context,
&prepared] {
return backend->render(scene_state,
context.frame.scene_state_revision(),
*prepared, context.frame.render_sequence);
});
if (context.metrics && frame)
context.metrics->set(
Node_Metric_Kind::pixel_count,
static_cast<std::uint64_t>(frame->extent.width) *
frame->extent.height);
std::lock_guard lock(frame_mutex);
latest = std::move(frame);
}
std::shared_ptr<Point_Visual> visual;
Renderable_Id point_id{};
detail::Render_Domain render_domain;
std::unique_ptr<detail::Datoviz_Visual_Backend> backend;
mutable std::mutex frame_mutex;
std::shared_ptr<const Pixel_Frame> latest;
};
std::unique_ptr<Scene_Model> make_scene_model(
const Scene_Options& options, std::shared_ptr<Point_Visual> visual) {
switch (options.frame_mode) {
case Frame_Mode::Manual:
return std::make_unique<
Basic_Point_Scene<Manual_Frame, Frame_Mode::Manual>>(
options, std::move(visual));
case Frame_Mode::Low_Latency:
return std::make_unique<
Basic_Point_Scene<Low_Latency_Frame, Frame_Mode::Low_Latency>>(
options, std::move(visual));
case Frame_Mode::Playback:
return std::make_unique<
Basic_Point_Scene<Playback_Frame, Frame_Mode::Playback>>(
options, std::move(visual));
}
throw std::invalid_argument("unknown Point_Scene frame mode");
}
} // namespace
struct Point_Scene::Impl {
Impl(const Scene_Options& options, std::shared_ptr<Point_Visual> point_visual)
: visual(std::move(point_visual)),
states(detail::Scene_State{options.viewport, options.clear_color}),
scheduler(options.frame_mode, options.maximum_frames_per_second) {
if (!visual)
throw std::invalid_argument("Point_Scene requires a Point_Visual");
render_domain.invoke([&] {
backend = std::make_unique<detail::Datoviz_Visual_Backend>(
options.gpu_index, options.validation_enabled,
detail::Scene_State{options.viewport, options.clear_color},
options.visual_family);
});
}
explicit Impl(const Scene_Options& options,
std::shared_ptr<Point_Visual> visual)
: model(make_scene_model(options, std::move(visual))) {}
~Impl() {
accepting.store(false, std::memory_order_release);
render_domain.invoke([&] { backend.reset(); });
}
[[nodiscard]] bool render() {
if (!accepting.load(std::memory_order_acquire))
return false;
return render_domain.invoke([&] {
auto lease = scheduler.acquire_renderer();
if (!lease)
return false;
auto rendered = backend->render(
static_cast<const detail::Point_Frame_Data&>(*lease));
if (!rendered)
return false;
std::lock_guard lock(frame_mutex);
latest = std::move(rendered);
return true;
});
}
std::shared_ptr<Point_Visual> visual;
detail::Scene_State_Buffer states;
detail::Input_Collector input;
detail::Point_Frame_Strategy scheduler;
detail::Render_Domain render_domain;
std::unique_ptr<detail::Datoviz_Visual_Backend> backend;
mutable std::mutex frame_mutex;
std::shared_ptr<const Pixel_Frame> latest;
std::atomic<bool> accepting{true};
std::unique_ptr<Scene_Model> model;
};
Point_Scene::Point_Scene(Scene_Options options,
@@ -103,7 +314,8 @@ Point_Scene::Point_Scene(Scene_Options options,
validate(options.clear_color);
if (!std::isfinite(options.maximum_frames_per_second) ||
options.maximum_frames_per_second <= 0.0)
throw std::invalid_argument("Point_Scene frame frequency must be positive");
throw std::invalid_argument(
"Point_Scene frame frequency must be positive");
impl_ = std::make_unique<Impl>(options, std::move(visual));
}
@@ -111,36 +323,29 @@ Point_Scene::~Point_Scene() = default;
void Point_Scene::resize(Extent extent) {
validate(extent);
impl_->states.update([extent](detail::Scene_State& state) {
state.viewport = extent;
});
impl_->model->resize(extent);
}
void Point_Scene::set_clear_color(Clear_Color color) {
validate(color);
impl_->states.update([color](detail::Scene_State& state) {
state.clear_color = color;
});
impl_->model->set_clear_color(color);
}
void Point_Scene::dispatch(const ::renderive::Event&) {
// Non-positional Show/Hide/Leave events carry no Datoviz controller payload.
// Show, hide and leave carry no Datoviz controller payload.
}
void Point_Scene::dispatch_pointer(
::renderive::Event_Type type, float x, float y,
::renderive::Mouse_Button button, ::renderive::Mouse_Button_Mask buttons,
::renderive::Mouse_Button button, ::renderive::Mouse_Button_Mask,
::renderive::Keyboard_Modifier modifiers) {
if (!std::isfinite(x) || !std::isfinite(y))
return;
detail::Input_Command command;
command.type = pointer_type(type);
command.x = x;
command.y = y;
command.button = button;
command.buttons = buttons;
command.modifiers = modifiers;
impl_->input.push(command);
if (type != ::renderive::Event_Type::Pointer_Move &&
type != ::renderive::Event_Type::Pointer_Press &&
type != ::renderive::Event_Type::Pointer_Release)
throw std::invalid_argument("Point_Scene expected a pointer event");
impl_->model->dispatch_pointer(type, x, y, button, modifiers);
}
void Point_Scene::dispatch_wheel(
@@ -151,72 +356,41 @@ void Point_Scene::dispatch_wheel(
!std::isfinite(pixel_delta_x) || !std::isfinite(pixel_delta_y) ||
!std::isfinite(angle_delta_x) || !std::isfinite(angle_delta_y))
return;
detail::Input_Command command;
command.type = detail::Input_Command_Type::Wheel;
command.x = x;
command.y = y;
command.delta_x = datoviz_wheel_step(pixel_delta_x, angle_delta_x);
command.delta_y = datoviz_wheel_step(pixel_delta_y, angle_delta_y);
command.modifiers = modifiers;
impl_->input.push(command);
impl_->model->dispatch_wheel(
x, y, datoviz_wheel_step(pixel_delta_x, angle_delta_x),
datoviz_wheel_step(pixel_delta_y, angle_delta_y), modifiers);
}
void Point_Scene::dispatch(const ::renderive::Key_Event& event) {
detail::Input_Command command;
command.type = event.type == ::renderive::Event_Type::Key_Release
? detail::Input_Command_Type::Key_Release
: event.auto_repeat
? detail::Input_Command_Type::Key_Repeat
: detail::Input_Command_Type::Key_Press;
command.key = event.key;
command.native_key = event.native_key;
command.modifiers = event.modifiers;
impl_->input.push(command);
}
bool Point_Scene::prepare_frame() {
if (!impl_->accepting.load(std::memory_order_acquire))
return false;
auto lease = impl_->scheduler.acquire_painter();
if (!lease)
return false;
auto scene = impl_->states.publish_state();
lease->scene = std::move(scene.state);
lease->scene_revision = scene.revision;
lease->point = detail::publish_point(*impl_->visual);
lease->input = impl_->input.drain();
return true;
impl_->model->dispatch_key(event);
}
bool Point_Scene::prepare_frame() { return impl_->model->prepare_frame(); }
bool Point_Scene::refresh_manual_frame() {
return impl_->accepting.load(std::memory_order_acquire) &&
impl_->scheduler.refresh();
return impl_->model->refresh_manual_frame();
}
bool Point_Scene::discard_pending_frame() {
return impl_->accepting.load(std::memory_order_acquire) &&
impl_->scheduler.discard_pending();
return impl_->model->discard_pending_frame();
}
bool Point_Scene::render_prepared_frame() { return impl_->render(); }
bool Point_Scene::request_frame() {
return prepare_frame() && impl_->scheduler.activate_for_request() &&
render_prepared_frame();
bool Point_Scene::render_prepared_frame() {
return impl_->model->render_prepared_frame();
}
bool Point_Scene::request_frame() { return impl_->model->request_frame(); }
std::shared_ptr<const Pixel_Frame> Point_Scene::latest_frame() const {
std::lock_guard lock(impl_->frame_mutex);
return impl_->latest;
return impl_->model->latest_frame();
}
Frame_Status Point_Scene::frame_status() const {
auto result = impl_->scheduler.status();
std::lock_guard lock(impl_->frame_mutex);
if (impl_->latest)
result.latest_sequence = std::max(result.latest_sequence,
impl_->latest->sequence);
return result;
return impl_->model->frame_status();
}
Scene_Base& Point_Scene::render_scene() noexcept {
return impl_->model->scene();
}
const Scene_Base& Point_Scene::render_scene() const noexcept {
return impl_->model->scene();
}
} // namespace renderive::render_3d