#pragma once #include "paint_renderer/compositor.h" #include #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; }; template < typename MarkCommitStarted, typename CaptureRenderState, typename PrepareRenderState, typename RestoreRenderState, typename PublishHistory, typename CaptureTimelapseFrame, typename BindLayerFramebuffer, typename CaptureHistoryRegion, typename ApplyLayerDirtyRegion, typename CopyLayerToCommitDestination, typename BindCommitInputs, typename ExecuteEraseComposite, typename ExecutePaintComposite, typename CopyCommittedToDilateSource, typename ExecuteCommitDilate, typename UnbindLayerFramebuffer> [[nodiscard]] inline LegacyCanvasStrokeCommitCallbacks make_legacy_canvas_stroke_commit_callbacks( MarkCommitStarted&& mark_commit_started, CaptureRenderState&& capture_render_state, PrepareRenderState&& prepare_render_state, RestoreRenderState&& restore_render_state, PublishHistory&& publish_history, CaptureTimelapseFrame&& capture_timelapse_frame, BindLayerFramebuffer&& bind_layer_framebuffer, CaptureHistoryRegion&& capture_history_region, ApplyLayerDirtyRegion&& apply_layer_dirty_region, CopyLayerToCommitDestination&& copy_layer_to_commit_destination, BindCommitInputs&& bind_commit_inputs, ExecuteEraseComposite&& execute_erase_composite, ExecutePaintComposite&& execute_paint_composite, CopyCommittedToDilateSource&& copy_committed_to_dilate_source, ExecuteCommitDilate&& execute_commit_dilate, UnbindLayerFramebuffer&& unbind_layer_framebuffer) { return LegacyCanvasStrokeCommitCallbacks { .mark_commit_started = std::forward(mark_commit_started), .capture_render_state = std::forward(capture_render_state), .prepare_render_state = std::forward(prepare_render_state), .restore_render_state = std::forward(restore_render_state), .publish_history = std::forward(publish_history), .capture_timelapse_frame = std::forward(capture_timelapse_frame), .bind_layer_framebuffer = std::forward(bind_layer_framebuffer), .capture_history_region = std::forward(capture_history_region), .apply_layer_dirty_region = std::forward(apply_layer_dirty_region), .copy_layer_to_commit_destination = std::forward(copy_layer_to_commit_destination), .bind_commit_inputs = std::forward(bind_commit_inputs), .execute_erase_composite = std::forward(execute_erase_composite), .execute_paint_composite = std::forward(execute_paint_composite), .copy_committed_to_dilate_source = std::forward(copy_committed_to_dilate_source), .execute_commit_dilate = std::forward(execute_commit_dilate), .unbind_layer_framebuffer = std::forward(unbind_layer_framebuffer), }; } [[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