Isolate legacy stroke sample execution
This commit is contained in:
@@ -18,6 +18,12 @@ agent or engineer to remove them without reconstructing context from chat.
|
|||||||
|
|
||||||
## Recent Reductions
|
## Recent Reductions
|
||||||
|
|
||||||
|
- 2026-06-12: DEBT-0036 was narrowed again. Canvas and `NodeStrokePreview`
|
||||||
|
stroke sample execution now delegate optional destination copy, brush vertex
|
||||||
|
upload, brush-shape draw, and destination unbind through
|
||||||
|
`execute_legacy_canvas_stroke_sample`. Shader setup, uniforms, RTT/texture
|
||||||
|
ownership, dirty-box policy, and retained callback execution remain in the
|
||||||
|
legacy callers until a renderer-owned stroke backend replaces the adapter.
|
||||||
- 2026-06-12: DEBT-0036 was narrowed again. `NodeStrokePreview` now consumes
|
- 2026-06-12: DEBT-0036 was narrowed again. `NodeStrokePreview` now consumes
|
||||||
the same `CanvasStrokeMaterialPlan` boundary for preview dual-brush,
|
the same `CanvasStrokeMaterialPlan` boundary for preview dual-brush,
|
||||||
each-sample pattern, and composite material decisions. Preview GL draw calls,
|
each-sample pattern, and composite material decisions. Preview GL draw calls,
|
||||||
|
|||||||
@@ -1343,8 +1343,12 @@ composite texture/uniform intent. Live `Canvas::stroke_draw`, stroke commit,
|
|||||||
and draw-merge paths consume that material plan for destination feedback,
|
and draw-merge paths consume that material plan for destination feedback,
|
||||||
each-sample pattern, dual-brush, and final composite decisions, and
|
each-sample pattern, dual-brush, and final composite decisions, and
|
||||||
`NodeStrokePreview` now consumes the same plan for preview material decisions.
|
`NodeStrokePreview` now consumes the same plan for preview material decisions.
|
||||||
Actual retained OpenGL draw execution and preview texture-binding details remain
|
Canvas and `NodeStrokePreview` stroke sample execution also delegate optional
|
||||||
under `DEBT-0036`.
|
destination copy, brush vertex upload, brush-shape draw, and destination unbind
|
||||||
|
through `execute_legacy_canvas_stroke_sample`, creating the first retained
|
||||||
|
OpenGL stroke execution service seam. Shader setup, uniforms, RTT/texture
|
||||||
|
ownership, dirty-box policy, and retained callback execution remain under
|
||||||
|
`DEBT-0036`.
|
||||||
It also owns renderer API texture-format to
|
It also owns renderer API texture-format to
|
||||||
OpenGL internal/pixel/component token mapping, including depth-stencil formats,
|
OpenGL internal/pixel/component token mapping, including depth-stencil formats,
|
||||||
for future backend texture objects. `Texture2D` 2D texture binding, upload,
|
for future backend texture objects. `Texture2D` 2D texture binding, upload,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "canvas.h"
|
#include "canvas.h"
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
#include "legacy_gl_renderbuffer_dispatch.h"
|
#include "legacy_gl_renderbuffer_dispatch.h"
|
||||||
|
#include "legacy_canvas_stroke_execution_services.h"
|
||||||
#include "legacy_canvas_stroke_services.h"
|
#include "legacy_canvas_stroke_services.h"
|
||||||
#include "legacy_ui_gl_dispatch.h"
|
#include "legacy_ui_gl_dispatch.h"
|
||||||
#include "legacy_ui_overlay_services.h"
|
#include "legacy_ui_overlay_services.h"
|
||||||
@@ -522,58 +523,44 @@ glm::vec4 Canvas::stroke_draw_samples(
|
|||||||
std::vector<vertex_t>& P,
|
std::vector<vertex_t>& P,
|
||||||
bool copy_stroke_destination)
|
bool copy_stroke_destination)
|
||||||
{
|
{
|
||||||
if (copy_stroke_destination)
|
if (P.size() != 3 && P.size() != 4) {
|
||||||
{
|
|
||||||
set_active_texture_unit(1);
|
|
||||||
m_tex[i].bind(); // bg, copy of framebuffer (copied before drawing)
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec2 bb_min(m_width, m_height);
|
|
||||||
glm::vec2 bb_max(0, 0);
|
|
||||||
for (int j = 0; j < P.size(); j++)
|
|
||||||
{
|
|
||||||
bb_min = glm::max({ 0, 0 }, glm::min(bb_min, xy(P[j].pos)));
|
|
||||||
bb_max = glm::min({ m_width, m_height }, glm::max(bb_max, xy(P[j].pos)));
|
|
||||||
}
|
|
||||||
auto bb_sz = bb_max - bb_min;
|
|
||||||
|
|
||||||
glm::vec2 pad(1);
|
|
||||||
glm::ivec2 tex_pos = glm::clamp(glm::floor(bb_min) - pad, { 0, 0 }, { m_width, m_height });
|
|
||||||
glm::ivec2 tex_sz = glm::clamp(glm::ceil(bb_sz) + pad * 2.f, { 0, 0 }, (glm::vec2)(glm::ivec2(m_width, m_height) - tex_pos));
|
|
||||||
if (copy_stroke_destination)
|
|
||||||
{
|
|
||||||
copy_framebuffer_to_texture_2d(tex_pos.x, tex_pos.y, tex_pos.x, tex_pos.y, tex_sz.x, tex_sz.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (P.size() == 4)
|
|
||||||
{
|
|
||||||
static vertex_t rect[6];
|
|
||||||
rect[0] = P[0];
|
|
||||||
rect[1] = P[1];
|
|
||||||
rect[2] = P[2];
|
|
||||||
rect[3] = P[0];
|
|
||||||
rect[4] = P[2];
|
|
||||||
rect[5] = P[3];
|
|
||||||
m_brush_shape.update_vertices(rect, 6);
|
|
||||||
}
|
|
||||||
else if (P.size() == 3)
|
|
||||||
{
|
|
||||||
m_brush_shape.update_vertices(P.data(), (int)P.size());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
P = triangulate_simple(P);
|
P = triangulate_simple(P);
|
||||||
m_brush_shape.update_vertices(P.data(), (int)P.size());
|
|
||||||
}
|
|
||||||
m_brush_shape.draw_fill();
|
|
||||||
|
|
||||||
if (copy_stroke_destination)
|
|
||||||
{
|
|
||||||
set_active_texture_unit(1);
|
|
||||||
m_tex[i].unbind();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return glm::vec4(tex_pos, tex_pos + tex_sz);
|
const auto result = pp::panopainter::execute_legacy_canvas_stroke_sample(
|
||||||
|
pp::panopainter::LegacyStrokeSampleExecutionRequest {
|
||||||
|
.context = "Canvas::stroke_draw_samples",
|
||||||
|
.target_size = { m_width, m_height },
|
||||||
|
.vertices = P,
|
||||||
|
.copy_stroke_destination = copy_stroke_destination,
|
||||||
|
.bind_destination_texture = [&] {
|
||||||
|
set_active_texture_unit(1);
|
||||||
|
m_tex[i].bind(); // bg, copy of framebuffer (copied before drawing)
|
||||||
|
},
|
||||||
|
.copy_framebuffer_to_destination_texture = [](
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
.unbind_destination_texture = [&] {
|
||||||
|
set_active_texture_unit(1);
|
||||||
|
m_tex[i].unbind();
|
||||||
|
},
|
||||||
|
.upload_brush_vertices = [&](std::span<const vertex_t> vertices) {
|
||||||
|
m_brush_shape.update_vertices(
|
||||||
|
const_cast<vertex_t*>(vertices.data()),
|
||||||
|
static_cast<int>(vertices.size()));
|
||||||
|
},
|
||||||
|
.draw_brush_shape = [&] {
|
||||||
|
m_brush_shape.draw_fill();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return result.dirty_bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Canvas::StrokeFrame> Canvas::stroke_draw_compute(Stroke& stroke) const
|
std::vector<Canvas::StrokeFrame> Canvas::stroke_draw_compute(Stroke& stroke) const
|
||||||
|
|||||||
106
src/legacy_canvas_stroke_execution_services.h
Normal file
106
src/legacy_canvas_stroke_execution_services.h
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <functional>
|
||||||
|
#include <span>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace pp::panopainter {
|
||||||
|
|
||||||
|
struct LegacyStrokeSampleExecutionRequest {
|
||||||
|
std::string_view context;
|
||||||
|
glm::vec2 target_size {};
|
||||||
|
std::span<const vertex_t> vertices;
|
||||||
|
bool copy_stroke_destination = false;
|
||||||
|
std::function<void()> bind_destination_texture;
|
||||||
|
std::function<void(int, int, int, int, int, int)> copy_framebuffer_to_destination_texture;
|
||||||
|
std::function<void()> unbind_destination_texture;
|
||||||
|
std::function<void(std::span<const vertex_t>)> upload_brush_vertices;
|
||||||
|
std::function<void()> draw_brush_shape;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LegacyStrokeSampleExecutionResult {
|
||||||
|
bool ok = false;
|
||||||
|
glm::ivec2 copy_position {};
|
||||||
|
glm::ivec2 copy_size {};
|
||||||
|
glm::vec4 dirty_bounds {};
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] inline LegacyStrokeSampleExecutionResult execute_legacy_canvas_stroke_sample(
|
||||||
|
const LegacyStrokeSampleExecutionRequest& request)
|
||||||
|
{
|
||||||
|
LegacyStrokeSampleExecutionResult result;
|
||||||
|
if (request.target_size.x <= 0.0f ||
|
||||||
|
request.target_size.y <= 0.0f ||
|
||||||
|
request.vertices.empty() ||
|
||||||
|
!request.upload_brush_vertices ||
|
||||||
|
!request.draw_brush_shape) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.copy_stroke_destination) {
|
||||||
|
if (!request.bind_destination_texture ||
|
||||||
|
!request.copy_framebuffer_to_destination_texture ||
|
||||||
|
!request.unbind_destination_texture) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
request.bind_destination_texture();
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec2 bb_min(request.target_size);
|
||||||
|
glm::vec2 bb_max(0.0f, 0.0f);
|
||||||
|
for (const auto& vertex : request.vertices) {
|
||||||
|
bb_min = glm::max({ 0.0f, 0.0f }, glm::min(bb_min, xy(vertex.pos)));
|
||||||
|
bb_max = glm::min(request.target_size, glm::max(bb_max, xy(vertex.pos)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto bb_sz = bb_max - bb_min;
|
||||||
|
const glm::vec2 pad(1.0f);
|
||||||
|
const glm::ivec2 target_extent(request.target_size);
|
||||||
|
result.copy_position = glm::clamp(
|
||||||
|
glm::floor(bb_min) - pad,
|
||||||
|
glm::vec2(0.0f),
|
||||||
|
request.target_size);
|
||||||
|
result.copy_size = glm::clamp(
|
||||||
|
glm::ceil(bb_sz) + pad * 2.0f,
|
||||||
|
glm::vec2(0.0f),
|
||||||
|
glm::vec2(target_extent - result.copy_position));
|
||||||
|
result.dirty_bounds = glm::vec4(result.copy_position, result.copy_position + result.copy_size);
|
||||||
|
|
||||||
|
if (request.copy_stroke_destination) {
|
||||||
|
request.copy_framebuffer_to_destination_texture(
|
||||||
|
result.copy_position.x,
|
||||||
|
result.copy_position.y,
|
||||||
|
result.copy_position.x,
|
||||||
|
result.copy_position.y,
|
||||||
|
result.copy_size.x,
|
||||||
|
result.copy_size.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.vertices.size() == 4) {
|
||||||
|
std::array<vertex_t, 6> rect {
|
||||||
|
request.vertices[0],
|
||||||
|
request.vertices[1],
|
||||||
|
request.vertices[2],
|
||||||
|
request.vertices[0],
|
||||||
|
request.vertices[2],
|
||||||
|
request.vertices[3],
|
||||||
|
};
|
||||||
|
request.upload_brush_vertices(rect);
|
||||||
|
} else {
|
||||||
|
request.upload_brush_vertices(request.vertices);
|
||||||
|
}
|
||||||
|
|
||||||
|
request.draw_brush_shape();
|
||||||
|
|
||||||
|
if (request.copy_stroke_destination) {
|
||||||
|
request.unbind_destination_texture();
|
||||||
|
}
|
||||||
|
|
||||||
|
result.ok = true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pp::panopainter
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "bezier.h"
|
#include "bezier.h"
|
||||||
#include "canvas.h"
|
#include "canvas.h"
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
|
#include "legacy_canvas_stroke_execution_services.h"
|
||||||
#include "legacy_canvas_stroke_services.h"
|
#include "legacy_canvas_stroke_services.h"
|
||||||
#include "legacy_ui_gl_dispatch.h"
|
#include "legacy_ui_gl_dispatch.h"
|
||||||
#include "paint_renderer/compositor.h"
|
#include "paint_renderer/compositor.h"
|
||||||
@@ -249,52 +250,42 @@ glm::vec4 NodeStrokePreview::stroke_draw_samples(
|
|||||||
Texture2D& blend_tex,
|
Texture2D& blend_tex,
|
||||||
bool copy_stroke_destination)
|
bool copy_stroke_destination)
|
||||||
{
|
{
|
||||||
if (copy_stroke_destination)
|
const glm::vec2 size = { m_rtt.getWidth(), m_rtt.getHeight() };
|
||||||
{
|
const auto result = pp::panopainter::execute_legacy_canvas_stroke_sample(
|
||||||
set_active_texture_unit(1U);
|
pp::panopainter::LegacyStrokeSampleExecutionRequest {
|
||||||
blend_tex.bind(); // bg, copy of framebuffer (copied before drawing)
|
.context = "NodeStrokePreview::stroke_draw_samples",
|
||||||
}
|
.target_size = size,
|
||||||
|
.vertices = P,
|
||||||
|
.copy_stroke_destination = copy_stroke_destination,
|
||||||
|
.bind_destination_texture = [&] {
|
||||||
|
set_active_texture_unit(1U);
|
||||||
|
blend_tex.bind(); // bg, copy of framebuffer (copied before drawing)
|
||||||
|
},
|
||||||
|
.copy_framebuffer_to_destination_texture = [](
|
||||||
|
int src_x,
|
||||||
|
int src_y,
|
||||||
|
int dst_x,
|
||||||
|
int dst_y,
|
||||||
|
int width,
|
||||||
|
int height) {
|
||||||
|
// this is also used by the mixer
|
||||||
|
copy_framebuffer_to_texture_2d(src_x, src_y, dst_x, dst_y, width, height);
|
||||||
|
},
|
||||||
|
.unbind_destination_texture = [&] {
|
||||||
|
set_active_texture_unit(1U);
|
||||||
|
blend_tex.unbind();
|
||||||
|
},
|
||||||
|
.upload_brush_vertices = [&](std::span<const vertex_t> vertices) {
|
||||||
|
m_brush_shape.update_vertices(
|
||||||
|
const_cast<vertex_t*>(vertices.data()),
|
||||||
|
static_cast<int>(vertices.size()));
|
||||||
|
},
|
||||||
|
.draw_brush_shape = [&] {
|
||||||
|
m_brush_shape.draw_fill();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
glm::vec2 size = { m_rtt.getWidth(), m_rtt.getHeight() };
|
return result.dirty_bounds;
|
||||||
|
|
||||||
glm::vec2 bb_min(size);
|
|
||||||
glm::vec2 bb_max(0, 0);
|
|
||||||
for (int j = 0; j < P.size(); j++)
|
|
||||||
{
|
|
||||||
bb_min = glm::max({ 0, 0 }, glm::min(bb_min, xy(P[j].pos)));
|
|
||||||
bb_max = glm::min(size, glm::max(bb_max, xy(P[j].pos)));
|
|
||||||
}
|
|
||||||
auto bb_sz = bb_max - bb_min;
|
|
||||||
|
|
||||||
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 (copy_stroke_destination)
|
|
||||||
{
|
|
||||||
// this is also used by the mixer
|
|
||||||
copy_framebuffer_to_texture_2d(tex_pos.x, tex_pos.y, tex_pos.x, tex_pos.y, tex_sz.x, tex_sz.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (P.size() == 4)
|
|
||||||
{
|
|
||||||
static vertex_t rect[6];
|
|
||||||
rect[0] = P[0];
|
|
||||||
rect[1] = P[1];
|
|
||||||
rect[2] = P[2];
|
|
||||||
rect[3] = P[0];
|
|
||||||
rect[4] = P[2];
|
|
||||||
rect[5] = P[3];
|
|
||||||
m_brush_shape.update_vertices(rect, 6);
|
|
||||||
}
|
|
||||||
m_brush_shape.draw_fill();
|
|
||||||
|
|
||||||
if (copy_stroke_destination)
|
|
||||||
{
|
|
||||||
set_active_texture_unit(1U);
|
|
||||||
blend_tex.unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
return glm::vec4(tex_pos, tex_pos + tex_sz);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<NodeStrokePreview::StrokeFrame> NodeStrokePreview::stroke_draw_compute(Stroke& stroke, float zoom) const
|
std::vector<NodeStrokePreview::StrokeFrame> NodeStrokePreview::stroke_draw_compute(Stroke& stroke, float zoom) const
|
||||||
|
|||||||
Reference in New Issue
Block a user