Trim main task queue, recording label, and canvas draw callbacks
This commit is contained in:
@@ -18,6 +18,22 @@ agent or engineer to remove them without reconstructing context from chat.
|
|||||||
|
|
||||||
## Reductions
|
## Reductions
|
||||||
|
|
||||||
|
- 2026-06-16: `DEBT-0003` was narrowed again. `main.cpp` main-thread queued
|
||||||
|
task state now lives behind a narrow retained helper instead of
|
||||||
|
`RetainedState.main_tasklist` / `main_task_mutex` directly; broader Win32
|
||||||
|
bootstrap/runtime ownership remains.
|
||||||
|
- 2026-06-16: `DEBT-0037` was narrowed again. `App::update_rec_frames()` in
|
||||||
|
`src/app.cpp` now delegates recording label refresh through
|
||||||
|
`update_legacy_recording_frame_label(...)` in
|
||||||
|
`src/legacy_recording_services.cpp` instead of building the label directly;
|
||||||
|
retained recording label lookup, encoder-state reads, and MP4 execution
|
||||||
|
remain.
|
||||||
|
- 2026-06-16: `DEBT-0036` was narrowed again. `NodeCanvas` non-`draw_merged`
|
||||||
|
per-frame layer draw callback setup now routes through
|
||||||
|
`make_legacy_canvas_draw_merge_layer_frame_draw(...)` in
|
||||||
|
`src/legacy_canvas_draw_merge_services.h` instead of keeping that callback
|
||||||
|
body inline in `NodeCanvas::draw()`; broader canvas draw orchestration and
|
||||||
|
retained GL resource ownership remain.
|
||||||
- 2026-06-16: `DEBT-0003` was narrowed again. The Win32 async GL/context lock
|
- 2026-06-16: `DEBT-0003` was narrowed again. The Win32 async GL/context lock
|
||||||
state now lives in `src/platform_windows/windows_platform_services.cpp`
|
state now lives in `src/platform_windows/windows_platform_services.cpp`
|
||||||
instead of `src/main.cpp` retained state, and `main.cpp` only seeds the
|
instead of `src/main.cpp` retained state, and `main.cpp` only seeds the
|
||||||
|
|||||||
@@ -117,9 +117,10 @@ Current architecture mismatches that must be treated as real blockers:
|
|||||||
checkerboard background setup now route through retained draw-merge helpers,
|
checkerboard background setup now route through retained draw-merge helpers,
|
||||||
with the cache-to-screen checkerboard-plane callback setup also reduced and
|
with the cache-to-screen checkerboard-plane callback setup also reduced and
|
||||||
the merged-path checkerboard background-plane callback plus per-plane
|
the merged-path checkerboard background-plane callback plus per-plane
|
||||||
merged-texture draw callback plus the smoothing-mask face shader/draw pass
|
merged-texture draw callback plus non-`draw_merged` per-frame layer draw
|
||||||
plus heightmap, current-mode, and grid-mode callback setup now routed
|
callback plus the smoothing-mask face shader/draw pass plus heightmap,
|
||||||
through the same retained helper family.
|
current-mode, and grid-mode callback setup now routed through the same
|
||||||
|
retained helper family.
|
||||||
- `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
|
||||||
@@ -137,13 +138,17 @@ Current architecture mismatches that must be treated as real blockers:
|
|||||||
retained local state object instead of separate process-wide globals, the
|
retained local state object instead of separate process-wide globals, the
|
||||||
Win32 async GL/context lock state now lives under
|
Win32 async GL/context lock state now lives under
|
||||||
`src/platform_windows/windows_platform_services.cpp` instead of `main.cpp`
|
`src/platform_windows/windows_platform_services.cpp` instead of `main.cpp`
|
||||||
retained state, the canvas async worker now sits behind a named retained
|
retained state, the main-thread queued task state now sits behind a narrow
|
||||||
|
retained helper instead of `RetainedState.main_tasklist` /
|
||||||
|
`main_task_mutex`, the canvas async worker now sits behind a named retained
|
||||||
local worker-state helper instead of a bare static accessor, the
|
local worker-state helper instead of a bare static accessor, the
|
||||||
prepared-file worker and the canvas async import/export/save/open worker now
|
prepared-file worker and the canvas async import/export/save/open worker now
|
||||||
live under `AppRuntime` instead of retained static app-events/canvas
|
live under `AppRuntime` instead of retained static app-events/canvas
|
||||||
workers, and `App::rec_loop()` now delegates worker-iteration orchestration
|
workers, `App::rec_loop()` now delegates worker-iteration orchestration into
|
||||||
into the retained recording bridge even though that retained recording path
|
the retained recording bridge, and `App::update_rec_frames()` now delegates
|
||||||
still owns the worker-side readback flow.
|
recording label refresh through that same retained recording path even though
|
||||||
|
the bridge still owns worker-side readback flow and encoder-state label
|
||||||
|
reads.
|
||||||
- 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
|
||||||
|
|||||||
@@ -159,6 +159,10 @@ Current slice:
|
|||||||
also routes through `make_legacy_canvas_draw_merge_layer_texture_draw(...)`,
|
also routes through `make_legacy_canvas_draw_merge_layer_texture_draw(...)`,
|
||||||
but the node still owns broader live layer traversal and renderer-state
|
but the node still owns broader live layer traversal and renderer-state
|
||||||
sequencing.
|
sequencing.
|
||||||
|
- `NodeCanvas` non-`draw_merged` per-frame layer draw callback setup now also
|
||||||
|
routes through `make_legacy_canvas_draw_merge_layer_frame_draw(...)`, but
|
||||||
|
the node still owns broader live layer traversal and renderer-state
|
||||||
|
sequencing.
|
||||||
- `NodeCanvas` smoothing-mask face shader setup plus per-face draw execution
|
- `NodeCanvas` smoothing-mask face shader setup plus per-face draw execution
|
||||||
now also route through
|
now also route through
|
||||||
`execute_legacy_canvas_draw_merge_smask_faces(...)`, but the node still owns
|
`execute_legacy_canvas_draw_merge_smask_faces(...)`, but the node still owns
|
||||||
@@ -364,6 +368,8 @@ Current slice:
|
|||||||
`src/platform_windows/windows_platform_services.cpp` instead of `main.cpp`
|
`src/platform_windows/windows_platform_services.cpp` instead of `main.cpp`
|
||||||
retained state, and `main.cpp` only seeds that platform-owned context handle
|
retained state, and `main.cpp` only seeds that platform-owned context handle
|
||||||
pair during initialization and context recreation
|
pair during initialization and context recreation
|
||||||
|
- `main.cpp` main-thread queued task state now sits behind a narrow retained
|
||||||
|
helper instead of `RetainedState.main_tasklist` / `main_task_mutex` directly
|
||||||
- prepared-file background work now runs through an `AppRuntime`-owned worker
|
- prepared-file background work now runs through an `AppRuntime`-owned worker
|
||||||
queue instead of a retained static worker in `src/app_events.cpp`
|
queue instead of a retained static worker in `src/app_events.cpp`
|
||||||
- canvas async import/export/save/open background work now also runs through an
|
- canvas async import/export/save/open background work now also runs through an
|
||||||
@@ -436,6 +442,9 @@ Current slice:
|
|||||||
retained recording bridge in `src/legacy_recording_services.cpp`, while
|
retained recording bridge in `src/legacy_recording_services.cpp`, while
|
||||||
`App::update()` no longer carries the dead update mutex residue; retained
|
`App::update()` no longer carries the dead update mutex residue; retained
|
||||||
recording loop control, readback ownership, and MP4 execution are still open
|
recording loop control, readback ownership, and MP4 execution are still open
|
||||||
|
- `App::update_rec_frames()` now delegates recording label refresh through
|
||||||
|
`src/legacy_recording_services.cpp`, but retained recording label lookup,
|
||||||
|
encoder-state reads, and MP4 execution still stay on the legacy bridge
|
||||||
|
|
||||||
Write scope:
|
Write scope:
|
||||||
- `src/canvas.cpp`
|
- `src/canvas.cpp`
|
||||||
|
|||||||
10
src/app.cpp
10
src/app.cpp
@@ -214,6 +214,7 @@ void App::initLog()
|
|||||||
namespace pp::panopainter
|
namespace pp::panopainter
|
||||||
{
|
{
|
||||||
bool process_legacy_recording_worker_iteration(App& app);
|
bool process_legacy_recording_worker_iteration(App& app);
|
||||||
|
void update_legacy_recording_frame_label(App& app);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool App::check_license()
|
bool App::check_license()
|
||||||
@@ -566,14 +567,7 @@ void App::update_memory_usage(size_t bytes)
|
|||||||
|
|
||||||
void App::update_rec_frames()
|
void App::update_rec_frames()
|
||||||
{
|
{
|
||||||
if (auto txt = layout[main_id]->find<NodeText>("txt-rec"))
|
pp::panopainter::update_legacy_recording_frame_label(*this);
|
||||||
{
|
|
||||||
const auto label = pp::app::make_recording_frame_label(
|
|
||||||
rec_running,
|
|
||||||
Canvas::I->m_encoder != nullptr,
|
|
||||||
Canvas::I->m_encoder ? Canvas::I->m_encoder->frames_count() : 0);
|
|
||||||
txt->set_text(label.text.c_str());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int App::res_from_index(int i)
|
int App::res_from_index(int i)
|
||||||
|
|||||||
@@ -529,7 +529,25 @@ template <typename LayerMergeT, typename SamplerT, typename FacePlaneT, typename
|
|||||||
.unbind_layer_texture = [layer_merge, plane_index] {
|
.unbind_layer_texture = [layer_merge, plane_index] {
|
||||||
layer_merge->rtt(plane_index).unbindTexture();
|
layer_merge->rtt(plane_index).unbindTexture();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename LayerT, typename FacePlaneT, typename SetActiveTextureUnit>
|
||||||
|
[[nodiscard]] inline auto make_legacy_canvas_draw_merge_layer_frame_draw(
|
||||||
|
LayerT* layer,
|
||||||
|
FacePlaneT* face_plane,
|
||||||
|
SetActiveTextureUnit set_active_texture_unit,
|
||||||
|
int plane_index,
|
||||||
|
float layer_opacity)
|
||||||
|
{
|
||||||
|
return [layer, face_plane, set_active_texture_unit, plane_index, layer_opacity](int frame, float onion_alpha) {
|
||||||
|
ShaderManager::u_float(kShaderUniform::Alpha, layer_opacity * onion_alpha);
|
||||||
|
set_active_texture_unit(0);
|
||||||
|
layer->rtt(plane_index, frame).bindTexture();
|
||||||
|
face_plane->draw_fill();
|
||||||
|
set_active_texture_unit(0);
|
||||||
|
layer->rtt(plane_index, frame).unbindTexture();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,14 @@
|
|||||||
|
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
#include "canvas.h"
|
#include "canvas.h"
|
||||||
|
#include "app_core/app_status.h"
|
||||||
#include "legacy_app_dialog_services.h"
|
#include "legacy_app_dialog_services.h"
|
||||||
#include "legacy_ui_overlay_services.h"
|
#include "legacy_ui_overlay_services.h"
|
||||||
#include "node_progress_bar.h"
|
#include "node_progress_bar.h"
|
||||||
|
|
||||||
namespace pp::panopainter {
|
namespace pp::panopainter {
|
||||||
|
void update_legacy_recording_frame_label(App& app);
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
pp::app::RecordingWorkerIterationPlan make_recording_worker_iteration_plan(App& app)
|
pp::app::RecordingWorkerIterationPlan make_recording_worker_iteration_plan(App& app)
|
||||||
@@ -42,9 +45,25 @@ void encode_recording_frame(
|
|||||||
LOG("rec frame encoded");
|
LOG("rec frame encoded");
|
||||||
|
|
||||||
if (plan.update_frame_label)
|
if (plan.update_frame_label)
|
||||||
app.update_rec_frames();
|
update_legacy_recording_frame_label(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void update_legacy_recording_frame_label(App& app)
|
||||||
|
{
|
||||||
|
if (auto txt = app.layout[app.main_id]->find<NodeText>("txt-rec"))
|
||||||
|
{
|
||||||
|
const auto label = pp::app::make_recording_frame_label(
|
||||||
|
app.rec_running,
|
||||||
|
Canvas::I->m_encoder != nullptr,
|
||||||
|
Canvas::I->m_encoder ? Canvas::I->m_encoder->frames_count() : 0);
|
||||||
|
txt->set_text(label.text.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
class LegacyRecordingServices final : public pp::app::RecordingServices {
|
class LegacyRecordingServices final : public pp::app::RecordingServices {
|
||||||
public:
|
public:
|
||||||
explicit LegacyRecordingServices(App& app) noexcept
|
explicit LegacyRecordingServices(App& app) noexcept
|
||||||
@@ -54,7 +73,7 @@ public:
|
|||||||
|
|
||||||
void start_thread() override
|
void start_thread() override
|
||||||
{
|
{
|
||||||
app_.update_rec_frames();
|
update_legacy_recording_frame_label(app_);
|
||||||
app_.rec_thread = std::jthread(&App::rec_loop, &app_);
|
app_.rec_thread = std::jthread(&App::rec_loop, &app_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +84,7 @@ public:
|
|||||||
app_.rec_cv.notify_all();
|
app_.rec_cv.notify_all();
|
||||||
if (app_.rec_thread.joinable())
|
if (app_.rec_thread.joinable())
|
||||||
app_.rec_thread.join();
|
app_.rec_thread.join();
|
||||||
app_.update_rec_frames();
|
update_legacy_recording_frame_label(app_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void delete_recorded_files() override
|
void delete_recorded_files() override
|
||||||
@@ -80,7 +99,7 @@ public:
|
|||||||
|
|
||||||
void update_frame_label() override
|
void update_frame_label() override
|
||||||
{
|
{
|
||||||
app_.update_rec_frames();
|
update_legacy_recording_frame_label(app_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void begin_export(int progress_total) override
|
void begin_export(int progress_total) override
|
||||||
|
|||||||
65
src/main.cpp
65
src/main.cpp
@@ -54,8 +54,6 @@ struct RetainedState
|
|||||||
std::map<kKey, int> vkey_map;
|
std::map<kKey, int> vkey_map;
|
||||||
wchar_t window_title[512]{};
|
wchar_t window_title[512]{};
|
||||||
std::jthread hmd_renderer;
|
std::jthread hmd_renderer;
|
||||||
std::deque<std::packaged_task<void()>> main_tasklist;
|
|
||||||
std::mutex main_task_mutex;
|
|
||||||
float timer_stylus = 0;
|
float timer_stylus = 0;
|
||||||
float timer_ink_touch = 0;
|
float timer_ink_touch = 0;
|
||||||
float timer_ink_pen = 0;
|
float timer_ink_pen = 0;
|
||||||
@@ -72,6 +70,46 @@ RetainedState& retained_state()
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct RetainedMainTaskQueue final
|
||||||
|
{
|
||||||
|
std::deque<std::packaged_task<void()>> tasklist;
|
||||||
|
std::mutex task_mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
RetainedMainTaskQueue& retained_main_task_queue()
|
||||||
|
{
|
||||||
|
static RetainedMainTaskQueue queue;
|
||||||
|
return queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Callable>
|
||||||
|
void enqueue_main_task(Callable&& task)
|
||||||
|
{
|
||||||
|
auto& queue = retained_main_task_queue();
|
||||||
|
std::lock_guard<std::mutex> lock(queue.task_mutex);
|
||||||
|
queue.tasklist.emplace_back(std::forward<Callable>(task));
|
||||||
|
}
|
||||||
|
|
||||||
|
void drain_main_tasks()
|
||||||
|
{
|
||||||
|
std::deque<std::packaged_task<void()>> working_list;
|
||||||
|
{
|
||||||
|
auto& queue = retained_main_task_queue();
|
||||||
|
std::lock_guard<std::mutex> lock(queue.task_mutex);
|
||||||
|
working_list = std::move(queue.tasklist);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!working_list.empty())
|
||||||
|
{
|
||||||
|
working_list.front()();
|
||||||
|
working_list.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
std::atomic<int> vr_frames{0};
|
std::atomic<int> vr_frames{0};
|
||||||
std::atomic<int> running{-1};
|
std::atomic<int> running{-1};
|
||||||
std::atomic_bool vr_running{false};
|
std::atomic_bool vr_running{false};
|
||||||
@@ -163,8 +201,7 @@ std::string GetLastErrorAsString()
|
|||||||
void destroy_window()
|
void destroy_window()
|
||||||
{
|
{
|
||||||
auto& state = retained_state();
|
auto& state = retained_state();
|
||||||
std::lock_guard<std::mutex> lock(state.main_task_mutex);
|
enqueue_main_task([hWnd = state.hWnd] {
|
||||||
state.main_tasklist.emplace_back([hWnd = state.hWnd] {
|
|
||||||
PostMessage(hWnd, WM_USER_CLOSE, 0, 0);
|
PostMessage(hWnd, WM_USER_CLOSE, 0, 0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -225,8 +262,7 @@ void win32_update_fps(int frames)
|
|||||||
swprintf_s(title_fps, L"%s - %d fps", state.window_title, frames);
|
swprintf_s(title_fps, L"%s - %d fps", state.window_title, frames);
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(state.main_task_mutex);
|
enqueue_main_task([hWnd = state.hWnd] {
|
||||||
state.main_tasklist.emplace_back([hWnd = state.hWnd] {
|
|
||||||
SetWindowText(hWnd, title_fps);
|
SetWindowText(hWnd, title_fps);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -991,22 +1027,7 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// list of tasks for the main thread
|
// list of tasks for the main thread
|
||||||
{
|
drain_main_tasks();
|
||||||
std::deque<std::packaged_task<void()>> working_list;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(state.main_task_mutex);
|
|
||||||
working_list = std::move(state.main_tasklist);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!working_list.empty())
|
|
||||||
{
|
|
||||||
while (!working_list.empty())
|
|
||||||
{
|
|
||||||
working_list.front()();
|
|
||||||
working_list.pop_front();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Clean up
|
// Clean up
|
||||||
WacomTablet::I.terminate();
|
WacomTablet::I.terminate();
|
||||||
|
|||||||
@@ -469,14 +469,12 @@ void NodeCanvas::draw()
|
|||||||
m_canvas->m_plane_transform[plane_index] *
|
m_canvas->m_plane_transform[plane_index] *
|
||||||
glm::translate(glm::vec3(0, 0, -1));
|
glm::translate(glm::vec3(0, 0, -1));
|
||||||
|
|
||||||
const auto draw_layer_frame = [&](int frame, float onion_alpha) {
|
const auto draw_layer_frame = pp::panopainter::make_legacy_canvas_draw_merge_layer_frame_draw(
|
||||||
ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_layers[layer_index]->m_opacity * onion_alpha);
|
m_canvas->m_layers[layer_index].get(),
|
||||||
set_active_texture_unit(0);
|
&m_face_plane,
|
||||||
m_canvas->m_layers[layer_index]->rtt(plane_index, frame).bindTexture();
|
set_active_texture_unit,
|
||||||
m_face_plane.draw_fill();
|
plane_index,
|
||||||
set_active_texture_unit(0);
|
m_canvas->m_layers[layer_index]->m_opacity);
|
||||||
m_canvas->m_layers[layer_index]->rtt(plane_index, frame).unbindTexture();
|
|
||||||
};
|
|
||||||
|
|
||||||
pp::panopainter::execute_legacy_canvas_draw_merge_layer_plane(
|
pp::panopainter::execute_legacy_canvas_draw_merge_layer_plane(
|
||||||
m_canvas->m_current_stroke && m_canvas->m_current_mode == kCanvasMode::Erase && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index,
|
m_canvas->m_current_stroke && m_canvas->m_current_mode == kCanvasMode::Erase && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index,
|
||||||
|
|||||||
Reference in New Issue
Block a user