This commit is contained in:
2026-08-15 21:48:23 +08:00
parent 26bbc29a16
commit 023250c0fc
45 changed files with 457 additions and 415 deletions
+36 -79
View File
@@ -4,20 +4,17 @@
#include "render_2D/Gallery_Scene2D.h"
#include "render_3D/Gallery_Scene3D.h"
#include <renderive/scheduling/Scheduler.hpp>
#include <trantor/net/EventLoopThread.h>
#include <nlohmann/json.hpp>
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cmath>
#include <condition_variable>
#include <cstdint>
#include <map>
#include <mutex>
#include <optional>
#include <string>
#include <string_view>
#include <thread>
#include <unordered_map>
#include <type_traits>
#include <utility>
namespace renderive::web {
@@ -124,80 +121,22 @@ std::unique_ptr<Gallery_Scene_Interface> make_gallery_scene(std::uint64_t sessio
return make_gallery_scene_3d(session_id, std::move(case_id), mode, automatic_low_latency);
return make_gallery_scene_2d(session_id, std::move(case_id), mode, automatic_low_latency);
}
trantor::EventLoop* automatic_render_loop() {
static trantor::EventLoopThread thread("RenderiveAutomaticRender");
static const bool started = [] {
thread.run();
return true;
}();
static_cast<void>(started);
return thread.getLoop();
}
}
struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Session::Impl> {
using Clock = std::chrono::steady_clock;
class Automatic_Render_Scheduler final {
public:
static Automatic_Render_Scheduler& instance() {
static Automatic_Render_Scheduler scheduler;
return scheduler;
}
void arm(std::uint64_t session_id, std::weak_ptr<Impl> session, Clock::time_point deadline) {
std::lock_guard lock(mutex_);
if (const auto current = entries_.find(session_id); current != entries_.end()) {
schedule_.erase(current->second);
entries_.erase(current);
}
const auto iterator = schedule_.emplace(deadline, Entry{session_id, std::move(session)});
entries_.emplace(session_id, iterator);
condition_.notify_one();
}
void disarm(std::uint64_t session_id) noexcept {
std::lock_guard lock(mutex_);
const auto current = entries_.find(session_id);
if (current == entries_.end())
return;
schedule_.erase(current->second);
entries_.erase(current);
condition_.notify_one();
}
private:
struct Entry {
std::uint64_t session_id{};
std::weak_ptr<Impl> session;
};
using Schedule = std::multimap<Clock::time_point, Entry>;
Automatic_Render_Scheduler() : thread_([this](std::stop_token stop) { run(stop); }) {}
~Automatic_Render_Scheduler() {
thread_.request_stop();
condition_.notify_all();
}
void run(std::stop_token stop) noexcept {
std::unique_lock lock(mutex_);
while (!stop.stop_requested()) {
if (schedule_.empty()) {
condition_.wait(lock, [this, &stop] {
return stop.stop_requested() || !schedule_.empty();
});
continue;
}
const auto deadline = schedule_.begin()->first;
if (condition_.wait_until(lock, deadline) != std::cv_status::timeout)
continue;
if (schedule_.empty() || schedule_.begin()->first > Clock::now())
continue;
auto iterator = schedule_.begin();
Entry entry = std::move(iterator->second);
entries_.erase(entry.session_id);
schedule_.erase(iterator);
lock.unlock();
if (auto session = entry.session.lock())
session->automatic_render_due();
lock.lock();
}
}
std::mutex mutex_;
std::condition_variable condition_;
Schedule schedule_;
std::unordered_map<std::uint64_t, Schedule::iterator> entries_;
std::jthread thread_;
};
explicit Impl(bool enable_automatic_low_latency)
: automatic_low_latency(enable_automatic_low_latency), session_id(next_session_id()) {}
~Impl() {
if (automatic_low_latency)
Automatic_Render_Scheduler::instance().disarm(session_id);
disarm_automatic_render();
}
static std::uint64_t next_session_id() noexcept {
static std::atomic<std::uint64_t> next{1};
@@ -266,27 +205,43 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
[[nodiscard]] std::unique_lock<std::mutex> acquire_foreground_lock() {
return std::unique_lock<std::mutex>(mutex);
}
void disarm_automatic_render() noexcept {
++automatic_timer_revision;
if (automatic_timer == trantor::InvalidTimerId)
return;
automatic_render_loop()->invalidateTimer(automatic_timer);
automatic_timer = trantor::InvalidTimerId;
}
void arm_automatic_render_locked() noexcept {
if (!automatic_low_latency)
return;
auto& scheduler = Automatic_Render_Scheduler::instance();
if (!scene || !scene->can_render_automatically() || render_task_pending) {
scheduler.disarm(session_id);
disarm_automatic_render();
if (!scene || !scene->can_render_automatically() || render_task_pending)
return;
}
try {
const auto interval = std::chrono::nanoseconds(
std::max<std::uint64_t>(1, scene->kernel_refresh_interval_ns()));
const auto deadline = last_render_started ? *last_render_started + interval : Clock::now();
scheduler.arm(session_id, weak_from_this(), deadline);
const auto delay = std::max(Clock::duration::zero(), deadline - Clock::now());
const std::uint64_t revision = automatic_timer_revision;
automatic_timer = automatic_render_loop()->runAfter(
std::chrono::duration<double>(delay).count(),
[session = weak_from_this(), revision] {
if (auto value = session.lock())
value->automatic_render_due(revision);
});
} catch (...) {
scheduler.disarm(session_id);
disarm_automatic_render();
}
}
void automatic_render_due() {
void automatic_render_due(std::uint64_t revision) {
std::uint64_t generation{};
{
std::lock_guard lock(mutex);
if (revision != automatic_timer_revision)
return;
automatic_timer = trantor::InvalidTimerId;
++automatic_timer_revision;
if (!automatic_low_latency || !scene || !scene->can_render_automatically() || render_task_pending)
return;
render_task_pending = true;
@@ -345,6 +300,8 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
bool automatic_low_latency{};
bool render_task_pending{};
std::uint64_t session_id{};
trantor::TimerId automatic_timer{trantor::InvalidTimerId};
std::uint64_t automatic_timer_revision{};
std::optional<Web_Response> handle_gallery(const Gallery_Request& request) {
if (request.kind == Gallery_Request_Kind::Catalog)
return Web_Response{Web_Response_Type::Json, Gallery_Protocol::catalog_json()};