222 lines
7.4 KiB
C++
222 lines
7.4 KiB
C++
#include "Point_Scene.h"
|
|
|
|
#include "detail/Datoviz_Point_Backend.h"
|
|
#include "detail/Point_Core.h"
|
|
#include "detail/Render_Domain.h"
|
|
|
|
#include <atomic>
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <mutex>
|
|
#include <stdexcept>
|
|
#include <utility>
|
|
|
|
namespace renderive::render_3d {
|
|
namespace {
|
|
|
|
void validate(Extent extent) {
|
|
if (extent.empty())
|
|
throw std::invalid_argument("Point_Scene viewport must be nonempty");
|
|
}
|
|
|
|
void validate(Clear_Color color) {
|
|
const auto component = [](float value) {
|
|
return std::isfinite(value) && value >= 0.0F && value <= 1.0F;
|
|
};
|
|
if (!component(color.red) || !component(color.green) ||
|
|
!component(color.blue) || !component(color.alpha))
|
|
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) {
|
|
if (angle_delta != 0.0F)
|
|
return angle_delta / 120.0F;
|
|
return pixel_delta / 100.0F;
|
|
}
|
|
|
|
} // 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_Point_Backend>(
|
|
options.gpu_index, options.validation_enabled,
|
|
detail::Scene_State{options.viewport, options.clear_color});
|
|
});
|
|
}
|
|
|
|
~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_Point_Backend> backend;
|
|
mutable std::mutex frame_mutex;
|
|
std::shared_ptr<const Pixel_Frame> latest;
|
|
std::atomic<bool> accepting{true};
|
|
};
|
|
|
|
Point_Scene::Point_Scene(Scene_Options options,
|
|
std::shared_ptr<Point_Visual> visual) {
|
|
validate(options.viewport);
|
|
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");
|
|
impl_ = std::make_unique<Impl>(options, std::move(visual));
|
|
}
|
|
|
|
Point_Scene::~Point_Scene() = default;
|
|
|
|
void Point_Scene::resize(Extent extent) {
|
|
validate(extent);
|
|
impl_->states.update([extent](detail::Scene_State& state) {
|
|
state.viewport = extent;
|
|
});
|
|
}
|
|
|
|
void Point_Scene::set_clear_color(Clear_Color color) {
|
|
validate(color);
|
|
impl_->states.update([color](detail::Scene_State& state) {
|
|
state.clear_color = color;
|
|
});
|
|
}
|
|
|
|
void Point_Scene::dispatch(const ::renderive::Event&) {
|
|
// Non-positional Show/Hide/Leave events 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::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);
|
|
}
|
|
|
|
void Point_Scene::dispatch_wheel(
|
|
float x, float y, float pixel_delta_x, float pixel_delta_y,
|
|
float angle_delta_x, float angle_delta_y,
|
|
::renderive::Keyboard_Modifier modifiers) {
|
|
if (!std::isfinite(x) || !std::isfinite(y) ||
|
|
!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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
bool Point_Scene::refresh_manual_frame() {
|
|
return impl_->accepting.load(std::memory_order_acquire) &&
|
|
impl_->scheduler.refresh();
|
|
}
|
|
|
|
bool Point_Scene::discard_pending_frame() {
|
|
return impl_->accepting.load(std::memory_order_acquire) &&
|
|
impl_->scheduler.discard_pending();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
std::shared_ptr<const Pixel_Frame> Point_Scene::latest_frame() const {
|
|
std::lock_guard lock(impl_->frame_mutex);
|
|
return impl_->latest;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
} // namespace renderive::render_3d
|