Thin canvas draw seams and own grid worker
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-0036` was narrowed again. `NodeCanvas` smoothing-mask
|
||||||
|
overlay draw, smoothing-mask face pass, grid keepalive draw, heightmap draw,
|
||||||
|
and current-mode draw now route through
|
||||||
|
`execute_legacy_canvas_draw_merge_post_draw(...)` in
|
||||||
|
`src/legacy_canvas_draw_merge_services.h` instead of living inline in
|
||||||
|
`NodeCanvas::draw()`; broader canvas draw orchestration and retained GL
|
||||||
|
resource ownership remain.
|
||||||
|
- 2026-06-16: `DEBT-0036` was narrowed again. The retained
|
||||||
|
`NodePanelGrid::bake_uvs()` worker now uses scoped `std::jthread` ownership
|
||||||
|
instead of a raw local `std::thread`; retained bake execution, progress-loop
|
||||||
|
polling, and grid rendering ownership remain.
|
||||||
|
- 2026-06-16: `DEBT-0017` was narrowed again.
|
||||||
|
`LegacyPlatformServices::prepare_storage_paths()` now routes Apple storage
|
||||||
|
path setup through a local helper instead of reading `App::I` directly in
|
||||||
|
the method body; the retained Apple fallback adapter and broader
|
||||||
|
platform-to-app singleton reach remain.
|
||||||
- 2026-06-16: `DEBT-0036` was narrowed again. `NodeStrokePreview` background
|
- 2026-06-16: `DEBT-0036` was narrowed again. `NodeStrokePreview` background
|
||||||
preview execution now owns its worker as `std::jthread` with explicit stop,
|
preview execution now owns its worker as `std::jthread` with explicit stop,
|
||||||
unblock, and join semantics instead of a raw `std::thread`; retained queue
|
unblock, and join semantics instead of a raw `std::thread`; retained queue
|
||||||
|
|||||||
@@ -102,13 +102,16 @@ Current architecture mismatches that must be treated as real blockers:
|
|||||||
app shell.
|
app shell.
|
||||||
- `pp_panopainter_ui` still depends on `pp_legacy_app`.
|
- `pp_panopainter_ui` still depends on `pp_legacy_app`.
|
||||||
- `Canvas`, `NodeCanvas`, and `NodeStrokePreview` still own too much live
|
- `Canvas`, `NodeCanvas`, and `NodeStrokePreview` still own too much live
|
||||||
OpenGL execution around the renderer boundary.
|
OpenGL execution around the renderer boundary, even though `NodeCanvas`
|
||||||
|
display resolve, cache-to-screen composite, and post-draw mask/grid/current-
|
||||||
|
mode sequencing now route through retained draw-merge helpers.
|
||||||
- `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, retained static worker
|
global singleton reach, raw observer pointers, retained static worker
|
||||||
ownership in several app families, and ad hoc mutex/condition-variable
|
ownership in several app families, and ad hoc mutex/condition-variable
|
||||||
ownership.
|
ownership, even though most previously detached or raw app-facing worker
|
||||||
|
launches now use owned `std::jthread` or service-owned worker queues.
|
||||||
- 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
|
||||||
|
|||||||
@@ -132,8 +132,11 @@ Current slice:
|
|||||||
through `legacy_node_stroke_preview_execution_services.h`, but the preview
|
through `legacy_node_stroke_preview_execution_services.h`, but the preview
|
||||||
node still owns most live-pass and retained GL resource execution.
|
node still owns most live-pass and retained GL resource execution.
|
||||||
- `NodeCanvas` display resolve plus cache-to-screen checkerboard/cache-texture
|
- `NodeCanvas` display resolve plus cache-to-screen checkerboard/cache-texture
|
||||||
composite now route through `legacy_canvas_draw_merge_services.h`, but
|
composite now route through `legacy_canvas_draw_merge_services.h`.
|
||||||
broader canvas draw orchestration is still inline.
|
- `NodeCanvas` smoothing-mask overlay, smoothing-mask face pass, grid keepalive
|
||||||
|
draw, heightmap draw, and current-mode draw now also route through
|
||||||
|
`execute_legacy_canvas_draw_merge_post_draw(...)`, but broader canvas draw
|
||||||
|
orchestration is still inline.
|
||||||
|
|
||||||
Write scope:
|
Write scope:
|
||||||
- `src/node_stroke_preview.cpp`
|
- `src/node_stroke_preview.cpp`
|
||||||
@@ -348,9 +351,9 @@ Mini-model packet:
|
|||||||
Status: In Progress
|
Status: In Progress
|
||||||
|
|
||||||
Why now:
|
Why now:
|
||||||
Canvas imports/exports/saves, cloud transfer, brush import/export, grid
|
The biggest app-facing async families have been moved off detached launches,
|
||||||
lightmap work, stroke preview, and event persistence still launch detached
|
but retained worker ownership and ad hoc runtime control are still not a safe
|
||||||
threads. That is not a safe modernization foundation.
|
modernization foundation.
|
||||||
|
|
||||||
Current slice:
|
Current slice:
|
||||||
- app-owned render/UI runtime queues and cloud worker ownership are already
|
- app-owned render/UI runtime queues and cloud worker ownership are already
|
||||||
@@ -361,8 +364,9 @@ 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 background rendering and recording thread ownership now also use
|
- preview background rendering, recording, and the retained
|
||||||
`std::jthread`, but their retained loop/control flow is still open
|
`NodePanelGrid::bake_uvs()` worker now also use `std::jthread`, but their
|
||||||
|
retained loop/control flow is still open
|
||||||
|
|
||||||
Write scope:
|
Write scope:
|
||||||
- `src/canvas.cpp`
|
- `src/canvas.cpp`
|
||||||
@@ -563,6 +567,9 @@ Current slice:
|
|||||||
`App::set_platform_services()`
|
`App::set_platform_services()`
|
||||||
- `platform_apple` clipboard, display/share, cursor, and save-ui-state calls
|
- `platform_apple` clipboard, display/share, cursor, and save-ui-state calls
|
||||||
now route through injected Apple bridge callbacks instead of `App::I`
|
now route through injected Apple bridge callbacks instead of `App::I`
|
||||||
|
- `LegacyPlatformServices::prepare_storage_paths()` now routes Apple path
|
||||||
|
preparation through a narrow local helper instead of reading `App::I`
|
||||||
|
directly in that method body
|
||||||
- retained Apple callback injection and broader `platform_legacy` singleton
|
- retained Apple callback injection and broader `platform_legacy` singleton
|
||||||
reach are still open
|
reach are still open
|
||||||
|
|
||||||
|
|||||||
@@ -203,6 +203,15 @@ struct LegacyCanvasDrawMergeDisplayResolveExecution {
|
|||||||
std::function<void()> unbind_resolve_texture;
|
std::function<void()> unbind_resolve_texture;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LegacyCanvasDrawMergePostDrawExecution {
|
||||||
|
std::function<void()> draw_mask_free;
|
||||||
|
std::function<void()> draw_mask_line;
|
||||||
|
std::function<void()> draw_smask_faces;
|
||||||
|
std::function<void()> draw_grid_modes;
|
||||||
|
std::function<void()> draw_heightmap;
|
||||||
|
std::function<void()> draw_current_modes;
|
||||||
|
};
|
||||||
|
|
||||||
[[nodiscard]] inline LegacyCanvasDrawMergeShaderExecution legacy_shader_manager_draw_merge_execution() noexcept
|
[[nodiscard]] inline LegacyCanvasDrawMergeShaderExecution legacy_shader_manager_draw_merge_execution() noexcept
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
@@ -492,4 +501,31 @@ inline void execute_legacy_canvas_draw_merge_display_resolve(
|
|||||||
execution.unbind_resolve_texture();
|
execution.unbind_resolve_texture();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void execute_legacy_canvas_draw_merge_post_draw(
|
||||||
|
bool smask_active,
|
||||||
|
bool draw_mask_overlay,
|
||||||
|
int smask_mode,
|
||||||
|
bool draw_grid_modes,
|
||||||
|
const LegacyCanvasDrawMergePostDrawExecution& execution)
|
||||||
|
{
|
||||||
|
if (smask_active || draw_mask_overlay) {
|
||||||
|
if (smask_mode == 1) {
|
||||||
|
execution.draw_mask_free();
|
||||||
|
} else if (smask_mode == 2) {
|
||||||
|
execution.draw_mask_line();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (smask_active) {
|
||||||
|
execution.draw_smask_faces();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (draw_grid_modes) {
|
||||||
|
execution.draw_grid_modes();
|
||||||
|
}
|
||||||
|
|
||||||
|
execution.draw_heightmap();
|
||||||
|
execution.draw_current_modes();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pp::panopainter
|
} // namespace pp::panopainter
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ void set_active_texture_unit(std::uint32_t unit_index)
|
|||||||
{
|
{
|
||||||
pp::legacy::ui_gl::activate_texture_unit(unit_index, "NodeCanvas");
|
pp::legacy::ui_gl::activate_texture_unit(unit_index, "NodeCanvas");
|
||||||
}
|
}
|
||||||
|
|
||||||
void unbind_texture_2d()
|
void unbind_texture_2d()
|
||||||
{
|
{
|
||||||
pp::legacy::ui_gl::unbind_texture_2d("NodeCanvas");
|
pp::legacy::ui_gl::unbind_texture_2d("NodeCanvas");
|
||||||
@@ -719,16 +718,19 @@ void NodeCanvas::draw()
|
|||||||
|
|
||||||
apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), false);
|
apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), false);
|
||||||
|
|
||||||
if (m_canvas->m_smask_active || m_canvas->m_current_mode == kCanvasMode::Copy || m_canvas->m_current_mode == kCanvasMode::Cut)
|
pp::panopainter::execute_legacy_canvas_draw_merge_post_draw(
|
||||||
|
m_canvas->m_smask_active,
|
||||||
|
m_canvas->m_current_mode == kCanvasMode::Copy || m_canvas->m_current_mode == kCanvasMode::Cut,
|
||||||
|
m_canvas->m_smask_mode,
|
||||||
|
m_canvas->m_current_mode != kCanvasMode::Grid,
|
||||||
{
|
{
|
||||||
if (m_canvas->m_smask_mode == 1)
|
.draw_mask_free = [&] {
|
||||||
m_canvas->modes[(int)kCanvasMode::MaskFree][0]->on_Draw(ortho_proj, proj, camera);
|
m_canvas->modes[(int)kCanvasMode::MaskFree][0]->on_Draw(ortho_proj, proj, camera);
|
||||||
else if (m_canvas->m_smask_mode == 2)
|
},
|
||||||
|
.draw_mask_line = [&] {
|
||||||
m_canvas->modes[(int)kCanvasMode::MaskLine][0]->on_Draw(ortho_proj, proj, camera);
|
m_canvas->modes[(int)kCanvasMode::MaskLine][0]->on_Draw(ortho_proj, proj, camera);
|
||||||
}
|
},
|
||||||
|
.draw_smask_faces = [&] {
|
||||||
if (m_canvas->m_smask_active)
|
|
||||||
{
|
|
||||||
pp::panopainter::setup_legacy_canvas_draw_merge_texture_mask_shader(
|
pp::panopainter::setup_legacy_canvas_draw_merge_texture_mask_shader(
|
||||||
pp::panopainter::LegacyCanvasDrawMergeTextureMaskUniforms {
|
pp::panopainter::LegacyCanvasDrawMergeTextureMaskUniforms {
|
||||||
.texture_slot = 0,
|
.texture_slot = 0,
|
||||||
@@ -750,19 +752,19 @@ void NodeCanvas::draw()
|
|||||||
m_face_plane.draw_fill();
|
m_face_plane.draw_fill();
|
||||||
m_canvas->m_smask.rtt(plane_index).unbindTexture();
|
m_canvas->m_smask.rtt(plane_index).unbindTexture();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
.draw_grid_modes = [&] {
|
||||||
}
|
|
||||||
|
|
||||||
// keep drawing the grids
|
|
||||||
if (m_canvas->m_current_mode != kCanvasMode::Grid)
|
|
||||||
for (auto& mode : Canvas::modes[(int)kCanvasMode::Grid])
|
for (auto& mode : Canvas::modes[(int)kCanvasMode::Grid])
|
||||||
mode->on_Draw(ortho_proj, proj, camera);
|
mode->on_Draw(ortho_proj, proj, camera);
|
||||||
|
},
|
||||||
|
.draw_heightmap = [&] {
|
||||||
App::I->grid->draw_heightmap(proj, camera, false);
|
App::I->grid->draw_heightmap(proj, camera, false);
|
||||||
|
},
|
||||||
|
.draw_current_modes = [&] {
|
||||||
for (auto& mode : *m_canvas->m_mode)
|
for (auto& mode : *m_canvas->m_mode)
|
||||||
mode->on_Draw(ortho_proj, proj, camera);
|
mode->on_Draw(ortho_proj, proj, camera);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (m_density != 1.f)
|
if (m_density != 1.f)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -449,7 +449,8 @@ void NodePanelGrid::bake_uvs()
|
|||||||
auto data_out = std::make_unique<uint8_t[]>(fb.getWidth() * fb.getHeight() * 4);
|
auto data_out = std::make_unique<uint8_t[]>(fb.getWidth() * fb.getHeight() * 4);
|
||||||
const auto samples = get_samples();
|
const auto samples = get_samples();
|
||||||
const auto radius = get_radius();
|
const auto radius = get_radius();
|
||||||
std::thread worker([&]
|
{
|
||||||
|
std::jthread worker([&]
|
||||||
{
|
{
|
||||||
BT_SetTerminate();
|
BT_SetTerminate();
|
||||||
float* d_pos = data_pos.get();
|
float* d_pos = data_pos.get();
|
||||||
@@ -495,14 +496,13 @@ void NodePanelGrid::bake_uvs()
|
|||||||
}
|
}
|
||||||
pb_value++;
|
pb_value++;
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
);
|
|
||||||
while (pb_value < fb.getHeight())
|
while (pb_value < fb.getHeight())
|
||||||
{
|
{
|
||||||
pb->set_progress((float)pb_value / (float)fb.getHeight());
|
pb->set_progress((float)pb_value / (float)fb.getHeight());
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
}
|
}
|
||||||
worker.join();
|
}
|
||||||
pp::panopainter::close_legacy_dialog_node(*pb);
|
pp::panopainter::close_legacy_dialog_node(*pb);
|
||||||
//stbi_write_jpg("bake-out.jpg", fb.getWidth(), fb.getHeight(), 4, data_out.get(), 75);
|
//stbi_write_jpg("bake-out.jpg", fb.getWidth(), fb.getHeight(), 4, data_out.get(), 75);
|
||||||
m_texture.update(data_out.get());
|
m_texture.update(data_out.get());
|
||||||
|
|||||||
@@ -105,6 +105,21 @@ public:
|
|||||||
return types;
|
return types;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] pp::platform::PlatformStoragePaths prepare_legacy_apple_storage_paths()
|
||||||
|
{
|
||||||
|
#ifdef __IOS__
|
||||||
|
[App::I->ios_view init_dirs];
|
||||||
|
#elif defined(__OSX__)
|
||||||
|
[App::I->osx_app init_dirs];
|
||||||
|
#endif
|
||||||
|
return {
|
||||||
|
App::I->data_path,
|
||||||
|
App::I->work_path,
|
||||||
|
App::I->rec_path,
|
||||||
|
App::I->tmp_path,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] pp::platform::apple::AppleDocumentPlatformServices& active_apple_document_platform_services()
|
[[nodiscard]] pp::platform::apple::AppleDocumentPlatformServices& active_apple_document_platform_services()
|
||||||
{
|
{
|
||||||
#ifdef __IOS__
|
#ifdef __IOS__
|
||||||
@@ -211,21 +226,9 @@ public:
|
|||||||
[[nodiscard]] pp::platform::PlatformStoragePaths prepare_storage_paths() override
|
[[nodiscard]] pp::platform::PlatformStoragePaths prepare_storage_paths() override
|
||||||
{
|
{
|
||||||
#if defined(__IOS__)
|
#if defined(__IOS__)
|
||||||
[App::I->ios_view init_dirs];
|
return prepare_legacy_apple_storage_paths();
|
||||||
return {
|
|
||||||
App::I->data_path,
|
|
||||||
App::I->work_path,
|
|
||||||
App::I->rec_path,
|
|
||||||
App::I->tmp_path,
|
|
||||||
};
|
|
||||||
#elif defined(__OSX__)
|
#elif defined(__OSX__)
|
||||||
[App::I->osx_app init_dirs];
|
return prepare_legacy_apple_storage_paths();
|
||||||
return {
|
|
||||||
App::I->data_path,
|
|
||||||
App::I->work_path,
|
|
||||||
App::I->rec_path,
|
|
||||||
App::I->tmp_path,
|
|
||||||
};
|
|
||||||
#elif __LINUX__
|
#elif __LINUX__
|
||||||
const std::string data_path = linux_home_path() + "/PanoPainter";
|
const std::string data_path = linux_home_path() + "/PanoPainter";
|
||||||
mkpath(data_path + "/brushes");
|
mkpath(data_path + "/brushes");
|
||||||
|
|||||||
Reference in New Issue
Block a user