3D性能优化
This commit is contained in:
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 文本消息。 */
|
||||
|
||||
Reference in New Issue
Block a user