Narrow retained canvas stroke execution helpers
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
# Modernization Debt Log
|
# Modernization Debt Log
|
||||||
|
|
||||||
Status: live
|
Status: live
|
||||||
Last updated: 2026-06-06
|
Last updated: 2026-06-13
|
||||||
|
|
||||||
Every shortcut, temporary adapter, retained vendored dependency, skipped
|
Every shortcut, temporary adapter, retained vendored dependency, skipped
|
||||||
platform gate, compatibility shim, or incomplete automation path must be
|
platform gate, compatibility shim, or incomplete automation path must be
|
||||||
@@ -18,6 +18,16 @@ agent or engineer to remove them without reconstructing context from chat.
|
|||||||
|
|
||||||
## Recent Reductions
|
## Recent Reductions
|
||||||
|
|
||||||
|
- 2026-06-13: DEBT-0036 was narrowed again. `Canvas::stroke_commit` retained
|
||||||
|
commit input texture/sampler binding, erase/composite draw dispatch,
|
||||||
|
committed-copy-to-dilate-source, and dilate draw now route through
|
||||||
|
`legacy_canvas_stroke_commit_services.h`; Canvas still owns history
|
||||||
|
readback, `ActionStroke` population, layer dirty-box mutation, and retained
|
||||||
|
RTT/framebuffer ownership.
|
||||||
|
- 2026-06-13: DEBT-0036 was narrowed again. `Canvas::stroke_draw` pad-pass
|
||||||
|
dirty-face iteration, pad-region planning, and NDC quad assembly now route
|
||||||
|
through a retained stroke execution helper callback boundary; Canvas still
|
||||||
|
owns framebuffer copies, brush-shape uploads, and draw execution.
|
||||||
- 2026-06-13: DEBT-0036 was narrowed again. `Canvas::stroke_draw` current and
|
- 2026-06-13: DEBT-0036 was narrowed again. `Canvas::stroke_draw` current and
|
||||||
dual stroke frame-face traversal now routes through the retained stroke
|
dual stroke frame-face traversal now routes through the retained stroke
|
||||||
execution helper; framebuffer binding, shader uniform timing, dirty-box
|
execution helper; framebuffer binding, shader uniform timing, dirty-box
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# PanoPainter Modernization Roadmap
|
# PanoPainter Modernization Roadmap
|
||||||
|
|
||||||
Status: live
|
Status: live
|
||||||
Last updated: 2026-06-12
|
Last updated: 2026-06-13
|
||||||
|
|
||||||
This is the living roadmap for modernizing PanoPainter into independently
|
This is the living roadmap for modernizing PanoPainter into independently
|
||||||
testable C++23 components while retaining all existing functionality. Keep this
|
testable C++23 components while retaining all existing functionality. Keep this
|
||||||
@@ -3083,6 +3083,15 @@ Results:
|
|||||||
execution helper wrapping `pp_paint_renderer`, while pad color selection,
|
execution helper wrapping `pp_paint_renderer`, while pad color selection,
|
||||||
dirty-face iteration, framebuffer copies, quad upload, and draw execution
|
dirty-face iteration, framebuffer copies, quad upload, and draw execution
|
||||||
remain retained.
|
remain retained.
|
||||||
|
- `Canvas::stroke_draw` pad-pass dirty-face iteration, pad-region planning, and
|
||||||
|
NDC quad assembly now share a retained stroke execution helper callback
|
||||||
|
boundary, while Canvas still owns framebuffer copies, brush-shape uploads,
|
||||||
|
and draw execution.
|
||||||
|
- `Canvas::stroke_commit` retained commit input texture/sampler binding,
|
||||||
|
erase/composite draw dispatch, committed-copy to the dilate scratch texture,
|
||||||
|
and dilate draw now share `legacy_canvas_stroke_commit_services.h`; history
|
||||||
|
readback, `ActionStroke` population, layer dirty-box mutation, and retained
|
||||||
|
RTT/framebuffer ownership remain in the legacy Canvas path.
|
||||||
- `Canvas::stroke_draw_compute` frame planning now shares the retained stroke
|
- `Canvas::stroke_draw_compute` frame planning now shares the retained stroke
|
||||||
execution helper for brush-quad construction, mixer feedback bounds, 2D/3D
|
execution helper for brush-quad construction, mixer feedback bounds, 2D/3D
|
||||||
projection selection intent, and frame assembly, while legacy projection
|
projection selection intent, and frame assembly, while legacy projection
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Modernization Task Tracker
|
# Modernization Task Tracker
|
||||||
|
|
||||||
Status: live
|
Status: live
|
||||||
Last updated: 2026-06-12
|
Last updated: 2026-06-13
|
||||||
|
|
||||||
This file turns the modernization roadmap into small, measurable work items.
|
This file turns the modernization roadmap into small, measurable work items.
|
||||||
The roadmap explains direction, the debt log explains why shortcuts remain, and
|
The roadmap explains direction, the debt log explains why shortcuts remain, and
|
||||||
@@ -509,6 +509,11 @@ Done Checks:
|
|||||||
|
|
||||||
Progress Notes:
|
Progress Notes:
|
||||||
|
|
||||||
|
- 2026-06-13: `Canvas::stroke_commit` now routes retained commit input
|
||||||
|
texture/sampler binding, erase/composite draw dispatch, committed-copy, and
|
||||||
|
dilate draw through `legacy_canvas_stroke_commit_services.h`; history
|
||||||
|
readback, `ActionStroke` population, and layer dirty-box mutation remain
|
||||||
|
local to Canvas.
|
||||||
- 2026-06-13: `pp_paint_renderer` owns tested Canvas stroke commit sequencing
|
- 2026-06-13: `pp_paint_renderer` owns tested Canvas stroke commit sequencing
|
||||||
and commit texture slot intent. Next slice should wire
|
and commit texture slot intent. Next slice should wire
|
||||||
`legacy_canvas_stroke_commit_services.h` into `Canvas::stroke_commit` while
|
`legacy_canvas_stroke_commit_services.h` into `Canvas::stroke_commit` while
|
||||||
|
|||||||
166
src/canvas.cpp
166
src/canvas.cpp
@@ -753,34 +753,29 @@ void Canvas::stroke_draw()
|
|||||||
{
|
{
|
||||||
set_active_texture_unit(1);
|
set_active_texture_unit(1);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < 6; i++)
|
const std::array<pp::panopainter::LegacyCanvasStrokePadFace, 6> pad_faces = {
|
||||||
{
|
pp::panopainter::LegacyCanvasStrokePadFace { .index = 0, .dirty = box_dirty[0], .pass_dirty_box = box_face[0] },
|
||||||
if (!box_dirty[i])
|
pp::panopainter::LegacyCanvasStrokePadFace { .index = 1, .dirty = box_dirty[1], .pass_dirty_box = box_face[1] },
|
||||||
continue;
|
pp::panopainter::LegacyCanvasStrokePadFace { .index = 2, .dirty = box_dirty[2], .pass_dirty_box = box_face[2] },
|
||||||
const auto pad_region = pp::panopainter::plan_legacy_canvas_stroke_pad_region(
|
pp::panopainter::LegacyCanvasStrokePadFace { .index = 3, .dirty = box_dirty[3], .pass_dirty_box = box_face[3] },
|
||||||
pp::panopainter::LegacyCanvasStrokePadRegionRequest {
|
pp::panopainter::LegacyCanvasStrokePadFace { .index = 4, .dirty = box_dirty[4], .pass_dirty_box = box_face[4] },
|
||||||
.extent = stroke_extent,
|
pp::panopainter::LegacyCanvasStrokePadFace { .index = 5, .dirty = box_dirty[5], .pass_dirty_box = box_face[5] },
|
||||||
.pass_dirty_box = box_face[i],
|
|
||||||
});
|
|
||||||
if (!pad_region.has_pixels)
|
|
||||||
continue;
|
|
||||||
// B(xw)--(zw)C box
|
|
||||||
// | // | coordinates
|
|
||||||
// A(xy)--(zy)D mapping
|
|
||||||
std::array<vertex_t, 6> pad_quad = {
|
|
||||||
vertex_t({pad_region.ndc_quad[0].x, pad_region.ndc_quad[0].y}), // A
|
|
||||||
vertex_t({pad_region.ndc_quad[1].x, pad_region.ndc_quad[1].y}), // B
|
|
||||||
vertex_t({pad_region.ndc_quad[2].x, pad_region.ndc_quad[2].y}), // C
|
|
||||||
vertex_t({pad_region.ndc_quad[3].x, pad_region.ndc_quad[3].y}), // A
|
|
||||||
vertex_t({pad_region.ndc_quad[4].x, pad_region.ndc_quad[4].y}), // C
|
|
||||||
vertex_t({pad_region.ndc_quad[5].x, pad_region.ndc_quad[5].y}), // D
|
|
||||||
};
|
};
|
||||||
|
[[maybe_unused]] const auto pad_result = pp::panopainter::execute_legacy_canvas_stroke_pad_faces(
|
||||||
|
pp::panopainter::LegacyCanvasStrokePadExecutionRequest {
|
||||||
|
.context = "Canvas::stroke_draw",
|
||||||
|
.extent = stroke_extent,
|
||||||
|
.faces = pad_faces,
|
||||||
|
.execute_face =
|
||||||
|
[&](int face_index,
|
||||||
|
const pp::panopainter::LegacyCanvasStrokePadRegionResult& pad_region,
|
||||||
|
std::span<vertex_t> pad_quad) {
|
||||||
m_brush_shape.update_vertices(pad_quad.data(), pad_quad.size());
|
m_brush_shape.update_vertices(pad_quad.data(), pad_quad.size());
|
||||||
|
|
||||||
m_tmp[i].bindFramebuffer();
|
m_tmp[face_index].bindFramebuffer();
|
||||||
if (copy_stroke_destination)
|
if (copy_stroke_destination)
|
||||||
{
|
{
|
||||||
m_tex[i].bind();
|
m_tex[face_index].bind();
|
||||||
copy_framebuffer_to_texture_2d(
|
copy_framebuffer_to_texture_2d(
|
||||||
pad_region.copy_region.x,
|
pad_region.copy_region.x,
|
||||||
pad_region.copy_region.y,
|
pad_region.copy_region.y,
|
||||||
@@ -790,8 +785,9 @@ void Canvas::stroke_draw()
|
|||||||
pad_region.copy_region.height);
|
pad_region.copy_region.height);
|
||||||
}
|
}
|
||||||
m_brush_shape.draw_fill();
|
m_brush_shape.draw_fill();
|
||||||
m_tmp[i].unbindFramebuffer();
|
m_tmp[face_index].unbindFramebuffer();
|
||||||
}
|
},
|
||||||
|
});
|
||||||
if (copy_stroke_destination)
|
if (copy_stroke_destination)
|
||||||
{
|
{
|
||||||
unbind_texture_2d();
|
unbind_texture_2d();
|
||||||
@@ -1047,16 +1043,51 @@ void Canvas::stroke_commit()
|
|||||||
m_tex2[i].unbind();
|
m_tex2[i].unbind();
|
||||||
},
|
},
|
||||||
.bind_commit_inputs = [&](int i) {
|
.bind_commit_inputs = [&](int i) {
|
||||||
m_tmp[i].bindTexture();
|
pp::panopainter::bind_legacy_canvas_stroke_commit_inputs(
|
||||||
set_active_texture_unit(1);
|
sequence,
|
||||||
m_tex2[i].bind();
|
[&](int texture_slot) {
|
||||||
m_sampler.bind(0);
|
set_active_texture_unit(texture_slot);
|
||||||
m_sampler_nearest.bind(1);
|
|
||||||
m_sampler.bind(2);
|
|
||||||
m_sampler.bind(3);
|
|
||||||
m_sampler_stencil.bind(4);
|
|
||||||
},
|
},
|
||||||
.execute_erase_composite = [&](int i) {
|
[&](pp::paint_renderer::CanvasStrokeCommitTextureRole role) {
|
||||||
|
switch (role) {
|
||||||
|
case pp::paint_renderer::CanvasStrokeCommitTextureRole::layer_scratch:
|
||||||
|
m_tex2[i].bind();
|
||||||
|
break;
|
||||||
|
case pp::paint_renderer::CanvasStrokeCommitTextureRole::stroke:
|
||||||
|
m_tmp[i].bindTexture();
|
||||||
|
break;
|
||||||
|
case pp::paint_renderer::CanvasStrokeCommitTextureRole::selection_mask:
|
||||||
|
m_smask.rtt(i).bindTexture();
|
||||||
|
break;
|
||||||
|
case pp::paint_renderer::CanvasStrokeCommitTextureRole::dual_stroke:
|
||||||
|
m_tmp_dual[i].bindTexture();
|
||||||
|
break;
|
||||||
|
case pp::paint_renderer::CanvasStrokeCommitTextureRole::pattern:
|
||||||
|
b->m_pattern_texture ? b->m_pattern_texture->bind() : unbind_texture_2d();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[&](pp::paint_renderer::CanvasStrokeCommitTextureRole role, int texture_slot) {
|
||||||
|
switch (role) {
|
||||||
|
case pp::paint_renderer::CanvasStrokeCommitTextureRole::layer_scratch:
|
||||||
|
m_sampler.bind(texture_slot);
|
||||||
|
break;
|
||||||
|
case pp::paint_renderer::CanvasStrokeCommitTextureRole::stroke:
|
||||||
|
m_sampler_nearest.bind(texture_slot);
|
||||||
|
break;
|
||||||
|
case pp::paint_renderer::CanvasStrokeCommitTextureRole::selection_mask:
|
||||||
|
case pp::paint_renderer::CanvasStrokeCommitTextureRole::dual_stroke:
|
||||||
|
m_sampler.bind(texture_slot);
|
||||||
|
break;
|
||||||
|
case pp::paint_renderer::CanvasStrokeCommitTextureRole::pattern:
|
||||||
|
m_sampler_stencil.bind(texture_slot);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
.execute_erase_composite = [&](int) {
|
||||||
|
pp::panopainter::execute_legacy_canvas_stroke_commit_erase(
|
||||||
|
[&]() {
|
||||||
pp::panopainter::setup_legacy_stroke_erase_shader(
|
pp::panopainter::setup_legacy_stroke_erase_shader(
|
||||||
pp::panopainter::LegacyStrokeEraseUniforms {
|
pp::panopainter::LegacyStrokeEraseUniforms {
|
||||||
.mvp = glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f),
|
.mvp = glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f),
|
||||||
@@ -1066,25 +1097,18 @@ void Canvas::stroke_commit()
|
|||||||
.alpha = 1.0f,
|
.alpha = 1.0f,
|
||||||
.mask_enabled = m_smask_active,
|
.mask_enabled = m_smask_active,
|
||||||
});
|
});
|
||||||
|
|
||||||
set_active_texture_unit(0);
|
|
||||||
m_tex2[i].bind();
|
|
||||||
set_active_texture_unit(1);
|
|
||||||
m_tmp[i].bindTexture();
|
|
||||||
set_active_texture_unit(2);
|
|
||||||
m_smask.rtt(i).bindTexture();
|
|
||||||
m_plane.draw_fill();
|
|
||||||
m_smask.rtt(i).unbindTexture();
|
|
||||||
set_active_texture_unit(1);
|
|
||||||
m_tmp[i].unbindTexture();
|
|
||||||
set_active_texture_unit(0);
|
|
||||||
m_tex2[i].unbind();
|
|
||||||
},
|
},
|
||||||
.execute_paint_composite = [&](int i) {
|
[&]() {
|
||||||
|
m_plane.draw_fill();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
.execute_paint_composite = [&](int) {
|
||||||
glm::vec2 patt_scale = glm::vec2(b->m_pattern_scale);
|
glm::vec2 patt_scale = glm::vec2(b->m_pattern_scale);
|
||||||
if (b->m_pattern_flipx) patt_scale.x *= -1.f;
|
if (b->m_pattern_flipx) patt_scale.x *= -1.f;
|
||||||
if (b->m_pattern_flipy) patt_scale.y *= -1.f;
|
if (b->m_pattern_flipy) patt_scale.y *= -1.f;
|
||||||
|
|
||||||
|
pp::panopainter::execute_legacy_canvas_stroke_commit_paint(
|
||||||
|
[&]() {
|
||||||
pp::panopainter::setup_legacy_stroke_composite_shader(
|
pp::panopainter::setup_legacy_stroke_composite_shader(
|
||||||
pp::panopainter::LegacyStrokeCompositeUniforms {
|
pp::panopainter::LegacyStrokeCompositeUniforms {
|
||||||
.resolution = m_size,
|
.resolution = m_size,
|
||||||
@@ -1108,43 +1132,39 @@ void Canvas::stroke_commit()
|
|||||||
.dual_alpha = stroke_material.composite_pass.dual_alpha,
|
.dual_alpha = stroke_material.composite_pass.dual_alpha,
|
||||||
.use_pattern = stroke_material.composite_pass.use_pattern,
|
.use_pattern = stroke_material.composite_pass.use_pattern,
|
||||||
});
|
});
|
||||||
|
},
|
||||||
set_active_texture_unit(0);
|
[&]() {
|
||||||
m_tex2[i].bind();
|
|
||||||
set_active_texture_unit(1);
|
|
||||||
m_tmp[i].bindTexture();
|
|
||||||
set_active_texture_unit(2);
|
|
||||||
m_smask.rtt(i).bindTexture();
|
|
||||||
set_active_texture_unit(3);
|
|
||||||
if (stroke_material.composite_pass.use_dual)
|
|
||||||
m_tmp_dual[i].bindTexture();
|
|
||||||
set_active_texture_unit(4);
|
|
||||||
b->m_pattern_texture ?
|
|
||||||
b->m_pattern_texture->bind() :
|
|
||||||
unbind_texture_2d();
|
|
||||||
m_plane.draw_fill();
|
m_plane.draw_fill();
|
||||||
set_active_texture_unit(3);
|
});
|
||||||
if (stroke_material.composite_pass.use_dual)
|
|
||||||
m_tmp_dual[i].unbindTexture();
|
|
||||||
set_active_texture_unit(2);
|
|
||||||
m_smask.rtt(i).unbindTexture();
|
|
||||||
set_active_texture_unit(1);
|
|
||||||
m_tmp[i].unbindTexture();
|
|
||||||
set_active_texture_unit(0);
|
|
||||||
m_tex2[i].unbind();
|
|
||||||
},
|
},
|
||||||
.copy_committed_to_dilate_source = [&](int i) {
|
.copy_committed_to_dilate_source = [&](int i) {
|
||||||
|
pp::panopainter::copy_legacy_canvas_stroke_commit_to_dilate_source(
|
||||||
|
sequence,
|
||||||
|
[&]() {
|
||||||
// Dilate borders to avoid interpolation bleeding
|
// Dilate borders to avoid interpolation bleeding
|
||||||
pp::panopainter::setup_legacy_stroke_dilate_shader(
|
pp::panopainter::setup_legacy_stroke_dilate_shader(
|
||||||
pp::panopainter::LegacyStrokeDilateUniforms {
|
pp::panopainter::LegacyStrokeDilateUniforms {
|
||||||
.mvp = glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f),
|
.mvp = glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f),
|
||||||
});
|
});
|
||||||
set_active_texture_unit(0);
|
},
|
||||||
|
[&](int texture_slot) {
|
||||||
|
set_active_texture_unit(texture_slot);
|
||||||
|
},
|
||||||
|
[&]() {
|
||||||
m_tex2[i].bind();
|
m_tex2[i].bind();
|
||||||
copy_framebuffer_to_texture_2d(0, 0, 0, 0, m_width, m_height);
|
},
|
||||||
|
[&](int src_x, int src_y, int dst_x, int dst_y, int width, int height) {
|
||||||
|
copy_framebuffer_to_texture_2d(src_x, src_y, dst_x, dst_y, width, height);
|
||||||
|
},
|
||||||
|
pp::panopainter::LegacyCanvasStrokeCommitCopyExtent {
|
||||||
|
.width = m_width,
|
||||||
|
.height = m_height,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
.execute_commit_dilate = [&](int) {
|
.execute_commit_dilate = [&](int) {
|
||||||
|
pp::panopainter::execute_legacy_canvas_stroke_commit_dilate([&]() {
|
||||||
m_plane.draw_fill();
|
m_plane.draw_fill();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
.unbind_layer_framebuffer = [&](int i) {
|
.unbind_layer_framebuffer = [&](int i) {
|
||||||
m_layers[m_current_layer_idx]->rtt(i).unbindFramebuffer();
|
m_layers[m_current_layer_idx]->rtt(i).unbindFramebuffer();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
@@ -46,12 +47,100 @@ struct LegacyCanvasStrokeCommitResult {
|
|||||||
int committed_faces = 0;
|
int committed_faces = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LegacyCanvasStrokeCommitCopyExtent {
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
};
|
||||||
|
|
||||||
[[nodiscard]] inline std::size_t legacy_canvas_stroke_commit_step_count(
|
[[nodiscard]] inline std::size_t legacy_canvas_stroke_commit_step_count(
|
||||||
const pp::paint_renderer::CanvasStrokeCommitSequencePlan& sequence) noexcept
|
const pp::paint_renderer::CanvasStrokeCommitSequencePlan& sequence) noexcept
|
||||||
{
|
{
|
||||||
return std::min(sequence.step_count, sequence.steps.size());
|
return std::min(sequence.step_count, sequence.steps.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline std::size_t legacy_canvas_stroke_commit_texture_binding_count(
|
||||||
|
const pp::paint_renderer::CanvasStrokeCommitSequencePlan& sequence) noexcept
|
||||||
|
{
|
||||||
|
return std::min(sequence.texture_binding_count, sequence.texture_bindings.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline int legacy_canvas_stroke_commit_texture_slot(
|
||||||
|
const pp::paint_renderer::CanvasStrokeCommitSequencePlan& sequence,
|
||||||
|
pp::paint_renderer::CanvasStrokeCommitTextureRole role) noexcept
|
||||||
|
{
|
||||||
|
const auto binding_count = legacy_canvas_stroke_commit_texture_binding_count(sequence);
|
||||||
|
for (std::size_t binding_index = 0; binding_index < binding_count; ++binding_index) {
|
||||||
|
const auto& binding = sequence.texture_bindings[binding_index];
|
||||||
|
if (binding.role == role) {
|
||||||
|
return static_cast<int>(binding.slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename SetActiveTextureUnit, typename BindTextureRole, typename BindSamplerRole>
|
||||||
|
inline void bind_legacy_canvas_stroke_commit_inputs(
|
||||||
|
const pp::paint_renderer::CanvasStrokeCommitSequencePlan& sequence,
|
||||||
|
SetActiveTextureUnit&& set_active_texture_unit,
|
||||||
|
BindTextureRole&& bind_texture_role,
|
||||||
|
BindSamplerRole&& bind_sampler_role)
|
||||||
|
{
|
||||||
|
const auto binding_count = legacy_canvas_stroke_commit_texture_binding_count(sequence);
|
||||||
|
for (std::size_t binding_index = 0; binding_index < binding_count; ++binding_index) {
|
||||||
|
const auto& binding = sequence.texture_bindings[binding_index];
|
||||||
|
set_active_texture_unit(static_cast<int>(binding.slot));
|
||||||
|
bind_texture_role(binding.role);
|
||||||
|
bind_sampler_role(binding.role, static_cast<int>(binding.slot));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename SetupShader, typename DrawPlane>
|
||||||
|
inline void execute_legacy_canvas_stroke_commit_erase(
|
||||||
|
SetupShader&& setup_shader,
|
||||||
|
DrawPlane&& draw_plane)
|
||||||
|
{
|
||||||
|
setup_shader();
|
||||||
|
draw_plane();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename SetupShader, typename DrawPlane>
|
||||||
|
inline void execute_legacy_canvas_stroke_commit_paint(
|
||||||
|
SetupShader&& setup_shader,
|
||||||
|
DrawPlane&& draw_plane)
|
||||||
|
{
|
||||||
|
setup_shader();
|
||||||
|
draw_plane();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename SetupShader, typename SetActiveTextureUnit, typename BindLayerScratch, typename CopyFramebufferToTexture>
|
||||||
|
inline void copy_legacy_canvas_stroke_commit_to_dilate_source(
|
||||||
|
const pp::paint_renderer::CanvasStrokeCommitSequencePlan& sequence,
|
||||||
|
SetupShader&& setup_shader,
|
||||||
|
SetActiveTextureUnit&& set_active_texture_unit,
|
||||||
|
BindLayerScratch&& bind_layer_scratch,
|
||||||
|
CopyFramebufferToTexture&& copy_framebuffer_to_texture,
|
||||||
|
LegacyCanvasStrokeCommitCopyExtent extent)
|
||||||
|
{
|
||||||
|
const auto layer_scratch_slot = legacy_canvas_stroke_commit_texture_slot(
|
||||||
|
sequence,
|
||||||
|
pp::paint_renderer::CanvasStrokeCommitTextureRole::layer_scratch);
|
||||||
|
if (layer_scratch_slot < 0 || extent.width <= 0 || extent.height <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_shader();
|
||||||
|
set_active_texture_unit(layer_scratch_slot);
|
||||||
|
bind_layer_scratch();
|
||||||
|
copy_framebuffer_to_texture(0, 0, 0, 0, extent.width, extent.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename DrawPlane>
|
||||||
|
inline void execute_legacy_canvas_stroke_commit_dilate(DrawPlane&& draw_plane)
|
||||||
|
{
|
||||||
|
draw_plane();
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline bool legacy_canvas_stroke_commit_callbacks_ready(
|
[[nodiscard]] inline bool legacy_canvas_stroke_commit_callbacks_ready(
|
||||||
const LegacyCanvasStrokeCommitCallbacks& callbacks) noexcept
|
const LegacyCanvasStrokeCommitCallbacks& callbacks) noexcept
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -64,6 +64,24 @@ struct LegacyCanvasStrokePadRegionResult {
|
|||||||
std::array<pp::paint_renderer::CanvasStrokePoint, 6> ndc_quad {};
|
std::array<pp::paint_renderer::CanvasStrokePoint, 6> ndc_quad {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LegacyCanvasStrokePadFace {
|
||||||
|
int index = 0;
|
||||||
|
bool dirty = false;
|
||||||
|
glm::vec4 pass_dirty_box {};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LegacyCanvasStrokePadExecutionRequest {
|
||||||
|
std::string_view context;
|
||||||
|
pp::renderer::Extent2D extent {};
|
||||||
|
std::span<const LegacyCanvasStrokePadFace> faces;
|
||||||
|
std::function<void(int, const LegacyCanvasStrokePadRegionResult&, std::span<vertex_t>)> execute_face;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LegacyCanvasStrokePadExecutionResult {
|
||||||
|
bool ok = false;
|
||||||
|
std::size_t padded_faces = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct LegacyCanvasStrokeComputeRequest {
|
struct LegacyCanvasStrokeComputeRequest {
|
||||||
StrokeSample previous_sample {};
|
StrokeSample previous_sample {};
|
||||||
std::span<const StrokeSample> samples;
|
std::span<const StrokeSample> samples;
|
||||||
@@ -228,6 +246,47 @@ std::size_t execute_legacy_canvas_stroke_frame_faces(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline LegacyCanvasStrokePadExecutionResult execute_legacy_canvas_stroke_pad_faces(
|
||||||
|
const LegacyCanvasStrokePadExecutionRequest& request)
|
||||||
|
{
|
||||||
|
LegacyCanvasStrokePadExecutionResult result;
|
||||||
|
if (request.extent.width == 0U ||
|
||||||
|
request.extent.height == 0U ||
|
||||||
|
!request.execute_face) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& face : request.faces) {
|
||||||
|
if (!face.dirty) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto pad_region = plan_legacy_canvas_stroke_pad_region(
|
||||||
|
LegacyCanvasStrokePadRegionRequest {
|
||||||
|
.extent = request.extent,
|
||||||
|
.pass_dirty_box = face.pass_dirty_box,
|
||||||
|
});
|
||||||
|
if (!pad_region.has_pixels) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<vertex_t, 6> pad_quad = {
|
||||||
|
vertex_t({ pad_region.ndc_quad[0].x, pad_region.ndc_quad[0].y }),
|
||||||
|
vertex_t({ pad_region.ndc_quad[1].x, pad_region.ndc_quad[1].y }),
|
||||||
|
vertex_t({ pad_region.ndc_quad[2].x, pad_region.ndc_quad[2].y }),
|
||||||
|
vertex_t({ pad_region.ndc_quad[3].x, pad_region.ndc_quad[3].y }),
|
||||||
|
vertex_t({ pad_region.ndc_quad[4].x, pad_region.ndc_quad[4].y }),
|
||||||
|
vertex_t({ pad_region.ndc_quad[5].x, pad_region.ndc_quad[5].y }),
|
||||||
|
};
|
||||||
|
|
||||||
|
request.execute_face(face.index, pad_region, pad_quad);
|
||||||
|
++result.padded_faces;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.ok = true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline LegacyStrokeSampleExecutionResult execute_legacy_canvas_stroke_sample(
|
[[nodiscard]] inline LegacyStrokeSampleExecutionResult execute_legacy_canvas_stroke_sample(
|
||||||
const LegacyStrokeSampleExecutionRequest& request)
|
const LegacyStrokeSampleExecutionRequest& request)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1832,6 +1832,99 @@ void retained_stroke_commit_runner_clamps_malformed_step_count(pp::tests::Harnes
|
|||||||
PP_EXPECT(h, timelapse == 1);
|
PP_EXPECT(h, timelapse == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void retained_stroke_commit_input_binder_uses_sequence_slots(pp::tests::Harness& h)
|
||||||
|
{
|
||||||
|
const auto sequence = plan_canvas_stroke_commit_sequence(
|
||||||
|
CanvasStrokeCommitRequest {
|
||||||
|
.erase_mode = false,
|
||||||
|
.alpha_locked = false,
|
||||||
|
.selection_mask_active = true,
|
||||||
|
.dual_stroke_enabled = true,
|
||||||
|
.pattern_enabled = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
std::vector<int> active_slots;
|
||||||
|
std::vector<CanvasStrokeCommitTextureRole> bound_textures;
|
||||||
|
std::vector<std::pair<CanvasStrokeCommitTextureRole, int>> bound_samplers;
|
||||||
|
|
||||||
|
pp::panopainter::bind_legacy_canvas_stroke_commit_inputs(
|
||||||
|
sequence,
|
||||||
|
[&](int texture_slot) {
|
||||||
|
active_slots.push_back(texture_slot);
|
||||||
|
},
|
||||||
|
[&](CanvasStrokeCommitTextureRole role) {
|
||||||
|
bound_textures.push_back(role);
|
||||||
|
},
|
||||||
|
[&](CanvasStrokeCommitTextureRole role, int texture_slot) {
|
||||||
|
bound_samplers.emplace_back(role, texture_slot);
|
||||||
|
});
|
||||||
|
|
||||||
|
PP_EXPECT(h, active_slots.size() == 5U);
|
||||||
|
PP_EXPECT(h, active_slots[0] == 0);
|
||||||
|
PP_EXPECT(h, active_slots[1] == 1);
|
||||||
|
PP_EXPECT(h, active_slots[2] == 2);
|
||||||
|
PP_EXPECT(h, active_slots[3] == 3);
|
||||||
|
PP_EXPECT(h, active_slots[4] == 4);
|
||||||
|
PP_EXPECT(h, bound_textures.size() == 5U);
|
||||||
|
PP_EXPECT(h, bound_textures[0] == CanvasStrokeCommitTextureRole::layer_scratch);
|
||||||
|
PP_EXPECT(h, bound_textures[1] == CanvasStrokeCommitTextureRole::stroke);
|
||||||
|
PP_EXPECT(h, bound_textures[2] == CanvasStrokeCommitTextureRole::selection_mask);
|
||||||
|
PP_EXPECT(h, bound_textures[3] == CanvasStrokeCommitTextureRole::dual_stroke);
|
||||||
|
PP_EXPECT(h, bound_textures[4] == CanvasStrokeCommitTextureRole::pattern);
|
||||||
|
PP_EXPECT(h, bound_samplers.size() == 5U);
|
||||||
|
PP_EXPECT(h, bound_samplers[0].first == CanvasStrokeCommitTextureRole::layer_scratch);
|
||||||
|
PP_EXPECT(h, bound_samplers[0].second == 0);
|
||||||
|
PP_EXPECT(h, bound_samplers[4].first == CanvasStrokeCommitTextureRole::pattern);
|
||||||
|
PP_EXPECT(h, bound_samplers[4].second == 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void retained_stroke_commit_dilate_copy_uses_layer_scratch_slot(pp::tests::Harness& h)
|
||||||
|
{
|
||||||
|
const auto sequence = plan_canvas_stroke_commit_sequence(
|
||||||
|
CanvasStrokeCommitRequest {
|
||||||
|
.erase_mode = true,
|
||||||
|
.alpha_locked = true,
|
||||||
|
.selection_mask_active = false,
|
||||||
|
.dual_stroke_enabled = false,
|
||||||
|
.pattern_enabled = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
int setup_calls = 0;
|
||||||
|
std::vector<int> active_slots;
|
||||||
|
int bind_layer_scratch_calls = 0;
|
||||||
|
std::vector<pp::paint_renderer::CanvasStrokeCopyRegion> copy_regions;
|
||||||
|
|
||||||
|
pp::panopainter::copy_legacy_canvas_stroke_commit_to_dilate_source(
|
||||||
|
sequence,
|
||||||
|
[&]() { ++setup_calls; },
|
||||||
|
[&](int texture_slot) { active_slots.push_back(texture_slot); },
|
||||||
|
[&]() { ++bind_layer_scratch_calls; },
|
||||||
|
[&](int src_x, int src_y, int dst_x, int dst_y, int width, int height) {
|
||||||
|
copy_regions.push_back(pp::paint_renderer::CanvasStrokeCopyRegion {
|
||||||
|
.x = src_x,
|
||||||
|
.y = src_y,
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
});
|
||||||
|
PP_EXPECT(h, dst_x == 0);
|
||||||
|
PP_EXPECT(h, dst_y == 0);
|
||||||
|
},
|
||||||
|
pp::panopainter::LegacyCanvasStrokeCommitCopyExtent {
|
||||||
|
.width = 256,
|
||||||
|
.height = 128,
|
||||||
|
});
|
||||||
|
|
||||||
|
PP_EXPECT(h, setup_calls == 1);
|
||||||
|
PP_EXPECT(h, active_slots.size() == 1U);
|
||||||
|
PP_EXPECT(h, active_slots[0] == 0);
|
||||||
|
PP_EXPECT(h, bind_layer_scratch_calls == 1);
|
||||||
|
PP_EXPECT(h, copy_regions.size() == 1U);
|
||||||
|
PP_EXPECT(h, copy_regions[0].x == 0);
|
||||||
|
PP_EXPECT(h, copy_regions[0].y == 0);
|
||||||
|
PP_EXPECT(h, copy_regions[0].width == 256);
|
||||||
|
PP_EXPECT(h, copy_regions[0].height == 128);
|
||||||
|
}
|
||||||
|
|
||||||
void plans_stroke_preview_composite_for_simple_brush(pp::tests::Harness& h)
|
void plans_stroke_preview_composite_for_simple_brush(pp::tests::Harness& h)
|
||||||
{
|
{
|
||||||
const auto plan = plan_stroke_preview_composite(StrokePreviewCompositeRequest {});
|
const auto plan = plan_stroke_preview_composite(StrokePreviewCompositeRequest {});
|
||||||
@@ -2344,6 +2437,12 @@ int main()
|
|||||||
harness.run(
|
harness.run(
|
||||||
"retained_stroke_commit_runner_clamps_malformed_step_count",
|
"retained_stroke_commit_runner_clamps_malformed_step_count",
|
||||||
retained_stroke_commit_runner_clamps_malformed_step_count);
|
retained_stroke_commit_runner_clamps_malformed_step_count);
|
||||||
|
harness.run(
|
||||||
|
"retained_stroke_commit_input_binder_uses_sequence_slots",
|
||||||
|
retained_stroke_commit_input_binder_uses_sequence_slots);
|
||||||
|
harness.run(
|
||||||
|
"retained_stroke_commit_dilate_copy_uses_layer_scratch_slot",
|
||||||
|
retained_stroke_commit_dilate_copy_uses_layer_scratch_slot);
|
||||||
harness.run("plans_stroke_preview_composite_for_simple_brush", plans_stroke_preview_composite_for_simple_brush);
|
harness.run("plans_stroke_preview_composite_for_simple_brush", plans_stroke_preview_composite_for_simple_brush);
|
||||||
harness.run("plans_stroke_preview_composite_with_mixer_input", plans_stroke_preview_composite_with_mixer_input);
|
harness.run("plans_stroke_preview_composite_with_mixer_input", plans_stroke_preview_composite_with_mixer_input);
|
||||||
harness.run("plans_stroke_preview_composite_with_dual_input", plans_stroke_preview_composite_with_dual_input);
|
harness.run("plans_stroke_preview_composite_with_dual_input", plans_stroke_preview_composite_with_dual_input);
|
||||||
|
|||||||
Reference in New Issue
Block a user