仍有bug
This commit is contained in:
@@ -1,196 +1,52 @@
|
||||
#include <renderive/error/Error_Policy.hpp>
|
||||
#include "Gallery_Scene3D.h"
|
||||
|
||||
#include "../Gallery_Capture_Json.h"
|
||||
#include "../Gallery_Enum.h"
|
||||
#include "../common/Gallery_Scheduler_Adminive.h"
|
||||
#include "../common/Observer_Json.h"
|
||||
#include "../common/Pixel_Frame.h"
|
||||
#include <render_3D/Render_Scene_3D.h>
|
||||
|
||||
#include "adminive/adminive.hpp"
|
||||
#include "adminive/adapters/nlohmann_json.hpp"
|
||||
#include "render_3D/Render_Scene_3D.h"
|
||||
|
||||
#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)
|
||||
::renderive::error::unexpected<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 {
|
||||
template <>
|
||||
struct Observer_Pfr_Adapter<renderive::render_3d::Render_Scene_3D::State> {
|
||||
using Scene = renderive::render_3d::Render_Scene_3D;
|
||||
using Type = adminive::Boost_Pfr_Base_Reflection_Adapter<
|
||||
Scene::State, Scene::State_Fields>;
|
||||
};
|
||||
template <>
|
||||
struct Observer_Pfr_Adapter<renderive::render_3d::Render_Scene_3D::Observer> {
|
||||
using Scene = renderive::render_3d::Render_Scene_3D;
|
||||
using Type = adminive::Boost_Pfr_Base_Reflection_Adapter<
|
||||
Scene::Observer, Scene::Observer_Fields>;
|
||||
};
|
||||
namespace {
|
||||
using namespace renderive::render_3d;
|
||||
Gallery_Render_Result gallery_render_error(Frame_Request_Result error) {
|
||||
switch (error) {
|
||||
case Frame_Request_Result::none:
|
||||
return Gallery_Render_Result::none;
|
||||
case Frame_Request_Result::cancelled:
|
||||
return Gallery_Render_Result::cancelled;
|
||||
case Frame_Request_Result::deadline_exceeded:
|
||||
return Gallery_Render_Result::deadline_exceeded;
|
||||
case Frame_Request_Result::external_failure:
|
||||
return Gallery_Render_Result::backend_failure;
|
||||
case Frame_Request_Result::scene_shutting_down:
|
||||
return Gallery_Render_Result::shutting_down;
|
||||
default:
|
||||
return Gallery_Render_Result::frame_unavailable;
|
||||
}
|
||||
}
|
||||
Gallery_Render_Result gallery_render_error(Scene_Render_Result error) {
|
||||
switch (error) {
|
||||
case Scene_Render_Result::none:
|
||||
return Gallery_Render_Result::none;
|
||||
case Scene_Render_Result::cancelled:
|
||||
return Gallery_Render_Result::cancelled;
|
||||
case Scene_Render_Result::deadline_exceeded:
|
||||
return Gallery_Render_Result::deadline_exceeded;
|
||||
case Scene_Render_Result::external_failure:
|
||||
return Gallery_Render_Result::backend_failure;
|
||||
case Scene_Render_Result::shutting_down:
|
||||
return Gallery_Render_Result::shutting_down;
|
||||
}
|
||||
::renderive::error::unexpected<std::logic_error>("unknown scene render error");
|
||||
}
|
||||
Frame_Request_Result frame_request_error(Scene_Render_Result error) {
|
||||
switch (error) {
|
||||
case Scene_Render_Result::none:
|
||||
return Frame_Request_Result::none;
|
||||
case Scene_Render_Result::cancelled:
|
||||
return Frame_Request_Result::cancelled;
|
||||
case Scene_Render_Result::deadline_exceeded:
|
||||
return Frame_Request_Result::deadline_exceeded;
|
||||
case Scene_Render_Result::external_failure:
|
||||
return Frame_Request_Result::external_failure;
|
||||
case Scene_Render_Result::shutting_down:
|
||||
return Frame_Request_Result::scene_shutting_down;
|
||||
}
|
||||
::renderive::error::unexpected<std::logic_error>("unknown scene render error");
|
||||
}
|
||||
|
||||
struct Gallery_Point {
|
||||
double x{};
|
||||
double y{};
|
||||
};
|
||||
|
||||
Frame_Mode render_frame_mode(Gallery_Frame_Mode mode) {
|
||||
std::shared_ptr<Frame_Control_Strategy_Base> make_strategy(
|
||||
Gallery_Frame_Mode mode) {
|
||||
switch (mode) {
|
||||
case Gallery_Frame_Mode::Manual:
|
||||
return Frame_Mode::Manual;
|
||||
return std::make_shared<Manual_Render_Scene_3D_Strategy>();
|
||||
case Gallery_Frame_Mode::Low_Latency:
|
||||
return Frame_Mode::Low_Latency;
|
||||
return std::make_shared<Low_Latency_Render_Scene_3D_Strategy>();
|
||||
case Gallery_Frame_Mode::Playback:
|
||||
return Frame_Mode::Playback;
|
||||
return std::make_shared<Playback_Render_Scene_3D_Strategy>();
|
||||
}
|
||||
::renderive::error::unexpected<std::invalid_argument>("unknown gallery frame mode");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string mode_id(Gallery_Frame_Mode mode) {
|
||||
std::string_view 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";
|
||||
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;
|
||||
return "low_latency";
|
||||
}
|
||||
|
||||
Visual_Family visual_family(std::string_view case_id) {
|
||||
@@ -211,501 +67,160 @@ Visual_Family visual_family(std::string_view case_id) {
|
||||
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_)
|
||||
::renderive::error::unexpected<std::runtime_error>("failed to create Point_Visual");
|
||||
visual_->set_object_name("3D Visual");
|
||||
render_initial_frame();
|
||||
Gallery_Scene3D(std::string case_id, Gallery_Frame_Mode mode,
|
||||
bool automatic)
|
||||
: case_id_(std::move(case_id)), mode_(mode), automatic_(automatic) {
|
||||
rebuild();
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string& case_id() const noexcept override {
|
||||
return case_id_;
|
||||
}
|
||||
const std::string& case_id() const noexcept override { return case_id_; }
|
||||
|
||||
[[nodiscard]] nlohmann::json controls() const override {
|
||||
const auto observation = visual_->observation();
|
||||
nlohmann::json controls() const override {
|
||||
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())}};
|
||||
{"observers", nlohmann::json::array()},
|
||||
{"render_plan", gallery_render_plan_json(*scene_)},
|
||||
{"performance_capture", gallery_performance_capture_json(*scene_)}};
|
||||
}
|
||||
|
||||
[[nodiscard]] Gallery_Frame_Mode frame_mode() const noexcept override {
|
||||
return frame_mode_;
|
||||
Gallery_Frame_Mode frame_mode() const noexcept override { return mode_; }
|
||||
void reset_monitoring() override { render_count_ = 0; }
|
||||
bool can_render_automatically() const noexcept override {
|
||||
return automatic_ && mode_ == Gallery_Frame_Mode::Low_Latency;
|
||||
}
|
||||
|
||||
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";
|
||||
std::uint64_t kernel_refresh_interval_ns() const override {
|
||||
return scene_->observer().next_refresh_interval_ns;
|
||||
}
|
||||
|
||||
[[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;
|
||||
client_ = metrics;
|
||||
}
|
||||
|
||||
[[nodiscard]] adminive::Update_Result apply_patch(
|
||||
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;
|
||||
return {false, "no editable resource matches the requested target"};
|
||||
}
|
||||
|
||||
void resize(int width, int height) override {
|
||||
if (width <= 0 || height <= 0)
|
||||
const Extent requested{static_cast<std::uint32_t>(std::max(1, width)),
|
||||
static_cast<std::uint32_t>(std::max(1, height))};
|
||||
if (requested.width == extent_.width && requested.height == extent_.height)
|
||||
return;
|
||||
const auto error = scene_.resize({static_cast<std::uint32_t>(width),
|
||||
static_cast<std::uint32_t>(height)});
|
||||
if (error != ::renderive::render_3d::Scene_Control_Error::none) {
|
||||
last_event_ = "resize_rejected";
|
||||
return;
|
||||
}
|
||||
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);
|
||||
if (scene_.dispatch(input) !=
|
||||
::renderive::render_3d::Scene_Control_Error::none)
|
||||
last_event_ = "pointer_rejected";
|
||||
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);
|
||||
if (scene_.dispatch(input) !=
|
||||
::renderive::render_3d::Scene_Control_Error::none)
|
||||
last_event_ = "wheel_rejected";
|
||||
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;
|
||||
if (scene_.dispatch(input) !=
|
||||
::renderive::render_3d::Scene_Control_Error::none)
|
||||
last_event_ = "key_rejected";
|
||||
extent_ = requested;
|
||||
rebuild();
|
||||
}
|
||||
void dispatch(const Gallery_Input_Event&) override {}
|
||||
|
||||
Gallery_Render_Result render_latest_frame() override {
|
||||
if (!can_render_automatically())
|
||||
return Gallery_Render_Result::inactive;
|
||||
advance_blue_point(0.025F);
|
||||
const auto request_error = render_request();
|
||||
return gallery_render_error(request_error);
|
||||
if (scene_->request_frame() != Frame_Request_Result::none)
|
||||
return Gallery_Render_Result::backend_failure;
|
||||
++render_count_;
|
||||
return Gallery_Render_Result::none;
|
||||
}
|
||||
|
||||
[[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(
|
||||
std::optional<std::string> encode_latest_pixels() override {
|
||||
const auto frame = scene_->latest_frame();
|
||||
if (!frame || frame->rgba8.empty()) return std::nullopt;
|
||||
auto pixels = encode_rgba8_pixel_frame(
|
||||
frame->rgba8.data(), frame->extent.width, frame->extent.height,
|
||||
frame->extent.width * 4U, frame->sequence);
|
||||
return pixels.empty() ? std::nullopt
|
||||
: std::optional<std::string>(std::move(pixels));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
void record_pixel_response(std::chrono::steady_clock::time_point,
|
||||
std::chrono::steady_clock::time_point,
|
||||
std::chrono::steady_clock::time_point,
|
||||
std::size_t) override {}
|
||||
|
||||
[[nodiscard]] std::string action(const Gallery_Action_Request& request,
|
||||
bool& recognized) override {
|
||||
std::string action(const Gallery_Action_Request& request,
|
||||
bool& recognized) override {
|
||||
recognized = true;
|
||||
if (request.id == "frame_strategy_manual" ||
|
||||
request.id == "frame_strategy_low_latency" ||
|
||||
request.id == "frame_strategy_playback") {
|
||||
const auto next = request.id == "frame_strategy_manual"
|
||||
? Gallery_Frame_Mode::Manual
|
||||
: request.id == "frame_strategy_playback"
|
||||
? Gallery_Frame_Mode::Playback
|
||||
: Gallery_Frame_Mode::Low_Latency;
|
||||
const auto operation = scene_->edit_renderables(
|
||||
[strategy = make_strategy(next)](
|
||||
Scene_3D_Base::Renderable_Editor& editor) mutable {
|
||||
editor.replace_frame_control_strategy(std::move(strategy));
|
||||
});
|
||||
if (operation.wait() != Scene_Edit_Error::none)
|
||||
return "frame strategy replacement rejected";
|
||||
mode_ = next;
|
||||
return "frame strategy replaced";
|
||||
}
|
||||
if (request.id == "mode_cycle" || request.id == "mode_render") {
|
||||
static_cast<void>(render_latest_frame());
|
||||
return "frame rendered";
|
||||
}
|
||||
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";
|
||||
::renderive::error::unexpected<std::logic_error>("validated capture request rejected");
|
||||
}
|
||||
last_action_result_ =
|
||||
"capture session=" + std::to_string(capture.session_id);
|
||||
return "Next 3D frame capture requested";
|
||||
const auto capture = scene_->capture_next_frame();
|
||||
if (capture) static_cast<void>(render_latest_frame());
|
||||
return capture ? "frame captured" : "capture request rejected";
|
||||
}
|
||||
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";
|
||||
::renderive::error::unexpected<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() == Frame_Request_Result::none;
|
||||
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() == Frame_Request_Result::none;
|
||||
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() == Frame_Request_Result::none;
|
||||
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";
|
||||
std::size_t count = 20;
|
||||
if (request.argument && std::holds_alternative<double>(*request.argument))
|
||||
count = static_cast<std::size_t>(std::clamp(
|
||||
std::get<double>(*request.argument), 1.0, 1000.0));
|
||||
const auto capture = scene_->capture_frames(count);
|
||||
if (capture) static_cast<void>(render_latest_frame());
|
||||
return capture ? "frame capture started" : "capture request rejected";
|
||||
}
|
||||
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);
|
||||
}
|
||||
void record_action_notice_if_empty(std::string_view) override {}
|
||||
|
||||
[[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();
|
||||
std::string telemetry_json() const override {
|
||||
const auto observer = scene_->observer();
|
||||
auto kernel_observer = observer_json(observer);
|
||||
kernel_observer["mode"] = mode_id(mode_);
|
||||
kernel_observer["event"] = "render_completed";
|
||||
kernel_observer["limit"] = "none";
|
||||
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}}},
|
||||
{"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_}}
|
||||
{"kernel_observer", std::move(kernel_observer)},
|
||||
{"performance", {{"successful_render_count", render_count_}}},
|
||||
{"client_performance", {{"transport_fps", client_.transport_fps}}},
|
||||
{"performance_capture", gallery_performance_capture_json(*scene_)}}
|
||||
.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();
|
||||
void rebuild() {
|
||||
std::vector<Point> points{
|
||||
{{-0.55F, -0.25F, 0.0F}, {255, 80, 80, 255}, 24.0F},
|
||||
{{0.0F, 0.45F, 0.0F}, {80, 255, 120, 255}, 28.0F},
|
||||
{{0.55F, -0.1F, 0.0F}, {80, 140, 255, 255}, 26.0F}};
|
||||
visual_ = Point_Visual::Builder{}.build(std::move(points));
|
||||
visual_->set_object_name(case_id_ + " Points");
|
||||
Render_Scene_3D::State state;
|
||||
state.viewport = extent_;
|
||||
state.visual_family = visual_family(case_id_);
|
||||
scene_ = Render_Scene_3D::Builder{state}.build(
|
||||
visual_, make_strategy(mode_));
|
||||
static_cast<void>(scene_->request_frame());
|
||||
}
|
||||
|
||||
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});
|
||||
if (visual_->update_points(std::move(points)) !=
|
||||
Visual_Data_Error::none)
|
||||
::renderive::error::unexpected<std::logic_error>("generated gallery point data is invalid");
|
||||
}
|
||||
|
||||
void advance_blue_point(float step) {
|
||||
blue_offset_ += step;
|
||||
if (blue_offset_ > 0.32F)
|
||||
blue_offset_ = -0.32F;
|
||||
publish_points();
|
||||
}
|
||||
|
||||
void render_initial_frame() {
|
||||
Frame_Request_Result error{Frame_Request_Result::none};
|
||||
if (frame_mode_ == Gallery_Frame_Mode::Manual) {
|
||||
if (scene_.prepare_frame() == Frame_Request_Result::none &&
|
||||
scene_.refresh_manual_frame() == Frame_Request_Result::none)
|
||||
error = render_prepared();
|
||||
else
|
||||
error = Frame_Request_Result::no_pending_frame;
|
||||
} else {
|
||||
error = render_request();
|
||||
}
|
||||
if (error != Frame_Request_Result::none)
|
||||
last_action_result_ = "initial frame is not available";
|
||||
}
|
||||
|
||||
Frame_Request_Result render_request() {
|
||||
const auto started = std::chrono::steady_clock::now();
|
||||
++render_attempt_count_;
|
||||
auto error = scene_.request_frame();
|
||||
if (error == Frame_Request_Result::none)
|
||||
error = frame_request_error(scene_.render_scene().wait_for_render());
|
||||
const bool rendered = error == Frame_Request_Result::none;
|
||||
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 error;
|
||||
}
|
||||
|
||||
Frame_Request_Result render_prepared() {
|
||||
const auto started = std::chrono::steady_clock::now();
|
||||
++render_attempt_count_;
|
||||
auto error = scene_.render_prepared_frame();
|
||||
if (error == Frame_Request_Result::none)
|
||||
error = frame_request_error(scene_.render_scene().wait_for_render());
|
||||
const bool rendered = error == Frame_Request_Result::none;
|
||||
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 error;
|
||||
}
|
||||
|
||||
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};
|
||||
Gallery_Frame_Mode mode_;
|
||||
bool automatic_{};
|
||||
Extent extent_{560, 320};
|
||||
std::shared_ptr<Point_Visual> visual_;
|
||||
Render_Scene_3D 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_;
|
||||
std::unique_ptr<Render_Scene_3D> scene_;
|
||||
Gallery_Client_Performance client_;
|
||||
std::uint64_t render_count_{};
|
||||
};
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Gallery_Scene_Interface> make_gallery_scene_3d(
|
||||
std::uint64_t session_id, std::string case_id, Gallery_Frame_Mode mode,
|
||||
std::uint64_t, 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);
|
||||
std::move(case_id), mode, automatic_low_latency);
|
||||
}
|
||||
|
||||
} // namespace renderive::web
|
||||
|
||||
Reference in New Issue
Block a user