后台服务协程化
This commit is contained in:
@@ -762,7 +762,10 @@ Scene_Render_Result Scene_Base::render() {
|
||||
Scene_Render_Result Scene_Base::render(Abstract_Frame& frame) {
|
||||
return d_func().submit_render(&frame);
|
||||
}
|
||||
Scene_Render_Result Scene_Base::Impl::submit_render(Abstract_Frame* frame) {
|
||||
Scene_Render_Result Scene_Base::render(Abstract_Frame& frame, Scene_Render_Completion completion) {
|
||||
return d_func().submit_render(&frame, std::move(completion));
|
||||
}
|
||||
Scene_Render_Result Scene_Base::Impl::submit_render(Abstract_Frame* frame, Scene_Render_Completion completion) {
|
||||
if (active_submitted_observer_scene_ == owner_) {
|
||||
std::lock_guard<std::recursive_mutex> lock(task_mutex_);
|
||||
++deferred_render_count_;
|
||||
@@ -802,6 +805,7 @@ Scene_Render_Result Scene_Base::Impl::submit_render(Abstract_Frame* frame) {
|
||||
task->topology =
|
||||
std::make_shared<Topology_Snapshot>(scene().topology_snapshot());
|
||||
task->completion = std::make_shared<Render_Completion>();
|
||||
task->completion_callback = std::move(completion);
|
||||
const Capture_Frame_Ticket capture_ticket = capture_controller_.begin_frame();
|
||||
snapshot->capture_ticket_ = capture_ticket;
|
||||
task->snapshot = std::move(snapshot);
|
||||
@@ -1526,6 +1530,16 @@ void Scene_Base::Impl::execute_render_task(std::shared_ptr<Render_Task> task) {
|
||||
<< " pending_operations=" << pending_operations;
|
||||
});
|
||||
render_completed_.notify_all();
|
||||
if (task->completion_callback) {
|
||||
try {
|
||||
task->completion_callback(error, exception);
|
||||
}
|
||||
catch (...) {
|
||||
record_pending_exception(::renderive::error::capture(
|
||||
"executing scene render completion callback",
|
||||
std::current_exception()));
|
||||
}
|
||||
}
|
||||
}
|
||||
std::shared_ptr<Scene_Base::Impl::Compiled_Render_Plan> Scene_Base::Impl::compile_render_plan(
|
||||
const Frame_Render_Snapshot& snapshot) {
|
||||
|
||||
@@ -48,6 +48,7 @@ enum class Scene_Render_Result : std::uint8_t {
|
||||
external_failure,
|
||||
shutting_down
|
||||
};
|
||||
using Scene_Render_Completion = std::function<void(Scene_Render_Result, std::exception_ptr)>;
|
||||
enum class Scene_Edit_Error : std::uint8_t {
|
||||
none,
|
||||
empty_edit,
|
||||
@@ -160,6 +161,7 @@ public:
|
||||
};
|
||||
Scene_Render_Result render();
|
||||
Scene_Render_Result render(Abstract_Frame& frame);
|
||||
Scene_Render_Result render(Abstract_Frame& frame, Scene_Render_Completion completion);
|
||||
Scene_Render_Result wait_for_render();
|
||||
void publish_frame_state();
|
||||
void publish_frame_state(Abstract_Frame& frame);
|
||||
|
||||
@@ -75,6 +75,7 @@ public:
|
||||
std::shared_ptr<const Topology_Snapshot> topology;
|
||||
std::shared_ptr<Abstract_Frame::Render_State> frame;
|
||||
std::shared_ptr<Render_Completion> completion;
|
||||
Scene_Render_Completion completion_callback;
|
||||
};
|
||||
class Execution_Context;
|
||||
class Render_Execution_Scope;
|
||||
@@ -95,7 +96,7 @@ public:
|
||||
[[nodiscard]] const Composition_Relationships* composition() const noexcept;
|
||||
[[nodiscard]] Raster_Capabilities* raster() noexcept;
|
||||
[[nodiscard]] const Raster_Capabilities* raster() const noexcept;
|
||||
[[nodiscard]] Scene_Render_Result submit_render(Abstract_Frame* frame);
|
||||
[[nodiscard]] Scene_Render_Result submit_render(Abstract_Frame* frame, Scene_Render_Completion completion = {});
|
||||
void complete_pending_operation();
|
||||
void record_pending_exception(std::exception_ptr exception);
|
||||
void capture_edit_renderable(const Renderable& renderable);
|
||||
|
||||
@@ -47,6 +47,9 @@ std::shared_ptr<const Frame_Control_Strategy_Base> Plot_Frame_Control::runtime()
|
||||
return impl_->strategy;
|
||||
}
|
||||
auto Plot_Frame_Control::request(Render_Scene_2D& scene) -> Expected<void, Plot_Render_Frame_Error> {
|
||||
return request(scene, {});
|
||||
}
|
||||
auto Plot_Frame_Control::request(Render_Scene_2D& scene, Scene_Render_Completion completion) -> Expected<void, Plot_Render_Frame_Error> {
|
||||
const bool prepared = visit(*impl_->strategy, [&scene](auto& strategy) {
|
||||
auto frame = strategy.acquire_painter();
|
||||
if (!frame) return false;
|
||||
@@ -58,13 +61,13 @@ auto Plot_Frame_Control::request(Render_Scene_2D& scene) -> Expected<void, Plot_
|
||||
dynamic_cast<Manual_Strategy*>(impl_->strategy.get())) {
|
||||
if (strategy->refresh() != Manual_Refresh_Result::none) return unexpected(Plot_Render_Frame_Error::no_pending_frame);
|
||||
}
|
||||
return visit(*impl_->strategy, [&scene](auto& strategy)
|
||||
return visit(*impl_->strategy, [&scene, completion = std::move(completion)](auto& strategy) mutable
|
||||
-> Expected<void, Plot_Render_Frame_Error> {
|
||||
auto frame = strategy.acquire_renderer();
|
||||
if (!frame)
|
||||
return unexpected(
|
||||
Plot_Render_Frame_Error::renderer_unavailable);
|
||||
const auto result = scene.Scene_Base::render(*frame);
|
||||
const auto result = scene.Scene_Base::render(*frame, std::move(completion));
|
||||
if (result == Scene_Render_Result::none) return {};
|
||||
const auto converted =
|
||||
adapter::enum_cast<Plot_Render_Frame_Error>(result);
|
||||
|
||||
@@ -14,6 +14,7 @@ public:
|
||||
[[nodiscard]] std::shared_ptr<Frame_Control_Strategy_Base> runtime();
|
||||
[[nodiscard]] std::shared_ptr<const Frame_Control_Strategy_Base> runtime() const;
|
||||
[[nodiscard]] auto request(Render_Scene_2D& scene) -> Expected<void, Plot_Render_Frame_Error>;
|
||||
[[nodiscard]] auto request(Render_Scene_2D& scene, Scene_Render_Completion completion) -> Expected<void, Plot_Render_Frame_Error>;
|
||||
void replace(std::shared_ptr<Frame_Control_Strategy_Base> strategy);
|
||||
private:
|
||||
struct Impl;
|
||||
|
||||
@@ -148,12 +148,15 @@ void detail::Render_Scene_2D::dispatch_event(const Event& event) {
|
||||
}
|
||||
}
|
||||
auto detail::Render_Scene_2D::render_frame() -> Expected<Plot_Render_Status, Plot_Render_Frame_Error> {
|
||||
return render_frame({});
|
||||
}
|
||||
auto detail::Render_Scene_2D::render_frame(Scene_Render_Completion completion) -> Expected<Plot_Render_Status, Plot_Render_Frame_Error> {
|
||||
if (!view_active()) return Plot_Render_Status::view_inactive;
|
||||
const Size viewport = get<&State::viewport>();
|
||||
if (viewport.empty()) return Plot_Render_Status::empty_viewport;
|
||||
const auto started = std::chrono::steady_clock::now();
|
||||
try {
|
||||
if (const auto result = d_func<Impl>().frame_control->request(*this); !result) return unexpected(result.error());
|
||||
if (const auto result = d_func<Impl>().frame_control->request(*this, std::move(completion)); !result) return unexpected(result.error());
|
||||
}
|
||||
catch (...) {
|
||||
::renderive::error::unexpected(
|
||||
|
||||
@@ -174,6 +174,7 @@ public:
|
||||
[[nodiscard]] ::renderive_Owner<::renderive::Renderable> root_renderable() const;
|
||||
void dispatch_event(const Event& event);
|
||||
[[nodiscard]] auto render_frame() -> Expected<Plot_Render_Status, Plot_Render_Frame_Error>;
|
||||
[[nodiscard]] auto render_frame(Scene_Render_Completion completion) -> Expected<Plot_Render_Status, Plot_Render_Frame_Error>;
|
||||
void with_frame(const std::function<void(Image_View)>& consumer);
|
||||
void activate_view();
|
||||
void deactivate_view() noexcept;
|
||||
|
||||
@@ -507,7 +507,7 @@ Scene_Control_Error detail::Render_Scene_3D::dispatch(
|
||||
return Scene_Control_Error::none;
|
||||
}
|
||||
Frame_Request_Result detail::Render_Scene_3D::request_frame() {
|
||||
const auto requested = d_func<Impl>().frame_control->request(*this);
|
||||
const auto requested = request_frame({});
|
||||
if (requested != Frame_Request_Result::none) return requested;
|
||||
switch (wait_for_render()) {
|
||||
case Scene_Render_Result::none: return Frame_Request_Result::none;
|
||||
@@ -521,6 +521,9 @@ Frame_Request_Result detail::Render_Scene_3D::request_frame() {
|
||||
}
|
||||
return Frame_Request_Result::external_failure;
|
||||
}
|
||||
Frame_Request_Result detail::Render_Scene_3D::request_frame(Scene_Render_Completion completion) {
|
||||
return d_func<Impl>().frame_control->request(*this, std::move(completion));
|
||||
}
|
||||
std::shared_ptr<const Pixel_Frame> detail::Render_Scene_3D::latest_frame() const {
|
||||
return d_func<Impl>().latest_frame();
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@ public:
|
||||
[[nodiscard]] Scene_Control_Error dispatch(
|
||||
const ::renderive::Event& event);
|
||||
[[nodiscard]] Frame_Request_Result request_frame();
|
||||
[[nodiscard]] Frame_Request_Result request_frame(Scene_Render_Completion completion);
|
||||
[[nodiscard]] std::shared_ptr<const Pixel_Frame> latest_frame() const;
|
||||
~Render_Scene_3D() override;
|
||||
private:
|
||||
|
||||
@@ -45,6 +45,9 @@ std::shared_ptr<const Frame_Control_Strategy_Base> Point_Frame_Control::runtime(
|
||||
return impl_->strategy;
|
||||
}
|
||||
Frame_Request_Result Point_Frame_Control::request(Render_Scene_3D& scene) {
|
||||
return request(scene, {});
|
||||
}
|
||||
Frame_Request_Result Point_Frame_Control::request(Render_Scene_3D& scene, Scene_Render_Completion completion) {
|
||||
const auto prepared = visit(*impl_->strategy, [&scene](auto& strategy) {
|
||||
auto frame = strategy.acquire_painter();
|
||||
if (!frame) return false;
|
||||
@@ -53,10 +56,10 @@ Frame_Request_Result Point_Frame_Control::request(Render_Scene_3D& scene) {
|
||||
});
|
||||
if (!prepared) return Frame_Request_Result::painter_unavailable;
|
||||
if (auto* strategy = dynamic_cast<Manual_Strategy*>(impl_->strategy.get()); strategy && strategy->refresh() != Manual_Refresh_Result::none) return Frame_Request_Result::no_pending_frame;
|
||||
return visit(*impl_->strategy, [&scene](auto& strategy) {
|
||||
return visit(*impl_->strategy, [&scene, completion = std::move(completion)](auto& strategy) mutable {
|
||||
auto frame = strategy.acquire_renderer();
|
||||
if (!frame) return Frame_Request_Result::renderer_unavailable;
|
||||
const auto result = scene.Scene_Base::render(*frame);
|
||||
const auto result = scene.Scene_Base::render(*frame, std::move(completion));
|
||||
const auto converted =
|
||||
adapter::enum_cast<Frame_Request_Result>(result);
|
||||
if (!converted)
|
||||
|
||||
@@ -14,6 +14,7 @@ public:
|
||||
[[nodiscard]] std::shared_ptr<Frame_Control_Strategy_Base> runtime();
|
||||
[[nodiscard]] std::shared_ptr<const Frame_Control_Strategy_Base> runtime() const;
|
||||
[[nodiscard]] Frame_Request_Result request(Render_Scene_3D& scene);
|
||||
[[nodiscard]] Frame_Request_Result request(Render_Scene_3D& scene, Scene_Render_Completion completion);
|
||||
void replace(std::shared_ptr<Frame_Control_Strategy_Base> strategy);
|
||||
private:
|
||||
struct Impl;
|
||||
|
||||
@@ -67,11 +67,19 @@ private:
|
||||
co_return;
|
||||
throw asio::system_error(error);
|
||||
}
|
||||
auto response = session.handle(event);
|
||||
using Completion_Channel = asio::experimental::concurrent_channel<void(asio::error_code, Web_Async_Response)>;
|
||||
auto completion_channel = std::make_shared<Completion_Channel>(events_.get_executor(), 1);
|
||||
session.async_handle(event, [completion_channel](Web_Async_Response response) {
|
||||
completion_channel->try_send(asio::error_code{}, std::move(response));
|
||||
});
|
||||
asio::error_code completion_error;
|
||||
auto completed = co_await completion_channel->async_receive(asio::redirect_error(asio::use_awaitable, completion_error));
|
||||
if (completion_error) throw asio::system_error(completion_error);
|
||||
if (completed.exception) std::rethrow_exception(completed.exception);
|
||||
if (stopped_.load(std::memory_order_acquire))
|
||||
co_return;
|
||||
if (response)
|
||||
response_handler_(std::move(*response));
|
||||
if (completed.response)
|
||||
response_handler_(std::move(*completed.response));
|
||||
}
|
||||
}
|
||||
asio::experimental::concurrent_channel<void(asio::error_code, Web_Event)> events_;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@@ -308,6 +309,40 @@ struct Gallery_Plot_Session::Impl {
|
||||
return std::nullopt;
|
||||
return Web_Response{Web_Response_Type::Pixels, std::move(*pixels)};
|
||||
}
|
||||
void async_handle_frame_request(Web_Response_Completion completion) {
|
||||
const auto request_started = std::chrono::steady_clock::now();
|
||||
if (!scene) {
|
||||
completion({});
|
||||
return;
|
||||
}
|
||||
const bool submitted = scene->async_request_frame(
|
||||
[this, request_started, completion](Scene_Render_Result render_result, std::exception_ptr exception) mutable {
|
||||
if (exception) {
|
||||
completion({{}, std::move(exception)});
|
||||
return;
|
||||
}
|
||||
if (render_result != Scene_Render_Result::none) {
|
||||
completion({});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const auto encode_started = std::chrono::steady_clock::now();
|
||||
auto pixels = scene->encode_latest_pixels();
|
||||
const auto encode_finished = std::chrono::steady_clock::now();
|
||||
if (pixels)
|
||||
scene->record_pixel_response(request_started, encode_started, encode_finished, pixels->size());
|
||||
if (!pixels) {
|
||||
completion({});
|
||||
return;
|
||||
}
|
||||
completion({Web_Response{Web_Response_Type::Pixels, std::move(*pixels)}, {}});
|
||||
}
|
||||
catch (...) {
|
||||
completion({{}, std::current_exception()});
|
||||
}
|
||||
});
|
||||
if (!submitted) completion({});
|
||||
}
|
||||
std::optional<Web_Response> handle(const Web_Event& event) {
|
||||
if (std::holds_alternative<Frame_Request>(event))
|
||||
return handle_frame_request();
|
||||
@@ -336,10 +371,20 @@ struct Gallery_Plot_Session::Impl {
|
||||
},
|
||||
event);
|
||||
}
|
||||
void async_handle(const Web_Event& event, Web_Response_Completion completion) {
|
||||
if (std::holds_alternative<Frame_Request>(event)) {
|
||||
async_handle_frame_request(std::move(completion));
|
||||
return;
|
||||
}
|
||||
completion({handle(event), {}});
|
||||
}
|
||||
};
|
||||
Gallery_Plot_Session::Gallery_Plot_Session() : impl_(std::make_unique<Impl>()) {}
|
||||
Gallery_Plot_Session::~Gallery_Plot_Session() = default;
|
||||
std::optional<Web_Response> Gallery_Plot_Session::handle(const Web_Event& event) {
|
||||
return impl_->handle(event);
|
||||
}
|
||||
void Gallery_Plot_Session::async_handle(const Web_Event& event, Web_Response_Completion completion) {
|
||||
impl_->async_handle(event, std::move(completion));
|
||||
}
|
||||
} // namespace renderive::web
|
||||
|
||||
@@ -15,7 +15,7 @@ public:
|
||||
Gallery_Plot_Session& operator=(const Gallery_Plot_Session&) = delete;
|
||||
|
||||
[[nodiscard]] std::optional<Web_Response> handle(const Web_Event& event);
|
||||
|
||||
void async_handle(const Web_Event& event, Web_Response_Completion completion);
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
|
||||
@@ -25,20 +25,21 @@ struct Web_Plot_Session::Impl {
|
||||
scene = std::move(*built_scene);
|
||||
scene->activate_view();
|
||||
}
|
||||
std::optional<Web_Response> encode_frame() {
|
||||
std::string pixels;
|
||||
scene->with_frame([&](Image_View image) {
|
||||
pixels = encode_pixel_frame(image, ++sequence);
|
||||
});
|
||||
if (pixels.empty()) return std::nullopt;
|
||||
return Web_Response{Web_Response_Type::Pixels, std::move(pixels)};
|
||||
}
|
||||
std::optional<Web_Response> handle(const Web_Event& event) {
|
||||
return std::visit([this](const auto& value) -> std::optional<Web_Response> {
|
||||
using T = std::decay_t<decltype(value)>;
|
||||
if constexpr (std::same_as<T, Frame_Request>) {
|
||||
const auto result = scene->render_frame();
|
||||
if (!result || scene->wait_for_render() != Scene_Render_Result::none)
|
||||
return std::nullopt;
|
||||
std::string pixels;
|
||||
scene->with_frame([&](Image_View image) {
|
||||
pixels = encode_pixel_frame(image, ++sequence);
|
||||
});
|
||||
if (pixels.empty()) return std::nullopt;
|
||||
return Web_Response{Web_Response_Type::Pixels,
|
||||
std::move(pixels)};
|
||||
if (!result || scene->wait_for_render() != Scene_Render_Result::none) return std::nullopt;
|
||||
return encode_frame();
|
||||
} else if constexpr (std::same_as<T, Viewport_Resize>) {
|
||||
viewport = value.size;
|
||||
rebuild();
|
||||
@@ -48,10 +49,37 @@ struct Web_Plot_Session::Impl {
|
||||
return std::nullopt;
|
||||
}, event);
|
||||
}
|
||||
void async_handle(const Web_Event& event, Web_Response_Completion completion) {
|
||||
if (!std::holds_alternative<Frame_Request>(event)) {
|
||||
completion({handle(event), {}});
|
||||
return;
|
||||
}
|
||||
const auto result = scene->render_frame(
|
||||
[this, completion](Scene_Render_Result render_result, std::exception_ptr exception) mutable {
|
||||
if (exception) {
|
||||
completion({{}, std::move(exception)});
|
||||
return;
|
||||
}
|
||||
if (render_result != Scene_Render_Result::none) {
|
||||
completion({});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
completion({encode_frame(), {}});
|
||||
}
|
||||
catch (...) {
|
||||
completion({{}, std::current_exception()});
|
||||
}
|
||||
});
|
||||
if (!result || *result != Plot_Render_Status::rendered) completion({});
|
||||
}
|
||||
};
|
||||
Web_Plot_Session::Web_Plot_Session() : impl_(std::make_unique<Impl>()) {}
|
||||
Web_Plot_Session::~Web_Plot_Session() = default;
|
||||
std::optional<Web_Response> Web_Plot_Session::handle(const Web_Event& event) {
|
||||
return impl_->handle(event);
|
||||
}
|
||||
void Web_Plot_Session::async_handle(const Web_Event& event, Web_Response_Completion completion) {
|
||||
impl_->async_handle(event, std::move(completion));
|
||||
}
|
||||
} // namespace renderive::web
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "Web_Event.h"
|
||||
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@@ -14,7 +16,11 @@ struct Web_Response {
|
||||
Web_Response_Type type = Web_Response_Type::Pixels;
|
||||
std::string payload;
|
||||
};
|
||||
|
||||
struct Web_Async_Response {
|
||||
std::optional<Web_Response> response;
|
||||
std::exception_ptr exception;
|
||||
};
|
||||
using Web_Response_Completion = std::function<void(Web_Async_Response)>;
|
||||
class Web_Plot_Session final {
|
||||
public:
|
||||
Web_Plot_Session();
|
||||
@@ -23,7 +29,7 @@ public:
|
||||
Web_Plot_Session& operator=(const Web_Plot_Session&) = delete;
|
||||
|
||||
[[nodiscard]] std::optional<Web_Response> handle(const Web_Event& event);
|
||||
|
||||
void async_handle(const Web_Event& event, Web_Response_Completion completion);
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Gallery_Performance_Types.h"
|
||||
#include "Gallery_Types.h"
|
||||
#include "adminive/adminive.hpp"
|
||||
#include <renderive/scene/base/Scene_Base.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
@@ -22,6 +23,7 @@ public:
|
||||
virtual void resize(int width, int height) = 0;
|
||||
virtual void dispatch(const Gallery_Input_Event& event) = 0;
|
||||
[[nodiscard]] virtual bool request_frame() = 0;
|
||||
[[nodiscard]] virtual bool async_request_frame(Scene_Render_Completion completion) = 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;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace renderive::web {
|
||||
@@ -159,7 +160,15 @@ public:
|
||||
++render_count_;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool async_request_frame(Scene_Render_Completion completion) override {
|
||||
update_samples();
|
||||
const auto result = scene_->render_frame(
|
||||
[this, completion = std::move(completion)](Scene_Render_Result render_result, std::exception_ptr exception) mutable {
|
||||
if (!exception && render_result == Scene_Render_Result::none) ++render_count_;
|
||||
completion(render_result, std::move(exception));
|
||||
});
|
||||
return result && *result == Plot_Render_Status::rendered;
|
||||
}
|
||||
std::optional<std::string> encode_latest_pixels() override {
|
||||
std::string result;
|
||||
scene_->with_frame([&](Image_View image) {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace renderive::web {
|
||||
@@ -137,7 +138,14 @@ public:
|
||||
++render_count_;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool async_request_frame(Scene_Render_Completion completion) override {
|
||||
const auto result = scene_->request_frame(
|
||||
[this, completion = std::move(completion)](Scene_Render_Result render_result, std::exception_ptr exception) mutable {
|
||||
if (!exception && render_result == Scene_Render_Result::none) ++render_count_;
|
||||
completion(render_result, std::move(exception));
|
||||
});
|
||||
return result == Frame_Request_Result::none;
|
||||
}
|
||||
std::optional<std::string> encode_latest_pixels() override {
|
||||
const auto frame = scene_->latest_frame();
|
||||
if (!frame || frame->rgba8.empty()) return std::nullopt;
|
||||
|
||||
Reference in New Issue
Block a user