优化
This commit is contained in:
@@ -2,17 +2,14 @@
|
||||
#include <frame_statistics.hpp>
|
||||
#include <render_common.hpp>
|
||||
#include "detail/Gallery_Frame_Atlas.hpp"
|
||||
#include <concurrentqueue-1.0.5/blockingconcurrentqueue.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <future>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
namespace aethera::web {
|
||||
namespace {
|
||||
@@ -46,71 +43,7 @@ std::string exception_description(const std::exception_ptr& failure) {
|
||||
return "empty gallery video failure";
|
||||
}
|
||||
}
|
||||
namespace detail {
|
||||
/*
|
||||
* FFmpeg 硬件编码 API 可能在驱动内部等待硬件队列。它仍是 Scene completion
|
||||
* DAG 的一个业务节点,但不能占住 Kernel Taskflow Worker。每个图集只有一个
|
||||
* H264_Encoder,因此用一个串行编码域维持 codec context 的唯一执行位置;
|
||||
* 调用节点通过 Task_Graph::corun_until 协作让出,完成后恢复同一帧 DAG。
|
||||
*/
|
||||
struct Video_Encode_Domain final {
|
||||
struct Work {
|
||||
std::function<void()> function;
|
||||
std::shared_ptr<std::promise<void>> completion;
|
||||
};
|
||||
public:
|
||||
Video_Encode_Domain() : thread_([this] {
|
||||
run();
|
||||
}) {}
|
||||
~Video_Encode_Domain() {
|
||||
stopping_.store(true, std::memory_order_release);
|
||||
if (thread_.joinable()) thread_.join();
|
||||
}
|
||||
Video_Encode_Domain(const Video_Encode_Domain&) = delete;
|
||||
Video_Encode_Domain& operator=(const Video_Encode_Domain&) = delete;
|
||||
void invoke(std::function<void()> function) {
|
||||
if (!function) throw std::invalid_argument("video encode work is empty");
|
||||
if (stopping_.load(std::memory_order_acquire)) throw std::runtime_error("video encode domain is stopping");
|
||||
auto completion = std::make_shared<std::promise<void>>();
|
||||
auto completed = completion->get_future();
|
||||
auto work = std::make_unique<Work>(
|
||||
Work{std::move(function), std::move(completion)});
|
||||
if (!queue_.enqueue(std::move(work))) throw std::bad_alloc{};
|
||||
Task_Graph::corun_until([&completed] {
|
||||
return completed.wait_for(std::chrono::seconds(0)) ==
|
||||
std::future_status::ready;
|
||||
});
|
||||
completed.get();
|
||||
}
|
||||
private:
|
||||
void run() noexcept {
|
||||
for (;;) {
|
||||
std::unique_ptr<Work> work;
|
||||
const bool received = queue_.wait_dequeue_timed(
|
||||
work, std::chrono::milliseconds(1));
|
||||
if (received && work) {
|
||||
try {
|
||||
work->function();
|
||||
work->completion->set_value();
|
||||
}
|
||||
catch (...) {
|
||||
try {
|
||||
work->completion->set_exception(
|
||||
std::current_exception());
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
}
|
||||
if (!received && stopping_.load(std::memory_order_acquire)) return;
|
||||
}
|
||||
}
|
||||
moodycamel::BlockingConcurrentQueue<std::unique_ptr<Work>> queue_{8};
|
||||
std::atomic_bool stopping_{};
|
||||
std::thread thread_;
|
||||
};
|
||||
}
|
||||
Gallery_Video_Stream::Private::Private() : encoder(gallery_frame_rate),
|
||||
encode_domain(std::make_unique<detail::Video_Encode_Domain>()) {}
|
||||
Gallery_Video_Stream::Private::Private() : encoder(gallery_frame_rate) {}
|
||||
Gallery_Video_Stream::Private::~Private() = default;
|
||||
void Gallery_Video_Stream::Private::initialize(
|
||||
std::vector<Plot_Entry> plots) {
|
||||
@@ -331,27 +264,32 @@ void Gallery_Video_Stream::Private::compose_media_frame() {
|
||||
}
|
||||
void Gallery_Video_Stream::Private::encode_media_frame() {
|
||||
if (!active_composition) return;
|
||||
encode_domain->invoke([this] {
|
||||
const auto started = std::chrono::steady_clock::now();
|
||||
if (key_frame_requested.exchange(false,
|
||||
std::memory_order_acq_rel))
|
||||
encoder.request_key_frame();
|
||||
active_video = encoder.encode(
|
||||
active_composition->pixels, active_composition->width,
|
||||
active_composition->height,
|
||||
active_composition->layout == Plot_Pixel_Layout::bgra8
|
||||
? Video_Pixel_Layout::bgra
|
||||
: Video_Pixel_Layout::rgba,
|
||||
active_encode_tick.sequence,
|
||||
std::chrono::microseconds{
|
||||
static_cast<std::int64_t>(
|
||||
std::llround(active_encode_tick.time_milliseconds *
|
||||
1'000.0))
|
||||
});
|
||||
static_cast<void>(encode_ms.submit(
|
||||
std::chrono::duration<double, std::milli>(
|
||||
std::chrono::steady_clock::now() - started).count()));
|
||||
});
|
||||
/*
|
||||
* 直接在 FFmpeg.H264.encode Taskflow 节点中调用 FFmpeg。这样实际
|
||||
* avcodec_send_frame/avcodec_receive_packet(以及硬件后端等待)全部落在
|
||||
* 同一个业务节点的 wall/CPU 统计中,不再由额外编码线程隐藏真实耗时。
|
||||
* gallery.video.frame 本身串行 compose -> encode -> publish,页面唯一
|
||||
* H264_Encoder 因而仍只有一个执行位置,不需要额外 mutex 或专用线程。
|
||||
*/
|
||||
const auto started = std::chrono::steady_clock::now();
|
||||
if (key_frame_requested.exchange(false,
|
||||
std::memory_order_acq_rel))
|
||||
encoder.request_key_frame();
|
||||
active_video = encoder.encode(
|
||||
active_composition->pixels, active_composition->width,
|
||||
active_composition->height,
|
||||
active_composition->layout == Plot_Pixel_Layout::bgra8
|
||||
? Video_Pixel_Layout::bgra
|
||||
: Video_Pixel_Layout::rgba,
|
||||
active_encode_tick.sequence,
|
||||
std::chrono::microseconds{
|
||||
static_cast<std::int64_t>(
|
||||
std::llround(active_encode_tick.time_milliseconds *
|
||||
1'000.0))
|
||||
});
|
||||
static_cast<void>(encode_ms.submit(
|
||||
std::chrono::duration<double, std::milli>(
|
||||
std::chrono::steady_clock::now() - started).count()));
|
||||
}
|
||||
void Gallery_Video_Stream::Private::publish_media_frame() {
|
||||
if (!active_video || !active_composition) return;
|
||||
@@ -418,7 +356,7 @@ void Gallery_Video_Stream::bind_plots() {
|
||||
});
|
||||
compose.describe("owner", "gallery")
|
||||
.describe("stage", "latest completed Plot frames to atlas");
|
||||
auto encode = media->add("gallery.h264.encode", [weak] {
|
||||
auto encode = media->add("FFmpeg.H264.encode", [weak] {
|
||||
if (const auto owner = weak.lock()) {
|
||||
auto& owner_data = static_cast<Private&>(*owner->d);
|
||||
try {
|
||||
@@ -430,7 +368,10 @@ void Gallery_Video_Stream::bind_plots() {
|
||||
}
|
||||
});
|
||||
encode.describe("owner", "gallery")
|
||||
.describe("stage", "H.264 encode in Scene completion pipeline");
|
||||
.describe("stage", "FFmpeg H.264 encode in Scene completion pipeline")
|
||||
.describe("backend", "FFmpeg")
|
||||
.describe("codec", "H.264")
|
||||
.describe("execution", "Taskflow worker");
|
||||
auto publish = media->add("gallery.webrtc.publish", [weak] {
|
||||
if (const auto owner = weak.lock()) {
|
||||
auto& owner_data = static_cast<Private&>(*owner->d);
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
namespace aethera::web {
|
||||
namespace detail {
|
||||
struct Video_Encode_Domain;
|
||||
}
|
||||
struct Gallery_Video_Stream::Private : Prev_Private {
|
||||
using Object = Impl<Gallery_Video_Stream>;
|
||||
struct Source {
|
||||
@@ -28,7 +25,6 @@ struct Gallery_Video_Stream::Private : Prev_Private {
|
||||
std::vector<Source> sources{}; /* 已按业务标识排序的稳定图集来源。 */
|
||||
std::unique_ptr<detail::Gallery_Frame_Atlas> atlas{}; /* 最近完成帧与 RGBA 图集的唯一状态源。 */
|
||||
H264_Encoder encoder; /* 页面唯一 H.264 编码器。 */
|
||||
std::unique_ptr<detail::Video_Encode_Domain> encode_domain{}; /* FFmpeg 驱动调用域。 */
|
||||
Plot_Render_Tick active_encode_tick{}; /* 当前媒体 DAG 的输入时钟。 */
|
||||
std::optional<detail::Gallery_Atlas_Composition> active_composition{}; /* 当前合成结果所有权。 */
|
||||
std::optional<Encoded_Video_Frame> active_video{}; /* 当前编码结果所有权。 */
|
||||
|
||||
@@ -243,6 +243,9 @@ nlohmann::json taskflow_trace_json(const Taskflow_Frame_Trace& trace) {
|
||||
{"completed_ms", task.completed_ms},
|
||||
{"duration_ms", task.duration_ms},
|
||||
{"cpu_duration_ms", task.cpu_duration_ms},
|
||||
{"cpu_cycles", task.cpu_cycles},
|
||||
{"cooperative_wait_ms", task.cooperative_wait_ms},
|
||||
{"cpu_time_coarse", task.cpu_time_coarse},
|
||||
{"observer_entry_ms", task.observer_entry_ms},
|
||||
{"observer_exit_ms", task.observer_exit_ms},
|
||||
{"observer_entry_cpu_ms", task.observer_entry_cpu_ms},
|
||||
|
||||
Reference in New Issue
Block a user