走偏的一版
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/Gallery_Types.h"
|
||||
#include "renderive/scene/Scene2D_Context.hpp"
|
||||
#include <structive/property/property.hpp>
|
||||
|
||||
@@ -11,12 +12,6 @@
|
||||
|
||||
namespace renderive::web {
|
||||
|
||||
enum class Gallery_Frame_Mode : std::uint8_t {
|
||||
Manual,
|
||||
Low_Latency,
|
||||
Playback
|
||||
};
|
||||
|
||||
struct Gallery_Action_Category {};
|
||||
|
||||
struct Gallery_Action_Attribute {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Gallery_Enum.h"
|
||||
#include "common/Gallery_Performance_Types.h"
|
||||
#include "render_2D/scene/Scene.h"
|
||||
#include "render_2D/renderable/Renderable.h"
|
||||
|
||||
@@ -59,28 +60,6 @@ struct Gallery_Render_Performance {
|
||||
bool automatic_low_latency_scheduler{};
|
||||
};
|
||||
|
||||
struct Gallery_Client_Performance {
|
||||
double transport_fps{};
|
||||
double presentation_fps{};
|
||||
std::uint64_t websocket_buffered_bytes{};
|
||||
std::uint64_t changed_pixel_frames{};
|
||||
std::uint64_t duplicate_pixel_frames{};
|
||||
std::uint64_t frame_request_timeout_count{};
|
||||
double frame_round_trip_ms{};
|
||||
double frame_round_trip_average_ms{};
|
||||
double frame_round_trip_deviation_ms{};
|
||||
double frame_round_trip_p95_ms{};
|
||||
double frame_round_trip_p99_ms{};
|
||||
double display_interval_ms{};
|
||||
double display_interval_average_ms{};
|
||||
double display_interval_latest_ms{};
|
||||
double display_interval_p95_ms{};
|
||||
double display_interval_p99_ms{};
|
||||
double display_interval_deviation_ms{};
|
||||
std::uint64_t overwritten_pixel_frames{};
|
||||
double last_pixel_receive_age_ms{};
|
||||
double last_pixel_change_age_ms{};
|
||||
};
|
||||
|
||||
} // namespace renderive::web
|
||||
|
||||
|
||||
+103
-1658
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
||||
#include "Gallery_Protocol.h"
|
||||
#include "Gallery_Actions.h"
|
||||
#include "Gallery_Enum.h"
|
||||
#include "Gallery_Observer_Adminive.h"
|
||||
#include "Gallery_Renderables.h"
|
||||
@@ -52,9 +53,9 @@ const std::vector<Case_Model>& cases() {
|
||||
{"selection_overlay", "框选叠层", "Selection_Rectangle_Overlay", "交互控件",
|
||||
"鼠标框选、多区域保留、标注样式和清空。", 70},
|
||||
{"constellation", "星座图", "Constellation_Diagram", "点图控件",
|
||||
"PSK4/PSK8/PSK16 模式、相位、寿命、坐标范围和方形拟合。", 80}
|
||||
,{"point_3d", "Datoviz 3D 点组件", "Point Visual", "3D",
|
||||
"Kernel 状态双缓冲、实时批量点数据、单线程 Datoviz 渲染与 RGBA 回读。", 90}
|
||||
"PSK4/PSK8/PSK16 模式、相位、寿命、坐标范围和方形拟合。", 80},
|
||||
{"datoviz_3d", "Datoviz 3D", "Mesh / Texture / MSDF Text", "3D",
|
||||
"Render3D 通过 Kernel 帧策略驱动迁移后的 Datoviz Vulkan 后端;3D 的已发布帧禁止自动覆盖。", 90}
|
||||
};
|
||||
return value;
|
||||
}
|
||||
@@ -81,7 +82,9 @@ void append_session_actions(Gallery_Frame_Mode frame_mode,
|
||||
}
|
||||
throw std::invalid_argument("unknown gallery frame mode");
|
||||
}
|
||||
std::size_t session_control_count(Gallery_Frame_Mode frame_mode) {
|
||||
std::size_t session_control_count(std::string_view case_id, Gallery_Frame_Mode frame_mode) {
|
||||
if (case_id == "datoviz_3d")
|
||||
return 0;
|
||||
switch (frame_mode) {
|
||||
case Gallery_Frame_Mode::Manual:
|
||||
return gallery_control_count<Gallery_Session_Control<Manual_Scene2D>>();
|
||||
@@ -92,40 +95,39 @@ std::size_t session_control_count(Gallery_Frame_Mode frame_mode) {
|
||||
}
|
||||
throw std::invalid_argument("unknown gallery frame mode");
|
||||
}
|
||||
std::vector<Action_Model> registered_actions(std::string_view case_id,
|
||||
Gallery_Frame_Mode frame_mode) {
|
||||
void append_3d_actions(Gallery_Frame_Mode frame_mode, std::vector<Action_Model>& result) {
|
||||
const auto append = [&result](const Gallery_Action_Attribute& action) {
|
||||
result.push_back(gallery_action_model(action));
|
||||
};
|
||||
switch (frame_mode) {
|
||||
case Gallery_Frame_Mode::Manual:
|
||||
append(gallery_action("mode_prepare", "准备 3D 帧", "Manual_Scene3D::prepare_frame", "3D 帧策略"));
|
||||
append(gallery_action("mode_refresh", "提交手动刷新", "Manual_Scene3D::refresh", "3D 帧策略"));
|
||||
append(gallery_action("mode_render", "渲染已刷新帧", "Manual_Scene3D::render_frame", "3D 帧策略", {}, {}, {}, 0.0, true));
|
||||
break;
|
||||
case Gallery_Frame_Mode::Low_Latency:
|
||||
append(gallery_action("mode_prepare", "发布 3D 帧", "Scene3D::prepare_frame", "3D 帧策略"));
|
||||
append(gallery_action("mode_render", "消费 3D 帧", "Scene3D::render_frame", "3D 帧策略", {}, {}, {}, 0.0, true));
|
||||
break;
|
||||
case Gallery_Frame_Mode::Playback:
|
||||
append(gallery_action("mode_enqueue", "压入 3D 帧", "Playback_Scene3D::prepare_frame", "3D 帧策略"));
|
||||
append(gallery_action("mode_dequeue", "消费队首 3D 帧", "Playback_Scene3D::render_frame", "3D 帧策略", {}, {}, {}, 0.0, true));
|
||||
break;
|
||||
}
|
||||
append(gallery_action("move_mesh", "移动蓝色对象", "Mesh::place", "3D 场景"));
|
||||
append(gallery_action("add_mesh", "添加黄色对象", "Scene_Edit::attach", "3D 场景"));
|
||||
append(gallery_action("remove_mesh", "移除黄色对象", "Scene_Edit::detach", "3D 场景"));
|
||||
append(gallery_action("mesh_churn", "Attach/Detach 验证", "Scene_Edit::attach/detach", "3D 场景"));
|
||||
append(gallery_action("reset_camera", "重置相机", "Camera::look_at/use_perspective", "3D 相机"));
|
||||
append(gallery_action("toggle_projection", "切换投影", "Camera::use_perspective/use_orthographic", "3D 相机"));
|
||||
append(gallery_action("toggle_texture", "切换纹理", "Material::use_texture", "3D 材质"));
|
||||
append(gallery_action("change_text", "修改 MSDF 文本", "Text::set_string", "3D 文本"));
|
||||
append(gallery_action("reset", "恢复 3D 场景", "Gallery_Scene3D factory", "3D 场景"));
|
||||
}
|
||||
std::vector<Action_Model> registered_actions(std::string_view case_id, Gallery_Frame_Mode frame_mode) {
|
||||
std::vector<Action_Model> result;
|
||||
if (case_id == "point_3d") {
|
||||
const auto keep_frame_action = [frame_mode](std::string_view id) {
|
||||
switch (frame_mode) {
|
||||
case Gallery_Frame_Mode::Manual:
|
||||
return id == "mode_prepare" || id == "mode_refresh" ||
|
||||
id == "mode_render" || id == "mode_discard";
|
||||
case Gallery_Frame_Mode::Low_Latency:
|
||||
return id == "mode_prepare" || id == "mode_render" ||
|
||||
id == "mode_discard";
|
||||
case Gallery_Frame_Mode::Playback:
|
||||
return id == "mode_enqueue" || id == "mode_dequeue";
|
||||
}
|
||||
return false;
|
||||
};
|
||||
for (auto& action : gallery_frame_actions(frame_mode)) {
|
||||
if (keep_frame_action(action.id))
|
||||
result.push_back(std::move(action));
|
||||
}
|
||||
const auto add = [&result](std::string id, std::string label,
|
||||
std::string api, std::string description) {
|
||||
result.push_back({std::move(id), std::move(label), std::move(api),
|
||||
std::move(description), "Point Visual", {}, {}, 0.0,
|
||||
true});
|
||||
};
|
||||
add("orbit_points", "移动点", "Point_Visual::update_points",
|
||||
"通过 Kernel Multi_Double_Buffer_Strategy 发布一批新位置");
|
||||
add("add_point", "添加点", "Point_Visual::edit_points", "向批量点数据追加一个点");
|
||||
add("remove_point", "移除点", "Point_Visual::edit_points", "移除最后追加的点");
|
||||
add("point_churn", "点增删压力", "Point_Visual::edit_points",
|
||||
"在帧快照前连续追加并移除临时点");
|
||||
add("reset_points", "重置点", "Point_Visual::update_points", "恢复 3D 点演示数据");
|
||||
if (case_id == "datoviz_3d") {
|
||||
append_3d_actions(frame_mode, result);
|
||||
return result;
|
||||
}
|
||||
append_session_actions(frame_mode, result);
|
||||
@@ -151,7 +153,7 @@ struct Type_Descriptor<renderive::web::gallery_detail::Case_Model> {
|
||||
return object<T>("renderive_gallery_case",
|
||||
ADMINIVE_FIELD_LABEL(T, id, "标识"),
|
||||
ADMINIVE_FIELD_LABEL(T, title, "标题"),
|
||||
ADMINIVE_FIELD_LABEL(T, component, "Core2 控件"),
|
||||
ADMINIVE_FIELD_LABEL(T, component, "Renderive 组件"),
|
||||
ADMINIVE_FIELD_LABEL(T, category, "分类"),
|
||||
ADMINIVE_FIELD_LABEL(T, description, "说明"),
|
||||
ADMINIVE_FIELD_LABEL(T, order, "顺序"),
|
||||
@@ -168,7 +170,7 @@ struct Type_Descriptor<renderive::web::gallery_detail::Action_Model> {
|
||||
return object<T>("renderive_gallery_action",
|
||||
ADMINIVE_FIELD_LABEL(T, id, "动作"),
|
||||
ADMINIVE_FIELD_LABEL(T, label, "名称"),
|
||||
ADMINIVE_FIELD_LABEL(T, api, "Core2 API"),
|
||||
ADMINIVE_FIELD_LABEL(T, api, "Renderive API"),
|
||||
ADMINIVE_FIELD_LABEL(T, description, "说明"),
|
||||
ADMINIVE_FIELD_LABEL(T, group, "分组"),
|
||||
ADMINIVE_FIELD_LABEL(T, argument_input, "参数类型"),
|
||||
@@ -188,7 +190,7 @@ adminive::Table_View action_view() {
|
||||
return adminive::table_view<T>(
|
||||
adminive::column<&T::label>("动作"),
|
||||
adminive::column<&T::group>("分组"),
|
||||
adminive::column<&T::api>("Core2 API"))
|
||||
adminive::column<&T::api>("Renderive API"))
|
||||
.titled("保留 API 动作");
|
||||
}
|
||||
|
||||
@@ -552,12 +554,11 @@ std::string Gallery_Protocol::catalog_json() {
|
||||
Json entry = adminive::to_frontend_json<Json>(item);
|
||||
entry["control_count_by_mode"] = Json::object();
|
||||
entry["action_count_by_mode"] = Json::object();
|
||||
const std::size_t renderable_controls = gallery_renderable_control_count(item.id);
|
||||
const std::size_t renderable_controls = item.id == "datoviz_3d" ? 0 : gallery_renderable_control_count(item.id);
|
||||
for (const auto mode : frame_modes) {
|
||||
const std::string key = gallery_enum_id(mode);
|
||||
const std::size_t controls = item.id == "point_3d"
|
||||
? 0U
|
||||
: gallery_detail::session_control_count(mode) + renderable_controls;
|
||||
const std::size_t controls =
|
||||
gallery_detail::session_control_count(item.id, mode) + renderable_controls;
|
||||
const std::size_t actions =
|
||||
gallery_detail::registered_actions(item.id, mode).size();
|
||||
entry["control_count_by_mode"][key] = controls;
|
||||
@@ -565,10 +566,9 @@ std::string Gallery_Protocol::catalog_json() {
|
||||
controls_total += controls;
|
||||
actions_total += actions;
|
||||
}
|
||||
entry["control_count"] = item.id == "point_3d"
|
||||
? 0U
|
||||
: gallery_detail::session_control_count(Gallery_Frame_Mode::Low_Latency) +
|
||||
renderable_controls;
|
||||
entry["control_count"] =
|
||||
gallery_detail::session_control_count(item.id, Gallery_Frame_Mode::Low_Latency) +
|
||||
renderable_controls;
|
||||
entry["action_count"] =
|
||||
gallery_detail::registered_actions(
|
||||
item.id, Gallery_Frame_Mode::Low_Latency).size();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include "Gallery_Actions.h"
|
||||
#include "common/Gallery_Types.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@@ -10,11 +10,6 @@ struct Gallery_Open_Request {
|
||||
std::string case_id;
|
||||
Gallery_Frame_Mode frame_mode = Gallery_Frame_Mode::Low_Latency;
|
||||
};
|
||||
using Gallery_Value = std::variant<bool, double, std::string>;
|
||||
struct Gallery_Action_Request {
|
||||
std::string id;
|
||||
std::optional<Gallery_Value> argument;
|
||||
};
|
||||
struct Gallery_Control_Patch_Request {
|
||||
std::string target;
|
||||
std::string patch_json;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
#include "common/Pixel_Frame.h"
|
||||
#include "render_2D/base/Types.h"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
namespace renderive::web {
|
||||
inline constexpr std::size_t pixel_frame_header_size = 16;
|
||||
[[nodiscard]] std::string encode_pixel_frame(Image_View image,
|
||||
Color background = Color::black());
|
||||
[[nodiscard]] std::string encode_rgba8_pixel_frame(const std::byte* pixels,
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <variant>
|
||||
namespace renderive::web {
|
||||
enum class Gallery_View_Input_Type : std::uint8_t {
|
||||
Show,
|
||||
Hide,
|
||||
Leave
|
||||
};
|
||||
struct Gallery_View_Input {
|
||||
Gallery_View_Input_Type type{};
|
||||
};
|
||||
enum class Gallery_Pointer_Input_Type : std::uint8_t {
|
||||
Move,
|
||||
Press,
|
||||
Release
|
||||
};
|
||||
enum class Gallery_Mouse_Button : std::uint8_t {
|
||||
None,
|
||||
Left,
|
||||
Right,
|
||||
Middle
|
||||
};
|
||||
struct Gallery_Pointer_Input {
|
||||
Gallery_Pointer_Input_Type type{};
|
||||
double x{};
|
||||
double y{};
|
||||
double global_x{};
|
||||
double global_y{};
|
||||
Gallery_Mouse_Button button{};
|
||||
std::uint8_t buttons{};
|
||||
std::uint8_t modifiers{};
|
||||
};
|
||||
struct Gallery_Wheel_Input {
|
||||
double x{};
|
||||
double y{};
|
||||
double global_x{};
|
||||
double global_y{};
|
||||
Gallery_Mouse_Button button{};
|
||||
std::uint8_t buttons{};
|
||||
std::uint8_t modifiers{};
|
||||
double angle_delta_x{};
|
||||
double angle_delta_y{};
|
||||
double pixel_delta_x{};
|
||||
double pixel_delta_y{};
|
||||
};
|
||||
enum class Gallery_Key_Input_Type : std::uint8_t {
|
||||
Press,
|
||||
Release
|
||||
};
|
||||
enum class Gallery_Key : std::uint16_t {
|
||||
Unknown,
|
||||
Escape,
|
||||
Enter,
|
||||
Space,
|
||||
Delete,
|
||||
Backspace,
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down
|
||||
};
|
||||
struct Gallery_Key_Input {
|
||||
Gallery_Key_Input_Type type{};
|
||||
Gallery_Key key{};
|
||||
std::uint32_t native_key{};
|
||||
std::uint8_t modifiers{};
|
||||
bool auto_repeat{};
|
||||
};
|
||||
using Gallery_Input_Event = std::variant<Gallery_View_Input, Gallery_Pointer_Input, Gallery_Wheel_Input, Gallery_Key_Input>;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
namespace renderive::web {
|
||||
struct Gallery_Client_Performance {
|
||||
double transport_fps{};
|
||||
double presentation_fps{};
|
||||
std::uint64_t websocket_buffered_bytes{};
|
||||
std::uint64_t changed_pixel_frames{};
|
||||
std::uint64_t duplicate_pixel_frames{};
|
||||
std::uint64_t frame_request_timeout_count{};
|
||||
double frame_round_trip_ms{};
|
||||
double frame_round_trip_average_ms{};
|
||||
double frame_round_trip_deviation_ms{};
|
||||
double frame_round_trip_p95_ms{};
|
||||
double frame_round_trip_p99_ms{};
|
||||
double display_interval_ms{};
|
||||
double display_interval_average_ms{};
|
||||
double display_interval_latest_ms{};
|
||||
double display_interval_p95_ms{};
|
||||
double display_interval_p99_ms{};
|
||||
double display_interval_deviation_ms{};
|
||||
std::uint64_t overwritten_pixel_frames{};
|
||||
double last_pixel_receive_age_ms{};
|
||||
double last_pixel_change_age_ms{};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include "Gallery_Input.h"
|
||||
#include "Gallery_Performance_Types.h"
|
||||
#include "Gallery_Types.h"
|
||||
#include "adminive/adminive.hpp"
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
namespace renderive::web {
|
||||
class Gallery_Scene_Interface {
|
||||
public:
|
||||
virtual ~Gallery_Scene_Interface() = default;
|
||||
[[nodiscard]] virtual const std::string& case_id() const noexcept = 0;
|
||||
[[nodiscard]] virtual nlohmann::json controls() const = 0;
|
||||
[[nodiscard]] virtual Gallery_Frame_Mode frame_mode() const noexcept = 0;
|
||||
virtual void reset_monitoring() = 0;
|
||||
[[nodiscard]] virtual bool can_render_automatically() const noexcept = 0;
|
||||
[[nodiscard]] virtual std::uint64_t kernel_refresh_interval_ns() const = 0;
|
||||
virtual void set_client_metrics(Gallery_Client_Performance metrics) noexcept = 0;
|
||||
[[nodiscard]] virtual adminive::Update_Result apply_patch(std::string_view target, const nlohmann::json& patch) = 0;
|
||||
virtual void resize(int width, int height) = 0;
|
||||
virtual void dispatch(const Gallery_Input_Event& event) = 0;
|
||||
virtual bool render_latest_frame() = 0;
|
||||
[[nodiscard]] virtual std::optional<std::string> encode_latest_pixels() = 0;
|
||||
virtual void record_pixel_response(std::chrono::steady_clock::time_point request_started, std::chrono::steady_clock::time_point encode_started, std::chrono::steady_clock::time_point encode_finished, std::size_t pixel_bytes) = 0;
|
||||
[[nodiscard]] virtual std::string action(const Gallery_Action_Request& request, bool& recognized) = 0;
|
||||
virtual void record_action_notice_if_empty(std::string_view notice) = 0;
|
||||
[[nodiscard]] virtual std::string telemetry_json() const = 0;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
namespace renderive::web {
|
||||
enum class Gallery_Frame_Mode : std::uint8_t {
|
||||
Manual,
|
||||
Low_Latency,
|
||||
Playback
|
||||
};
|
||||
using Gallery_Value = std::variant<bool, double, std::string>;
|
||||
struct Gallery_Action_Request {
|
||||
std::string id;
|
||||
std::optional<Gallery_Value> argument;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#include "Pixel_Frame.h"
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
namespace renderive::web {
|
||||
namespace {
|
||||
void write_u32_le(char* target, std::uint32_t value) {
|
||||
target[0] = static_cast<char>(value & 0xffU);
|
||||
target[1] = static_cast<char>((value >> 8U) & 0xffU);
|
||||
target[2] = static_cast<char>((value >> 16U) & 0xffU);
|
||||
target[3] = static_cast<char>((value >> 24U) & 0xffU);
|
||||
}
|
||||
}
|
||||
std::string encode_rgba8_pixel_frame(const std::byte* data, std::uint32_t width, std::uint32_t height, std::uint32_t stride) {
|
||||
if (data == nullptr || width == 0 || height == 0 || stride < width * 4U)
|
||||
return {};
|
||||
const std::size_t row_size = static_cast<std::size_t>(width) * 4U;
|
||||
if (static_cast<std::size_t>(height) > (std::numeric_limits<std::size_t>::max() - pixel_frame_header_size) / row_size)
|
||||
return {};
|
||||
const std::size_t pixel_bytes = row_size * height;
|
||||
std::string frame(pixel_frame_header_size + pixel_bytes, '\0');
|
||||
frame[0] = 'R';
|
||||
frame[1] = 'V';
|
||||
frame[2] = 'P';
|
||||
frame[3] = '1';
|
||||
write_u32_le(frame.data() + 4, width);
|
||||
write_u32_le(frame.data() + 8, height);
|
||||
write_u32_le(frame.data() + 12, static_cast<std::uint32_t>(row_size));
|
||||
char* output = frame.data() + pixel_frame_header_size;
|
||||
for (std::uint32_t y = 0; y < height; ++y) {
|
||||
const auto* row = data + static_cast<std::size_t>(y) * stride;
|
||||
std::memcpy(output + static_cast<std::size_t>(y) * row_size, row, row_size);
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
namespace renderive::web {
|
||||
inline constexpr std::size_t pixel_frame_header_size = 16;
|
||||
[[nodiscard]] std::string encode_rgba8_pixel_frame(const std::byte* data, std::uint32_t width, std::uint32_t height, std::uint32_t stride);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "../common/Gallery_Scene_Interface.h"
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
namespace renderive::web {
|
||||
[[nodiscard]] std::unique_ptr<Gallery_Scene_Interface> make_gallery_scene_2d(std::uint64_t session_id, std::string case_id, Gallery_Frame_Mode mode, bool automatic_low_latency);
|
||||
}
|
||||
@@ -0,0 +1,451 @@
|
||||
#include "Gallery_Scene3D.h"
|
||||
|
||||
#include "../common/Pixel_Frame.h"
|
||||
#include <render_3D/Point_Scene.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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_) {
|
||||
if (!visual_)
|
||||
throw std::runtime_error("failed to create Point_Visual");
|
||||
render_initial_frame();
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string& case_id() const noexcept override {
|
||||
return case_id_;
|
||||
}
|
||||
|
||||
[[nodiscard]] nlohmann::json controls() const override {
|
||||
return {{"resources", nlohmann::json::array()},
|
||||
{"observers", nlohmann::json::array()},
|
||||
{"render_plan", nullptr},
|
||||
{"performance_capture", nullptr}};
|
||||
}
|
||||
|
||||
[[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_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);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string action(const Gallery_Action_Request& request,
|
||||
bool& recognized) override {
|
||||
recognized = true;
|
||||
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();
|
||||
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}}},
|
||||
{"render_performance",
|
||||
{{"render_attempt_count", render_attempt_count_},
|
||||
{"successful_render_count", successful_render_count_},
|
||||
{"failed_render_count",
|
||||
render_attempt_count_ - successful_render_count_},
|
||||
{"last_render_ms", last_render_ms_},
|
||||
{"pixel_response_fps", 0.0},
|
||||
{"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}}},
|
||||
{"session_id", session_id_},
|
||||
{"last_action", last_action_result_}}
|
||||
.dump();
|
||||
}
|
||||
|
||||
private:
|
||||
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;
|
||||
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;
|
||||
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::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
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "../common/Gallery_Scene_Interface.h"
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
namespace renderive::web {
|
||||
[[nodiscard]] 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);
|
||||
}
|
||||
Reference in New Issue
Block a user