From 873d0ed26ec587707d71e2803a3bce7aa5d7bedd Mon Sep 17 00:00:00 2001 From: Ian Bassi Date: Sun, 17 May 2026 02:31:51 -0300 Subject: [PATCH] Graphics Preferences: Anti-aliasing + FPS (#13538) * Expose Antialiasing velues Expose Antialiasing multipliers. Default to 4 as current implementation but enables the user to disable it to improve performante or increase sampling to improve quality. * FXAA * Improve descriptions * Require restart when MSAA setting changes Detect changes to the OpenGL MSAA (multisample anti-aliasing) preference when opening Preferences and prompt the user to restart the application to apply the change. Adds a constant key for the MSAA setting, stores the previous value, checks for changes (similar to the existing FXAA handling), and shows a warning dialog explaining the restart will close the current project without saving. If the user accepts, recreate_GUI is invoked to apply the MSAA change immediately. * Revert "Require restart when MSAA setting changes" This reverts commit dde134d346c3849598c91d025d2faed1b51c8a22. * Menu and FPS options * Fix FPS limiter and remove VSYNC * Grouped FPS settings and mode up rigth fps counter --------- Co-authored-by: Noisyfox --- resources/shaders/110/fxaa.fs | 50 +++++++++++ resources/shaders/110/fxaa.vs | 15 ++++ resources/shaders/140/fxaa.fs | 52 +++++++++++ resources/shaders/140/fxaa.vs | 15 ++++ src/libslic3r/AppConfig.cpp | 23 +++++ src/libslic3r/AppConfig.hpp | 4 + src/slic3r/GUI/GLCanvas3D.cpp | 130 +++++++++++++++++++++++++++- src/slic3r/GUI/GLCanvas3D.hpp | 8 ++ src/slic3r/GUI/GLShadersManager.cpp | 2 + src/slic3r/GUI/GUI_App.cpp | 15 ++++ src/slic3r/GUI/OpenGLManager.cpp | 25 ++++-- src/slic3r/GUI/Preferences.cpp | 61 ++++++++++++- 12 files changed, 393 insertions(+), 7 deletions(-) create mode 100644 resources/shaders/110/fxaa.fs create mode 100644 resources/shaders/110/fxaa.vs create mode 100644 resources/shaders/140/fxaa.fs create mode 100644 resources/shaders/140/fxaa.vs diff --git a/resources/shaders/110/fxaa.fs b/resources/shaders/110/fxaa.fs new file mode 100644 index 0000000000..c4db1a36ad --- /dev/null +++ b/resources/shaders/110/fxaa.fs @@ -0,0 +1,50 @@ +#version 110 + +uniform sampler2D uniform_texture; +uniform vec2 inv_tex_size; + +varying vec2 tex_coord; + +void main() +{ + vec3 rgbNW = texture2D(uniform_texture, tex_coord + vec2(-1.0, -1.0) * inv_tex_size).rgb; + vec3 rgbNE = texture2D(uniform_texture, tex_coord + vec2(1.0, -1.0) * inv_tex_size).rgb; + vec3 rgbSW = texture2D(uniform_texture, tex_coord + vec2(-1.0, 1.0) * inv_tex_size).rgb; + vec3 rgbSE = texture2D(uniform_texture, tex_coord + vec2(1.0, 1.0) * inv_tex_size).rgb; + vec3 rgbM = texture2D(uniform_texture, tex_coord).rgb; + + vec3 luma_coeff = vec3(0.299, 0.587, 0.114); + float lumaNW = dot(rgbNW, luma_coeff); + float lumaNE = dot(rgbNE, luma_coeff); + float lumaSW = dot(rgbSW, luma_coeff); + float lumaSE = dot(rgbSE, luma_coeff); + float lumaM = dot(rgbM, luma_coeff); + + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); + + vec2 dir; + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + + const float FXAA_REDUCE_MIN = 1.0 / 128.0; + const float FXAA_REDUCE_MUL = 1.0 / 8.0; + const float FXAA_SPAN_MAX = 8.0; + + float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); + float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); + dir = min(vec2(FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX), dir * rcpDirMin)) * inv_tex_size; + + vec3 rgbA = 0.5 * ( + texture2D(uniform_texture, tex_coord + dir * (1.0 / 3.0 - 0.5)).rgb + + texture2D(uniform_texture, tex_coord + dir * (2.0 / 3.0 - 0.5)).rgb + ); + + vec3 rgbB = rgbA * 0.5 + 0.25 * ( + texture2D(uniform_texture, tex_coord + dir * -0.5).rgb + + texture2D(uniform_texture, tex_coord + dir * 0.5).rgb + ); + + float lumaB = dot(rgbB, luma_coeff); + gl_FragColor = (lumaB < lumaMin || lumaB > lumaMax) ? vec4(rgbA, 1.0) : vec4(rgbB, 1.0); +} diff --git a/resources/shaders/110/fxaa.vs b/resources/shaders/110/fxaa.vs new file mode 100644 index 0000000000..f066b5dc5f --- /dev/null +++ b/resources/shaders/110/fxaa.vs @@ -0,0 +1,15 @@ +#version 110 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; + +attribute vec3 v_position; +attribute vec2 v_tex_coord; + +varying vec2 tex_coord; + +void main() +{ + tex_coord = v_tex_coord; + gl_Position = projection_matrix * view_model_matrix * vec4(v_position, 1.0); +} diff --git a/resources/shaders/140/fxaa.fs b/resources/shaders/140/fxaa.fs new file mode 100644 index 0000000000..5b2c034ea7 --- /dev/null +++ b/resources/shaders/140/fxaa.fs @@ -0,0 +1,52 @@ +#version 140 + +uniform sampler2D uniform_texture; +uniform vec2 inv_tex_size; + +in vec2 tex_coord; + +out vec4 out_color; + +void main() +{ + vec3 rgbNW = texture(uniform_texture, tex_coord + vec2(-1.0, -1.0) * inv_tex_size).rgb; + vec3 rgbNE = texture(uniform_texture, tex_coord + vec2(1.0, -1.0) * inv_tex_size).rgb; + vec3 rgbSW = texture(uniform_texture, tex_coord + vec2(-1.0, 1.0) * inv_tex_size).rgb; + vec3 rgbSE = texture(uniform_texture, tex_coord + vec2(1.0, 1.0) * inv_tex_size).rgb; + vec3 rgbM = texture(uniform_texture, tex_coord).rgb; + + vec3 luma_coeff = vec3(0.299, 0.587, 0.114); + float lumaNW = dot(rgbNW, luma_coeff); + float lumaNE = dot(rgbNE, luma_coeff); + float lumaSW = dot(rgbSW, luma_coeff); + float lumaSE = dot(rgbSE, luma_coeff); + float lumaM = dot(rgbM, luma_coeff); + + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); + + vec2 dir; + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + + const float FXAA_REDUCE_MIN = 1.0 / 128.0; + const float FXAA_REDUCE_MUL = 1.0 / 8.0; + const float FXAA_SPAN_MAX = 8.0; + + float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); + float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); + dir = min(vec2(FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX), dir * rcpDirMin)) * inv_tex_size; + + vec3 rgbA = 0.5 * ( + texture(uniform_texture, tex_coord + dir * (1.0 / 3.0 - 0.5)).rgb + + texture(uniform_texture, tex_coord + dir * (2.0 / 3.0 - 0.5)).rgb + ); + + vec3 rgbB = rgbA * 0.5 + 0.25 * ( + texture(uniform_texture, tex_coord + dir * -0.5).rgb + + texture(uniform_texture, tex_coord + dir * 0.5).rgb + ); + + float lumaB = dot(rgbB, luma_coeff); + out_color = (lumaB < lumaMin || lumaB > lumaMax) ? vec4(rgbA, 1.0) : vec4(rgbB, 1.0); +} diff --git a/resources/shaders/140/fxaa.vs b/resources/shaders/140/fxaa.vs new file mode 100644 index 0000000000..5ca1517db0 --- /dev/null +++ b/resources/shaders/140/fxaa.vs @@ -0,0 +1,15 @@ +#version 140 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; + +in vec3 v_position; +in vec2 v_tex_coord; + +out vec2 tex_coord; + +void main() +{ + tex_coord = v_tex_coord; + gl_Position = projection_matrix * view_model_matrix * vec4(v_position, 1.0); +} diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 55918e86c2..33e25f5d9c 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -230,6 +230,29 @@ void AppConfig::set_defaults() if (get("camera_orbit_mult").empty()) set("camera_orbit_mult", "1.0"); + if (get(SETTING_OPENGL_AA_SAMPLES).empty()) + set(SETTING_OPENGL_AA_SAMPLES, "4"); + + if (get(SETTING_OPENGL_FXAA_ENABLED).empty()) + set_bool(SETTING_OPENGL_FXAA_ENABLED, false); + + if (get(SETTING_OPENGL_FPS_CAP).empty()) + set(SETTING_OPENGL_FPS_CAP, "0"); + else { + int fps_cap = 0; + try { + fps_cap = std::stoi(get(SETTING_OPENGL_FPS_CAP)); + } + catch (...) { + fps_cap = 0; + } + fps_cap = std::max(0, std::min(fps_cap, 240)); + set(SETTING_OPENGL_FPS_CAP, std::to_string(fps_cap)); + } + + if (get(SETTING_OPENGL_SHOW_FPS_OVERLAY).empty()) + set_bool(SETTING_OPENGL_SHOW_FPS_OVERLAY, false); + if (get("export_sources_full_pathnames").empty()) set_bool("export_sources_full_pathnames", false); diff --git a/src/libslic3r/AppConfig.hpp b/src/libslic3r/AppConfig.hpp index 06b919484f..33f243905a 100644 --- a/src/libslic3r/AppConfig.hpp +++ b/src/libslic3r/AppConfig.hpp @@ -30,6 +30,10 @@ using namespace nlohmann; #define SETTING_NETWORK_PLUGIN_REMIND_LATER "network_plugin_remind_later" #define SETTING_USE_ENCRYPTED_TOKEN_FILE "use_encrypted_token_file" #define SETTING_CLOUD_PROVIDERS "cloud_providers" +#define SETTING_OPENGL_AA_SAMPLES "opengl_antialiasing_samples" +#define SETTING_OPENGL_FXAA_ENABLED "opengl_fxaa_enabled" +#define SETTING_OPENGL_FPS_CAP "opengl_fps_cap" +#define SETTING_OPENGL_SHOW_FPS_OVERLAY "opengl_show_fps_overlay" #if defined(_WIN32) || defined(_WIN64) #define BAMBU_NETWORK_AGENT_VERSION_LEGACY "01.10.01.09" diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index d50fa8ed97..b630f46794 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -14,6 +14,7 @@ #include "libslic3r/Technologies.hpp" #include "libslic3r/Tesselate.hpp" #include "libslic3r/PresetBundle.hpp" +#include "libslic3r/AppConfig.hpp" #include "3DScene.hpp" #include "BackgroundSlicingProcess.hpp" #include "GLShader.hpp" @@ -1202,6 +1203,11 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed) GLCanvas3D::~GLCanvas3D() { + if (m_fxaa_texture_id != 0 && _set_current()) { + glsafe(::glDeleteTextures(1, &m_fxaa_texture_id)); + m_fxaa_texture_id = 0; + } + reset_volumes(); m_sel_plate_toolbar.del_all_item(); @@ -2082,9 +2088,16 @@ void GLCanvas3D::render(bool only_init) if (m_picking_enabled && m_rectangle_selection.is_dragging()) m_rectangle_selection.render(*this); + if (_is_fxaa_enabled()) + _render_fxaa_pass(static_cast(cnv_size.get_width()), static_cast(cnv_size.get_height())); + // draw overlays _render_overlays(); + const int current_fps = m_render_stats.get_fps_and_reset_if_needed(); + if (_is_fps_overlay_enabled()) + _render_fps_overlay(current_fps); + if (wxGetApp().plater()->is_render_statistic_dialog_visible()) { ImGui::ShowMetricsWindow(); @@ -2092,7 +2105,7 @@ void GLCanvas3D::render(bool only_init) imgui.begin(std::string("Render statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); imgui.text("FPS (SwapBuffers() calls per second):"); ImGui::SameLine(); - imgui.text(std::to_string(m_render_stats.get_fps_and_reset_if_needed())); + imgui.text(std::to_string(current_fps)); ImGui::Separator(); imgui.text("Compressed textures:"); ImGui::SameLine(); @@ -3194,6 +3207,22 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) wxGetApp().imgui()->reset_requires_extra_frame(); #endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT + const int fps_cap = _get_effective_fps_cap(); + if (fps_cap > 0) { + const auto now = std::chrono::steady_clock::now(); + const auto min_frame_time = std::chrono::duration(1.0 / static_cast(fps_cap)); + const auto elapsed = now - m_last_frame_start_time; + if (elapsed < min_frame_time) { + const int wait_ms = std::max(1, static_cast(std::ceil(std::chrono::duration(min_frame_time - elapsed).count()))); + schedule_extra_frame(wait_ms); + evt.RequestMore(); + return; + } + + // Pace by frame-start interval so rendering time is part of the target budget. + m_last_frame_start_time = now; + } + _refresh_if_shown_on_screen(); #if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT @@ -7422,6 +7451,105 @@ void GLCanvas3D::_rectangular_selection_picking_pass() _update_volumes_hover_state(); } +bool GLCanvas3D::_is_fxaa_enabled() const +{ + return wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_FXAA_ENABLED); +} + +int GLCanvas3D::_get_effective_fps_cap() const +{ + if (wxGetApp().app_config == nullptr) + return 0; + + int fps_cap = 0; + try { + fps_cap = std::stoi(wxGetApp().app_config->get(SETTING_OPENGL_FPS_CAP)); + } + catch (...) { + fps_cap = 0; + } + + fps_cap = std::max(0, std::min(fps_cap, 240)); + + return fps_cap; +} + +bool GLCanvas3D::_is_fps_overlay_enabled() const +{ + return wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_SHOW_FPS_OVERLAY); +} + +void GLCanvas3D::_render_fps_overlay(int fps) const +{ + if (fps < 0) + return; + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + const float margin = 10.0f * get_scale(); + const ImVec2 display_size = ImGui::GetIO().DisplaySize; + ImGui::SetNextWindowPos(ImVec2(display_size.x - margin, margin), ImGuiCond_Always, ImVec2(1.0f, 0.0f)); + ImGui::SetNextWindowBgAlpha(0.35f); + imgui.begin( + std::string("###fps_overlay"), + ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoCollapse | + ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_NoInputs); + imgui.text(std::string("FPS: ") + std::to_string(fps)); + imgui.end(); +} + +void GLCanvas3D::_render_fxaa_pass(unsigned int width, unsigned int height) +{ + if (width == 0 || height == 0) + return; + + GLShaderProgram* shader = wxGetApp().get_shader("fxaa"); + if (shader == nullptr) + return; + + if (m_fxaa_texture_id == 0) { + glsafe(::glGenTextures(1, &m_fxaa_texture_id)); + glsafe(::glBindTexture(GL_TEXTURE_2D, m_fxaa_texture_id)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); + } + + glsafe(::glBindTexture(GL_TEXTURE_2D, m_fxaa_texture_id)); + if (m_fxaa_texture_size[0] != width || m_fxaa_texture_size[1] != height) { + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr)); + m_fxaa_texture_size = { width, height }; + } + + glsafe(::glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width, height)); + + glsafe(::glDisable(GL_DEPTH_TEST)); + glsafe(::glDisable(GL_BLEND)); + glsafe(::glClear(GL_COLOR_BUFFER_BIT)); + + shader->start_using(); + shader->set_uniform("view_model_matrix", Transform3d::Identity()); + shader->set_uniform("projection_matrix", Transform3d::Identity()); + shader->set_uniform("uniform_texture", 0); + shader->set_uniform("inv_tex_size", Vec2f(1.0f / static_cast(width), 1.0f / static_cast(height))); + + glsafe(::glActiveTexture(GL_TEXTURE0)); + glsafe(::glBindTexture(GL_TEXTURE_2D, m_fxaa_texture_id)); + m_background.render(); + glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); + shader->stop_using(); + + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); +} + void GLCanvas3D::_render_background() { bool use_error_color = false; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index db0bb5181a..f6c42bdaa9 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -607,6 +607,7 @@ private: bool m_reload_delayed; RenderStats m_render_stats; + std::chrono::time_point m_last_frame_start_time{ std::chrono::steady_clock::now() }; int m_imgui_undo_redo_hovered_pos{ -1 }; int m_mouse_wheel{ 0 }; @@ -724,6 +725,8 @@ public: CameraTarget m_camera_target; #endif // ENABLE_SHOW_CAMERA_TARGET GLModel m_background; + unsigned int m_fxaa_texture_id{ 0 }; + std::array m_fxaa_texture_size{ 0, 0 }; public: explicit GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed); ~GLCanvas3D(); @@ -1231,6 +1234,11 @@ private: void _picking_pass(); void _rectangular_selection_picking_pass(); + bool _is_fxaa_enabled() const; + int _get_effective_fps_cap() const; + bool _is_fps_overlay_enabled() const; + void _render_fps_overlay(int fps) const; + void _render_fxaa_pass(unsigned int width, unsigned int height); void _render_background(); void _render_bed(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_axes); //BBS: add part plate related logic diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index 7f28d8f777..620fc2e773 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -48,6 +48,8 @@ std::pair GLShadersManager::init() valid &= append_shader("flat_clip", { prefix + "flat_clip.vs", prefix + "flat_clip.fs" }); // basic shader for textures, used to render textures valid &= append_shader("flat_texture", { prefix + "flat_texture.vs", prefix + "flat_texture.fs" }); + // used to apply post-processing antialiasing in screen space + valid &= append_shader("fxaa", { prefix + "fxaa.vs", prefix + "fxaa.fs" }); // used to render 3D scene background valid &= append_shader("background", { prefix + "background.vs", prefix + "background.fs" }); #if SLIC3R_OPENGL_ES diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index c12fa964f8..3169e53eee 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -7553,6 +7553,13 @@ void GUI_App::open_exportpresetbundledialog(size_t open_on_tab, const std::strin void GUI_App::open_preferences(size_t open_on_tab, const std::string& highlight_option) { + static constexpr const char* opengl_fxaa_setting_key = "opengl_fxaa_enabled"; + static constexpr const char* opengl_fps_cap_setting_key = "opengl_fps_cap"; + static constexpr const char* opengl_show_fps_overlay_setting_key = "opengl_show_fps_overlay"; + const std::string previous_opengl_fxaa = app_config->get(opengl_fxaa_setting_key); + const std::string previous_opengl_fps_cap = app_config->get(opengl_fps_cap_setting_key); + const std::string previous_opengl_show_fps_overlay = app_config->get(opengl_show_fps_overlay_setting_key); + bool need_recreate_gui = false; std::string pending_language; { @@ -7591,6 +7598,14 @@ void GUI_App::open_preferences(size_t open_on_tab, const std::string& highlight_ } } + const bool opengl_fxaa_changed = app_config->get(opengl_fxaa_setting_key) != previous_opengl_fxaa; + const bool opengl_fps_cap_changed = app_config->get(opengl_fps_cap_setting_key) != previous_opengl_fps_cap; + const bool opengl_show_fps_overlay_changed = app_config->get(opengl_show_fps_overlay_setting_key) != previous_opengl_show_fps_overlay; + if ((opengl_fxaa_changed || opengl_fps_cap_changed || opengl_show_fps_overlay_changed) && !need_recreate_gui && this->plater_ != nullptr) { + this->plater_->set_current_canvas_as_dirty(); + this->plater_->get_current_canvas3D()->force_set_focus(); + } + if (!pending_language.empty()) { const std::string previous_language = app_config->get("language"); app_config->set("language", pending_language); diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index 638b8b01f5..2b5ebcd086 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -5,6 +5,7 @@ #include "I18N.hpp" #include "3DScene.hpp" +#include "libslic3r/AppConfig.hpp" #include "libslic3r/Platform.hpp" #include @@ -408,6 +409,13 @@ wxGLContext* OpenGLManager::init_glcontext(wxGLCanvas& canvas, const std::pairget(SETTING_OPENGL_AA_SAMPLES); + if (value == "0" || value == "2" || value == "4" || value == "8" || value == "16") + antialiasing_samples = ::atoi(value.c_str()); + } + int attribList[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, @@ -421,19 +429,26 @@ wxGLCanvas* OpenGLManager::create_wxglcanvas(wxWindow& parent) WX_GL_DEPTH_SIZE, 24, //BBS: turn on stencil buffer for outline WX_GL_STENCIL_SIZE, 8, - WX_GL_SAMPLE_BUFFERS, GL_TRUE, - WX_GL_SAMPLES, 4, + WX_GL_SAMPLE_BUFFERS, antialiasing_samples > 0 ? GL_TRUE : GL_FALSE, + WX_GL_SAMPLES, antialiasing_samples, 0 }; - if (s_multisample == EMultisampleState::Unknown) { + constexpr int sample_buffers_value_idx = 15; + constexpr int samples_value_idx = 17; + + if (antialiasing_samples <= 0) + s_multisample = EMultisampleState::Disabled; + else if (s_multisample == EMultisampleState::Unknown) { detect_multisample(attribList); // // debug output // std::cout << "Multisample " << (can_multisample() ? "enabled" : "disabled") << std::endl; } - if (! can_multisample()) - attribList[12] = 0; + if (! can_multisample()) { + attribList[sample_buffers_value_idx] = GL_FALSE; + attribList[samples_value_idx] = 0; + } wxGLCanvas* canvas = new wxGLCanvas(&parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS); // The GL canvas paints its entire surface, so background erasing is unnecessary. diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index bf1c69a7c0..f061de7bb3 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -114,7 +114,8 @@ wxBoxSizer *PreferencesDialog::create_item_combobox(wxString title, wxString too if (!current_setting.empty()) { auto compare = [current_setting](string possible_setting) { return current_setting == possible_setting; }; auto iterator = find_if(config_name_index.begin(), config_name_index.end(), compare); - current_index = iterator - config_name_index.begin(); + if (iterator != config_name_index.end()) + current_index = static_cast(iterator - config_name_index.begin()); } auto [sizer, combobox] = create_item_combobox_base(title, tooltip, param, vlist, current_index); @@ -1526,6 +1527,64 @@ void PreferencesDialog::create_items() g_sizer->AddSpacer(FromDIP(10)); sizer_page->Add(g_sizer, 0, wxEXPAND); + ////////////////////////// + //// GRAPHICS TAB + ///////////////////////////////////// + m_pref_tabs->AppendItem(_L("Graphics")); + f_sizers.push_back(new wxFlexGridSizer(1, 1, v_gap, 0)); + g_sizer = f_sizers.back(); + g_sizer->AddGrowableCol(0, 1); + + //// GRAPHICS > Anti-aliasing + g_sizer->Add(create_item_title(_L("Anti-aliasing")), 1, wxEXPAND); + + auto item_antialiasing = create_item_combobox( + _L("MSAA Multiplier"), + _L("Set the Multi-Sample Anti-Aliasing level.\n" + "Higher values result in smoother edges, but the impact on performance is exponential.\n" + "Lower values improve performance, at the cost of jagged edges.\n" + "If disabled, its recommended to enable FXAA to reduce jagged edges with minimal performance impact.\n\n" + "Requires application restart."), + SETTING_OPENGL_AA_SAMPLES, + {_L("Disabled"), "2x", "4x", "8x", "16x"}, + {"0", "2", "4", "8", "16"} + ); + g_sizer->Add(item_antialiasing); + + auto item_fxaa = create_item_checkbox( + _L("FXAA post-processing"), + _L("Applies Fast Approximate Anti-Aliasing as a screen-space pass.\n" + "Useful for disabling or reducing the MSAA setting to improve performance.\n\n" + "Takes effect immediately."), + SETTING_OPENGL_FXAA_ENABLED + ); + g_sizer->Add(item_fxaa); + + //// GRAPHICS > FPS + g_sizer->Add(create_item_title(_L("FPS")), 1, wxEXPAND); + + auto item_fps_cap = create_item_spinctrl( + _L("FPS cap"), + _L("(0 = unlimited)"), + _L("FPS"), + _L("Limits viewport frame rate to reduce GPU load and power usage.\n" + "Set to 0 for unlimited frame rate."), + SETTING_OPENGL_FPS_CAP, + 0, + 240 + ); + g_sizer->Add(item_fps_cap); + + auto item_fps_overlay = create_item_checkbox( + _L("Show FPS overlay"), + _L("Displays current viewport FPS in the top-right corner."), + SETTING_OPENGL_SHOW_FPS_OVERLAY + ); + g_sizer->Add(item_fps_overlay); + + g_sizer->AddSpacer(FromDIP(10)); + sizer_page->Add(g_sizer, 0, wxEXPAND); + ////////////////////////// //// ONLINE TAB /////////////////////////////////////