3D性能优化

This commit is contained in:
2026-08-22 14:23:05 +08:00
parent 049516bf3b
commit e2ba9b8384
15 changed files with 347 additions and 270 deletions
+3 -1
View File
@@ -18,7 +18,7 @@ struct Graph_WebSocket::Private {
};
Graph_WebSocket::Graph_WebSocket(drogon::WebSocketConnectionPtr connection, std::shared_ptr<Plot> plot) : d(std::make_unique<Private>()) { d->connection = connection; d->plot = std::move(plot); d->owner = connection.get(); }
Graph_WebSocket::~Graph_WebSocket() { close(); }
void Graph_WebSocket::start() { if (d->attached) return; const auto weak = weak_from_this(); d->plot->attach(d->owner, [weak](std::shared_ptr<const Plot_Frame_Message> frame) { const auto socket = weak.lock(); if (!socket) return; const auto connection = socket->d->connection.lock(); if (!connection || !connection->connected()) return; connection->send(frame->metadata, drogon::WebSocketMessageType::Text); connection->send(frame->pixels.data(), frame->pixels.size(), drogon::WebSocketMessageType::Binary); }); d->attached = true; }
void Graph_WebSocket::start() { if (d->attached) return; const auto weak = weak_from_this(); d->plot->attach(d->owner, [weak](std::shared_ptr<const Plot_Frame_Message> frame) { const auto socket = weak.lock(); if (!socket) return; const auto connection = socket->d->connection.lock(); if (!connection || !connection->connected()) return; connection->send(frame->metadata, drogon::WebSocketMessageType::Text); if (!frame->pixels.empty()) connection->send(frame->pixels.data(), frame->pixels.size(), drogon::WebSocketMessageType::Binary); }); d->attached = true; }
void Graph_WebSocket::receive(std::string_view message) {
const auto json = nlohmann::json::parse(message, nullptr, false);
if (json.is_discarded() || !json.is_object()) return;
@@ -31,6 +31,8 @@ void Graph_WebSocket::receive(std::string_view message) {
request.correlation_id = value->get<std::uint64_t>();
if (const auto value = json.find("time"); value != json.end() && value->is_number())
request.time_milliseconds = value->get<double>();
if (const auto value = json.find("delivery"); value != json.end() && value->is_string())
request.delivery = *value == "diagnostics" ? Plot_Frame_Delivery::diagnostics : Plot_Frame_Delivery::pixels;
if (const auto viewport = json.find("viewport"); viewport != json.end() && viewport->is_object()) {
if (const auto value = viewport->find("width"); value != viewport->end() && value->is_number_unsigned())
request.width = std::clamp(value->get<std::uint32_t>(), 160U, 1920U);
+31 -21
View File
@@ -33,7 +33,7 @@ using Frequency_Axis_Object = Impl<Frequency_Axis>;
using Numeric_Axis_Object = Impl<Numeric_Axis>;
using Time_Axis_Object = Impl<Time_Axis>;
using Selection_Object = Impl<Selection_Rectangle_Overlay>;
constexpr std::uint16_t frame_protocol_version{4};
constexpr std::uint16_t frame_protocol_version{5};
enum class Frame_Pacing_Mode : std::uint32_t {
manual,
fixed_rate,
@@ -70,6 +70,13 @@ std::optional<Frame_Pacing_Mode> parse_pacing_mode(std::string_view value) {
if (value == "maximum_rate") return Frame_Pacing_Mode::maximum_rate;
return std::nullopt;
}
std::string_view delivery_name(Plot_Frame_Delivery delivery) {
switch (delivery) {
case Plot_Frame_Delivery::pixels: return "pixels";
case Plot_Frame_Delivery::diagnostics: return "diagnostics";
}
throw std::logic_error("unknown plot frame delivery");
}
Frame_Pacing_Properties Frame_Policy::snapshot() const {
std::lock_guard lock(mutex);
return pacing;
@@ -107,7 +114,7 @@ nlohmann::json Frame_Policy::write_prop(std::string_view key, const nlohmann::js
}
return {{"success", false}, {"error", "unknown frame runtime property"}};
}
nlohmann::json frame_metadata(Render_Frame& frame, std::uint32_t width, std::uint32_t height, std::size_t byte_length, const Frame_Pacing_Properties& pacing) {
nlohmann::json frame_metadata(Render_Frame& frame, std::uint32_t width, std::uint32_t height, std::size_t byte_length, Plot_Frame_Delivery delivery, const Frame_Pacing_Properties& pacing) {
nlohmann::json markers = nlohmann::json::object();
for (const auto& point : frame.trace_points()) markers[std::string(magic_enum::enum_name(point.marker))] = point.elapsed_ns;
nlohmann::json measurements = nlohmann::json::object();
@@ -116,42 +123,44 @@ nlohmann::json frame_metadata(Render_Frame& frame, std::uint32_t width, std::uin
return {
{"kind", "frame_metadata"}, {"protocol", "aethera.frame"}, {"version", frame_protocol_version},
{"sequence", identity.sequence}, {"correlation_id", identity.correlation_id},
{"created_time_unix_ms", static_cast<double>(frame.created_time_unix_ns()) / 1'000'000.0},
{"created_time_unix_ms", static_cast<double>(frame.created_time_unix_ns()) / 1'000'000.0}, {"delivery", delivery_name(delivery)},
{"pixel", {{"width", width}, {"height", height}, {"format", "rgba8"}, {"byte_length", byte_length}}},
{"pacing", {{"mode", pacing_mode_name(pacing.mode)}, {"fixed_rate_fps", pacing.fixed_rate_fps}, {"minimum_latency_headroom", pacing.minimum_latency_headroom}}},
{"trace", {{"clock", "steady_elapsed_ns"}, {"markers", std::move(markers)}, {"measurements", std::move(measurements)}}}
};
}
std::shared_ptr<const Plot_Frame_Message> encode_frame(Frame_2D* frame, const Frame_Pacing_Properties& pacing) {
std::shared_ptr<const Plot_Frame_Message> encode_frame(Frame_2D* frame, Plot_Frame_Delivery delivery, const Frame_Pacing_Properties& pacing) {
frame->mark(Frame_Trace_Marker::websocket_publish_started);
const Image_View image = frame->image();
std::string output;
output.reserve(static_cast<std::size_t>(image.width) * image.height * 4);
for (int y = 0; y < image.height; ++y) {
const auto* row = reinterpret_cast<const std::uint8_t*>(
image.data + static_cast<std::ptrdiff_t>(y) * image.stride);
for (int x = 0; x < image.width; ++x) {
const auto* pixel = row + x * 4;
output.push_back(static_cast<char>(pixel[2]));
output.push_back(static_cast<char>(pixel[1]));
output.push_back(static_cast<char>(pixel[0]));
output.push_back(static_cast<char>(pixel[3]));
if (delivery == Plot_Frame_Delivery::pixels) {
output.reserve(static_cast<std::size_t>(image.width) * image.height * 4);
for (int y = 0; y < image.height; ++y) {
const auto* row = reinterpret_cast<const std::uint8_t*>(
image.data + static_cast<std::ptrdiff_t>(y) * image.stride);
for (int x = 0; x < image.width; ++x) {
const auto* pixel = row + x * 4;
output.push_back(static_cast<char>(pixel[2]));
output.push_back(static_cast<char>(pixel[1]));
output.push_back(static_cast<char>(pixel[0]));
output.push_back(static_cast<char>(pixel[3]));
}
}
}
frame->mark(Frame_Trace_Marker::websocket_publish_finished);
auto message = std::make_shared<Plot_Frame_Message>();
message->pixels = std::move(output);
message->metadata = frame_metadata(*frame, static_cast<std::uint32_t>(image.width), static_cast<std::uint32_t>(image.height), message->pixels.size(), pacing).dump();
message->metadata = frame_metadata(*frame, static_cast<std::uint32_t>(image.width), static_cast<std::uint32_t>(image.height), message->pixels.size(), delivery, pacing).dump();
return message;
}
std::shared_ptr<const Plot_Frame_Message> encode_frame(Frame_3D* frame, const Frame_Pacing_Properties& pacing) {
std::shared_ptr<const Plot_Frame_Message> encode_frame(Frame_3D* frame, Plot_Frame_Delivery delivery, const Frame_Pacing_Properties& pacing) {
frame->mark(Frame_Trace_Marker::websocket_publish_started);
const auto pixels = frame->pixels();
auto message = std::make_shared<Plot_Frame_Message>();
message->pixels.assign(reinterpret_cast<const char*>(pixels.data()), pixels.size());
if (delivery == Plot_Frame_Delivery::pixels) message->pixels.assign(reinterpret_cast<const char*>(pixels.data()), pixels.size());
frame->mark(Frame_Trace_Marker::websocket_publish_finished);
const auto extent = frame->extent();
message->metadata = frame_metadata(*frame, extent.width, extent.height, message->pixels.size(), pacing).dump();
message->metadata = frame_metadata(*frame, extent.width, extent.height, message->pixels.size(), delivery, pacing).dump();
return message;
}
struct Schema_Query {
@@ -273,7 +282,8 @@ nlohmann::json Plot::Private::schema() const {
Plot::Private::Managed_Frame Plot::Private::make_frame(Frame_Submission submission) {
const Frame_Identity identity{next_frame_sequence++, submission.request.correlation_id};
if (std::holds_alternative<std::unique_ptr<Scene_2D>>(scene)) return {submission.owner, std::move(submission.request), std::make_unique<Frame_2D>(identity)};
return {submission.owner, std::move(submission.request), std::make_unique<Frame_3D>(identity)};
const auto output = submission.request.delivery == Plot_Frame_Delivery::pixels ? Frame_3D_Output::pixels : Frame_3D_Output::diagnostics;
return {submission.owner, std::move(submission.request), std::make_unique<Frame_3D>(identity, output)};
}
void Plot::Private::request_frame(Frame_Submission submission) {
auto frame = make_frame(std::move(submission));
@@ -316,8 +326,8 @@ void Plot::Private::render_frame(Managed_Frame frame) {
void Plot::Private::publish_completed_frame(Render_Frame* frame) {
if (!active_frame) throw std::logic_error("frame callback has no externally owned active frame");
const auto pacing = frame_policy.snapshot();
if (auto* frame_2d = std::get_if<std::unique_ptr<Frame_2D>>(&active_frame->frame); frame_2d && frame_2d->get() == frame) publish(active_frame->owner, encode_frame(frame_2d->get(), pacing));
else if (auto* frame_3d = std::get_if<std::unique_ptr<Frame_3D>>(&active_frame->frame); frame_3d && frame_3d->get() == frame) publish(active_frame->owner, encode_frame(frame_3d->get(), pacing));
if (auto* frame_2d = std::get_if<std::unique_ptr<Frame_2D>>(&active_frame->frame); frame_2d && frame_2d->get() == frame) publish(active_frame->owner, encode_frame(frame_2d->get(), active_frame->request.delivery, pacing));
else if (auto* frame_3d = std::get_if<std::unique_ptr<Frame_3D>>(&active_frame->frame); frame_3d && frame_3d->get() == frame) publish(active_frame->owner, encode_frame(frame_3d->get(), active_frame->request.delivery, pacing));
else throw std::logic_error("frame callback does not match the externally owned active frame");
frame_completed();
}
+5
View File
@@ -24,11 +24,16 @@ struct Plot_Input_Event {
std::uint32_t native_key{};
bool auto_repeat{};
};
enum class Plot_Frame_Delivery : std::uint8_t {
pixels,
diagnostics
};
struct Plot_Frame_Request {
std::uint64_t correlation_id{}; /* 浏览器请求标识;完成帧元数据原样返回。 */
double time_milliseconds{}; /* 使用层推进图表时间轴的本地日内毫秒。 */
std::uint32_t width{720}; /* 请求帧宽度,单位为物理像素。 */
std::uint32_t height{420}; /* 请求帧高度,单位为物理像素。 */
Plot_Frame_Delivery delivery{Plot_Frame_Delivery::pixels}; /* 本次请求返回完整像素或仅返回诊断元数据。 */
};
struct Plot_Frame_Message {
std::string metadata{}; /* 单帧 JSON 元数据 WebSocket 文本消息。 */