Extract node lifecycle, preview runtime, and Win32 input state

This commit is contained in:
2026-06-16 22:18:30 +02:00
parent 24d9d5b6e2
commit 2a2f0c7dd6
12 changed files with 420 additions and 245 deletions

View File

@@ -1,4 +1,5 @@
#include "pch.h"
#include "legacy_node_stroke_preview_runtime_services.h"
#include "log.h"
#include "node_stroke_preview.h"
#include "texture.h"
@@ -21,6 +22,86 @@
#include <cstdint>
#include <stop_token>
namespace pp::panopainter {
bool execute_legacy_node_stroke_preview_immediate_runtime(
const LegacyNodeStrokePreviewImmediateRuntimeRequest& request)
{
if (!request.brush ||
!request.prepare_render_target ||
!request.finish_render_target ||
!request.set_blend_enabled ||
!request.setup_stroke_shader) {
return false;
}
const glm::vec2 render_target_size = {
static_cast<float>(request.preview_rtt.getWidth()),
static_cast<float>(request.preview_rtt.getHeight()),
};
const auto stroke_setup = plan_legacy_node_stroke_preview_stroke_setup(
LegacyNodeStrokePreviewStrokeSetupRequest {
.preview_size = request.preview_size,
.zoom = request.zoom,
.brush_tip_size = request.brush->m_tip_size,
.stroke_max_size_override = request.stroke_max_size_override,
.pad_override = request.pad_override,
.tip_size_pressure = request.brush->m_tip_size_pressure,
.dual_enabled = request.brush->m_dual_enabled,
.dual_size = request.brush->m_dual_size,
.pattern_scale = request.brush->m_pattern_scale,
.pattern_flipx = request.brush->m_pattern_flipx,
.pattern_flipy = request.brush->m_pattern_flipy,
});
const auto prepared_strokes = prepare_legacy_node_stroke_preview_strokes(
request.brush,
stroke_setup,
request.camera_fov,
request.camera_rot);
const glm::mat4 ortho_proj = glm::ortho<float>(0, render_target_size.x, 0, render_target_size.y, -1, 1);
request.prepare_render_target();
request.set_blend_enabled(false);
const auto pass_orchestration = plan_legacy_node_stroke_preview_pass_orchestration(
make_legacy_node_stroke_preview_pass_orchestration_request(
request.render_device_features,
render_target_size,
*request.brush,
ortho_proj));
request.setup_stroke_shader(pass_orchestration.stroke_shader);
const bool sequence_ok = execute_legacy_node_stroke_preview_live_render_passes(
LegacyNodeStrokePreviewLiveRenderRequest {
.brush = *request.brush,
.pass_orchestration = pass_orchestration,
.prepared_strokes = prepared_strokes,
.stroke_texture = request.preview_stroke_texture,
.mixer_rtt = request.preview_rtt_mixer,
.render_target = request.preview_rtt,
.background_texture = request.preview_background_texture,
.dual_texture = request.preview_dual_texture,
.preview_texture = request.preview_image_texture,
.linear_sampler = request.linear_sampler,
.repeat_sampler = request.repeat_sampler,
.zoom = request.zoom,
.min_flow = request.min_flow,
.copy_stroke_destination = pass_orchestration.copy_stroke_destination,
.size = render_target_size,
.bind_dual_pass_textures = request.bind_dual_pass_textures,
.capture_background = request.capture_background,
.compute_frames = request.compute_frames,
.draw_samples = request.draw_samples,
.draw_mix = request.draw_mix,
.unbind_mixer_texture = request.unbind_mixer_texture,
.bind_pattern_texture = request.bind_pattern_texture,
.draw_composite = request.draw_composite,
});
request.finish_render_target();
return sequence_ok;
}
} // namespace pp::panopainter
std::atomic_int NodeStrokePreview::s_instances{ 0 };
std::atomic_bool NodeStrokePreview::s_running{ false };
std::mutex NodeStrokePreview::s_render_mutex;

View File

@@ -0,0 +1,53 @@
#pragma once
#include "../libs/glm/glm/ext/matrix_float4x4.hpp"
#include "../libs/glm/glm/ext/vector_float2.hpp"
#include "brush.h"
#include "legacy_node_stroke_preview_draw_services.h"
#include "renderer_api/renderer_api.h"
#include "rtt.h"
#include "texture.h"
#include <cmath>
#include <functional>
#include <memory>
namespace pp::panopainter {
struct LegacyNodeStrokePreviewImmediateRuntimeRequest {
const std::shared_ptr<Brush>& brush;
glm::vec2 preview_size {};
float zoom = 1.0f;
float min_flow = 0.0f;
float stroke_max_size_override = 0.0f;
float pad_override = NAN;
float camera_fov = 0.0f;
glm::mat4 camera_rot { 1.0f };
pp::renderer::RenderDeviceFeatures render_device_features {};
RTT& preview_rtt;
RTT& preview_rtt_mixer;
Texture2D& preview_stroke_texture;
Texture2D& preview_dual_texture;
Texture2D& preview_background_texture;
Texture2D& preview_image_texture;
Sampler& linear_sampler;
Sampler& repeat_sampler;
std::function<void()> prepare_render_target;
std::function<void()> finish_render_target;
std::function<void(bool)> set_blend_enabled;
std::function<void(const LegacyStrokeShaderSetupUniforms&)> setup_stroke_shader;
std::function<void(const Brush&)> bind_dual_pass_textures;
std::function<void(bool)> capture_background;
std::function<std::vector<LegacyNodeStrokePreviewFrame>(const Stroke&, float)> compute_frames;
std::function<glm::vec4(std::array<vertex_t, 4>&, Texture2D&, bool)> draw_samples;
std::function<void(const glm::vec2&, const glm::vec2&)> draw_mix;
std::function<void()> unbind_mixer_texture;
std::function<void()> bind_pattern_texture;
std::function<void()> draw_composite;
};
[[nodiscard]] bool execute_legacy_node_stroke_preview_immediate_runtime(
const LegacyNodeStrokePreviewImmediateRuntimeRequest& request);
} // namespace pp::panopainter

View File

@@ -0,0 +1,178 @@
#include "pch.h"
#include "legacy_ui_node_lifecycle.h"
#include "node.h"
namespace pp::panopainter
{
void legacy_ui_node_move_construct(Node& dest, Node&& src)
{
dest.m_name = std::move(src.m_name);
dest.m_nodeID_s = std::move(src.m_nodeID_s);
dest.m_children = std::move(src.m_children);
dest.m_nodeID = src.m_nodeID;
dest.m_display = src.m_display;
dest.m_parent = src.m_parent;
dest.y_node = src.y_node;
dest.m_pos = src.m_pos;
dest.m_size = src.m_size;
dest.m_clip = src.m_clip;
dest.m_zoom = src.m_zoom;
dest.m_mouse_ignore = src.m_mouse_ignore;
src.y_node = nullptr;
src.m_parent = nullptr;
dest.set_manager(src.m_manager);
for (auto& child : dest.m_children)
child->m_parent = &dest;
dest.current_mouse_capture = src.current_mouse_capture;
dest.current_key_capture = src.current_key_capture;
dest.m_mouse_captured = src.m_mouse_captured;
dest.m_key_captured = src.m_key_captured;
dest.m_proj = src.m_proj;
dest.m_mvp = src.m_mvp;
dest.m_mouse_inside = src.m_mouse_inside;
dest.m_flood_events = src.m_flood_events;
dest.m_force_mouse_capture = src.m_force_mouse_capture;
dest.m_capture_children = src.m_capture_children;
dest.m_destroyed = false; // src.m_destroyed
dest.m_scale = src.m_scale;
dest.m_pos_origin = src.m_pos_origin;
dest.m_pos_offset = src.m_pos_offset;
dest.m_pos_offset_childred = src.m_pos_offset_childred;
dest.m_clip_uncut = src.m_clip_uncut;
dest.auto_width = src.auto_width;
dest.auto_height = src.auto_height;
}
void legacy_ui_node_destroy(Node& node)
{
node.m_children.clear();
if (node.y_node)
YGNodeFree(node.y_node);
if (node.y_placeholder)
YGNodeFree(node.y_placeholder);
}
void legacy_ui_node_clone_copy(const Node& src, Node& dest)
{
YGNodeCopyStyle(dest.y_node, src.y_node);
dest.set_manager(src.m_manager);
dest.m_name = src.m_name;
dest.m_nodeID_s = src.m_nodeID_s;
dest.m_nodeID = src.m_nodeID;
dest.m_display = src.m_display;
dest.m_pos = src.m_pos;
dest.m_size = src.m_size;
dest.m_clip = src.m_clip;
dest.m_flood_events = src.m_flood_events;
dest.m_force_mouse_capture = src.m_force_mouse_capture;
dest.current_mouse_capture = src.current_mouse_capture;
dest.current_key_capture = src.current_key_capture;
dest.m_mouse_captured = src.m_mouse_captured;
dest.m_key_captured = src.m_key_captured;
dest.m_proj = src.m_proj;
dest.m_mvp = src.m_mvp;
dest.m_mouse_inside = src.m_mouse_inside;
dest.m_capture_children = src.m_capture_children;
dest.m_destroyed = false; // src.m_destroyed
dest.m_scale = src.m_scale;
dest.m_pos_origin = src.m_pos_origin;
dest.m_pos_offset = src.m_pos_offset;
dest.m_pos_offset_childred = src.m_pos_offset_childred;
dest.m_clip_uncut = src.m_clip_uncut;
dest.auto_width = src.auto_width;
dest.auto_height = src.auto_height;
}
void legacy_ui_node_clone_children(const Node& src, Node& dest)
{
for (auto& child : src.m_children)
{
std::shared_ptr<Node> clone = child->clone();
dest.m_children.push_back(clone);
clone->m_parent = &dest;
clone->set_manager(dest.m_manager);
clone->loaded();
YGNodeInsertChild(dest.y_node, clone->y_node, YGNodeGetChildCount(dest.y_node));
}
}
}
void Node::create()
{
}
void Node::init()
{
}
void Node::loaded()
{
}
void Node::added(Node* parent)
{
for (auto& child : m_children)
child->added(this);
}
void Node::removed(Node* parent)
{
for (auto current = m_parent; current; current = current->m_parent)
if (current->child_mouse_focus.get() == this)
current->child_mouse_focus = nullptr;
}
Node&& Node::operator=(Node&& o)
{
return std::forward<Node>(o);
}
Node::Node()
{
y_node = YGNodeNew();
YGNodeSetContext(y_node, this);
y_placeholder = nullptr;
}
Node::Node(Node&& o)
{
pp::panopainter::legacy_ui_node_move_construct(*this, std::move(o));
}
Node::~Node()
{
pp::panopainter::legacy_ui_node_destroy(*this);
}
void Node::draw()
{
}
Node* Node::clone_instantiate() const
{
return new Node();
}
void Node::clone_copy(Node* dest) const
{
pp::panopainter::legacy_ui_node_clone_copy(*this, *dest);
}
void Node::clone_children(Node* dest) const
{
pp::panopainter::legacy_ui_node_clone_children(*this, *dest);
}
void Node::clone_finalize(Node* dest) const
{
/* find controllers and stuff */
}

View File

@@ -0,0 +1,11 @@
#pragma once
class Node;
namespace pp::panopainter
{
void legacy_ui_node_move_construct(Node& dest, Node&& src);
void legacy_ui_node_destroy(Node& node);
void legacy_ui_node_clone_copy(const Node& src, Node& dest);
void legacy_ui_node_clone_children(const Node& src, Node& dest);
}

View File

@@ -6,7 +6,6 @@
#include "image.h"
#include "app.h"
#include "canvas.h"
#include "keymap.h"
#include "platform_windows/windows_bootstrap_helpers.h"
#include "platform_windows/windows_platform_services.h"
#include "platform_windows/windows_lifecycle_shell.h"
@@ -75,30 +74,6 @@ void win32_update_fps(int frames)
}));
}
// create a reverse map from kKey to VK_XXX
void init_vk_map()
{
auto& state = retained_state();
for (int k = 1; k < 256; k++) // ignore kKey::Unknown = 0
{
for (int vk = 0; vk < 256; vk++)
{
if (k == (int)convert_key(vk))
{
if (state.vkey_map.find((kKey)k) == state.vkey_map.end())
{
state.vkey_map.insert({ (kKey)k, vk });
}
else
{
LOG("KEY MAP COLLISION %d and %d maps to %d",
(int)vk, (int)state.vkey_map[(kKey)k], (int)k);
}
}
}
}
}
bool win32_vr_start()
{
auto& state = retained_state();
@@ -133,7 +108,7 @@ int main(int argc, char** argv)
pp::platform::windows::SplashScreen splash(GetModuleHandle(NULL));
init_vk_map();
pp::platform::windows::initialize_retained_input_state();
pp::platform::windows::setup_exception_handler();
@@ -141,7 +116,6 @@ int main(int argc, char** argv)
App::I->create();
memset(&state.keys, 0, sizeof(state.keys));
auto startup = pp::platform::windows::initialize_main_window_startup_state();
auto context = pp::platform::windows::OpenGlWindowContext {};
switch (pp::platform::windows::initialize_main_window_and_gl(startup, state.hWnd, state.hInst, state.window_title, context))

View File

@@ -49,34 +49,6 @@ void Node::handle_parent_resize(glm::vec2 old_size, glm::vec2 new_size)
}
void Node::create()
{
}
void Node::init()
{
}
void Node::loaded()
{
}
void Node::added(Node* parent)
{
for (auto& c : m_children)
c->added(this);
}
void Node::removed(Node* parent)
{
for (auto p = m_parent; p; p = p->m_parent)
if (p->child_mouse_focus.get() == this)
p->child_mouse_focus = nullptr;
}
void Node::mouse_capture()
{
pp::panopainter::legacy_ui_node_mouse_capture(*this);
@@ -97,71 +69,6 @@ void Node::key_release()
pp::panopainter::legacy_ui_node_key_release(*this);
}
Node&& Node::operator=(Node&& o)
{
return std::forward<Node>(o);
}
Node::Node()
{
y_node = YGNodeNew();
YGNodeSetContext(y_node, this);
y_placeholder = nullptr;
}
Node::Node(Node&& o)
{
m_name = std::move(o.m_name);
m_nodeID_s = std::move(o.m_nodeID_s);
m_children = std::move(o.m_children);
m_nodeID = o.m_nodeID;
m_display = o.m_display;
m_parent = o.m_parent;
y_node = o.y_node;
m_pos = o.m_pos;
m_size = o.m_size;
m_clip = o.m_clip;
m_zoom = o.m_zoom;
m_mouse_ignore = o.m_mouse_ignore;
o.y_node = nullptr;
o.m_parent = nullptr;
set_manager(o.m_manager);
for (auto& c : m_children)
c->m_parent = this;
current_mouse_capture = o.current_mouse_capture;
current_key_capture = o.current_key_capture;
m_mouse_captured = o.m_mouse_captured;
m_key_captured = o.m_key_captured;
m_proj = o.m_proj;
m_mvp = o.m_mvp;
m_mouse_inside = o.m_mouse_inside;
m_flood_events = o.m_flood_events;
m_force_mouse_capture = o.m_force_mouse_capture;
m_capture_children = o.m_capture_children;
m_destroyed = false;// o.m_destroyed;
m_scale = o.m_scale;
m_pos_origin = o.m_pos_origin;
m_pos_offset = o.m_pos_offset;
m_pos_offset_childred = o.m_pos_offset_childred;
m_clip_uncut = o.m_clip_uncut;
auto_width = o.auto_width;
auto_height = o.auto_height;
}
Node::~Node()
{
m_children.clear();
if (y_node)
YGNodeFree(y_node);
if (y_placeholder)
YGNodeFree(y_placeholder);
}
void Node::SetWidth(float value)
{
pp::panopainter::legacy_ui_node_set_width(*this, value);
@@ -412,65 +319,3 @@ void Node::load_internal(const tinyxml2::XMLElement* x_node, bool skip_children
pp::panopainter::load_legacy_ui_children(*this, *x_node);
loaded();
}
void Node::draw()
{
}
Node* Node::clone_instantiate() const
{
return new Node();
}
void Node::clone_copy(Node* dest) const
{
YGNodeCopyStyle(dest->y_node, y_node);
dest->set_manager(m_manager);
dest->m_name = m_name;
dest->m_nodeID_s = m_nodeID_s;
dest->m_nodeID = m_nodeID;
dest->m_display = m_display;
dest->m_pos = m_pos;
dest->m_size = m_size;
dest->m_clip = m_clip;
dest->m_flood_events = m_flood_events;
dest->m_force_mouse_capture = m_force_mouse_capture;
dest->current_mouse_capture = current_mouse_capture;
dest->current_key_capture = current_key_capture;
dest->m_mouse_captured = m_mouse_captured;
dest->m_key_captured = m_key_captured;
dest->m_proj = m_proj;
dest->m_mvp = m_mvp;
dest->m_mouse_inside = m_mouse_inside;
dest->m_capture_children = m_capture_children;
dest->m_destroyed = false;// m_destroyed;
dest->m_scale = m_scale;
dest->m_pos_origin = m_pos_origin;
dest->m_pos_offset = m_pos_offset;
dest->m_pos_offset_childred = m_pos_offset_childred;
dest->m_clip_uncut = m_clip_uncut;
dest->auto_width = auto_width;
dest->auto_height = auto_height;
}
void Node::clone_children(Node* dest) const
{
for (auto& c : m_children)
{
std::shared_ptr<Node> cn = c->clone();
dest->m_children.push_back(cn);
cn->m_parent = dest;
cn->set_manager(dest->m_manager);
cn->loaded();
YGNodeInsertChild(dest->y_node, cn->y_node, YGNodeGetChildCount(dest->y_node));
}
}
void Node::clone_finalize(Node* dest) const
{
/* find controllers and stuff */
}

View File

@@ -13,6 +13,7 @@
#include "legacy_canvas_stroke_shader_services.h"
#include "legacy_canvas_stroke_services.h"
#include "legacy_node_stroke_preview_execution_services.h"
#include "legacy_node_stroke_preview_runtime_services.h"
#include "legacy_ui_gl_dispatch.h"
#include "paint_renderer/compositor.h"
#include "renderer_gl/opengl_capabilities.h"
@@ -506,66 +507,45 @@ void NodeStrokePreview::draw_stroke_immediate()
const auto vp = query_stroke_preview_viewport();
const auto cc = query_stroke_preview_clear_color();
float zoom = root()->m_zoom;
glm::vec2 size = { m_rtt.getWidth(), m_rtt.getHeight() };
const auto stroke_setup = pp::panopainter::plan_legacy_node_stroke_preview_stroke_setup(
pp::panopainter::LegacyNodeStrokePreviewStrokeSetupRequest {
const glm::vec2 size = { m_rtt.getWidth(), m_rtt.getHeight() };
const float zoom = root()->m_zoom;
const bool sequence_ok = pp::panopainter::execute_legacy_node_stroke_preview_immediate_runtime(
pp::panopainter::LegacyNodeStrokePreviewImmediateRuntimeRequest {
.brush = m_brush,
.preview_size = m_size,
.zoom = zoom,
.brush_tip_size = m_brush->m_tip_size,
.min_flow = m_min_flow,
.stroke_max_size_override = m_max_size,
.pad_override = m_pad_override,
.tip_size_pressure = m_brush->m_tip_size_pressure,
.dual_enabled = m_brush->m_dual_enabled,
.dual_size = m_brush->m_dual_size,
.pattern_scale = m_brush->m_pattern_scale,
.pattern_flipx = m_brush->m_pattern_flipx,
.pattern_flipy = m_brush->m_pattern_flipy,
});
glm::mat4 ortho_proj = glm::ortho<float>(0, size.x, 0, size.y, -1, 1);
apply_stroke_preview_viewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight());
m_rtt.bindFramebuffer();
m_rtt.clear();
bind_stroke_preview_live_samplers(
m_sampler_mipmap,
m_sampler_linear,
m_sampler_linear_repeat);
const auto& b = m_brush;
auto prepared_strokes = pp::panopainter::prepare_legacy_node_stroke_preview_strokes(
b,
stroke_setup,
Canvas::I->m_cam_fov,
Canvas::I->m_cam_rot);
apply_stroke_preview_capability(pp::renderer::gl::blend_state(), false);
const auto pass_orchestration = pp::panopainter::plan_legacy_node_stroke_preview_pass_orchestration(
pp::panopainter::make_legacy_node_stroke_preview_pass_orchestration_request(
stroke_preview_render_device_features(),
size,
*b,
ortho_proj));
pp::panopainter::setup_legacy_stroke_shader(pass_orchestration.stroke_shader);
const bool sequence_ok = pp::panopainter::execute_legacy_node_stroke_preview_live_render_passes(
pp::panopainter::LegacyNodeStrokePreviewLiveRenderRequest {
.brush = *b,
.pass_orchestration = pass_orchestration,
.prepared_strokes = prepared_strokes,
.stroke_texture = m_tex,
.mixer_rtt = m_rtt_mixer,
.render_target = m_rtt,
.background_texture = m_tex_background,
.dual_texture = m_tex_dual,
.preview_texture = m_tex_preview,
.camera_fov = Canvas::I->m_cam_fov,
.camera_rot = Canvas::I->m_cam_rot,
.render_device_features = stroke_preview_render_device_features(),
.preview_rtt = m_rtt,
.preview_rtt_mixer = m_rtt_mixer,
.preview_stroke_texture = m_tex,
.preview_dual_texture = m_tex_dual,
.preview_background_texture = m_tex_background,
.preview_image_texture = m_tex_preview,
.linear_sampler = m_sampler_linear,
.repeat_sampler = m_sampler_linear_repeat,
.zoom = zoom,
.min_flow = m_min_flow,
.copy_stroke_destination = pass_orchestration.copy_stroke_destination,
.size = size,
.prepare_render_target = [&] {
apply_stroke_preview_viewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight());
m_rtt.bindFramebuffer();
m_rtt.clear();
bind_stroke_preview_live_samplers(
m_sampler_mipmap,
m_sampler_linear,
m_sampler_linear_repeat);
},
.finish_render_target = [&] {
m_rtt.unbindFramebuffer();
},
.set_blend_enabled = [&](bool enabled) {
apply_stroke_preview_capability(pp::renderer::gl::blend_state(), enabled);
},
.setup_stroke_shader = [](const pp::panopainter::LegacyStrokeShaderSetupUniforms& uniforms) {
pp::panopainter::setup_legacy_stroke_shader(uniforms);
},
.bind_dual_pass_textures = [](const Brush& dual_brush) {
bind_stroke_preview_dual_pass_textures(dual_brush);
},
@@ -592,7 +572,7 @@ void NodeStrokePreview::draw_stroke_immediate()
m_rtt_mixer.unbindTexture();
},
.bind_pattern_texture = [&] {
b->m_pattern_texture ? b->m_pattern_texture->bind() : unbind_texture_2d();
m_brush->m_pattern_texture ? m_brush->m_pattern_texture->bind() : unbind_texture_2d();
},
.draw_composite = [&] {
m_plane.draw_fill();
@@ -600,8 +580,6 @@ void NodeStrokePreview::draw_stroke_immediate()
});
assert(sequence_ok);
m_rtt.unbindFramebuffer();
apply_stroke_preview_viewport(vp.x, vp.y, vp.width, vp.height);
apply_stroke_preview_clear_color(cc);
}

View File

@@ -13,6 +13,31 @@ void destroy_window();
namespace pp::platform::windows {
void initialize_retained_input_state()
{
auto& state = retained_state();
memset(&state.keys, 0, sizeof(state.keys));
for (int k = 1; k < 256; ++k) // ignore kKey::Unknown = 0
{
for (int vk = 0; vk < 256; ++vk)
{
if (k != (int)convert_key(vk))
continue;
auto key = (kKey)k;
if (state.vkey_map.find(key) == state.vkey_map.end())
{
state.vkey_map.insert({ key, vk });
}
else
{
LOG("KEY MAP COLLISION %d and %d maps to %d",
vk, state.vkey_map[key], k);
}
}
}
}
RetainedState& retained_state()
{
static RetainedState state;

View File

@@ -19,6 +19,7 @@ struct RetainedState
namespace pp::platform::windows {
void initialize_retained_input_state();
RetainedState& retained_state();
LRESULT CALLBACK main_window_proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);