642 lines
26 KiB
C++
642 lines
26 KiB
C++
#include "Gallery_Scene3D.h"
|
|
|
|
#include "../Gallery_Capture_Json.h"
|
|
#include "../Gallery_Enum.h"
|
|
#include "../common/Gallery_Scheduler_Adminive.h"
|
|
#include "../common/Pixel_Frame.h"
|
|
#include <render_3D/Point_Scene.h>
|
|
|
|
#include "adminive/adminive.hpp"
|
|
#include "adminive/adapters/nlohmann_json.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <cmath>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <deque>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace adminive {
|
|
|
|
template <class Json>
|
|
struct Value_Adapter<
|
|
renderive::render_3d::Renderable_Observer_Event, Json> {
|
|
using value_type = std::string;
|
|
static std::string read(
|
|
renderive::render_3d::Renderable_Observer_Event value) {
|
|
return renderive::web::gallery_enum_id(value);
|
|
}
|
|
static void write(
|
|
renderive::render_3d::Renderable_Observer_Event& target,
|
|
std::string value) {
|
|
const auto parsed = renderive::web::gallery_enum_cast<
|
|
renderive::render_3d::Renderable_Observer_Event>(value);
|
|
if (!parsed)
|
|
throw std::invalid_argument("unknown 3D observer event");
|
|
target = *parsed;
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct Type_Descriptor<renderive::render_3d::Renderable_Observation> {
|
|
static auto get() {
|
|
using T = renderive::render_3d::Renderable_Observation;
|
|
return object<T>(
|
|
"renderable_3d_observer",
|
|
ADMINIVE_FIELD_LABEL(T, event, "事件"),
|
|
ADMINIVE_FIELD_LABEL(T, event_time_ns, "事件时间"),
|
|
ADMINIVE_FIELD_LABEL(T, cache_update_count, "缓存更新"),
|
|
ADMINIVE_FIELD_LABEL(T, publish_count, "发布"),
|
|
ADMINIVE_FIELD_LABEL(T, data_revision, "数据版本"),
|
|
ADMINIVE_FIELD_LABEL(T, item_count, "项目数"));
|
|
}
|
|
};
|
|
|
|
} // namespace adminive
|
|
|
|
namespace renderive::web {
|
|
namespace {
|
|
using namespace renderive::render_3d;
|
|
|
|
struct Gallery_Point {
|
|
double x{};
|
|
double y{};
|
|
};
|
|
|
|
Frame_Mode render_frame_mode(Gallery_Frame_Mode mode) {
|
|
switch (mode) {
|
|
case Gallery_Frame_Mode::Manual:
|
|
return Frame_Mode::Manual;
|
|
case Gallery_Frame_Mode::Low_Latency:
|
|
return Frame_Mode::Low_Latency;
|
|
case Gallery_Frame_Mode::Playback:
|
|
return Frame_Mode::Playback;
|
|
}
|
|
throw std::invalid_argument("unknown gallery frame mode");
|
|
}
|
|
|
|
std::string mode_id(Gallery_Frame_Mode mode) {
|
|
switch (mode) {
|
|
case Gallery_Frame_Mode::Manual:
|
|
return "manual";
|
|
case Gallery_Frame_Mode::Low_Latency:
|
|
return "low_latency";
|
|
case Gallery_Frame_Mode::Playback:
|
|
return "playback";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
::renderive::Event_Type event_type(Gallery_Pointer_Input_Type type) {
|
|
switch (type) {
|
|
case Gallery_Pointer_Input_Type::Move:
|
|
return ::renderive::Event_Type::Pointer_Move;
|
|
case Gallery_Pointer_Input_Type::Press:
|
|
return ::renderive::Event_Type::Pointer_Press;
|
|
case Gallery_Pointer_Input_Type::Release:
|
|
return ::renderive::Event_Type::Pointer_Release;
|
|
}
|
|
return ::renderive::Event_Type::Pointer_Move;
|
|
}
|
|
|
|
::renderive::Mouse_Button mouse_button(Gallery_Mouse_Button button) {
|
|
switch (button) {
|
|
case Gallery_Mouse_Button::Left:
|
|
return ::renderive::Mouse_Button::Left;
|
|
case Gallery_Mouse_Button::Right:
|
|
return ::renderive::Mouse_Button::Right;
|
|
case Gallery_Mouse_Button::Middle:
|
|
return ::renderive::Mouse_Button::Middle;
|
|
case Gallery_Mouse_Button::None:
|
|
return ::renderive::Mouse_Button::None;
|
|
}
|
|
return ::renderive::Mouse_Button::None;
|
|
}
|
|
|
|
::renderive::Key key(Gallery_Key value) {
|
|
switch (value) {
|
|
case Gallery_Key::Escape:
|
|
return ::renderive::Key::Escape;
|
|
case Gallery_Key::Enter:
|
|
return ::renderive::Key::Enter;
|
|
case Gallery_Key::Space:
|
|
return ::renderive::Key::Space;
|
|
case Gallery_Key::Delete:
|
|
return ::renderive::Key::Delete;
|
|
case Gallery_Key::Backspace:
|
|
return ::renderive::Key::Backspace;
|
|
case Gallery_Key::Left:
|
|
return ::renderive::Key::Left;
|
|
case Gallery_Key::Right:
|
|
return ::renderive::Key::Right;
|
|
case Gallery_Key::Up:
|
|
return ::renderive::Key::Up;
|
|
case Gallery_Key::Down:
|
|
return ::renderive::Key::Down;
|
|
case Gallery_Key::Unknown:
|
|
return ::renderive::Key::Unknown;
|
|
}
|
|
return ::renderive::Key::Unknown;
|
|
}
|
|
|
|
Visual_Family visual_family(std::string_view case_id) {
|
|
if (case_id == "datoviz_splat") return Visual_Family::Splat;
|
|
if (case_id == "datoviz_pixel") return Visual_Family::Pixel;
|
|
if (case_id == "datoviz_marker") return Visual_Family::Marker;
|
|
if (case_id == "datoviz_sphere") return Visual_Family::Sphere;
|
|
if (case_id == "datoviz_segment") return Visual_Family::Segment;
|
|
if (case_id == "datoviz_vector") return Visual_Family::Vector;
|
|
if (case_id == "datoviz_primitive") return Visual_Family::Primitive;
|
|
if (case_id == "datoviz_mesh") return Visual_Family::Mesh;
|
|
if (case_id == "datoviz_path") return Visual_Family::Path;
|
|
if (case_id == "datoviz_image") return Visual_Family::Image;
|
|
if (case_id == "datoviz_labels") return Visual_Family::Labels;
|
|
if (case_id == "datoviz_glyph") return Visual_Family::Glyph;
|
|
if (case_id == "datoviz_text") return Visual_Family::Text;
|
|
if (case_id == "datoviz_volume") return Visual_Family::Volume;
|
|
return Visual_Family::Point;
|
|
}
|
|
|
|
std::optional<double> action_number(
|
|
const Gallery_Action_Request& request) {
|
|
if (!request.argument ||
|
|
!std::holds_alternative<double>(*request.argument))
|
|
return std::nullopt;
|
|
return std::get<double>(*request.argument);
|
|
}
|
|
|
|
class Gallery_Scene3D final : public Gallery_Scene_Interface {
|
|
public:
|
|
Gallery_Scene3D(std::uint64_t session_id, std::string case_id,
|
|
Gallery_Frame_Mode mode, bool automatic_low_latency)
|
|
: session_id_(session_id), case_id_(std::move(case_id)),
|
|
frame_mode_(mode), automatic_low_latency_(automatic_low_latency),
|
|
visual_(Point_Visual::Builder{}.build(initial_points())),
|
|
scene_(Scene_Options{.viewport = {560, 320},
|
|
.frame_mode = render_frame_mode(mode),
|
|
.maximum_frames_per_second = 30.0,
|
|
.visual_family = visual_family(case_id_)},
|
|
visual_) {
|
|
if (!visual_)
|
|
throw std::runtime_error("failed to create Point_Visual");
|
|
visual_->set_object_name("3D Visual");
|
|
render_initial_frame();
|
|
}
|
|
|
|
[[nodiscard]] const std::string& case_id() const noexcept override {
|
|
return case_id_;
|
|
}
|
|
|
|
[[nodiscard]] nlohmann::json controls() const override {
|
|
const auto observation = visual_->observation();
|
|
return {{"resources", nlohmann::json::array()},
|
|
{"observers",
|
|
nlohmann::json::array({
|
|
{{"descriptor", adminive::to_descriptor_json<
|
|
nlohmann::json,
|
|
Renderable_Observation>()},
|
|
{"data", adminive::to_frontend_json<nlohmann::json>(
|
|
observation)},
|
|
{"target", "point-visual"},
|
|
{"title", "3D Visual"}}})},
|
|
{"render_plan",
|
|
gallery_render_plan_json(scene_.render_scene())},
|
|
{"performance_capture",
|
|
gallery_performance_capture_json(scene_.render_scene())}};
|
|
}
|
|
|
|
[[nodiscard]] Gallery_Frame_Mode frame_mode() const noexcept override {
|
|
return frame_mode_;
|
|
}
|
|
|
|
void reset_monitoring() override {
|
|
render_attempt_count_ = 0;
|
|
successful_render_count_ = 0;
|
|
pixel_frame_count_ = 0;
|
|
last_render_ms_ = 0.0;
|
|
last_pixel_encode_ms_ = 0.0;
|
|
render_history_.clear();
|
|
pixel_history_.clear();
|
|
last_event_ = "monitoring_reset";
|
|
}
|
|
|
|
[[nodiscard]] bool can_render_automatically() const noexcept override {
|
|
return active_ && automatic_low_latency_ &&
|
|
frame_mode_ == Gallery_Frame_Mode::Low_Latency;
|
|
}
|
|
|
|
[[nodiscard]] std::uint64_t kernel_refresh_interval_ns() const override {
|
|
return scene_.frame_status().next_refresh_interval_ns;
|
|
}
|
|
|
|
void set_client_metrics(Gallery_Client_Performance metrics) noexcept override {
|
|
client_performance_ = metrics;
|
|
}
|
|
|
|
[[nodiscard]] adminive::Update_Result apply_patch(
|
|
std::string_view, const nlohmann::json&) override {
|
|
adminive::Update_Result result;
|
|
result.success = false;
|
|
result.message = "Render3D gallery has no patchable controls";
|
|
return result;
|
|
}
|
|
|
|
void resize(int width, int height) override {
|
|
if (width <= 0 || height <= 0)
|
|
return;
|
|
scene_.resize({static_cast<std::uint32_t>(width),
|
|
static_cast<std::uint32_t>(height)});
|
|
last_event_ = "resized";
|
|
}
|
|
|
|
void dispatch(const Gallery_Input_Event& event) override {
|
|
if (const auto* view = std::get_if<Gallery_View_Input>(&event)) {
|
|
if (view->type == Gallery_View_Input_Type::Show)
|
|
active_ = true;
|
|
else if (view->type == Gallery_View_Input_Type::Hide)
|
|
active_ = false;
|
|
return;
|
|
}
|
|
if (const auto* pointer = std::get_if<Gallery_Pointer_Input>(&event)) {
|
|
::renderive::Basic_Pointer_Event<Gallery_Point> input(
|
|
event_type(pointer->type));
|
|
input.position = {pointer->x, pointer->y};
|
|
input.global_position = {pointer->global_x, pointer->global_y};
|
|
input.button = mouse_button(pointer->button);
|
|
input.buttons = pointer->buttons;
|
|
input.modifiers = static_cast<::renderive::Keyboard_Modifier>(
|
|
pointer->modifiers);
|
|
scene_.dispatch(input);
|
|
return;
|
|
}
|
|
if (const auto* wheel = std::get_if<Gallery_Wheel_Input>(&event)) {
|
|
::renderive::Basic_Wheel_Event<Gallery_Point> input;
|
|
input.position = {wheel->x, wheel->y};
|
|
input.global_position = {wheel->global_x, wheel->global_y};
|
|
input.pixel_delta_x = wheel->pixel_delta_x;
|
|
input.pixel_delta_y = wheel->pixel_delta_y;
|
|
input.angle_delta_x = wheel->angle_delta_x;
|
|
input.angle_delta_y = wheel->angle_delta_y;
|
|
input.modifiers = static_cast<::renderive::Keyboard_Modifier>(
|
|
wheel->modifiers);
|
|
scene_.dispatch(input);
|
|
return;
|
|
}
|
|
const auto& keyboard = std::get<Gallery_Key_Input>(event);
|
|
::renderive::Key_Event input(
|
|
keyboard.type == Gallery_Key_Input_Type::Release
|
|
? ::renderive::Event_Type::Key_Release
|
|
: ::renderive::Event_Type::Key_Press);
|
|
input.key = key(keyboard.key);
|
|
input.native_key = keyboard.native_key;
|
|
input.modifiers = static_cast<::renderive::Keyboard_Modifier>(
|
|
keyboard.modifiers);
|
|
input.auto_repeat = keyboard.auto_repeat;
|
|
scene_.dispatch(input);
|
|
}
|
|
|
|
bool render_latest_frame() override {
|
|
if (!can_render_automatically())
|
|
return false;
|
|
advance_blue_point(0.025F);
|
|
return render_request();
|
|
}
|
|
|
|
[[nodiscard]] std::optional<std::string> encode_latest_pixels() override {
|
|
if (!active_)
|
|
return std::nullopt;
|
|
const auto frame = scene_.latest_frame();
|
|
if (!frame || frame->rgba8.empty())
|
|
return std::nullopt;
|
|
return encode_rgba8_pixel_frame(
|
|
frame->rgba8.data(), frame->extent.width, frame->extent.height,
|
|
frame->extent.width * 4U, frame->sequence);
|
|
}
|
|
|
|
void record_pixel_response(
|
|
std::chrono::steady_clock::time_point,
|
|
std::chrono::steady_clock::time_point encode_started,
|
|
std::chrono::steady_clock::time_point encode_finished,
|
|
std::size_t pixel_bytes) override {
|
|
++pixel_frame_count_;
|
|
last_pixel_bytes_ = pixel_bytes;
|
|
last_pixel_encode_ms_ = std::chrono::duration<double, std::milli>(
|
|
encode_finished - encode_started)
|
|
.count();
|
|
record_sample(pixel_history_, encode_finished);
|
|
}
|
|
|
|
[[nodiscard]] std::string action(const Gallery_Action_Request& request,
|
|
bool& recognized) override {
|
|
recognized = true;
|
|
if (request.id == "capture_next_frame") {
|
|
auto& scene = scene_.render_scene();
|
|
if (scene.capture_state().enabled())
|
|
return "A performance capture session is already active";
|
|
const auto capture = scene.capture_next_frame();
|
|
if (!capture) {
|
|
if (capture.error == Capture_Error::session_active)
|
|
return "A performance capture session is already active";
|
|
throw std::logic_error("validated capture request rejected");
|
|
}
|
|
last_action_result_ =
|
|
"capture session=" + std::to_string(capture.session_id);
|
|
return "Next 3D frame capture requested";
|
|
}
|
|
if (request.id == "capture_frames") {
|
|
const auto argument = action_number(request);
|
|
if (!argument) {
|
|
recognized = false;
|
|
return {};
|
|
}
|
|
const std::size_t count = static_cast<std::size_t>(
|
|
std::clamp(std::llround(*argument), 1LL, 120LL));
|
|
auto& scene = scene_.render_scene();
|
|
if (scene.capture_state().enabled())
|
|
return "A performance capture session is already active";
|
|
const auto capture = scene.capture_frames(count);
|
|
if (!capture) {
|
|
if (capture.error == Capture_Error::session_active)
|
|
return "A performance capture session is already active";
|
|
throw std::logic_error("validated capture request rejected");
|
|
}
|
|
last_action_result_ = "capture session=" +
|
|
std::to_string(capture.session_id) +
|
|
" frames=" + std::to_string(count);
|
|
return "Consecutive 3D frame capture requested";
|
|
}
|
|
if (request.id == "mode_prepare" || request.id == "mode_enqueue") {
|
|
if (frame_mode_ == Gallery_Frame_Mode::Playback) {
|
|
blue_offset_ = std::min(0.25F, blue_offset_ + 0.0125F);
|
|
publish_points();
|
|
}
|
|
const bool prepared = scene_.prepare_frame();
|
|
last_event_ = prepared ? "frame_prepared" : "prepare_rejected";
|
|
return prepared ? "3D frame prepared" : "3D frame prepare rejected";
|
|
}
|
|
if (request.id == "mode_refresh") {
|
|
const bool refreshed = scene_.refresh_manual_frame();
|
|
last_event_ = refreshed ? "manual_refreshed" : "manual_refresh_failed";
|
|
return refreshed ? "Manual 3D frame refreshed" : "No manual frame to refresh";
|
|
}
|
|
if (request.id == "mode_render" || request.id == "mode_dequeue") {
|
|
const bool rendered = render_prepared();
|
|
last_event_ = rendered ? "frame_rendered" : "render_rejected";
|
|
return rendered ? "3D frame rendered" : "No 3D frame to render";
|
|
}
|
|
if (request.id == "move_mesh") {
|
|
advance_blue_point(0.08F);
|
|
last_event_ = "mesh_moved";
|
|
return "blue point moved";
|
|
}
|
|
if (request.id == "add_mesh") {
|
|
extra_attached_ = true;
|
|
publish_points();
|
|
last_event_ = "mesh_added";
|
|
return "yellow point added";
|
|
}
|
|
if (request.id == "remove_mesh") {
|
|
extra_attached_ = false;
|
|
publish_points();
|
|
last_event_ = "mesh_removed";
|
|
return "yellow point removed";
|
|
}
|
|
if (request.id == "mesh_churn") {
|
|
last_event_ = "mesh_churned";
|
|
return "point attach/detach lifecycle completed";
|
|
}
|
|
if (request.id == "reset" || request.id == "reset_camera") {
|
|
blue_offset_ = 0.0F;
|
|
projection_scale_ = 1.0F;
|
|
visual_->set<&Point_State::transform>(transform());
|
|
publish_points();
|
|
last_event_ = "camera_reset";
|
|
return "3D view reset";
|
|
}
|
|
if (request.id == "toggle_projection") {
|
|
projection_scale_ = projection_scale_ == 1.0F ? 0.72F : 1.0F;
|
|
visual_->set<&Point_State::transform>(transform());
|
|
last_event_ = "projection_toggled";
|
|
return "3D projection toggled";
|
|
}
|
|
if (request.id == "toggle_texture") {
|
|
texture_enabled_ = !texture_enabled_;
|
|
publish_points();
|
|
last_event_ = "texture_toggled";
|
|
return texture_enabled_ ? "point pattern enabled" : "point pattern disabled";
|
|
}
|
|
if (request.id == "change_text") {
|
|
alternate_text_ = !alternate_text_;
|
|
publish_points();
|
|
last_event_ = "text_changed";
|
|
return "point annotation representation changed";
|
|
}
|
|
recognized = false;
|
|
return {};
|
|
}
|
|
|
|
void record_action_notice_if_empty(std::string_view notice) override {
|
|
if (last_action_result_.empty())
|
|
last_action_result_ = std::string(notice);
|
|
}
|
|
|
|
[[nodiscard]] std::string telemetry_json() const override {
|
|
const auto status = scene_.frame_status();
|
|
const auto now = std::chrono::steady_clock::now();
|
|
const auto scene_execution = scene_.render_scene().execution_statistics();
|
|
const auto runtime = scene_.runtime_statistics();
|
|
return nlohmann::json{
|
|
{"kernel_observer",
|
|
{{"mode", mode_id(frame_mode_)},
|
|
{"last_event", last_event_},
|
|
{"pending_frame_count", status.pending_frame_count},
|
|
{"dropped_frame_count", status.dropped_frame_count}}},
|
|
{"performance",
|
|
{{"render_attempt_count", render_attempt_count_},
|
|
{"successful_render_count", successful_render_count_},
|
|
{"failed_render_count",
|
|
render_attempt_count_ - successful_render_count_},
|
|
{"measured_fps", recent_rate(render_history_, now)},
|
|
{"last_render_ms", last_render_ms_},
|
|
{"pixel_response_fps", recent_rate(pixel_history_, now)},
|
|
{"last_pixel_encode_ms", last_pixel_encode_ms_},
|
|
{"last_pixel_bytes", last_pixel_bytes_},
|
|
{"automatic_low_latency_scheduler", can_render_automatically()}}},
|
|
{"client_performance",
|
|
{{"transport_fps", client_performance_.transport_fps},
|
|
{"presentation_fps", client_performance_.presentation_fps},
|
|
{"websocket_buffered_bytes",
|
|
client_performance_.websocket_buffered_bytes}}},
|
|
{"scheduler", adminive::to_frontend_json<nlohmann::json>(
|
|
renderive::scheduling::scheduler_statistics())},
|
|
{"queue_pressure",
|
|
{{"scene_execution",
|
|
{{"capacity", scene_execution.capacity},
|
|
{"queued", scene_execution.queued},
|
|
{"peak_queued", scene_execution.peak_queued},
|
|
{"backpressure_count", scene_execution.backpressure_count},
|
|
{"backpressure_wait_ns", scene_execution.backpressure_wait_ns}}},
|
|
{"render_domain",
|
|
{{"capacity", runtime.render_domain.capacity},
|
|
{"active", runtime.render_domain.active},
|
|
{"peak_active", runtime.render_domain.peak_active},
|
|
{"queued", runtime.render_domain.queued},
|
|
{"peak_queued", runtime.render_domain.peak_queued},
|
|
{"backpressure_count", runtime.render_domain.backpressure_count},
|
|
{"backpressure_wait_ns", runtime.render_domain.backpressure_wait_ns},
|
|
{"unhandled_exception_count",
|
|
runtime.render_domain_unhandled_exception_count}}},
|
|
{"gpu_completion",
|
|
{{"capacity", runtime.gpu_completion.capacity},
|
|
{"active", runtime.gpu_completion.active},
|
|
{"peak_active", runtime.gpu_completion.peak_active},
|
|
{"queued", runtime.gpu_completion.queued},
|
|
{"peak_queued", runtime.gpu_completion.peak_queued},
|
|
{"backpressure_count", runtime.gpu_completion.backpressure_count},
|
|
{"backpressure_wait_ns", runtime.gpu_completion.backpressure_wait_ns},
|
|
{"fault_count", runtime.gpu_completion_fault_count},
|
|
{"abandoned_count", runtime.gpu_completion_abandoned_count}}}}},
|
|
{"session_id", session_id_},
|
|
{"performance_capture",
|
|
gallery_performance_capture_json(scene_.render_scene())},
|
|
{"last_action", last_action_result_}}
|
|
.dump();
|
|
}
|
|
|
|
private:
|
|
using Performance_Clock = std::chrono::steady_clock;
|
|
|
|
static void record_sample(std::deque<Performance_Clock::time_point>& samples,
|
|
Performance_Clock::time_point now) {
|
|
samples.push_back(now);
|
|
const auto cutoff = now - std::chrono::seconds(2);
|
|
while (!samples.empty() && samples.front() < cutoff)
|
|
samples.pop_front();
|
|
}
|
|
|
|
static double recent_rate(
|
|
const std::deque<Performance_Clock::time_point>& samples,
|
|
Performance_Clock::time_point now) {
|
|
const auto cutoff = now - std::chrono::seconds(1);
|
|
const auto first = std::find_if(samples.begin(), samples.end(),
|
|
[cutoff](auto sample) {
|
|
return sample >= cutoff;
|
|
});
|
|
const auto count = std::distance(first, samples.end());
|
|
if (count < 2)
|
|
return 0.0;
|
|
const double seconds = std::chrono::duration<double>(samples.back() - *first).count();
|
|
return seconds > 0.0 ? static_cast<double>(count - 1) / seconds : 0.0;
|
|
}
|
|
|
|
static std::vector<Point> initial_points() {
|
|
return {{{-0.62F, -0.10F, 0.0F}, {255, 32, 24, 255}, 74.0F},
|
|
{{0.0F, -0.10F, 0.0F}, {24, 255, 36, 255}, 74.0F},
|
|
{{0.62F, -0.10F, 0.0F}, {28, 52, 255, 255}, 74.0F}};
|
|
}
|
|
|
|
Matrix4 transform() const {
|
|
Matrix4 value;
|
|
value.values[0] = projection_scale_;
|
|
value.values[5] = projection_scale_;
|
|
return value;
|
|
}
|
|
|
|
void publish_points() {
|
|
auto points = initial_points();
|
|
points[2].position.x += blue_offset_;
|
|
if (!texture_enabled_)
|
|
points[0].color = {255, 128, 128, 255};
|
|
if (alternate_text_) {
|
|
points[1].diameter_px = 46.0F;
|
|
points[1].color = {24, 220, 255, 255};
|
|
}
|
|
if (extra_attached_)
|
|
points.push_back({{0.0F, 0.62F, 0.0F}, {255, 230, 20, 255}, 58.0F});
|
|
visual_->update_points(std::move(points));
|
|
}
|
|
|
|
void advance_blue_point(float step) {
|
|
blue_offset_ += step;
|
|
if (blue_offset_ > 0.32F)
|
|
blue_offset_ = -0.32F;
|
|
publish_points();
|
|
}
|
|
|
|
void render_initial_frame() {
|
|
if (frame_mode_ == Gallery_Frame_Mode::Manual) {
|
|
if (scene_.prepare_frame() && scene_.refresh_manual_frame())
|
|
render_prepared();
|
|
} else {
|
|
render_request();
|
|
}
|
|
}
|
|
|
|
bool render_request() {
|
|
const auto started = std::chrono::steady_clock::now();
|
|
++render_attempt_count_;
|
|
const bool rendered = scene_.request_frame();
|
|
last_render_ms_ = std::chrono::duration<double, std::milli>(
|
|
std::chrono::steady_clock::now() - started)
|
|
.count();
|
|
successful_render_count_ += rendered ? 1U : 0U;
|
|
if (rendered)
|
|
record_sample(render_history_, std::chrono::steady_clock::now());
|
|
return rendered;
|
|
}
|
|
|
|
bool render_prepared() {
|
|
const auto started = std::chrono::steady_clock::now();
|
|
++render_attempt_count_;
|
|
const bool rendered = scene_.render_prepared_frame();
|
|
last_render_ms_ = std::chrono::duration<double, std::milli>(
|
|
std::chrono::steady_clock::now() - started)
|
|
.count();
|
|
successful_render_count_ += rendered ? 1U : 0U;
|
|
if (rendered)
|
|
record_sample(render_history_, std::chrono::steady_clock::now());
|
|
return rendered;
|
|
}
|
|
|
|
std::uint64_t session_id_{};
|
|
std::string case_id_;
|
|
Gallery_Frame_Mode frame_mode_{Gallery_Frame_Mode::Low_Latency};
|
|
bool automatic_low_latency_{};
|
|
bool active_{true};
|
|
std::shared_ptr<Point_Visual> visual_;
|
|
Point_Scene scene_;
|
|
Gallery_Client_Performance client_performance_;
|
|
float blue_offset_{};
|
|
float projection_scale_{1.0F};
|
|
bool extra_attached_{};
|
|
bool texture_enabled_{true};
|
|
bool alternate_text_{};
|
|
std::uint64_t render_attempt_count_{};
|
|
std::uint64_t successful_render_count_{};
|
|
std::uint64_t pixel_frame_count_{};
|
|
double last_render_ms_{};
|
|
double last_pixel_encode_ms_{};
|
|
std::size_t last_pixel_bytes_{};
|
|
std::deque<Performance_Clock::time_point> render_history_;
|
|
std::deque<Performance_Clock::time_point> pixel_history_;
|
|
std::string last_event_{"initialized"};
|
|
std::string last_action_result_;
|
|
};
|
|
} // namespace
|
|
|
|
std::unique_ptr<Gallery_Scene_Interface> make_gallery_scene_3d(
|
|
std::uint64_t session_id, std::string case_id, Gallery_Frame_Mode mode,
|
|
bool automatic_low_latency) {
|
|
return std::make_unique<Gallery_Scene3D>(
|
|
session_id, std::move(case_id), mode, automatic_low_latency);
|
|
}
|
|
|
|
} // namespace renderive::web
|