Plan stroke preview feedback copies
This commit is contained in:
@@ -429,6 +429,7 @@ if(PP_BUILD_APP)
|
||||
pp_legacy_engine
|
||||
pp_project_options
|
||||
PRIVATE
|
||||
pp_paint_renderer
|
||||
pp_renderer_api
|
||||
pp_project_warnings)
|
||||
if(TARGET pp_renderer_gl)
|
||||
|
||||
@@ -37,7 +37,7 @@ and validation command.
|
||||
| PPBR import/export | brush panel/dialog | `pp_assets`, `pp_panopainter_ui` | Round-trip fixture |
|
||||
| Stroke sampling | `Stroke`, `Canvas` | `pp_paint` | Property tests for spacing, pressure, jitter |
|
||||
| Dual brush/pattern behavior | `Brush`, shaders | `pp_paint`, `pp_paint_renderer` | Stroke-alpha CPU reference, dual/pattern feedback planning, GPU golden |
|
||||
| Blend modes | GLSL include files, layer rendering | `pp_paint`, `pp_paint_renderer` | Final RGBA and stroke-alpha CPU reference vectors, fixed-function/framebuffer-fetch/ping-pong stroke composite planning, live `Canvas`/`NodeCanvas` blend-gate coverage, live canvas stroke/thumbnail destination-copy coverage, and GPU parity |
|
||||
| Blend modes | GLSL include files, layer rendering | `pp_paint`, `pp_paint_renderer` | Final RGBA and stroke-alpha CPU reference vectors, fixed-function/framebuffer-fetch/ping-pong stroke composite planning, live `Canvas`/`NodeCanvas` blend-gate coverage, live canvas stroke/thumbnail/brush-preview destination-copy coverage, and GPU parity |
|
||||
| Erase/flood fill/masks | `Canvas`, modes, shaders | `pp_document`, `pp_paint_renderer` | Edge masks, alpha lock, dirty rects |
|
||||
|
||||
## Layers And Animation
|
||||
|
||||
@@ -53,7 +53,7 @@ agent or engineer to remove them without reconstructing context from chat.
|
||||
| DEBT-0033 | Open | Modernization | Tools menu planning and direct command execution dispatch now consume pure `pp_app_core` through `App::init_menu_tools`, `pano_cli plan-tools-menu`, `pano_cli plan-tools-panel`, and the `ToolsMenuServices` boundary, but live adapters still construct legacy `NodePanelFloating` panels, mutate legacy panel nodes, clear `CanvasModeGrid`, reset `NodeCanvas` camera state, open legacy shortcuts UI, and call the iOS SonarPen bridge directly | Preserve current Tools menu behavior while UI shell actions move toward app/UI/platform services | `pp_app_core_tools_menu_tests`; `pano_cli plan-tools-menu --command shortcuts`; `pano_cli plan-tools-panel --panel layers`; `pano_cli plan-tools-panel --panel animation --already-visible`; `ctest --preset desktop-fast --build-config Debug` | Tools panel creation, submenu routing, grid clear, camera reset, shortcuts dialog, and SonarPen dispatch are owned by injected app/UI/platform services with `App::init_menu_tools` acting only as a UI adapter and no legacy Tools adapter |
|
||||
| DEBT-0034 | Open | Modernization | About menu command planning and execution dispatch now consume pure `pp_app_core` through `App::init_menu_about`, `pano_cli plan-about-menu`, and the `AboutMenuServices` boundary, but the live adapter still opens legacy About/manual/what's-new dialogs, invokes the injected crash hook, and runs the legacy Canvas stroke performance test directly | Preserve About menu behavior while dialogs and diagnostics move toward app/UI/platform services | `pp_app_core_about_menu_tests`; `pano_cli plan-about-menu --command news --version-major 2 --version-minor 5 --version-fix 7`; `pano_cli plan-about-menu --command performance --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | About/manual/what's-new dialog dispatch, crash-test dispatch, and performance-test execution are owned by injected app/UI/platform services with `App::init_menu_about` acting only as a UI adapter and no legacy About adapter |
|
||||
| DEBT-0035 | Open | Modernization | Main toolbar/status command planning and execution dispatch now consume pure `pp_app_core` through `App::init_toolbar_main`, `pano_cli plan-main-toolbar`, and the `MainToolbarServices` boundary, and history/canvas commands now hand off through `HistoryUiServices` and `DocumentCanvasClearServices`, but the live adapter still opens legacy open/save/settings/message-box dialogs and delegates to legacy history/canvas adapters | Preserve reachable toolbar/status behavior while app shell commands move toward app/document/UI services | `pp_app_core_main_toolbar_tests`; `pano_cli plan-main-toolbar --command undo --undo-count 2`; `pano_cli plan-main-toolbar --command clear-canvas --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | Open/save/settings/message-box routing, undo/redo/clear-history execution, and canvas-clear execution are owned by injected app/document/UI services with `App::init_toolbar_main` acting only as a UI adapter and no legacy toolbar adapter |
|
||||
| DEBT-0036 | Open | Modernization | `pp_renderer_api`, `pp_paint_renderer`, `pano_cli plan-paint-feedback`, and `pano_cli plan-stroke-composite` can choose backend-neutral complex paint feedback strategies for fixed-function blending, framebuffer-fetch-capable renderers, or ping-pong render targets. OpenGL extension detection now stores `pp::renderer::RenderDeviceFeatures` through `ShaderManager`, using `pp_renderer_gl::render_device_features` as the backend conversion point. `pp_paint_renderer::plan_canvas_blend_gate` owns the compatibility mapping from persisted layer/brush blend indices to the extracted stroke-composite planner, and live `Canvas::draw_merge` plus `NodeCanvas` panorama rendering both call it with the stored renderer-neutral feature set for their existing shader-blend gates and destination-copy versus framebuffer-fetch decisions. `pp_paint_renderer::plan_canvas_stroke_feedback` also owns the current destination-feedback decision, and live `Canvas::stroke_draw` plus thumbnail layer blending use it for framebuffer-fetch versus destination-copy decisions. Actual live stroke rasterization, dual-brush compositing, pattern feedback math, and thumbnail layer compositing still use legacy OpenGL canvas execution | Preserve current painting behavior while the renderer boundary matures for OpenGL parity and later Vulkan/Metal experiments | `pp_renderer_api_tests`; `pp_renderer_gl_capabilities_tests`; `pp_paint_renderer_compositor_tests`; `pano_cli plan-paint-feedback --framebuffer-fetch --explicit-transitions --render-only`; `pano_cli plan-paint-feedback --texture-copy`; `pano_cli plan-stroke-composite --stroke-blend 10 --framebuffer-fetch --explicit-transitions --render-only`; `pano_cli plan-stroke-composite --layer-blend 4 --dual-blend --texture-copy`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | Live stroke/layer compositing chooses its feedback path through `pp_paint_renderer` and renderer services, with OpenGL golden parity and Vulkan/Metal lab tests covering framebuffer-fetch and ping-pong behavior |
|
||||
| DEBT-0036 | Open | Modernization | `pp_renderer_api`, `pp_paint_renderer`, `pano_cli plan-paint-feedback`, and `pano_cli plan-stroke-composite` can choose backend-neutral complex paint feedback strategies for fixed-function blending, framebuffer-fetch-capable renderers, or ping-pong render targets. OpenGL extension detection now stores `pp::renderer::RenderDeviceFeatures` through `ShaderManager`, using `pp_renderer_gl::render_device_features` as the backend conversion point. `pp_paint_renderer::plan_canvas_blend_gate` owns the compatibility mapping from persisted layer/brush blend indices to the extracted stroke-composite planner, and live `Canvas::draw_merge` plus `NodeCanvas` panorama rendering both call it with the stored renderer-neutral feature set for their existing shader-blend gates and destination-copy versus framebuffer-fetch decisions. `pp_paint_renderer::plan_canvas_stroke_feedback` also owns the current destination-feedback decision, and live `Canvas::stroke_draw`, thumbnail layer blending, and `NodeStrokePreview` brush-preview rendering use it for framebuffer-fetch versus destination-copy decisions. Actual live stroke rasterization, dual-brush compositing, pattern feedback math, thumbnail layer compositing, and brush-preview compositing still use legacy OpenGL canvas/UI execution | Preserve current painting behavior while the renderer boundary matures for OpenGL parity and later Vulkan/Metal experiments | `pp_renderer_api_tests`; `pp_renderer_gl_capabilities_tests`; `pp_paint_renderer_compositor_tests`; `pano_cli plan-paint-feedback --framebuffer-fetch --explicit-transitions --render-only`; `pano_cli plan-paint-feedback --texture-copy`; `pano_cli plan-stroke-composite --stroke-blend 10 --framebuffer-fetch --explicit-transitions --render-only`; `pano_cli plan-stroke-composite --layer-blend 4 --dual-blend --texture-copy`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | Live stroke/layer compositing chooses its feedback path through `pp_paint_renderer` and renderer services, with OpenGL golden parity and Vulkan/Metal lab tests covering framebuffer-fetch and ping-pong behavior |
|
||||
|
||||
## Closed Debt
|
||||
|
||||
|
||||
@@ -841,7 +841,8 @@ error codes, state queries, framebuffer targets, texture binding targets, and
|
||||
active texture units to `pp_renderer_gl`.
|
||||
`NodeStrokePreview` brush preview rendering now delegates depth/scissor/blend
|
||||
state, viewport/clear-color queries, active texture units, 2D texture targets,
|
||||
copy targets, and sampler filters/wraps to `pp_renderer_gl`.
|
||||
copy targets, sampler filters/wraps, and destination-feedback copy/fetch
|
||||
decisions to `pp_renderer_gl` and `pp_paint_renderer`.
|
||||
Legacy `Texture2D`, `TextureManager`, `Sampler`, and `RTT` public headers no
|
||||
longer expose raw OpenGL enum defaults; default texture formats, sampler
|
||||
filters/wraps, and render-target formats are resolved through backend-owned
|
||||
@@ -893,8 +894,10 @@ shader's required destination feedback without changing the legacy shader math.
|
||||
Live `Canvas::stroke_draw` consumes that plan for main-brush, dual-brush, and
|
||||
stroke-pad destination-copy versus framebuffer-fetch decisions. Thumbnail layer
|
||||
blending now consumes the same canvas destination-feedback decision for its
|
||||
legacy `TextureBlend` path; the full thumbnail compositing execution remains
|
||||
legacy OpenGL until a fuller live paint-renderer boundary can take over.
|
||||
legacy `TextureBlend` path. `NodeStrokePreview` uses the same destination
|
||||
feedback plan for its live brush-preview copy/fetch decision. The full
|
||||
thumbnail and brush-preview compositing execution remains legacy OpenGL until a
|
||||
fuller live paint-renderer boundary can take over.
|
||||
The existing renderer classes are not yet fully
|
||||
behind the renderer interfaces.
|
||||
|
||||
|
||||
@@ -6,7 +6,40 @@
|
||||
#include "bezier.h"
|
||||
#include "canvas.h"
|
||||
#include "app.h"
|
||||
#include "paint_renderer/compositor.h"
|
||||
#include "renderer_gl/opengl_capabilities.h"
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
||||
namespace {
|
||||
|
||||
pp::renderer::RenderDeviceFeatures stroke_preview_render_device_features() noexcept
|
||||
{
|
||||
return ShaderManager::render_device_features();
|
||||
}
|
||||
|
||||
pp::paint_renderer::CanvasStrokeFeedbackPlan stroke_preview_destination_feedback_plan(
|
||||
int width,
|
||||
int height) noexcept
|
||||
{
|
||||
const auto plan = pp::paint_renderer::plan_canvas_stroke_feedback(
|
||||
stroke_preview_render_device_features(),
|
||||
pp::renderer::Extent2D {
|
||||
.width = static_cast<std::uint32_t>(std::max(width, 0)),
|
||||
.height = static_cast<std::uint32_t>(std::max(height, 0)),
|
||||
});
|
||||
if (plan) {
|
||||
return plan.value();
|
||||
}
|
||||
|
||||
pp::paint_renderer::CanvasStrokeFeedbackPlan fallback;
|
||||
fallback.compatibility_fallback = true;
|
||||
fallback.path = pp::paint_renderer::StrokeCompositePath::ping_pong_textures;
|
||||
fallback.requires_auxiliary_texture = true;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::atomic_int NodeStrokePreview::s_instances{ 0 };
|
||||
std::atomic_bool NodeStrokePreview::s_running{ false };
|
||||
@@ -147,9 +180,12 @@ void NodeStrokePreview::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2
|
||||
gl.restore();
|
||||
}
|
||||
|
||||
glm::vec4 NodeStrokePreview::stroke_draw_samples(std::array<vertex_t, 4>& P, Texture2D& blend_tex)
|
||||
glm::vec4 NodeStrokePreview::stroke_draw_samples(
|
||||
std::array<vertex_t, 4>& P,
|
||||
Texture2D& blend_tex,
|
||||
bool copy_stroke_destination)
|
||||
{
|
||||
if (!ShaderManager::ext_framebuffer_fetch)
|
||||
if (copy_stroke_destination)
|
||||
{
|
||||
glActiveTexture(pp::renderer::gl::active_texture_unit(1U));
|
||||
blend_tex.bind(); // bg, copy of framebuffer (copied before drawing)
|
||||
@@ -169,7 +205,7 @@ glm::vec4 NodeStrokePreview::stroke_draw_samples(std::array<vertex_t, 4>& P, Tex
|
||||
glm::vec2 pad(1);
|
||||
glm::ivec2 tex_pos = glm::clamp(glm::floor(bb_min) - pad, { 0, 0 }, size);
|
||||
glm::ivec2 tex_sz = glm::clamp(glm::ceil(bb_sz) + pad * 2.f, { 0, 0 }, (glm::vec2)(glm::ivec2(size) - tex_pos));
|
||||
if (!ShaderManager::ext_framebuffer_fetch)
|
||||
if (copy_stroke_destination)
|
||||
{
|
||||
// this is also used by the mixer
|
||||
glCopyTexSubImage2D(pp::renderer::gl::texture_2d_target(), 0, tex_pos.x, tex_pos.y,
|
||||
@@ -189,7 +225,7 @@ glm::vec4 NodeStrokePreview::stroke_draw_samples(std::array<vertex_t, 4>& P, Tex
|
||||
}
|
||||
m_brush_shape.draw_fill();
|
||||
|
||||
if (!ShaderManager::ext_framebuffer_fetch)
|
||||
if (copy_stroke_destination)
|
||||
{
|
||||
glActiveTexture(pp::renderer::gl::active_texture_unit(1U));
|
||||
blend_tex.unbind();
|
||||
@@ -353,7 +389,9 @@ void NodeStrokePreview::draw_stroke_immediate()
|
||||
glDisable(pp::renderer::gl::blend_state());
|
||||
ShaderManager::use(kShader::Stroke);
|
||||
ShaderManager::u_int(kShaderUniform::Tex, 0); // brush
|
||||
if (!ShaderManager::ext_framebuffer_fetch)
|
||||
const auto stroke_feedback = stroke_preview_destination_feedback_plan(m_rtt.getWidth(), m_rtt.getHeight());
|
||||
const bool copy_stroke_destination = !stroke_feedback.reads_destination_color;
|
||||
if (copy_stroke_destination)
|
||||
ShaderManager::u_int(kShaderUniform::TexBG, 1); // bg
|
||||
ShaderManager::u_int(kShaderUniform::TexPattern, 2); // pattern
|
||||
ShaderManager::u_int(kShaderUniform::TexMix, 3); // mixer
|
||||
@@ -388,7 +426,7 @@ void NodeStrokePreview::draw_stroke_immediate()
|
||||
ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 });
|
||||
ShaderManager::u_float(kShaderUniform::Alpha, f.flow);
|
||||
ShaderManager::u_float(kShaderUniform::Opacity, f.opacity);
|
||||
/*auto rect =*/ stroke_draw_samples(f.shapes, m_tex_dual);
|
||||
/*auto rect =*/ stroke_draw_samples(f.shapes, m_tex_dual, copy_stroke_destination);
|
||||
}
|
||||
|
||||
// copy raw stroke to tex
|
||||
@@ -435,7 +473,7 @@ void NodeStrokePreview::draw_stroke_immediate()
|
||||
|
||||
glActiveTexture(pp::renderer::gl::active_texture_unit(0U));
|
||||
b->m_tip_texture->bind();
|
||||
if (!ShaderManager::ext_framebuffer_fetch)
|
||||
if (copy_stroke_destination)
|
||||
{
|
||||
glActiveTexture(pp::renderer::gl::active_texture_unit(1U));
|
||||
m_tex.bind(); // tmp swap for blending
|
||||
@@ -462,7 +500,7 @@ void NodeStrokePreview::draw_stroke_immediate()
|
||||
ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 } /*f.col*/);
|
||||
ShaderManager::u_float(kShaderUniform::Alpha, glm::max(f.flow, m_min_flow));
|
||||
ShaderManager::u_float(kShaderUniform::Opacity, f.opacity);
|
||||
/*auto rect =*/ stroke_draw_samples(f.shapes, m_tex);
|
||||
/*auto rect =*/ stroke_draw_samples(f.shapes, m_tex, copy_stroke_destination);
|
||||
}
|
||||
glActiveTexture(pp::renderer::gl::active_texture_unit(3U));
|
||||
m_rtt_mixer.unbindTexture();
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
virtual void clear_context() override;
|
||||
void stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz);
|
||||
// return rect {origin, size}
|
||||
glm::vec4 stroke_draw_samples(std::array<vertex_t, 4>& P, Texture2D& blend_tex);
|
||||
glm::vec4 stroke_draw_samples(std::array<vertex_t, 4>& P, Texture2D& blend_tex, bool copy_stroke_destination);
|
||||
std::vector<StrokeFrame> stroke_draw_compute(Stroke& stroke, float zoom) const;
|
||||
void draw_stroke();
|
||||
void draw_stroke_immediate();
|
||||
|
||||
Reference in New Issue
Block a user