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 <timemanager.rick@gmail.com>
This commit is contained in:
Ian Bassi
2026-05-17 02:31:51 -03:00
committed by GitHub
parent b3fe733bf2
commit 873d0ed26e
12 changed files with 393 additions and 7 deletions

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -230,6 +230,29 @@ void AppConfig::set_defaults()
if (get("camera_orbit_mult").empty()) if (get("camera_orbit_mult").empty())
set("camera_orbit_mult", "1.0"); 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()) if (get("export_sources_full_pathnames").empty())
set_bool("export_sources_full_pathnames", false); set_bool("export_sources_full_pathnames", false);

View File

@@ -30,6 +30,10 @@ using namespace nlohmann;
#define SETTING_NETWORK_PLUGIN_REMIND_LATER "network_plugin_remind_later" #define SETTING_NETWORK_PLUGIN_REMIND_LATER "network_plugin_remind_later"
#define SETTING_USE_ENCRYPTED_TOKEN_FILE "use_encrypted_token_file" #define SETTING_USE_ENCRYPTED_TOKEN_FILE "use_encrypted_token_file"
#define SETTING_CLOUD_PROVIDERS "cloud_providers" #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) #if defined(_WIN32) || defined(_WIN64)
#define BAMBU_NETWORK_AGENT_VERSION_LEGACY "01.10.01.09" #define BAMBU_NETWORK_AGENT_VERSION_LEGACY "01.10.01.09"

View File

@@ -14,6 +14,7 @@
#include "libslic3r/Technologies.hpp" #include "libslic3r/Technologies.hpp"
#include "libslic3r/Tesselate.hpp" #include "libslic3r/Tesselate.hpp"
#include "libslic3r/PresetBundle.hpp" #include "libslic3r/PresetBundle.hpp"
#include "libslic3r/AppConfig.hpp"
#include "3DScene.hpp" #include "3DScene.hpp"
#include "BackgroundSlicingProcess.hpp" #include "BackgroundSlicingProcess.hpp"
#include "GLShader.hpp" #include "GLShader.hpp"
@@ -1202,6 +1203,11 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed)
GLCanvas3D::~GLCanvas3D() GLCanvas3D::~GLCanvas3D()
{ {
if (m_fxaa_texture_id != 0 && _set_current()) {
glsafe(::glDeleteTextures(1, &m_fxaa_texture_id));
m_fxaa_texture_id = 0;
}
reset_volumes(); reset_volumes();
m_sel_plate_toolbar.del_all_item(); 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()) if (m_picking_enabled && m_rectangle_selection.is_dragging())
m_rectangle_selection.render(*this); m_rectangle_selection.render(*this);
if (_is_fxaa_enabled())
_render_fxaa_pass(static_cast<unsigned int>(cnv_size.get_width()), static_cast<unsigned int>(cnv_size.get_height()));
// draw overlays // draw overlays
_render_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()) { if (wxGetApp().plater()->is_render_statistic_dialog_visible()) {
ImGui::ShowMetricsWindow(); 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.begin(std::string("Render statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
imgui.text("FPS (SwapBuffers() calls per second):"); imgui.text("FPS (SwapBuffers() calls per second):");
ImGui::SameLine(); 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::Separator();
imgui.text("Compressed textures:"); imgui.text("Compressed textures:");
ImGui::SameLine(); ImGui::SameLine();
@@ -3194,6 +3207,22 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
wxGetApp().imgui()->reset_requires_extra_frame(); wxGetApp().imgui()->reset_requires_extra_frame();
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT #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<double>(1.0 / static_cast<double>(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<int>(std::ceil(std::chrono::duration<double, std::milli>(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(); _refresh_if_shown_on_screen();
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT #if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
@@ -7422,6 +7451,105 @@ void GLCanvas3D::_rectangular_selection_picking_pass()
_update_volumes_hover_state(); _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<float>(width), 1.0f / static_cast<float>(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() void GLCanvas3D::_render_background()
{ {
bool use_error_color = false; bool use_error_color = false;

View File

@@ -607,6 +607,7 @@ private:
bool m_reload_delayed; bool m_reload_delayed;
RenderStats m_render_stats; RenderStats m_render_stats;
std::chrono::time_point<std::chrono::steady_clock> m_last_frame_start_time{ std::chrono::steady_clock::now() };
int m_imgui_undo_redo_hovered_pos{ -1 }; int m_imgui_undo_redo_hovered_pos{ -1 };
int m_mouse_wheel{ 0 }; int m_mouse_wheel{ 0 };
@@ -724,6 +725,8 @@ public:
CameraTarget m_camera_target; CameraTarget m_camera_target;
#endif // ENABLE_SHOW_CAMERA_TARGET #endif // ENABLE_SHOW_CAMERA_TARGET
GLModel m_background; GLModel m_background;
unsigned int m_fxaa_texture_id{ 0 };
std::array<unsigned int, 2> m_fxaa_texture_size{ 0, 0 };
public: public:
explicit GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed); explicit GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed);
~GLCanvas3D(); ~GLCanvas3D();
@@ -1231,6 +1234,11 @@ private:
void _picking_pass(); void _picking_pass();
void _rectangular_selection_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_background();
void _render_bed(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_axes); void _render_bed(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_axes);
//BBS: add part plate related logic //BBS: add part plate related logic

View File

@@ -48,6 +48,8 @@ std::pair<bool, std::string> GLShadersManager::init()
valid &= append_shader("flat_clip", { prefix + "flat_clip.vs", prefix + "flat_clip.fs" }); valid &= append_shader("flat_clip", { prefix + "flat_clip.vs", prefix + "flat_clip.fs" });
// basic shader for textures, used to render textures // basic shader for textures, used to render textures
valid &= append_shader("flat_texture", { prefix + "flat_texture.vs", prefix + "flat_texture.fs" }); 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 // used to render 3D scene background
valid &= append_shader("background", { prefix + "background.vs", prefix + "background.fs" }); valid &= append_shader("background", { prefix + "background.vs", prefix + "background.fs" });
#if SLIC3R_OPENGL_ES #if SLIC3R_OPENGL_ES

View File

@@ -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) 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; bool need_recreate_gui = false;
std::string pending_language; 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()) { if (!pending_language.empty()) {
const std::string previous_language = app_config->get("language"); const std::string previous_language = app_config->get("language");
app_config->set("language", pending_language); app_config->set("language", pending_language);

View File

@@ -5,6 +5,7 @@
#include "I18N.hpp" #include "I18N.hpp"
#include "3DScene.hpp" #include "3DScene.hpp"
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/Platform.hpp" #include "libslic3r/Platform.hpp"
#include <glad/gl.h> #include <glad/gl.h>
@@ -408,6 +409,13 @@ wxGLContext* OpenGLManager::init_glcontext(wxGLCanvas& canvas, const std::pair<i
wxGLCanvas* OpenGLManager::create_wxglcanvas(wxWindow& parent) wxGLCanvas* OpenGLManager::create_wxglcanvas(wxWindow& parent)
{ {
int antialiasing_samples = 4;
if (const AppConfig* app_config = get_app_config(); app_config != nullptr) {
const std::string value = app_config->get(SETTING_OPENGL_AA_SAMPLES);
if (value == "0" || value == "2" || value == "4" || value == "8" || value == "16")
antialiasing_samples = ::atoi(value.c_str());
}
int attribList[] = { int attribList[] = {
WX_GL_RGBA, WX_GL_RGBA,
WX_GL_DOUBLEBUFFER, WX_GL_DOUBLEBUFFER,
@@ -421,19 +429,26 @@ wxGLCanvas* OpenGLManager::create_wxglcanvas(wxWindow& parent)
WX_GL_DEPTH_SIZE, 24, WX_GL_DEPTH_SIZE, 24,
//BBS: turn on stencil buffer for outline //BBS: turn on stencil buffer for outline
WX_GL_STENCIL_SIZE, 8, WX_GL_STENCIL_SIZE, 8,
WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLE_BUFFERS, antialiasing_samples > 0 ? GL_TRUE : GL_FALSE,
WX_GL_SAMPLES, 4, WX_GL_SAMPLES, antialiasing_samples,
0 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); detect_multisample(attribList);
// // debug output // // debug output
// std::cout << "Multisample " << (can_multisample() ? "enabled" : "disabled") << std::endl; // std::cout << "Multisample " << (can_multisample() ? "enabled" : "disabled") << std::endl;
} }
if (! can_multisample()) if (! can_multisample()) {
attribList[12] = 0; attribList[sample_buffers_value_idx] = GL_FALSE;
attribList[samples_value_idx] = 0;
}
wxGLCanvas* canvas = new wxGLCanvas(&parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS); wxGLCanvas* canvas = new wxGLCanvas(&parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS);
// The GL canvas paints its entire surface, so background erasing is unnecessary. // The GL canvas paints its entire surface, so background erasing is unnecessary.

View File

@@ -114,7 +114,8 @@ wxBoxSizer *PreferencesDialog::create_item_combobox(wxString title, wxString too
if (!current_setting.empty()) { if (!current_setting.empty()) {
auto compare = [current_setting](string possible_setting) { return current_setting == possible_setting; }; 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); 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<unsigned int>(iterator - config_name_index.begin());
} }
auto [sizer, combobox] = create_item_combobox_base(title, tooltip, param, vlist, current_index); 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)); g_sizer->AddSpacer(FromDIP(10));
sizer_page->Add(g_sizer, 0, wxEXPAND); 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 //// ONLINE TAB
///////////////////////////////////// /////////////////////////////////////