接入3D secene taskflow
This commit is contained in:
+302
-128
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user