Own preview and recording workers
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -106,9 +106,9 @@ Current architecture mismatches that must be treated as real blockers:
|
|||||||
- `app_layout.cpp` and `app_dialogs.cpp` are still mixed shell/controller files
|
- `app_layout.cpp` and `app_dialogs.cpp` are still mixed shell/controller files
|
||||||
rather than thin composition/binding surfaces.
|
rather than thin composition/binding surfaces.
|
||||||
- `App`, `Canvas`, `Node`, retained workers, and platform entrypoints still use
|
- `App`, `Canvas`, `Node`, retained workers, and platform entrypoints still use
|
||||||
global singleton reach, raw observer pointers, detached `std::thread`
|
global singleton reach, raw observer pointers, retained static worker
|
||||||
launches in preview/recording paths, and ad hoc
|
ownership in several app families, and ad hoc mutex/condition-variable
|
||||||
mutex/condition-variable ownership.
|
ownership.
|
||||||
- Modern C++23 usage exists in extracted components, especially `std::span`,
|
- Modern C++23 usage exists in extracted components, especially `std::span`,
|
||||||
explicit result/status objects, and a few concepts, but the live app still
|
explicit result/status objects, and a few concepts, but the live app still
|
||||||
does not consistently express ownership, thread affinity, or renderer
|
does not consistently express ownership, thread affinity, or renderer
|
||||||
|
|||||||
@@ -49,7 +49,9 @@ Completed, blocked, and superseded task history moved to
|
|||||||
- `platform_legacy` is still part of the live app shell
|
- `platform_legacy` is still part of the live app shell
|
||||||
- The app runtime boundary is not finished:
|
- The app runtime boundary is not finished:
|
||||||
- render/UI queues are static `App` state
|
- render/UI queues are static `App` state
|
||||||
- detached workers still launch from preview and recording code
|
- app-facing detached launches are no longer the main issue; preview and
|
||||||
|
recording now use owned worker threads, but those families still rely on
|
||||||
|
retained static/global ownership and ad hoc runtime control
|
||||||
- canvas async import/export/save/open now run through an owned in-file
|
- canvas async import/export/save/open now run through an owned in-file
|
||||||
worker, but their retained progress execution is still not a clean runtime
|
worker, but their retained progress execution is still not a clean runtime
|
||||||
service boundary
|
service boundary
|
||||||
@@ -359,7 +361,8 @@ Current slice:
|
|||||||
workers with explicit UI-thread handoff
|
workers with explicit UI-thread handoff
|
||||||
- canvas async import/export/save/open and timelapse export now also use owned
|
- canvas async import/export/save/open and timelapse export now also use owned
|
||||||
worker queues instead of detached threads
|
worker queues instead of detached threads
|
||||||
- preview and recording-side detached work are still open
|
- preview background rendering and recording thread ownership now also use
|
||||||
|
`std::jthread`, but their retained loop/control flow is still open
|
||||||
|
|
||||||
Write scope:
|
Write scope:
|
||||||
- `src/canvas.cpp`
|
- `src/canvas.cpp`
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ public:
|
|||||||
std::string work_path{ "." };
|
std::string work_path{ "." };
|
||||||
std::string rec_path{ "." };
|
std::string rec_path{ "." };
|
||||||
std::string tmp_path{ "." };
|
std::string tmp_path{ "." };
|
||||||
std::thread rec_thread;
|
std::jthread rec_thread;
|
||||||
bool rec_running = false;
|
bool rec_running = false;
|
||||||
int rec_count = 0;
|
int rec_count = 0;
|
||||||
std::mutex rec_mutex;
|
std::mutex rec_mutex;
|
||||||
|
|||||||
@@ -21,11 +21,12 @@ public:
|
|||||||
void start_thread() override
|
void start_thread() override
|
||||||
{
|
{
|
||||||
app_.update_rec_frames();
|
app_.update_rec_frames();
|
||||||
app_.rec_thread = std::thread(&App::rec_loop, &app_);
|
app_.rec_thread = std::jthread(&App::rec_loop, &app_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop_thread() override
|
void stop_thread() override
|
||||||
{
|
{
|
||||||
|
app_.rec_thread.request_stop();
|
||||||
app_.rec_running = false;
|
app_.rec_running = false;
|
||||||
app_.rec_cv.notify_all();
|
app_.rec_cv.notify_all();
|
||||||
if (app_.rec_thread.joinable())
|
if (app_.rec_thread.joinable())
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <stop_token>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -410,7 +411,6 @@ void execute_stroke_preview_background_capture_pass(
|
|||||||
std::atomic_int NodeStrokePreview::s_instances{ 0 };
|
std::atomic_int NodeStrokePreview::s_instances{ 0 };
|
||||||
std::atomic_bool NodeStrokePreview::s_running{ false };
|
std::atomic_bool NodeStrokePreview::s_running{ false };
|
||||||
std::mutex NodeStrokePreview::s_render_mutex;
|
std::mutex NodeStrokePreview::s_render_mutex;
|
||||||
std::thread NodeStrokePreview::s_renderer;
|
|
||||||
BlockingQueue<std::shared_ptr<NodeStrokePreview>> NodeStrokePreview::s_queue;
|
BlockingQueue<std::shared_ptr<NodeStrokePreview>> NodeStrokePreview::s_queue;
|
||||||
|
|
||||||
RTT NodeStrokePreview::m_rtt;
|
RTT NodeStrokePreview::m_rtt;
|
||||||
@@ -422,17 +422,19 @@ Sampler NodeStrokePreview::m_sampler_linear;
|
|||||||
Sampler NodeStrokePreview::m_sampler_linear_repeat;
|
Sampler NodeStrokePreview::m_sampler_linear_repeat;
|
||||||
Sampler NodeStrokePreview::m_sampler_mipmap;
|
Sampler NodeStrokePreview::m_sampler_mipmap;
|
||||||
DynamicShape NodeStrokePreview::m_brush_shape;
|
DynamicShape NodeStrokePreview::m_brush_shape;
|
||||||
|
std::jthread NodeStrokePreview::s_renderer;
|
||||||
|
|
||||||
|
|
||||||
void NodeStrokePreview::terminate_renderer()
|
void NodeStrokePreview::terminate_renderer()
|
||||||
{
|
{
|
||||||
if (s_running && s_renderer.joinable())
|
if (!s_renderer.joinable())
|
||||||
{
|
return;
|
||||||
|
|
||||||
s_running = false;
|
s_running = false;
|
||||||
|
s_renderer.request_stop();
|
||||||
s_queue.UnlockGetters();
|
s_queue.UnlockGetters();
|
||||||
s_renderer.join();
|
s_renderer.join();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void NodeStrokePreview::empty_queue()
|
void NodeStrokePreview::empty_queue()
|
||||||
{
|
{
|
||||||
@@ -906,11 +908,11 @@ void NodeStrokePreview::draw_stroke()
|
|||||||
{
|
{
|
||||||
if (m_size.x == 0 || m_size.y == 0)
|
if (m_size.x == 0 || m_size.y == 0)
|
||||||
return;
|
return;
|
||||||
s_queue.mutex.lock();
|
std::unique_lock<std::mutex> queue_lock(s_queue.mutex);
|
||||||
if (!s_running)
|
if (!s_renderer.joinable())
|
||||||
{
|
{
|
||||||
s_running = true;
|
s_running = true;
|
||||||
s_renderer = std::thread([] {
|
s_renderer = std::jthread([](std::stop_token stop_token) {
|
||||||
BT_SetTerminate();
|
BT_SetTerminate();
|
||||||
|
|
||||||
m_sampler_linear.create();
|
m_sampler_linear.create();
|
||||||
@@ -922,7 +924,7 @@ void NodeStrokePreview::draw_stroke()
|
|||||||
pp::renderer::gl::linear_mipmap_linear_texture_filter(),
|
pp::renderer::gl::linear_mipmap_linear_texture_filter(),
|
||||||
pp::renderer::gl::linear_texture_filter());
|
pp::renderer::gl::linear_texture_filter());
|
||||||
m_brush_shape.create();
|
m_brush_shape.create();
|
||||||
while (s_running)
|
while (s_running && !stop_token.stop_requested())
|
||||||
{
|
{
|
||||||
auto node = s_queue.Get();
|
auto node = s_queue.Get();
|
||||||
if (node)
|
if (node)
|
||||||
@@ -970,9 +972,10 @@ void NodeStrokePreview::draw_stroke()
|
|||||||
m_tex_dual.destroy();
|
m_tex_dual.destroy();
|
||||||
m_tex_background.destroy();
|
m_tex_background.destroy();
|
||||||
m_brush_shape.destroy();
|
m_brush_shape.destroy();
|
||||||
|
s_running = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
s_queue.mutex.unlock();
|
queue_lock.unlock();
|
||||||
s_queue.PostUnique(std::static_pointer_cast<NodeStrokePreview>(shared_from_this()), m_draw_first);
|
s_queue.PostUnique(std::static_pointer_cast<NodeStrokePreview>(shared_from_this()), m_draw_first);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <atomic>
|
||||||
|
#include <thread>
|
||||||
#include "node_border.h"
|
#include "node_border.h"
|
||||||
#include "rtt.h"
|
#include "rtt.h"
|
||||||
#include "brush.h"
|
#include "brush.h"
|
||||||
@@ -33,7 +35,7 @@ public:
|
|||||||
static std::atomic_int s_instances;
|
static std::atomic_int s_instances;
|
||||||
static std::atomic_bool s_running;
|
static std::atomic_bool s_running;
|
||||||
static std::mutex s_render_mutex;
|
static std::mutex s_render_mutex;
|
||||||
static std::thread s_renderer;
|
static std::jthread s_renderer;
|
||||||
static BlockingQueue<std::shared_ptr<NodeStrokePreview>> s_queue;
|
static BlockingQueue<std::shared_ptr<NodeStrokePreview>> s_queue;
|
||||||
static void terminate_renderer();
|
static void terminate_renderer();
|
||||||
static void empty_queue();
|
static void empty_queue();
|
||||||
|
|||||||
@@ -108,84 +108,87 @@ public:
|
|||||||
[[nodiscard]] pp::platform::apple::AppleDocumentPlatformServices& active_apple_document_platform_services()
|
[[nodiscard]] pp::platform::apple::AppleDocumentPlatformServices& active_apple_document_platform_services()
|
||||||
{
|
{
|
||||||
#ifdef __IOS__
|
#ifdef __IOS__
|
||||||
|
auto* const ios_view = App::I->ios_view;
|
||||||
static pp::platform::apple::AppleDocumentPlatformServices services(
|
static pp::platform::apple::AppleDocumentPlatformServices services(
|
||||||
pp::platform::PlatformFamily::ios,
|
pp::platform::PlatformFamily::ios,
|
||||||
[] {
|
[ios_view] {
|
||||||
pp::platform::apple::AppleDocumentPickerBridge bridge;
|
pp::platform::apple::AppleDocumentPickerBridge bridge;
|
||||||
bridge.clipboard_text = [] {
|
bridge.clipboard_text = [ios_view] {
|
||||||
return [App::I->ios_view clipboard_get_string];
|
return [ios_view clipboard_get_string];
|
||||||
};
|
};
|
||||||
bridge.set_clipboard_text = [](std::string_view text) {
|
bridge.set_clipboard_text = [ios_view](std::string_view text) {
|
||||||
const std::string value(text);
|
const std::string value(text);
|
||||||
return [App::I->ios_view clipboard_set_string:value];
|
return [ios_view clipboard_set_string:value];
|
||||||
};
|
};
|
||||||
bridge.display_file = [](std::string path) {
|
bridge.display_file = [ios_view](std::string path) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[App::I->ios_view display_file:path];
|
[ios_view display_file:path];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
bridge.share_file = [](std::string path) {
|
bridge.share_file = [ios_view](std::string path) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[App::I->ios_view share_file:[NSString stringWithUTF8String:path.c_str()]];
|
[ios_view share_file:[NSString stringWithUTF8String:path.c_str()]];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
bridge.pick_image = [](pp::platform::PickedPathCallback callback) {
|
bridge.pick_image = [ios_view](pp::platform::PickedPathCallback callback) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[App::I->ios_view pick_photo:callback];
|
[ios_view pick_photo:callback];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
bridge.pick_file = [](
|
bridge.pick_file = [ios_view](
|
||||||
std::vector<std::string> file_types,
|
std::vector<std::string> file_types,
|
||||||
pp::platform::PickedPathCallback callback) {
|
pp::platform::PickedPathCallback callback) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[App::I->ios_view pick_file:apple_file_types_array(file_types) then:callback];
|
[ios_view pick_file:apple_file_types_array(file_types) then:callback];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
return bridge;
|
return bridge;
|
||||||
}());
|
}());
|
||||||
return services;
|
return services;
|
||||||
#else
|
#else
|
||||||
|
auto* const osx_view = App::I->osx_view;
|
||||||
|
auto* const osx_app = App::I->osx_app;
|
||||||
static pp::platform::apple::AppleDocumentPlatformServices services(
|
static pp::platform::apple::AppleDocumentPlatformServices services(
|
||||||
pp::platform::PlatformFamily::macos,
|
pp::platform::PlatformFamily::macos,
|
||||||
[] {
|
[osx_view, osx_app] {
|
||||||
pp::platform::apple::AppleDocumentPickerBridge bridge;
|
pp::platform::apple::AppleDocumentPickerBridge bridge;
|
||||||
bridge.clipboard_text = [] {
|
bridge.clipboard_text = [osx_view] {
|
||||||
return [App::I->osx_view clipboard_get_string];
|
return [osx_view clipboard_get_string];
|
||||||
};
|
};
|
||||||
bridge.set_clipboard_text = [](std::string_view text) {
|
bridge.set_clipboard_text = [osx_view](std::string_view text) {
|
||||||
const std::string value(text);
|
const std::string value(text);
|
||||||
return [App::I->osx_view clipboard_set_string:value];
|
return [osx_view clipboard_set_string:value];
|
||||||
};
|
};
|
||||||
bridge.share_file = [](std::string path) {
|
bridge.share_file = [osx_view](std::string path) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[App::I->osx_view share_file:[NSString stringWithUTF8String:path.c_str()]];
|
[osx_view share_file:[NSString stringWithUTF8String:path.c_str()]];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
bridge.set_cursor_visible = [](bool visible) {
|
bridge.set_cursor_visible = [osx_view](bool visible) {
|
||||||
[App::I->osx_view show_cursor:visible];
|
[osx_view show_cursor:visible];
|
||||||
};
|
};
|
||||||
bridge.save_ui_state = [] {
|
bridge.save_ui_state = [osx_app] {
|
||||||
[App::I->osx_app save_ui_state];
|
[osx_app save_ui_state];
|
||||||
};
|
};
|
||||||
bridge.pick_file = [](
|
bridge.pick_file = [osx_view](
|
||||||
std::vector<std::string> file_types,
|
std::vector<std::string> file_types,
|
||||||
pp::platform::PickedPathCallback callback) {
|
pp::platform::PickedPathCallback callback) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
const std::string path = [App::I->osx_view pick_file:apple_file_types_array(file_types)];
|
const std::string path = [osx_view pick_file:apple_file_types_array(file_types)];
|
||||||
callback(path);
|
callback(path);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
bridge.pick_save_file = [](
|
bridge.pick_save_file = [osx_view](
|
||||||
std::vector<std::string> file_types,
|
std::vector<std::string> file_types,
|
||||||
pp::platform::PickedPathCallback callback) {
|
pp::platform::PickedPathCallback callback) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
const std::string path = [App::I->osx_view pick_file_save:apple_file_types_array(file_types)];
|
const std::string path = [osx_view pick_file_save:apple_file_types_array(file_types)];
|
||||||
callback(path);
|
callback(path);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
bridge.pick_directory = [](pp::platform::PickedPathCallback callback) {
|
bridge.pick_directory = [osx_view](pp::platform::PickedPathCallback callback) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
const std::string path = [App::I->osx_view pick_dir];
|
const std::string path = [osx_view pick_dir];
|
||||||
callback(path);
|
callback(path);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user