Extract edit menu and Windows/bootstrap preview seams

This commit is contained in:
2026-06-16 12:53:49 +02:00
parent 184f662493
commit cb9d06c6dc
12 changed files with 493 additions and 409 deletions

View File

@@ -32,6 +32,7 @@
namespace pp::panopainter {
void bind_legacy_main_toolbar(App& app);
void bind_legacy_file_menu(App& app);
void bind_legacy_edit_menu(App& app);
void bind_legacy_about_menu(App& app);
void bind_legacy_layer_menu(App& app);
void bind_legacy_tools_menu(App& app);
@@ -54,25 +55,6 @@ bool apply_brush_preset_plan(App& app, const std::shared_ptr<Brush>& brush)
return pp::panopainter::apply_legacy_brush_preset_plan(app, brush);
}
std::shared_ptr<NodePopupMenu> add_menu_popup(
App& app,
const char* template_id,
glm::vec2 position,
float rtl_anchor_width)
{
const auto popup = pp::panopainter::add_legacy_popup_menu(
app,
template_id,
position.x,
position.y,
rtl_anchor_width);
if (!popup) {
LOG("Popup menu '%s' failed: %s", template_id ? template_id : "<null>", popup.status().message);
return nullptr;
}
return popup.value();
}
[[nodiscard]] bool should_open_tools_panel(const pp::app::ToolsPanelPlan& plan) noexcept
{
return plan.action == pp::app::ToolsPanelAction::open_floating_panel;
@@ -197,15 +179,7 @@ void App::init_menu_file()
void App::init_menu_edit()
{
if (auto* menu_file = layout[main_id]->find<NodeButtonCustom>("menu-edit"))
{
menu_file->on_click = [=](Node*) {
glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
auto popup = add_menu_popup(*this, "edit-menu", pos, menu_file->m_size.x);
if (!popup)
return;
};
}
pp::panopainter::bind_legacy_edit_menu(*this);
}
void App::init_menu_tools()

View File

@@ -0,0 +1,47 @@
#include "pch.h"
#include "app.h"
#include "legacy_ui_overlay_services.h"
#include "node_button_custom.h"
#include "node_popup_menu.h"
namespace {
std::shared_ptr<NodePopupMenu> add_menu_popup(
App& app,
const char* template_id,
glm::vec2 position,
float rtl_anchor_width)
{
const auto popup = pp::panopainter::add_legacy_popup_menu(
app,
template_id,
position.x,
position.y,
rtl_anchor_width);
if (!popup) {
LOG("Popup menu '%s' failed: %s", template_id ? template_id : "<null>", popup.status().message);
return nullptr;
}
return popup.value();
}
} // namespace
namespace pp::panopainter {
void bind_legacy_edit_menu(App& app)
{
auto main = app.layout[app.main_id];
if (auto* menu_edit = main->find<NodeButtonCustom>("menu-edit"))
{
menu_edit->on_click = [&app, menu_edit](Node*) {
glm::vec2 pos = menu_edit->m_pos + glm::vec2(0, menu_edit->m_size.y);
auto popup = add_menu_popup(app, "edit-menu", pos, menu_edit->m_size.x);
if (!popup)
return;
};
}
}
} // namespace pp::panopainter

View File

@@ -10,6 +10,7 @@
#include "legacy_ui_gl_dispatch.h"
#include "paint_renderer/compositor.h"
#include "texture.h"
#include "util.h"
#include <algorithm>
#include <cmath>
@@ -225,6 +226,22 @@ inline void bind_legacy_node_stroke_preview_main_pass_textures(
}
}
inline void copy_legacy_node_stroke_preview_framebuffer_to_texture(
Texture2D& texture,
glm::vec2 size,
std::uint32_t texture_slot)
{
pp::legacy::ui_gl::activate_texture_unit(texture_slot, "NodeStrokePreview");
texture.bind();
copy_framebuffer_to_texture_2d(
0,
0,
0,
0,
static_cast<int>(size.x),
static_cast<int>(size.y));
}
template <typename Frame>
struct LegacyNodeStrokePreviewMainLivePassRequestT {
std::function<void()> setup_blend_uniforms;
@@ -238,6 +255,81 @@ struct LegacyNodeStrokePreviewMainLivePassRequestT {
std::function<void()> finish_main_pass;
};
inline constexpr std::uint32_t kLegacyNodeStrokePreviewStrokeTextureSlot = 1U;
template <
typename Frame,
typename ComputeFrames,
typename BeforeFrame,
typename SetupSampleShader,
typename DrawSample,
typename FinishMainPass>
[[nodiscard]] inline LegacyNodeStrokePreviewMainLivePassRequestT<Frame>
make_legacy_node_stroke_preview_main_live_pass_request(
const Brush& brush,
const LegacyNodeStrokePreviewPassOrchestrationPlan& pass_orchestration,
Texture2D& stroke_texture,
RTT& mixer_rtt,
RTT& render_target,
bool copy_stroke_destination,
glm::vec2 size,
std::uint32_t stroke_texture_slot,
ComputeFrames&& compute_frames,
BeforeFrame&& before_frame,
SetupSampleShader&& setup_sample_shader,
DrawSample&& draw_sample,
FinishMainPass&& finish_main_pass)
{
return LegacyNodeStrokePreviewMainLivePassRequestT<Frame> {
.setup_blend_uniforms = [&] {
pp::panopainter::apply_legacy_stroke_blend_uniforms(
pass_orchestration.material.stroke_pass.uses_pattern,
brush.m_tip_mix,
brush.m_tip_wet,
brush.m_tip_noise);
},
.bind_main_pass_textures = [&] {
pp::panopainter::bind_legacy_node_stroke_preview_main_pass_textures(
pp::panopainter::make_legacy_node_stroke_preview_main_pass_texture_dispatch(
[&](int texture_slot) {
pp::legacy::ui_gl::activate_texture_unit(texture_slot, "NodeStrokePreview");
},
[&] {
brush.m_tip_texture ?
brush.m_tip_texture->bind() :
pp::legacy::ui_gl::unbind_texture_2d("NodeStrokePreview");
},
[&] {
stroke_texture.bind();
},
[&] {
brush.m_pattern_texture ?
brush.m_pattern_texture->bind() :
pp::legacy::ui_gl::unbind_texture_2d("NodeStrokePreview");
},
[&] {
mixer_rtt.bindTexture();
}),
copy_stroke_destination,
pass_orchestration.material.stroke_pass.uses_mixer);
},
.clear_target = [&] {
render_target.clear();
},
.compute_frames = std::forward<ComputeFrames>(compute_frames),
.before_frame = std::forward<BeforeFrame>(before_frame),
.setup_sample_shader = std::forward<SetupSampleShader>(setup_sample_shader),
.draw_sample = std::forward<DrawSample>(draw_sample),
.copy_pass_result = [&] {
copy_legacy_node_stroke_preview_framebuffer_to_texture(
stroke_texture,
size,
stroke_texture_slot);
},
.finish_main_pass = std::forward<FinishMainPass>(finish_main_pass),
};
}
template <typename Frame>
[[nodiscard]] inline bool execute_legacy_node_stroke_preview_main_live_pass(
const LegacyNodeStrokePreviewMainLivePassRequestT<Frame>& request)

View File

@@ -7,9 +7,7 @@
#include "app.h"
#include "canvas.h"
#include "keymap.h"
#include "legacy_gl_runtime_dispatch.h"
#include "legacy_preference_storage.h"
#include "renderer_gl/opengl_capabilities.h"
#include "platform_windows/windows_bootstrap_helpers.h"
#include "platform_windows/windows_platform_services.h"
#include "platform_windows/windows_lifecycle_shell.h"
#include "platform_windows/windows_splash.h"
@@ -17,22 +15,15 @@
#include "platform_windows/windows_vr_shell.h"
#include "../resource.h"
#include <shellscalingapi.h>
#include <WbemCli.h>
#include <deque>
#include "wacom.h"
#include "abr.h"
#if __has_include(<renderdoc_app.h>)
#include <renderdoc_app.h>
#define USE_RENDERDOC
#endif
#include <iomanip>
#include <ctime>
#include <sstream>
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
namespace pp::platform::windows {
void set_async_render_context(HDC hdc, HGLRC hrc);
void lock_async_render_context();
@@ -40,20 +31,10 @@ namespace pp::platform::windows {
void unlock_async_render_context();
void swap_async_render_context();
}
extern HRESULT(*GetDpiForMonitor_fn)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY);
extern HRESULT(*SetProcessDpiAwareness_fn)(PROCESS_DPI_AWARENESS value);
bool win32_renderdoc_init();
void win32_renderdoc_frame_start();
void win32_renderdoc_frame_end();
void init_shcore_API();
std::string GetLastErrorAsString();
void _pre_call_callback(const char* name, void* funcptr, int len_args, ...);
void _post_call_callback(const char* name, void* funcptr, int len_args, ...);
struct RetainedState
{
HINSTANCE hInst{};
HWND hWnd{};
const wchar_t* className{};
bool keys[256]{};
std::map<kKey, int> vkey_map;
wchar_t window_title[512]{};
@@ -112,239 +93,6 @@ void drain_main_tasks()
}
}
struct MainWindowStartupState
{
WNDCLASS window_class{};
PIXELFORMATDESCRIPTOR pixel_format{};
DWORD window_style = WS_OVERLAPPEDWINDOW;
RECT client_rect{ 0, 0, 0, 0 };
POINT client_pos{ CW_USEDEFAULT, CW_USEDEFAULT };
int show_command = SW_NORMAL;
};
struct OpenGlWindowContext
{
HDC device_context{};
HGLRC render_context{};
pp::renderer::gl::OpenGlRuntimeInfo runtime_info{};
};
enum class MainStartupResult
{
Ok = 0,
GladLoadFailure = 1,
MissingCoreContextSupport = 2,
};
void ensure_runtime_data_directory()
{
FILE* fp_check = fopen("data\\layout.xml", "rb");
if (!fp_check)
{
LOG("data files not found");
static char path[MAX_PATH];
GetModuleFileNameA(NULL, path, MAX_PATH);
LOG("current dir %s", path);
PathRemoveFileSpecA(path);
SetCurrentDirectoryA(path);
LOG("change dir to %s", path);
return;
}
fclose(fp_check);
LOG("data files ok");
}
MainWindowStartupState initialize_main_window_startup_state()
{
auto& state = retained_state();
auto startup = MainWindowStartupState {};
state.hInst = GetModuleHandle(NULL);
state.className = L"EngineMain";
startup.window_class.hInstance = state.hInst;
startup.window_class.lpfnWndProc = (WNDPROC)WndProc;
startup.window_class.lpszClassName = state.className;
startup.window_class.hbrBackground = (HBRUSH)COLOR_WINDOW;
startup.window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClass(&startup.window_class);
auto monitor = MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY);
auto x = unsigned{ 96 };
auto y = unsigned{ 96 };
if (GetDpiForMonitor_fn)
GetDpiForMonitor_fn(monitor, MDT_EFFECTIVE_DPI, &x, &y);
App::I->display_density = (float)x / 96.f;
const auto window_preferences = pp::panopainter::read_legacy_window_preferences(SW_NORMAL);
if (window_preferences.has_ui_scale)
App::I->zoom = window_preferences.ui_scale;
else
App::I->zoom = (float)x / 96.f;
startup.show_command = window_preferences.show_command;
startup.client_rect = {
0,
0,
static_cast<LONG>(App::I->width * App::I->zoom),
static_cast<LONG>(App::I->height * App::I->zoom),
};
if (window_preferences.has_window_rect)
{
auto wnd_rect = window_preferences.window_rect;
App::I->width = wnd_rect.z - wnd_rect.x;
App::I->height = wnd_rect.w - wnd_rect.y;
startup.client_rect = { wnd_rect.x, wnd_rect.y, wnd_rect.z, wnd_rect.w };
startup.client_pos = { wnd_rect.x, wnd_rect.y };
}
else
{
AdjustWindowRect(&startup.client_rect, startup.window_style, false);
}
return startup;
}
void create_main_window(const MainWindowStartupState& startup, const wchar_t* window_title)
{
auto& state = retained_state();
const int window_width = startup.client_rect.right - startup.client_rect.left;
const int window_height = startup.client_rect.bottom - startup.client_rect.top;
state.hWnd = CreateWindow(
startup.window_class.lpszClassName,
window_title,
startup.window_style,
startup.client_pos.x,
startup.client_pos.y,
window_width,
window_height,
0,
0,
state.hInst,
0);
}
void initialize_pixel_format_descriptor(PIXELFORMATDESCRIPTOR& pixel_format)
{
pixel_format.nSize = sizeof(pixel_format);
pixel_format.nVersion = 1;
pixel_format.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pixel_format.iPixelType = PFD_TYPE_RGBA;
pixel_format.cColorBits = 32;
pixel_format.cDepthBits = 24;
pixel_format.iLayerType = PFD_MAIN_PLANE;
}
bool load_glad_entry_points(HDC device_context)
{
if (!gladLoadGL())
{
LOG("gladLoadGL() failed");
return false;
}
if (!gladLoadWGL(device_context))
{
LOG("gladLoadWGL() failed");
return false;
}
return true;
}
pp::renderer::gl::OpenGlRuntimeInfo log_runtime_info()
{
auto runtime_info = pp::renderer::gl::OpenGlRuntimeInfo {};
const auto runtime_info_result = pp::renderer::gl::query_opengl_runtime_info(
pp::legacy::gl_runtime::runtime_info_dispatch());
if (runtime_info_result.ok())
{
runtime_info = runtime_info_result.value();
LOG("GL version: %s", runtime_info.version);
LOG("GL vendor: %s", runtime_info.vendor);
LOG("GL renderer: %s", runtime_info.renderer);
}
else
{
LOG("OpenGL runtime info query failed: %s", runtime_info_result.status().message);
}
return runtime_info;
}
bool upgrade_to_core_gl_context(const MainWindowStartupState& startup, OpenGlWindowContext& context)
{
if (!GLAD_WGL_ARB_create_context)
{
LOG("WGL_ARB_create_context not supported");
// If not supported, go fuck yourself we are not gonna support your shitty device
return false;
}
const auto wgl_config = pp::renderer::gl::windows_wgl_core_context_3_3_config();
UINT num_format = 0;
wglMakeCurrent(NULL, NULL);
wglDeleteContext(context.render_context);
DestroyWindow(retained_state().hWnd);
create_main_window(startup, retained_state().window_title);
context.device_context = GetDC(retained_state().hWnd);
int pixel_format = 0;
wglChoosePixelFormatARB(
context.device_context,
wgl_config.pixel_format_attributes.data(),
nullptr,
1,
&pixel_format,
&num_format);
SetPixelFormat(context.device_context, pixel_format, &startup.pixel_format);
context.render_context = wglCreateContextAttribsARB(
context.device_context,
NULL,
wgl_config.context_attributes.data());
wglMakeCurrent(context.device_context, context.render_context);
pp::platform::windows::set_async_render_context(context.device_context, context.render_context);
return true;
}
MainStartupResult initialize_main_window_and_gl(MainWindowStartupState& startup, OpenGlWindowContext& context)
{
create_main_window(startup, L"PanoPainter");
initialize_pixel_format_descriptor(startup.pixel_format);
context.device_context = GetDC(retained_state().hWnd);
const int pixel_format = ChoosePixelFormat(context.device_context, &startup.pixel_format);
SetPixelFormat(context.device_context, pixel_format, &startup.pixel_format);
context.render_context = wglCreateContext(context.device_context);
wglMakeCurrent(context.device_context, context.render_context);
pp::platform::windows::set_async_render_context(context.device_context, context.render_context);
if (!load_glad_entry_points(context.device_context))
return MainStartupResult::GladLoadFailure;
context.runtime_info = log_runtime_info();
#ifdef USE_RENDERDOC
if (!win32_renderdoc_init())
LOG("Renderdoc not started");
#endif
const auto renderer_name = std::string(context.runtime_info.renderer != nullptr ? context.runtime_info.renderer : "");
swprintf_s(
retained_state().window_title,
L"PanoPainter %s (%s)",
g_version_number_w,
str2wstr(renderer_name).c_str());
if (!upgrade_to_core_gl_context(startup, context))
return MainStartupResult::MissingCoreContextSupport;
return MainStartupResult::Ok;
}
}
HWND pp_windows_main_window_handle()
@@ -687,18 +435,19 @@ void win32_save_window_state()
int main(int argc, char** argv)
{
auto& state = retained_state();
state.hInst = GetModuleHandle(NULL);
App::I = new App();
App::I->set_platform_services(&pp::platform::windows::platform_services());
App::I->initLog();
init_shcore_API();
pp::platform::windows::init_shcore_API();
pp::platform::windows::initialize_stylus_input();
if(SetProcessDpiAwareness_fn)
SetProcessDpiAwareness_fn(PROCESS_PER_MONITOR_DPI_AWARE);
if (pp::platform::windows::SetProcessDpiAwareness_fn)
pp::platform::windows::SetProcessDpiAwareness_fn(PROCESS_PER_MONITOR_DPI_AWARE);
ensure_runtime_data_directory();
pp::platform::windows::ensure_runtime_data_directory();
pp::platform::windows::SplashScreen splash(GetModuleHandle(NULL));
@@ -712,15 +461,15 @@ int main(int argc, char** argv)
App::I->create();
memset(&state.keys, 0, sizeof(state.keys));
auto startup = initialize_main_window_startup_state();
auto context = OpenGlWindowContext {};
switch (initialize_main_window_and_gl(startup, context))
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))
{
case MainStartupResult::Ok:
case pp::platform::windows::MainStartupResult::Ok:
break;
case MainStartupResult::GladLoadFailure:
case pp::platform::windows::MainStartupResult::GladLoadFailure:
return 0;
case MainStartupResult::MissingCoreContextSupport:
case pp::platform::windows::MainStartupResult::MissingCoreContextSupport:
return -1;
}
@@ -746,7 +495,7 @@ int main(int argc, char** argv)
// link: https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-registertouchwindow
if (RegisterTouchWindow(state.hWnd, 0) == 0)
{
LOG("RegisterTouchWindow error: %s", GetLastErrorAsString().c_str());
LOG("RegisterTouchWindow error: %s", pp::platform::windows::GetLastErrorAsString().c_str());
}
wglMakeCurrent(NULL, NULL);
@@ -756,8 +505,8 @@ int main(int argc, char** argv)
App::I->runtime().ui_thread_start(*App::I);
#ifdef _DEBUG
glad_set_pre_callback(_pre_call_callback);
glad_set_post_callback(_post_call_callback);
glad_set_pre_callback(pp::platform::windows::_pre_call_callback);
glad_set_post_callback(pp::platform::windows::_post_call_callback);
#endif
if (!state.sandboxed)

View File

@@ -199,22 +199,6 @@ pp::panopainter::LegacyCanvasStrokeMixPassRequest make_stroke_preview_mix_pass_e
});
}
void copy_stroke_preview_framebuffer_to_texture(
Texture2D& texture,
glm::vec2 size,
std::uint32_t texture_unit)
{
set_active_texture_unit(texture_unit);
texture.bind();
copy_framebuffer_to_texture_2d(
0,
0,
0,
0,
static_cast<int>(size.x),
static_cast<int>(size.y));
}
void bind_stroke_preview_live_samplers(
Sampler& mipmap_sampler,
Sampler& linear_sampler,
@@ -235,38 +219,6 @@ void bind_stroke_preview_dual_pass_textures(const Brush& dual_brush)
unbind_texture_2d();
}
void bind_stroke_preview_main_pass_textures(
const Brush& brush,
Texture2D& stroke_destination_texture,
RTT& mixer_rtt,
bool copy_stroke_destination,
bool uses_mixer)
{
pp::panopainter::bind_legacy_node_stroke_preview_main_pass_textures(
pp::panopainter::make_legacy_node_stroke_preview_main_pass_texture_dispatch(
[&](int texture_slot) {
set_active_texture_unit(texture_slot);
},
[&] {
brush.m_tip_texture ?
brush.m_tip_texture->bind() :
unbind_texture_2d();
},
[&] {
stroke_destination_texture.bind();
},
[&] {
brush.m_pattern_texture ?
brush.m_pattern_texture->bind() :
unbind_texture_2d();
},
[&] {
mixer_rtt.bindTexture();
}),
copy_stroke_destination,
uses_mixer);
}
void bind_stroke_preview_destination_texture(Texture2D& texture)
{
set_active_texture_unit(stroke_preview_live_slots::kDestination);
@@ -782,29 +734,19 @@ NodeStrokePreview::make_stroke_draw_immediate_main_live_pass_request(
float zoom,
const glm::vec2& size)
{
return StrokeMainLivePassRequest {
.setup_blend_uniforms = [&] {
pp::panopainter::apply_legacy_stroke_blend_uniforms(
pass_orchestration.material.stroke_pass.uses_pattern,
brush.m_tip_mix,
brush.m_tip_wet,
brush.m_tip_noise);
},
.bind_main_pass_textures = [&] {
bind_stroke_preview_main_pass_textures(
brush,
m_tex,
m_rtt_mixer,
copy_stroke_destination,
pass_orchestration.material.stroke_pass.uses_mixer);
},
.clear_target = [&] {
m_rtt.clear();
},
.compute_frames = [&] {
return pp::panopainter::make_legacy_node_stroke_preview_main_live_pass_request<StrokeFrame>(
brush,
pass_orchestration,
m_tex,
m_rtt_mixer,
m_rtt,
copy_stroke_destination,
size,
pp::panopainter::kLegacyNodeStrokePreviewStrokeTextureSlot,
[&] {
return stroke_draw_compute(stroke, zoom);
},
.before_frame = [&](auto& frame) {
[&](auto& frame) {
if (brush.m_tip_mix > 0.f)
{
stroke_draw_mix(xy(frame.m_mixer_rect), zw(frame.m_mixer_rect));
@@ -815,24 +757,17 @@ NodeStrokePreview::make_stroke_draw_immediate_main_live_pass_request(
glm::vec4 { 0, 0, 0, 1 };
frame.flow = glm::max(frame.flow, m_min_flow);
},
.setup_sample_shader = [&](auto& frame) {
[&](auto& frame) {
execute_stroke_draw_immediate_main_live_sample_pass(
brush,
copy_stroke_destination,
frame,
size);
},
.draw_sample = [&](auto& frame) {
[&](auto& frame) {
/*auto rect =*/ stroke_draw_samples(frame.shapes, m_tex, copy_stroke_destination);
},
.copy_pass_result = [&] {
copy_stroke_preview_framebuffer_to_texture(
m_tex,
size,
stroke_preview_composite_slots::kStroke);
},
.finish_main_pass = [&] { set_active_texture_unit(stroke_preview_live_slots::kMixer); m_rtt_mixer.unbindTexture(); },
};
[&] { set_active_texture_unit(stroke_preview_live_slots::kMixer); m_rtt_mixer.unbindTexture(); });
}
void NodeStrokePreview::execute_stroke_draw_immediate_dual_pass(
@@ -869,7 +804,7 @@ void NodeStrokePreview::execute_stroke_draw_immediate_dual_pass(
/*auto rect =*/ stroke_draw_samples(frame.shapes, m_tex_dual, copy_stroke_destination);
},
[&] {
copy_stroke_preview_framebuffer_to_texture(
pp::panopainter::copy_legacy_node_stroke_preview_framebuffer_to_texture(
m_tex_dual,
size,
stroke_preview_composite_slots::kStroke);

View File

@@ -1,7 +1,11 @@
#include "pch.h"
#include "platform_windows/windows_bootstrap_helpers.h"
#include "app.h"
#include "legacy_gl_runtime_dispatch.h"
#include "legacy_preference_storage.h"
#include "log.h"
#include "renderer_gl/opengl_capabilities.h"
#include <shellscalingapi.h>
#include <string>
@@ -11,6 +15,14 @@
#define USE_RENDERDOC
#endif
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
namespace pp::platform::windows {
void set_async_render_context(HDC hdc, HGLRC hrc);
}
namespace pp::platform::windows {
#ifdef USE_RENDERDOC
RENDERDOC_API_1_4_0* rdoc_api = NULL;
@@ -75,6 +87,213 @@ std::string GetLastErrorAsString()
return message;
}
void ensure_runtime_data_directory()
{
FILE* fp_check = fopen("data\\layout.xml", "rb");
if (!fp_check)
{
LOG("data files not found");
static char path[MAX_PATH];
GetModuleFileNameA(NULL, path, MAX_PATH);
LOG("current dir %s", path);
PathRemoveFileSpecA(path);
SetCurrentDirectoryA(path);
LOG("change dir to %s", path);
return;
}
fclose(fp_check);
LOG("data files ok");
}
MainWindowStartupState initialize_main_window_startup_state()
{
auto startup = MainWindowStartupState {};
const auto hInst = GetModuleHandle(NULL);
const auto className = L"EngineMain";
startup.window_class.hInstance = hInst;
startup.window_class.lpfnWndProc = (WNDPROC)WndProc;
startup.window_class.lpszClassName = className;
startup.window_class.hbrBackground = (HBRUSH)COLOR_WINDOW;
startup.window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClass(&startup.window_class);
auto monitor = MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY);
auto x = unsigned{ 96 };
auto y = unsigned{ 96 };
if (GetDpiForMonitor_fn)
GetDpiForMonitor_fn(monitor, MDT_EFFECTIVE_DPI, &x, &y);
App::I->display_density = (float)x / 96.f;
const auto window_preferences = pp::panopainter::read_legacy_window_preferences(SW_NORMAL);
if (window_preferences.has_ui_scale)
App::I->zoom = window_preferences.ui_scale;
else
App::I->zoom = (float)x / 96.f;
startup.show_command = window_preferences.show_command;
startup.client_rect = {
0,
0,
static_cast<LONG>(App::I->width * App::I->zoom),
static_cast<LONG>(App::I->height * App::I->zoom),
};
if (window_preferences.has_window_rect)
{
auto wnd_rect = window_preferences.window_rect;
App::I->width = wnd_rect.z - wnd_rect.x;
App::I->height = wnd_rect.w - wnd_rect.y;
startup.client_rect = { wnd_rect.x, wnd_rect.y, wnd_rect.z, wnd_rect.w };
startup.client_pos = { wnd_rect.x, wnd_rect.y };
}
else
{
AdjustWindowRect(&startup.client_rect, startup.window_style, false);
}
return startup;
}
void create_main_window(const MainWindowStartupState& startup, HWND& hWnd, HINSTANCE hInst, const wchar_t* window_title)
{
const int window_width = startup.client_rect.right - startup.client_rect.left;
const int window_height = startup.client_rect.bottom - startup.client_rect.top;
hWnd = CreateWindow(
startup.window_class.lpszClassName,
window_title,
startup.window_style,
startup.client_pos.x,
startup.client_pos.y,
window_width,
window_height,
0,
0,
hInst,
0);
}
void initialize_pixel_format_descriptor(PIXELFORMATDESCRIPTOR& pixel_format)
{
pixel_format.nSize = sizeof(pixel_format);
pixel_format.nVersion = 1;
pixel_format.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pixel_format.iPixelType = PFD_TYPE_RGBA;
pixel_format.cColorBits = 32;
pixel_format.cDepthBits = 24;
pixel_format.iLayerType = PFD_MAIN_PLANE;
}
bool load_glad_entry_points(HDC device_context)
{
if (!gladLoadGL())
{
LOG("gladLoadGL() failed");
return false;
}
if (!gladLoadWGL(device_context))
{
LOG("gladLoadWGL() failed");
return false;
}
return true;
}
pp::renderer::gl::OpenGlRuntimeInfo log_runtime_info()
{
auto runtime_info = pp::renderer::gl::OpenGlRuntimeInfo {};
const auto runtime_info_result = pp::renderer::gl::query_opengl_runtime_info(
pp::legacy::gl_runtime::runtime_info_dispatch());
if (runtime_info_result.ok())
{
runtime_info = runtime_info_result.value();
LOG("GL version: %s", runtime_info.version);
LOG("GL vendor: %s", runtime_info.vendor);
LOG("GL renderer: %s", runtime_info.renderer);
}
else
{
LOG("OpenGL runtime info query failed: %s", runtime_info_result.status().message);
}
return runtime_info;
}
bool upgrade_to_core_gl_context(const MainWindowStartupState& startup, HWND& hWnd, HINSTANCE hInst, const wchar_t* window_title, OpenGlWindowContext& context)
{
if (!GLAD_WGL_ARB_create_context)
{
LOG("WGL_ARB_create_context not supported");
// If not supported, go fuck yourself we are not gonna support your shitty device
return false;
}
const auto wgl_config = pp::renderer::gl::windows_wgl_core_context_3_3_config();
UINT num_format = 0;
wglMakeCurrent(NULL, NULL);
wglDeleteContext(context.render_context);
DestroyWindow(hWnd);
create_main_window(startup, hWnd, hInst, window_title);
context.device_context = GetDC(hWnd);
int pixel_format = 0;
wglChoosePixelFormatARB(
context.device_context,
wgl_config.pixel_format_attributes.data(),
nullptr,
1,
&pixel_format,
&num_format);
SetPixelFormat(context.device_context, pixel_format, &startup.pixel_format);
context.render_context = wglCreateContextAttribsARB(
context.device_context,
NULL,
wgl_config.context_attributes.data());
wglMakeCurrent(context.device_context, context.render_context);
set_async_render_context(context.device_context, context.render_context);
return true;
}
MainStartupResult initialize_main_window_and_gl(MainWindowStartupState& startup, HWND& hWnd, HINSTANCE hInst, wchar_t* window_title, OpenGlWindowContext& context)
{
create_main_window(startup, hWnd, hInst, L"PanoPainter");
initialize_pixel_format_descriptor(startup.pixel_format);
context.device_context = GetDC(hWnd);
const int pixel_format = ChoosePixelFormat(context.device_context, &startup.pixel_format);
SetPixelFormat(context.device_context, pixel_format, &startup.pixel_format);
context.render_context = wglCreateContext(context.device_context);
wglMakeCurrent(context.device_context, context.render_context);
set_async_render_context(context.device_context, context.render_context);
if (!load_glad_entry_points(context.device_context))
return MainStartupResult::GladLoadFailure;
context.runtime_info = log_runtime_info();
#ifdef USE_RENDERDOC
if (!win32_renderdoc_init())
LOG("Renderdoc not started");
#endif
const auto renderer_name = std::string(context.runtime_info.renderer != nullptr ? context.runtime_info.renderer : "");
swprintf_s(
window_title,
512,
L"PanoPainter %s (%s)",
g_version_number_w,
str2wstr(renderer_name).c_str());
if (!upgrade_to_core_gl_context(startup, hWnd, hInst, window_title, context))
return MainStartupResult::MissingCoreContextSupport;
return MainStartupResult::Ok;
}
BOOL UnadjustWindowRectEx(LPRECT prc, DWORD dwStyle, BOOL fMenu, DWORD dwExStyle)
{
RECT rc;
@@ -104,3 +323,5 @@ void _post_call_callback(const char* name, void* funcptr, int len_args, ...)
LOG("ERROR %d in %s\n", error_code, name);
}
}
}

View File

@@ -0,0 +1,57 @@
#pragma once
#include <Windows.h>
#include <shellscalingapi.h>
#include <string>
#include "renderer_gl/opengl_capabilities.h"
namespace pp::platform::windows {
bool win32_renderdoc_init();
void win32_renderdoc_frame_start();
void win32_renderdoc_frame_end();
extern HRESULT (*GetDpiForMonitor_fn)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY);
extern HRESULT (*SetProcessDpiAwareness_fn)(PROCESS_DPI_AWARENESS value);
void init_shcore_API();
std::string GetLastErrorAsString();
void _pre_call_callback(const char* name, void* funcptr, int len_args, ...);
void _post_call_callback(const char* name, void* funcptr, int len_args, ...);
struct MainWindowStartupState
{
WNDCLASS window_class{};
PIXELFORMATDESCRIPTOR pixel_format{};
DWORD window_style = WS_OVERLAPPEDWINDOW;
RECT client_rect{ 0, 0, 0, 0 };
POINT client_pos{ CW_USEDEFAULT, CW_USEDEFAULT };
int show_command = SW_NORMAL;
};
struct OpenGlWindowContext
{
HDC device_context{};
HGLRC render_context{};
pp::renderer::gl::OpenGlRuntimeInfo runtime_info{};
};
enum class MainStartupResult
{
Ok = 0,
GladLoadFailure = 1,
MissingCoreContextSupport = 2,
};
void ensure_runtime_data_directory();
MainWindowStartupState initialize_main_window_startup_state();
void create_main_window(const MainWindowStartupState& startup, HWND& hWnd, HINSTANCE hInst, const wchar_t* window_title);
void initialize_pixel_format_descriptor(PIXELFORMATDESCRIPTOR& pixel_format);
bool load_glad_entry_points(HDC device_context);
pp::renderer::gl::OpenGlRuntimeInfo log_runtime_info();
bool upgrade_to_core_gl_context(const MainWindowStartupState& startup, HWND& hWnd, HINSTANCE hInst, const wchar_t* window_title, OpenGlWindowContext& context);
MainStartupResult initialize_main_window_and_gl(MainWindowStartupState& startup, HWND& hWnd, HINSTANCE hInst, wchar_t* window_title, OpenGlWindowContext& context);
}

View File

@@ -1,4 +1,5 @@
#include "pch.h"
#include "platform_windows/windows_bootstrap_helpers.h"
#include "platform_windows/windows_platform_services.h"
#include "log.h"
@@ -15,19 +16,15 @@ void destroy_window();
void async_lock();
void async_unlock();
void win32_async_swap();
void win32_renderdoc_frame_start();
void win32_renderdoc_frame_end();
void win32_update_fps(int frames);
void win32_update_stylus(float dt);
void win32_save_window_state();
bool win32_vr_start();
void win32_vr_stop();
std::string GetLastErrorAsString();
HWND pp_windows_main_window_handle();
void pp_windows_enqueue_main_task(std::packaged_task<void()> task);
namespace pp::platform::windows {
namespace {
struct RetainedWin32AsyncRenderContextState final {
@@ -43,8 +40,7 @@ struct RetainedWin32AsyncRenderContextState final {
static RetainedWin32AsyncRenderContextState state;
return state;
}
}
} // namespace
void set_async_render_context(HDC hdc, HGLRC hrc)
{
@@ -63,7 +59,7 @@ void lock_async_render_context()
state.gl_mutex.lock();
const bool ret = wglMakeCurrent(state.hdc, state.hrc);
if (!ret)
LOG("FAILED wglMakeCurrent: %s", GetLastErrorAsString().c_str());
LOG("FAILED wglMakeCurrent: %s", pp::platform::windows::GetLastErrorAsString().c_str());
state.gl_thread = std::this_thread::get_id();
}
state.gl_count++;
@@ -78,7 +74,7 @@ bool try_lock_async_render_context()
return false;
const bool ret = wglMakeCurrent(state.hdc, state.hrc);
if (!ret)
LOG("FAILED wglMakeCurrent: %s", GetLastErrorAsString().c_str());
LOG("FAILED wglMakeCurrent: %s", pp::platform::windows::GetLastErrorAsString().c_str());
state.gl_thread = std::this_thread::get_id();
}
state.gl_count++;
@@ -100,8 +96,7 @@ void swap_async_render_context()
{
SwapBuffers(retained_win32_async_render_context_state().hdc);
}
}
} // namespace pp::platform::windows
namespace {
@@ -438,12 +433,12 @@ public:
void begin_render_capture_frame() override
{
win32_renderdoc_frame_start();
pp::platform::windows::win32_renderdoc_frame_start();
}
void end_render_capture_frame() override
{
win32_renderdoc_frame_end();
pp::platform::windows::win32_renderdoc_frame_end();
}
[[nodiscard]] bool deletes_recorded_files_on_clear() override

View File

@@ -1,5 +1,6 @@
#include "pch.h"
#include "platform_windows/windows_splash.h"
#include "platform_windows/windows_bootstrap_helpers.h"
#include "app.h"
#include "image.h"
@@ -13,8 +14,6 @@
#include <string>
#include <stop_token>
extern HRESULT(*GetDpiForMonitor_fn)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY);
namespace {
constexpr UINT kSplashCloseMessage = WM_USER + 1;
@@ -42,8 +41,8 @@ LRESULT CALLBACK splash_proc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lPara
auto monitor = MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY);
auto x = unsigned{ 96 };
auto y = unsigned{ 96 };
if (GetDpiForMonitor_fn)
GetDpiForMonitor_fn(monitor, MDT_EFFECTIVE_DPI, &x, &y);
if (pp::platform::windows::GetDpiForMonitor_fn)
pp::platform::windows::GetDpiForMonitor_fn(monitor, MDT_EFFECTIVE_DPI, &x, &y);
float z = (float)x / 96.f;
static char base_path[MAX_PATH];