#pragma once #include "paint_renderer/compositor.h" #include #include #include #include #include namespace pp::panopainter { struct LegacyCanvasStrokeCommitFace { int index = 0; bool dirty = false; }; struct LegacyCanvasStrokeCommitCallbacks { std::function mark_commit_started; std::function capture_render_state; std::function prepare_render_state; std::function restore_render_state; std::function publish_history; std::function capture_timelapse_frame; std::function bind_layer_framebuffer; std::function capture_history_region; std::function apply_layer_dirty_region; std::function copy_layer_to_commit_destination; std::function bind_commit_inputs; std::function execute_erase_composite; std::function execute_paint_composite; std::function copy_committed_to_dilate_source; std::function execute_commit_dilate; std::function unbind_layer_framebuffer; }; struct LegacyCanvasStrokeCommitRequest { std::string_view context; std::array faces {}; pp::paint_renderer::CanvasStrokeCommitSequencePlan sequence; LegacyCanvasStrokeCommitCallbacks callbacks; }; struct LegacyCanvasStrokeCommitResult { bool ok = false; int committed_faces = 0; }; struct LegacyCanvasStrokeCommitCopyExtent { int width = 0; int height = 0; }; [[nodiscard]] inline std::size_t legacy_canvas_stroke_commit_step_count( const pp::paint_renderer::CanvasStrokeCommitSequencePlan& sequence) noexcept { 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(binding.slot); } } return -1; } template 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(binding.slot)); bind_texture_role(binding.role); bind_sampler_role(binding.role, static_cast(binding.slot)); } } template inline void execute_legacy_canvas_stroke_commit_erase( SetupShader&& setup_shader, DrawPlane&& draw_plane) { setup_shader(); draw_plane(); } template inline void execute_legacy_canvas_stroke_commit_paint( SetupShader&& setup_shader, DrawPlane&& draw_plane) { setup_shader(); draw_plane(); } template 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 inline void execute_legacy_canvas_stroke_commit_dilate(DrawPlane&& draw_plane) { draw_plane(); } [[nodiscard]] inline bool legacy_canvas_stroke_commit_callbacks_ready( const LegacyCanvasStrokeCommitCallbacks& callbacks) noexcept { return callbacks.mark_commit_started && callbacks.capture_render_state && callbacks.prepare_render_state && callbacks.restore_render_state && callbacks.publish_history && callbacks.capture_timelapse_frame && callbacks.bind_layer_framebuffer && callbacks.capture_history_region && callbacks.apply_layer_dirty_region && callbacks.copy_layer_to_commit_destination && callbacks.bind_commit_inputs && callbacks.execute_erase_composite && callbacks.execute_paint_composite && callbacks.copy_committed_to_dilate_source && callbacks.execute_commit_dilate && callbacks.unbind_layer_framebuffer; } [[nodiscard]] inline LegacyCanvasStrokeCommitResult execute_legacy_canvas_stroke_commit_sequence( const LegacyCanvasStrokeCommitRequest& request) { LegacyCanvasStrokeCommitResult result; if (!legacy_canvas_stroke_commit_callbacks_ready(request.callbacks)) { return result; } request.callbacks.mark_commit_started(); request.callbacks.capture_render_state(); request.callbacks.prepare_render_state(); for (const auto& face : request.faces) { if (!face.dirty) { continue; } request.callbacks.bind_layer_framebuffer(face.index); const auto step_count = legacy_canvas_stroke_commit_step_count(request.sequence); for (std::size_t step_index = 0; step_index < step_count; ++step_index) { switch (request.sequence.steps[step_index]) { case pp::paint_renderer::CanvasStrokeCommitStep::readback_history_region: request.callbacks.capture_history_region(face.index); break; case pp::paint_renderer::CanvasStrokeCommitStep::update_layer_dirty_state: request.callbacks.apply_layer_dirty_region(face.index); break; case pp::paint_renderer::CanvasStrokeCommitStep::copy_layer_rtt_to_scratch: request.callbacks.copy_layer_to_commit_destination(face.index); break; case pp::paint_renderer::CanvasStrokeCommitStep::bind_commit_inputs: request.callbacks.bind_commit_inputs(face.index); break; case pp::paint_renderer::CanvasStrokeCommitStep::erase_draw: request.callbacks.execute_erase_composite(face.index); break; case pp::paint_renderer::CanvasStrokeCommitStep::composite_draw: request.callbacks.execute_paint_composite(face.index); break; case pp::paint_renderer::CanvasStrokeCommitStep::copy_committed_rtt_to_scratch: request.callbacks.copy_committed_to_dilate_source(face.index); break; case pp::paint_renderer::CanvasStrokeCommitStep::dilate_edges_draw: request.callbacks.execute_commit_dilate(face.index); break; } } request.callbacks.unbind_layer_framebuffer(face.index); ++result.committed_faces; } request.callbacks.restore_render_state(); request.callbacks.publish_history(); request.callbacks.capture_timelapse_frame(); result.ok = true; return result; } } // namespace pp::panopainter