#include "pch.h" #include "log.h" #include "node_stroke_preview.h" #include "texture.h" #include "shader.h" #include "bezier.h" #include "canvas.h" #include "app.h" #include "legacy_canvas_draw_merge_services.h" #include "legacy_canvas_stroke_composite_services.h" #include "legacy_canvas_stroke_execution_services.h" #include "legacy_canvas_stroke_preview_services.h" #include "legacy_canvas_stroke_shader_services.h" #include "legacy_canvas_stroke_services.h" #include "legacy_node_stroke_preview_execution_services.h" #include "legacy_ui_gl_dispatch.h" #include "paint_renderer/compositor.h" #include "renderer_gl/opengl_capabilities.h" #include "util.h" #include #include #include std::atomic_int NodeStrokePreview::s_instances{ 0 }; std::atomic_bool NodeStrokePreview::s_running{ false }; std::mutex NodeStrokePreview::s_render_mutex; BlockingQueue> NodeStrokePreview::s_queue; RTT NodeStrokePreview::m_rtt; RTT NodeStrokePreview::m_rtt_mixer; Texture2D NodeStrokePreview::m_tex; // blending tmp texture Texture2D NodeStrokePreview::m_tex_dual; Texture2D NodeStrokePreview::m_tex_background; Sampler NodeStrokePreview::m_sampler_linear; Sampler NodeStrokePreview::m_sampler_linear_repeat; Sampler NodeStrokePreview::m_sampler_mipmap; DynamicShape NodeStrokePreview::m_brush_shape; std::jthread NodeStrokePreview::s_renderer; void NodeStrokePreview::terminate_renderer() { if (!s_renderer.joinable()) return; s_running = false; s_renderer.request_stop(); s_queue.UnlockGetters(); s_renderer.join(); } void NodeStrokePreview::empty_queue() { s_queue.q.clear(); } void NodeStrokePreview::restore_context() { NodeBorder::restore_context(); init_controls(); if (m_size.x > 0 && m_size.y > 0) m_tex_preview.create(static_cast(m_size.x), static_cast(m_size.y)); draw_stroke(); } void NodeStrokePreview::clear_context() { NodeBorder::clear_context(); m_tex_preview.destroy(); } Image NodeStrokePreview::render_to_image() { std::lock_guard _lock(s_render_mutex); App::I->render_task([this] { draw_stroke_immediate(); }); return m_tex_preview.get_image(); } void NodeStrokePreview::draw_stroke() { if (m_size.x == 0 || m_size.y == 0) return; std::unique_lock queue_lock(s_queue.mutex); if (!s_renderer.joinable()) { s_running = true; s_renderer = std::jthread([](std::stop_token stop_token) { BT_SetTerminate(); m_sampler_linear.create(); m_sampler_linear_repeat.create( pp::renderer::gl::linear_texture_filter(), pp::renderer::gl::repeat_texture_wrap()); m_sampler_mipmap.create(); m_sampler_mipmap.set_filter( pp::renderer::gl::linear_mipmap_linear_texture_filter(), pp::renderer::gl::linear_texture_filter()); m_brush_shape.create(); while (s_running && !stop_token.stop_requested()) { auto node = s_queue.Get(); if (node) { std::lock_guard _lock(s_render_mutex); // if the brush is not already loaded, load it and then destroy it bool to_unload = (node->m_brush->m_tip_texture == nullptr); node->m_brush->preload(); App::I->render_task([node, to_unload] { gl_state gl; gl.save(); node->m_brush->load(); node->draw_stroke_immediate(); if (to_unload) node->m_brush->unload(); gl.restore(); }); node->app_redraw(); //std::this_thread::sleep_for(std::chrono::milliseconds(30)); std::this_thread::yield(); } } m_rtt.destroy(); m_rtt_mixer.destroy(); m_tex.destroy(); m_tex_dual.destroy(); m_tex_background.destroy(); m_brush_shape.destroy(); s_running = false; }); } queue_lock.unlock(); s_queue.PostUnique(std::static_pointer_cast(shared_from_this()), m_draw_first); } void NodeStrokePreview::draw() { pp::panopainter::setup_legacy_canvas_draw_merge_texture_shader( pp::panopainter::LegacyCanvasDrawMergeTextureUniforms { .mvp = m_mvp, .texture_slot = 0, }); m_tex_preview.bind(); m_sampler_linear.bind(0); m_plane.draw_fill(); m_sampler_linear.unbind(); m_tex_preview.unbind(); } void NodeStrokePreview::handle_resize(glm::vec2 old_size, glm::vec2 new_size, float zoom) { if (m_preview_size == (new_size * root()->m_zoom) || !m_brush) return; m_preview_size = new_size * root()->m_zoom; if (m_on_screen) draw_stroke(); } void NodeStrokePreview::destroy() { m_tex_preview.destroy(); Node::destroy(); } void NodeStrokePreview::handle_on_screen(bool old_visibility, bool new_visibility) { parent::handle_on_screen(old_visibility, new_visibility); if (new_visibility) { draw_stroke(); } else { s_queue.Remove(std::static_pointer_cast(shared_from_this())); m_tex_preview.destroy(); } }