Route app async work through AppRuntime
This commit is contained in:
@@ -18,6 +18,28 @@ agent or engineer to remove them without reconstructing context from chat.
|
||||
|
||||
## Reductions
|
||||
|
||||
- 2026-06-17: `DEBT-0039` was narrowed again. `App::dialog_browse()` in
|
||||
`src/app_dialogs_workflow.cpp` now delegates retained browse-dialog button
|
||||
wiring and selected-path open execution through
|
||||
`src/legacy_document_open_services.*`, so the app dialog shell no longer
|
||||
owns that document-open handoff inline while retained dialog creation,
|
||||
unsaved-project prompting, project-open execution, and title/layer refresh
|
||||
still remain.
|
||||
- 2026-06-17: `DEBT-0038` was narrowed again. The retained cloud upload and
|
||||
download background execution in `src/legacy_cloud_services.cpp` now routes
|
||||
through `AppRuntime::canvas_async_task` instead of a file-static worker
|
||||
singleton, while retained prompt/progress lifetime, OpenGL context guards,
|
||||
thumbnail loading, and transfer execution still remain in the cloud bridge.
|
||||
- 2026-06-17: `DEBT-0048` was narrowed again. The retained ABR/PPBR import path
|
||||
in `src/legacy_brush_package_import_services.cpp` now uses
|
||||
`AppRuntime::canvas_async_task` instead of a file-static worker singleton,
|
||||
while the legacy preset panel remains the importer/storage owner and broader
|
||||
brush asset/UI ownership transfer still remains open.
|
||||
- 2026-06-17: `DEBT-0044` was narrowed again. The retained asynchronous
|
||||
timelapse export path in `src/legacy_document_export_services.cpp` now routes
|
||||
through `AppRuntime::canvas_async_task` instead of a file-static worker
|
||||
singleton, while retained `App::rec_export`, animation MP4 execution, and
|
||||
mobile/Web completion paths still remain in the video-export bridge.
|
||||
- 2026-06-17: `DEBT-0036` was narrowed again. `src/canvas_layer.cpp` now
|
||||
routes retained `Layer` / `LayerFrame` render queueing through
|
||||
`src/renderer_gl/render_runtime_dispatch.h` instead of direct
|
||||
|
||||
@@ -79,6 +79,10 @@ Current conclusion:
|
||||
- `AppRuntime` now owns synchronized running flags and explicit
|
||||
same-thread/post-reject queue behavior, but broader app/runtime singleton
|
||||
reach and retained shell ownership still remain.
|
||||
- The document-browse dialog handoff now lives in
|
||||
`src/legacy_document_open_services.*`, and retained cloud upload/download,
|
||||
brush-package import, and timelapse-export async paths now use
|
||||
`AppRuntime::canvas_async_task` instead of file-static worker singletons.
|
||||
- Platform extraction improved substantially and the root app source group no
|
||||
longer compiles Web platform sources directly, but broader CMake and
|
||||
entrypoint cleanup are not complete.
|
||||
|
||||
@@ -59,6 +59,12 @@ Key facts:
|
||||
- `AppRuntime` now owns synchronized running flags plus explicit post/reject,
|
||||
same-thread execution, and queue-drain behavior, but broader singleton reach
|
||||
and app-shell ownership remain.
|
||||
- Retained cloud upload/download, brush-package import, and timelapse-export
|
||||
async paths now route through `AppRuntime::canvas_async_task`, but dialog and
|
||||
execution ownership still remains in retained app/document/cloud bridges.
|
||||
- `App::dialog_browse()` no longer owns browse-dialog button wiring inline; the
|
||||
retained document-open bridge now owns that handoff in
|
||||
`src/legacy_document_open_services.*`.
|
||||
|
||||
## Parallel Assignment Rules
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "pch.h"
|
||||
#include "app.h"
|
||||
#include "app_core/document_resize.h"
|
||||
#include "legacy_document_open_services.h"
|
||||
#include "legacy_document_canvas_services.h"
|
||||
#include "legacy_document_session_services.h"
|
||||
#include "legacy_ui_overlay_services.h"
|
||||
@@ -10,32 +11,6 @@
|
||||
|
||||
namespace {
|
||||
|
||||
void wire_document_browse_dialog_actions(
|
||||
App& app,
|
||||
const std::shared_ptr<NodeDialogBrowse>& dialog,
|
||||
Node& overlay_anchor,
|
||||
pp::ui::NodeHandle overlay_handle)
|
||||
{
|
||||
const auto close_dialog = [&overlay_anchor, overlay_handle]() {
|
||||
const auto close_status =
|
||||
pp::panopainter::close_legacy_overlay_node(overlay_anchor, overlay_handle);
|
||||
(void)close_status;
|
||||
};
|
||||
|
||||
dialog->btn_ok->on_click = [&app, dialog, close_dialog](Node*)
|
||||
{
|
||||
if (dialog->is_selected())
|
||||
{
|
||||
app.open_document(dialog->selected_path);
|
||||
close_dialog();
|
||||
}
|
||||
};
|
||||
dialog->btn_cancel->on_click = [close_dialog](Node*)
|
||||
{
|
||||
close_dialog();
|
||||
};
|
||||
}
|
||||
|
||||
void wire_document_save_dialog_buttons(
|
||||
App& app,
|
||||
const std::shared_ptr<NodeDialogSave>& dialog,
|
||||
@@ -201,7 +176,11 @@ void App::dialog_browse()
|
||||
return;
|
||||
}
|
||||
const auto overlay_handle = overlay.value();
|
||||
wire_document_browse_dialog_actions(*this, dialog, *overlay_anchor, overlay_handle);
|
||||
pp::panopainter::wire_legacy_document_browse_dialog_actions(
|
||||
*this,
|
||||
dialog,
|
||||
*overlay_anchor,
|
||||
overlay_handle);
|
||||
};
|
||||
|
||||
continue_document_workflow_after_optional_save(show_dialog);
|
||||
|
||||
@@ -5,102 +5,21 @@
|
||||
#include "app.h"
|
||||
#include "node_panel_brush.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <stop_token>
|
||||
#include <thread>
|
||||
|
||||
namespace pp::panopainter {
|
||||
namespace {
|
||||
|
||||
class LegacyBrushPackageWorker final {
|
||||
public:
|
||||
LegacyBrushPackageWorker()
|
||||
: worker_([this](std::stop_token stop_token) {
|
||||
run(stop_token);
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
~LegacyBrushPackageWorker()
|
||||
{
|
||||
shutdown();
|
||||
}
|
||||
|
||||
void post(std::function<void()> task)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (stopping_)
|
||||
return;
|
||||
tasks_.push_back(std::move(task));
|
||||
}
|
||||
cv_.notify_one();
|
||||
}
|
||||
|
||||
private:
|
||||
void shutdown()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
stopping_ = true;
|
||||
}
|
||||
cv_.notify_all();
|
||||
}
|
||||
|
||||
void run(std::stop_token stop_token)
|
||||
{
|
||||
for (;;) {
|
||||
std::function<void()> task;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
cv_.wait(lock, [&] {
|
||||
return stopping_ || stop_token.stop_requested() || !tasks_.empty();
|
||||
});
|
||||
if ((stopping_ || stop_token.stop_requested()) && tasks_.empty())
|
||||
break;
|
||||
task = std::move(tasks_.front());
|
||||
tasks_.pop_front();
|
||||
}
|
||||
|
||||
if (task) {
|
||||
try {
|
||||
task();
|
||||
} catch (...) {
|
||||
LOG("brush package import worker task failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::mutex mutex_;
|
||||
std::condition_variable cv_;
|
||||
std::deque<std::function<void()>> tasks_;
|
||||
bool stopping_ = false;
|
||||
std::jthread worker_;
|
||||
};
|
||||
|
||||
LegacyBrushPackageWorker& brush_package_worker()
|
||||
void queue_legacy_brush_package_import_job(
|
||||
App& app,
|
||||
std::shared_ptr<NodePanelBrushPreset> presets,
|
||||
pp::app::BrushPackageImportKind kind,
|
||||
std::string path_string)
|
||||
{
|
||||
static LegacyBrushPackageWorker worker;
|
||||
return worker;
|
||||
}
|
||||
|
||||
class LegacyBrushPackageImportServices final : public pp::app::BrushPackageImportServices {
|
||||
public:
|
||||
explicit LegacyBrushPackageImportServices(App& app) noexcept
|
||||
: app_(app)
|
||||
{
|
||||
}
|
||||
|
||||
void import_brush_package(pp::app::BrushPackageImportKind kind, std::string_view path) override
|
||||
{
|
||||
auto presets = app_.presets;
|
||||
const auto path_string = std::string(path);
|
||||
brush_package_worker().post([presets, kind, path_string] {
|
||||
app.runtime().canvas_async_task([presets = std::move(presets),
|
||||
kind,
|
||||
path_string = std::move(path_string)]() mutable {
|
||||
BT_SetTerminate();
|
||||
if (!presets) {
|
||||
return;
|
||||
@@ -112,6 +31,19 @@ public:
|
||||
|
||||
presets->import_ppbr(path_string);
|
||||
});
|
||||
}
|
||||
|
||||
class LegacyBrushPackageImportServices final : public pp::app::BrushPackageImportServices {
|
||||
public:
|
||||
explicit LegacyBrushPackageImportServices(App& app) noexcept
|
||||
: app_(app)
|
||||
{
|
||||
}
|
||||
|
||||
void import_brush_package(pp::app::BrushPackageImportKind kind, std::string_view path) override
|
||||
{
|
||||
const auto path_string = std::string(path);
|
||||
queue_legacy_brush_package_import_job(app_, app_.presets, kind, std::move(path_string));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -21,79 +21,6 @@ pp::foundation::Status execute_legacy_cloud_download_selection_action(
|
||||
|
||||
namespace {
|
||||
|
||||
class LegacyCloudWorker final {
|
||||
public:
|
||||
LegacyCloudWorker()
|
||||
: worker_([this](std::stop_token stop_token) {
|
||||
run(stop_token);
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
~LegacyCloudWorker()
|
||||
{
|
||||
shutdown();
|
||||
}
|
||||
|
||||
void post(std::function<void()> task)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (stopping_)
|
||||
return;
|
||||
tasks_.push_back(std::move(task));
|
||||
}
|
||||
cv_.notify_one();
|
||||
}
|
||||
|
||||
private:
|
||||
void shutdown()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
stopping_ = true;
|
||||
}
|
||||
cv_.notify_all();
|
||||
}
|
||||
|
||||
void run(std::stop_token stop_token)
|
||||
{
|
||||
for (;;) {
|
||||
std::function<void()> task;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
cv_.wait(lock, [&] {
|
||||
return stopping_ || stop_token.stop_requested() || !tasks_.empty();
|
||||
});
|
||||
if ((stopping_ || stop_token.stop_requested()) && tasks_.empty())
|
||||
break;
|
||||
task = std::move(tasks_.front());
|
||||
tasks_.pop_front();
|
||||
}
|
||||
|
||||
if (task) {
|
||||
try {
|
||||
task();
|
||||
} catch (...) {
|
||||
LOG("cloud worker task failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::mutex mutex_;
|
||||
std::condition_variable cv_;
|
||||
std::deque<std::function<void()>> tasks_;
|
||||
bool stopping_ = false;
|
||||
std::jthread worker_;
|
||||
};
|
||||
|
||||
LegacyCloudWorker& cloud_worker()
|
||||
{
|
||||
static LegacyCloudWorker worker;
|
||||
return worker;
|
||||
}
|
||||
|
||||
#if WITH_CURL
|
||||
int progress_callback_download(void* clientp, curl_off_t dltotal,
|
||||
curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
|
||||
@@ -330,7 +257,7 @@ void setup_cloud_publish_prompt(App& app, bool save_before_upload)
|
||||
const auto prompt_plan = pp::app::plan_cloud_publish_prompt();
|
||||
auto dialog = app.message_box(prompt_plan.title, prompt_plan.message, prompt_plan.show_cancel);
|
||||
wire_cloud_publish_prompt_buttons(dialog, [&app, save_before_upload] {
|
||||
queue_legacy_cloud_worker_task([app = &app, save_before_upload] {
|
||||
app.runtime().canvas_async_task([app = &app, save_before_upload] {
|
||||
execute_cloud_publish_worker(*app, save_before_upload);
|
||||
});
|
||||
});
|
||||
@@ -438,7 +365,7 @@ public:
|
||||
|
||||
void start_download(const pp::app::CloudDownloadRequest& request) override
|
||||
{
|
||||
queue_legacy_cloud_worker_task([app = &app_, request] {
|
||||
app_.runtime().canvas_async_task([app = &app_, request] {
|
||||
execute_cloud_download_thread(*app, request);
|
||||
});
|
||||
}
|
||||
@@ -458,7 +385,11 @@ void show_cloud_save_required_warning(App& app)
|
||||
|
||||
void queue_legacy_cloud_worker_task(std::function<void()> task)
|
||||
{
|
||||
cloud_worker().post(std::move(task));
|
||||
if (App::I == nullptr) {
|
||||
LOG("cloud worker task skipped: app unavailable");
|
||||
return;
|
||||
}
|
||||
App::I->runtime().canvas_async_task(std::move(task));
|
||||
}
|
||||
|
||||
pp::foundation::Status execute_legacy_cloud_upload_plan(
|
||||
|
||||
@@ -8,16 +8,11 @@
|
||||
#include "paint_renderer/compositor.h"
|
||||
|
||||
#include <array>
|
||||
#include <condition_variable>
|
||||
#include <fstream>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <limits>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <stop_token>
|
||||
#include <thread>
|
||||
|
||||
namespace pp::panopainter {
|
||||
namespace {
|
||||
@@ -36,79 +31,6 @@ struct LegacyDocumentExportSnapshotReports {
|
||||
pp::paint_renderer::DocumentFrameFacePngExportResult face_pngs;
|
||||
};
|
||||
|
||||
class LegacyDocumentVideoExportWorker final {
|
||||
public:
|
||||
LegacyDocumentVideoExportWorker()
|
||||
: worker_([this](std::stop_token stop_token) {
|
||||
run(stop_token);
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
~LegacyDocumentVideoExportWorker()
|
||||
{
|
||||
shutdown();
|
||||
}
|
||||
|
||||
void post(std::function<void()> task)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (stopping_)
|
||||
return;
|
||||
tasks_.push_back(std::move(task));
|
||||
}
|
||||
cv_.notify_one();
|
||||
}
|
||||
|
||||
private:
|
||||
void shutdown()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
stopping_ = true;
|
||||
}
|
||||
cv_.notify_all();
|
||||
}
|
||||
|
||||
void run(std::stop_token stop_token)
|
||||
{
|
||||
for (;;) {
|
||||
std::function<void()> task;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
cv_.wait(lock, [&] {
|
||||
return stopping_ || stop_token.stop_requested() || !tasks_.empty();
|
||||
});
|
||||
if ((stopping_ || stop_token.stop_requested()) && tasks_.empty())
|
||||
break;
|
||||
task = std::move(tasks_.front());
|
||||
tasks_.pop_front();
|
||||
}
|
||||
|
||||
if (task) {
|
||||
try {
|
||||
task();
|
||||
} catch (...) {
|
||||
LOG("document video export worker task failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::mutex mutex_;
|
||||
std::condition_variable cv_;
|
||||
std::deque<std::function<void()>> tasks_;
|
||||
bool stopping_ = false;
|
||||
std::jthread worker_;
|
||||
};
|
||||
|
||||
LegacyDocumentVideoExportWorker& document_video_export_worker()
|
||||
{
|
||||
static LegacyDocumentVideoExportWorker worker;
|
||||
return worker;
|
||||
}
|
||||
|
||||
pp::foundation::Status write_export_binary_file(std::string_view path, std::span<const std::byte> bytes)
|
||||
{
|
||||
if (path.empty()) {
|
||||
@@ -846,7 +768,7 @@ public:
|
||||
auto* app = &app_;
|
||||
auto path_string = std::string(path);
|
||||
if (asynchronous_) {
|
||||
document_video_export_worker().post([app, path_string = std::move(path_string)]() mutable {
|
||||
app_.runtime().canvas_async_task([app, path_string = std::move(path_string)]() mutable {
|
||||
BT_SetTerminate();
|
||||
app->rec_export(path_string);
|
||||
app->ui_task([app, path_string = std::move(path_string)]() mutable {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "legacy_history_services.h"
|
||||
#include "legacy_ui_overlay_services.h"
|
||||
#include "log.h"
|
||||
#include "node_dialog_browse.h"
|
||||
#include "node_panel_brush.h"
|
||||
#include "node_panel_layer.h"
|
||||
|
||||
@@ -154,6 +155,32 @@ private:
|
||||
|
||||
} // namespace
|
||||
|
||||
void wire_legacy_document_browse_dialog_actions(
|
||||
App& app,
|
||||
const std::shared_ptr<NodeDialogBrowse>& dialog,
|
||||
Node& overlay_anchor,
|
||||
pp::ui::NodeHandle overlay_handle)
|
||||
{
|
||||
const auto close_dialog = [&overlay_anchor, overlay_handle]() {
|
||||
const auto close_status =
|
||||
pp::panopainter::close_legacy_overlay_node(overlay_anchor, overlay_handle);
|
||||
(void)close_status;
|
||||
};
|
||||
|
||||
dialog->btn_ok->on_click = [&app, dialog, close_dialog](Node*)
|
||||
{
|
||||
if (dialog->is_selected())
|
||||
{
|
||||
app.open_document(dialog->selected_path);
|
||||
close_dialog();
|
||||
}
|
||||
};
|
||||
dialog->btn_cancel->on_click = [close_dialog](Node*)
|
||||
{
|
||||
close_dialog();
|
||||
};
|
||||
}
|
||||
|
||||
pp::foundation::Status execute_legacy_document_open_plan(
|
||||
App& app,
|
||||
pp::app::DocumentOpenPlanAction action,
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
|
||||
#include "app_core/document_session.h"
|
||||
#include "foundation/result.h"
|
||||
#include "ui_core/overlay_lifetime.h"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <memory>
|
||||
|
||||
class App;
|
||||
class Node;
|
||||
class NodeDialogBrowse;
|
||||
|
||||
namespace pp::panopainter {
|
||||
|
||||
@@ -19,6 +23,12 @@ namespace pp::panopainter {
|
||||
pp::app::DocumentOpenPlanAction action,
|
||||
const pp::app::DocumentOpenRoute& route);
|
||||
|
||||
void wire_legacy_document_browse_dialog_actions(
|
||||
App& app,
|
||||
const std::shared_ptr<NodeDialogBrowse>& dialog,
|
||||
Node& overlay_anchor,
|
||||
pp::ui::NodeHandle overlay_handle);
|
||||
|
||||
void execute_legacy_downloaded_project_open(
|
||||
App& app,
|
||||
std::string_view path,
|
||||
|
||||
Reference in New Issue
Block a user