Route stroke commit sequence through adapter
This commit is contained in:
@@ -18,6 +18,13 @@ 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` now routes
|
||||||
|
its retained per-face commit order through
|
||||||
|
`execute_legacy_canvas_stroke_commit_sequence`, consuming the tested
|
||||||
|
`CanvasStrokeCommitSequencePlan` while keeping history `ActionStroke`
|
||||||
|
mutation, layer dirty state, RTT/framebuffer binding, shader execution, and
|
||||||
|
sampler/texture binding inside Canvas callbacks. The adapter remains retained
|
||||||
|
until stroke commit execution is owned by the renderer backend implementation.
|
||||||
- 2026-06-13: DEBT-0036 was narrowed again. `pp_paint_renderer` now owns a
|
- 2026-06-13: DEBT-0036 was narrowed again. `pp_paint_renderer` now owns a
|
||||||
tested `CanvasStrokeCommitSequencePlan` for `Canvas::stroke_commit`
|
tested `CanvasStrokeCommitSequencePlan` for `Canvas::stroke_commit`
|
||||||
readback, dirty-state, scratch-copy, erase/composite draw, committed-copy,
|
readback, dirty-state, scratch-copy, erase/composite draw, committed-copy,
|
||||||
|
|||||||
@@ -2980,6 +2980,10 @@ Results:
|
|||||||
roles. A retained commit adapter skeleton consumes that semantic sequence,
|
roles. A retained commit adapter skeleton consumes that semantic sequence,
|
||||||
while the live Canvas body still owns history/layer mutation and OpenGL
|
while the live Canvas body still owns history/layer mutation and OpenGL
|
||||||
execution until the next wiring slice.
|
execution until the next wiring slice.
|
||||||
|
- Live `Canvas::stroke_commit` now consumes that semantic commit sequence
|
||||||
|
through retained callbacks, so the legacy body no longer owns the loop order
|
||||||
|
directly. The callbacks still execute the existing OpenGL RTT, texture,
|
||||||
|
sampler, shader, history, and layer mutation work under DEBT-0036.
|
||||||
- Canvas thumbnail layer blending now uses the same canvas destination-feedback
|
- Canvas thumbnail layer blending now uses the same canvas destination-feedback
|
||||||
plan for framebuffer-fetch versus texture-copy decisions; the thumbnail draw
|
plan for framebuffer-fetch versus texture-copy decisions; the thumbnail draw
|
||||||
itself still executes through retained OpenGL canvas code under DEBT-0036.
|
itself still executes through retained OpenGL canvas code under DEBT-0036.
|
||||||
|
|||||||
363
src/canvas.cpp
363
src/canvas.cpp
@@ -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_commit_services.h"
|
||||||
#include "legacy_canvas_stroke_composite_services.h"
|
#include "legacy_canvas_stroke_composite_services.h"
|
||||||
#include "legacy_canvas_stroke_edge_services.h"
|
#include "legacy_canvas_stroke_edge_services.h"
|
||||||
#include "legacy_canvas_stroke_execution_services.h"
|
#include "legacy_canvas_stroke_execution_services.h"
|
||||||
@@ -1014,10 +1015,6 @@ void Canvas::stroke_commit()
|
|||||||
if (!m_dirty || m_layers.empty())
|
if (!m_dirty || m_layers.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_dirty = false;
|
|
||||||
m_dirty_stroke = true; // new stroke ready for timelapse capture
|
|
||||||
App::I->redraw = true;
|
|
||||||
|
|
||||||
// save viewport and clear color states
|
// save viewport and clear color states
|
||||||
const auto vp = query_canvas_viewport();
|
const auto vp = query_canvas_viewport();
|
||||||
const auto cc = query_canvas_clear_color();
|
const auto cc = query_canvas_clear_color();
|
||||||
@@ -1027,181 +1024,199 @@ void Canvas::stroke_commit()
|
|||||||
auto action = new ActionStroke;
|
auto action = new ActionStroke;
|
||||||
action->was_saved = !m_unsaved;
|
action->was_saved = !m_unsaved;
|
||||||
|
|
||||||
m_unsaved = true;
|
|
||||||
App::I->title_update();
|
|
||||||
|
|
||||||
// prepare common states
|
|
||||||
apply_canvas_viewport(0, 0, m_width, m_height);
|
|
||||||
apply_canvas_capability(blend_state(), false);
|
|
||||||
|
|
||||||
const auto& b = m_current_stroke->m_brush;
|
const auto& b = m_current_stroke->m_brush;
|
||||||
|
const auto stroke_material = canvas_stroke_material_plan(*b, false);
|
||||||
|
const auto sequence = pp::paint_renderer::plan_canvas_stroke_commit_sequence(
|
||||||
|
pp::paint_renderer::CanvasStrokeCommitRequest {
|
||||||
|
.erase_mode = m_current_mode == kCanvasMode::Erase,
|
||||||
|
.alpha_locked = m_layers[m_current_layer_idx]->m_alpha_locked,
|
||||||
|
.selection_mask_active = m_smask_active,
|
||||||
|
.dual_stroke_enabled = stroke_material.composite_pass.use_dual,
|
||||||
|
.pattern_enabled = stroke_material.composite_pass.use_pattern,
|
||||||
|
});
|
||||||
|
|
||||||
for (int i = 0; i < 6; i++)
|
std::array<pp::panopainter::LegacyCanvasStrokeCommitFace, 6> faces {};
|
||||||
{
|
for (int i = 0; i < 6; ++i) {
|
||||||
//m_dirty_box[i] = glm::vec4(0, 0, m_width, m_height); // reset bounding box
|
faces[i] = pp::panopainter::LegacyCanvasStrokeCommitFace {
|
||||||
if (!m_dirty_face[i])
|
.index = i,
|
||||||
continue; // no stroke on this face, skip it
|
.dirty = m_dirty_face[i],
|
||||||
|
};
|
||||||
m_layers[m_current_layer_idx]->rtt(i).bindFramebuffer();
|
|
||||||
|
|
||||||
// save image before commit
|
|
||||||
glm::vec2 box_sz = zw(m_dirty_box[i]) - xy(m_dirty_box[i]);
|
|
||||||
action->m_image[i] = std::make_unique<uint8_t[]>(box_sz.x * box_sz.y * 4);
|
|
||||||
m_layers[m_current_layer_idx]->rtt(i).readPixelsRgba8(
|
|
||||||
static_cast<int>(m_dirty_box[i].x),
|
|
||||||
static_cast<int>(m_dirty_box[i].y),
|
|
||||||
static_cast<int>(box_sz.x),
|
|
||||||
static_cast<int>(box_sz.y),
|
|
||||||
action->m_image[i].get());
|
|
||||||
|
|
||||||
action->m_box[i] = m_dirty_box[i];
|
|
||||||
action->m_old_box[i] = m_layers[m_current_layer_idx]->box(i);
|
|
||||||
action->m_old_dirty[i] = m_layers[m_current_layer_idx]->face(i);
|
|
||||||
|
|
||||||
if (!m_layers[m_current_layer_idx]->m_alpha_locked)
|
|
||||||
{
|
|
||||||
auto& lbox = m_layers[m_current_layer_idx]->box(i);
|
|
||||||
lbox = glm::vec4(
|
|
||||||
glm::min(xy(m_dirty_box[i]), xy(lbox)),
|
|
||||||
glm::max(zw(m_dirty_box[i]), zw(lbox))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
m_layers[m_current_layer_idx]->face(i) = true;
|
|
||||||
|
|
||||||
// copy to tmp2 for layer blending
|
|
||||||
set_active_texture_unit(0);
|
|
||||||
m_tex2[i].bind();
|
|
||||||
copy_framebuffer_to_texture_2d(0, 0, 0, 0, m_width, m_height);
|
|
||||||
m_tex2[i].unbind();
|
|
||||||
|
|
||||||
m_tmp[i].bindTexture();
|
|
||||||
set_active_texture_unit(1);
|
|
||||||
m_tex2[i].bind();
|
|
||||||
m_sampler.bind(0);
|
|
||||||
m_sampler_nearest.bind(1);
|
|
||||||
m_sampler.bind(2);
|
|
||||||
m_sampler.bind(3);
|
|
||||||
m_sampler_stencil.bind(4);
|
|
||||||
if (m_current_mode == kCanvasMode::Erase)
|
|
||||||
{
|
|
||||||
ShaderManager::use(kShader::CompErase);
|
|
||||||
ShaderManager::u_int(kShaderUniform::Tex, 0);
|
|
||||||
ShaderManager::u_int(kShaderUniform::TexStroke, 1);
|
|
||||||
ShaderManager::u_int(kShaderUniform::TexMask, 2);
|
|
||||||
ShaderManager::u_int(kShaderUniform::Mask, m_smask_active);
|
|
||||||
ShaderManager::u_float(kShaderUniform::Alpha, 1);
|
|
||||||
ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f));
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto stroke_material = canvas_stroke_material_plan(*b, false);
|
|
||||||
glm::vec2 patt_scale = glm::vec2(b->m_pattern_scale);
|
|
||||||
if (b->m_pattern_flipx) patt_scale.x *= -1.f;
|
|
||||||
if (b->m_pattern_flipy) patt_scale.y *= -1.f;
|
|
||||||
|
|
||||||
pp::panopainter::setup_legacy_stroke_composite_shader(
|
|
||||||
pp::panopainter::LegacyStrokeCompositeUniforms {
|
|
||||||
.resolution = m_size,
|
|
||||||
.pattern = {
|
|
||||||
.scale = patt_scale,
|
|
||||||
.invert = static_cast<float>(b->m_pattern_invert),
|
|
||||||
.brightness = b->m_pattern_brightness,
|
|
||||||
.contrast = b->m_pattern_contrast,
|
|
||||||
.depth = b->m_pattern_depth,
|
|
||||||
.blend_mode = b->m_pattern_blend_mode,
|
|
||||||
.offset = m_pattern_offset,
|
|
||||||
},
|
|
||||||
.mvp = glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f),
|
|
||||||
.layer_alpha = 1.0f,
|
|
||||||
.alpha_lock = m_layers[m_current_layer_idx]->m_alpha_locked,
|
|
||||||
.mask_enabled = m_smask_active,
|
|
||||||
.use_fragcoord = false,
|
|
||||||
.blend_mode = b->m_blend_mode,
|
|
||||||
.use_dual = stroke_material.composite_pass.use_dual,
|
|
||||||
.dual_blend_mode = stroke_material.composite_pass.dual_blend_mode,
|
|
||||||
.dual_alpha = stroke_material.composite_pass.dual_alpha,
|
|
||||||
.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();
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// ShaderManager::use(kShader::StrokeLayer);
|
|
||||||
// ShaderManager::u_int(kShaderUniform::TexBG, 1);
|
|
||||||
// ShaderManager::u_int(kShaderUniform::Lock, m_layers[m_current_layer_idx]->m_alpha_locked);
|
|
||||||
// ShaderManager::u_float(kShaderUniform::Alpha, b->m_tip_opacity);
|
|
||||||
//
|
|
||||||
// ShaderManager::u_int(kShaderUniform::Tex, 0);
|
|
||||||
// ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f));
|
|
||||||
// m_plane.draw_fill();
|
|
||||||
// m_sampler.unbind();
|
|
||||||
// m_sampler_bg.unbind();
|
|
||||||
// m_tex2[i].unbind();
|
|
||||||
// m_tmp[i].unbindTexture();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Dilate borders to avoid interpolation bleeding
|
|
||||||
pp::panopainter::setup_legacy_stroke_dilate_shader(
|
|
||||||
pp::panopainter::LegacyStrokeDilateUniforms {
|
|
||||||
.mvp = glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f),
|
|
||||||
});
|
|
||||||
set_active_texture_unit(0);
|
|
||||||
m_tex2[i].bind();
|
|
||||||
copy_framebuffer_to_texture_2d(0, 0, 0, 0, m_width, m_height);
|
|
||||||
m_plane.draw_fill();
|
|
||||||
|
|
||||||
m_layers[m_current_layer_idx]->rtt(i).unbindFramebuffer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore viewport and clear color states
|
[[maybe_unused]] const auto commit_result = pp::panopainter::execute_legacy_canvas_stroke_commit_sequence(
|
||||||
blend ? apply_canvas_capability(blend_state(), true) : apply_canvas_capability(blend_state(), false);
|
pp::panopainter::LegacyCanvasStrokeCommitRequest {
|
||||||
apply_canvas_viewport(vp.x, vp.y, vp.width, vp.height);
|
.context = "Canvas::stroke_commit",
|
||||||
apply_canvas_clear_color(cc);
|
.faces = faces,
|
||||||
set_active_texture_unit(0);
|
.sequence = sequence,
|
||||||
|
.callbacks = {
|
||||||
|
.mark_commit_started = [&]() {
|
||||||
|
m_dirty = false;
|
||||||
|
m_dirty_stroke = true; // new stroke ready for timelapse capture
|
||||||
|
App::I->redraw = true;
|
||||||
|
m_unsaved = true;
|
||||||
|
App::I->title_update();
|
||||||
|
},
|
||||||
|
.capture_render_state = []() {},
|
||||||
|
.prepare_render_state = [&]() {
|
||||||
|
apply_canvas_viewport(0, 0, m_width, m_height);
|
||||||
|
apply_canvas_capability(blend_state(), false);
|
||||||
|
},
|
||||||
|
.restore_render_state = [&]() {
|
||||||
|
blend ? apply_canvas_capability(blend_state(), true) : apply_canvas_capability(blend_state(), false);
|
||||||
|
apply_canvas_viewport(vp.x, vp.y, vp.width, vp.height);
|
||||||
|
apply_canvas_clear_color(cc);
|
||||||
|
set_active_texture_unit(0);
|
||||||
|
},
|
||||||
|
.publish_history = [&]() {
|
||||||
|
action->m_layer_idx = m_current_layer_idx;
|
||||||
|
action->m_frame_idx = layer().m_frame_index;
|
||||||
|
action->m_canvas = this;
|
||||||
|
//action->m_stroke = std::move(m_current_stroke);
|
||||||
|
ActionManager::add(action);
|
||||||
|
},
|
||||||
|
.capture_timelapse_frame = [&]() {
|
||||||
|
stroke_commit_timelapse();
|
||||||
|
},
|
||||||
|
.bind_layer_framebuffer = [&](int i) {
|
||||||
|
m_layers[m_current_layer_idx]->rtt(i).bindFramebuffer();
|
||||||
|
},
|
||||||
|
.capture_history_region = [&](int i) {
|
||||||
|
// save image before commit
|
||||||
|
glm::vec2 box_sz = zw(m_dirty_box[i]) - xy(m_dirty_box[i]);
|
||||||
|
action->m_image[i] = std::make_unique<uint8_t[]>(
|
||||||
|
static_cast<std::size_t>(box_sz.x * box_sz.y * 4));
|
||||||
|
m_layers[m_current_layer_idx]->rtt(i).readPixelsRgba8(
|
||||||
|
static_cast<int>(m_dirty_box[i].x),
|
||||||
|
static_cast<int>(m_dirty_box[i].y),
|
||||||
|
static_cast<int>(box_sz.x),
|
||||||
|
static_cast<int>(box_sz.y),
|
||||||
|
action->m_image[i].get());
|
||||||
|
|
||||||
// save history
|
action->m_box[i] = m_dirty_box[i];
|
||||||
action->m_layer_idx = m_current_layer_idx;
|
action->m_old_box[i] = m_layers[m_current_layer_idx]->box(i);
|
||||||
action->m_frame_idx = layer().m_frame_index;
|
action->m_old_dirty[i] = m_layers[m_current_layer_idx]->face(i);
|
||||||
action->m_canvas = this;
|
},
|
||||||
//action->m_stroke = std::move(m_current_stroke);
|
.apply_layer_dirty_region = [&](int i) {
|
||||||
ActionManager::add(action);
|
if (!m_layers[m_current_layer_idx]->m_alpha_locked)
|
||||||
stroke_commit_timelapse();
|
{
|
||||||
|
auto& lbox = m_layers[m_current_layer_idx]->box(i);
|
||||||
|
lbox = glm::vec4(
|
||||||
|
glm::min(xy(m_dirty_box[i]), xy(lbox)),
|
||||||
|
glm::max(zw(m_dirty_box[i]), zw(lbox))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
m_layers[m_current_layer_idx]->face(i) = true;
|
||||||
|
},
|
||||||
|
.copy_layer_to_commit_destination = [&](int i) {
|
||||||
|
// copy to tmp2 for layer blending
|
||||||
|
set_active_texture_unit(0);
|
||||||
|
m_tex2[i].bind();
|
||||||
|
copy_framebuffer_to_texture_2d(0, 0, 0, 0, m_width, m_height);
|
||||||
|
m_tex2[i].unbind();
|
||||||
|
},
|
||||||
|
.bind_commit_inputs = [&](int i) {
|
||||||
|
m_tmp[i].bindTexture();
|
||||||
|
set_active_texture_unit(1);
|
||||||
|
m_tex2[i].bind();
|
||||||
|
m_sampler.bind(0);
|
||||||
|
m_sampler_nearest.bind(1);
|
||||||
|
m_sampler.bind(2);
|
||||||
|
m_sampler.bind(3);
|
||||||
|
m_sampler_stencil.bind(4);
|
||||||
|
},
|
||||||
|
.execute_erase_composite = [&](int i) {
|
||||||
|
ShaderManager::use(kShader::CompErase);
|
||||||
|
ShaderManager::u_int(kShaderUniform::Tex, 0);
|
||||||
|
ShaderManager::u_int(kShaderUniform::TexStroke, 1);
|
||||||
|
ShaderManager::u_int(kShaderUniform::TexMask, 2);
|
||||||
|
ShaderManager::u_int(kShaderUniform::Mask, m_smask_active);
|
||||||
|
ShaderManager::u_float(kShaderUniform::Alpha, 1);
|
||||||
|
ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f));
|
||||||
|
|
||||||
|
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) {
|
||||||
|
glm::vec2 patt_scale = glm::vec2(b->m_pattern_scale);
|
||||||
|
if (b->m_pattern_flipx) patt_scale.x *= -1.f;
|
||||||
|
if (b->m_pattern_flipy) patt_scale.y *= -1.f;
|
||||||
|
|
||||||
|
pp::panopainter::setup_legacy_stroke_composite_shader(
|
||||||
|
pp::panopainter::LegacyStrokeCompositeUniforms {
|
||||||
|
.resolution = m_size,
|
||||||
|
.pattern = {
|
||||||
|
.scale = patt_scale,
|
||||||
|
.invert = static_cast<float>(b->m_pattern_invert),
|
||||||
|
.brightness = b->m_pattern_brightness,
|
||||||
|
.contrast = b->m_pattern_contrast,
|
||||||
|
.depth = b->m_pattern_depth,
|
||||||
|
.blend_mode = b->m_pattern_blend_mode,
|
||||||
|
.offset = m_pattern_offset,
|
||||||
|
},
|
||||||
|
.mvp = glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f),
|
||||||
|
.layer_alpha = 1.0f,
|
||||||
|
.alpha_lock = m_layers[m_current_layer_idx]->m_alpha_locked,
|
||||||
|
.mask_enabled = m_smask_active,
|
||||||
|
.use_fragcoord = false,
|
||||||
|
.blend_mode = b->m_blend_mode,
|
||||||
|
.use_dual = stroke_material.composite_pass.use_dual,
|
||||||
|
.dual_blend_mode = stroke_material.composite_pass.dual_blend_mode,
|
||||||
|
.dual_alpha = stroke_material.composite_pass.dual_alpha,
|
||||||
|
.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();
|
||||||
|
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) {
|
||||||
|
// Dilate borders to avoid interpolation bleeding
|
||||||
|
pp::panopainter::setup_legacy_stroke_dilate_shader(
|
||||||
|
pp::panopainter::LegacyStrokeDilateUniforms {
|
||||||
|
.mvp = glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f),
|
||||||
|
});
|
||||||
|
set_active_texture_unit(0);
|
||||||
|
m_tex2[i].bind();
|
||||||
|
copy_framebuffer_to_texture_2d(0, 0, 0, 0, m_width, m_height);
|
||||||
|
},
|
||||||
|
.execute_commit_dilate = [&](int) {
|
||||||
|
m_plane.draw_fill();
|
||||||
|
},
|
||||||
|
.unbind_layer_framebuffer = [&](int i) {
|
||||||
|
m_layers[m_current_layer_idx]->rtt(i).unbindFramebuffer();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::stroke_commit_timelapse()
|
void Canvas::stroke_commit_timelapse()
|
||||||
|
|||||||
Reference in New Issue
Block a user