mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-07 15:51:08 +00:00
* Remove Unused Project Includes and Forward-Declare Where a Type Is Only Referenced Generated with include-what-you-use and applied conservatively. Only OrcaSlicer's own headers, the ones under src/ and tests/, are removed or forward-declared; standard-library and third-party includes are left alone. An include is removed only when both the Release and the Debug configuration leave it unused, never from inside a conditional block, and never from a file with platform-specific blocks, which only gain includes. Files whose only use of a header sits behind a feature or debug macro (libvgcode's OpenGL ES and marker code, the ARACHNE/TESTS_EXPORT_SVGS debug output) keep their includes. clonable_ptr.hpp gains #pragma once; it had no include guard and was only safe while Config.hpp was its sole includer. * Remove Unused Project Includes From Files With Platform-Specific Code A Linux include-what-you-use run cannot see the code inside _WIN32, __APPLE__ or __linux__ blocks, so its verdict is only taken where nothing the removed header declares, directly or through what it includes, is named inside those blocks. Removals also have to hold in both the Release and Debug configuration and never touch a line inside a conditional block. * Restore the libslic3r Precompiled Header and Direct Includes Lost in the Platform Pass The platform-file pass treated pchheader.hpp as an ordinary header and emptied it, and left GUI_Preview.hpp and 14 other files relying on headers they no longer reached directly. * Restore MainFrame.hpp in ParamsDialog.cpp for the Windows-Only Reparent Call * Include Headers That Files Reached Through Ones the Cleanup Removed * Drop Includes Duplicated by the Cleanup or by Main's Own Additions * Leave PreciseSeam.cpp as Main Has It After the Precise Seam Rework
510 lines
19 KiB
C++
510 lines
19 KiB
C++
#include "libslic3r/libslic3r.h"
|
|
#include "OpenGLManager.hpp"
|
|
|
|
#include "GUI.hpp"
|
|
#include "I18N.hpp"
|
|
#include "3DScene.hpp"
|
|
|
|
#include "libslic3r/AppConfig.hpp"
|
|
#include "libslic3r/Platform.hpp"
|
|
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
#include <algorithm>
|
|
#include <boost/algorithm/string/constants.hpp>
|
|
#include <cstdlib>
|
|
#include <glad/gl.h>
|
|
|
|
#include <boost/algorithm/string/split.hpp>
|
|
#include <boost/algorithm/string/classification.hpp>
|
|
#include <boost/log/trivial.hpp>
|
|
|
|
#include <string>
|
|
#include <ostream>
|
|
#include "libslic3r/Utils.hpp"
|
|
#include <vector>
|
|
#include <sstream>
|
|
#include <utility>
|
|
#include <wx/gdicmn.h>
|
|
#include <wx/glcanvas.h>
|
|
#include <wx/log.h>
|
|
#include <wx/msgdlg.h>
|
|
#include <wx/string.h>
|
|
#include <wx/version.h>
|
|
|
|
#include "GUI_Init.hpp"
|
|
#include "slic3r/GUI/GLShadersManager.hpp"
|
|
|
|
#ifdef __APPLE__
|
|
#include "../Utils/MacDarkMode.hpp"
|
|
#endif // __APPLE__
|
|
|
|
#ifdef __WXGTK__
|
|
#include "LinuxDisplayBackend.hpp"
|
|
#ifdef wxHAS_EGL
|
|
#include <EGL/egl.h>
|
|
#endif
|
|
#endif
|
|
|
|
namespace Slic3r {
|
|
namespace GUI {
|
|
|
|
// A safe wrapper around glGetString to report a "N/A" string in case glGetString returns nullptr.
|
|
std::string gl_get_string_safe(GLenum param, const std::string& default_value)
|
|
{
|
|
const char* value = (const char*)::glGetString(param);
|
|
return std::string((value != nullptr) ? value : default_value);
|
|
}
|
|
|
|
const std::string& OpenGLManager::GLInfo::get_version() const
|
|
{
|
|
if (!m_detected)
|
|
detect();
|
|
|
|
return m_version;
|
|
}
|
|
|
|
const std::string& OpenGLManager::GLInfo::get_glsl_version() const
|
|
{
|
|
if (!m_detected)
|
|
detect();
|
|
|
|
return m_glsl_version;
|
|
}
|
|
|
|
const std::string& OpenGLManager::GLInfo::get_vendor() const
|
|
{
|
|
if (!m_detected)
|
|
detect();
|
|
|
|
return m_vendor;
|
|
}
|
|
|
|
const std::string& OpenGLManager::GLInfo::get_renderer() const
|
|
{
|
|
if (!m_detected)
|
|
detect();
|
|
|
|
return m_renderer;
|
|
}
|
|
|
|
bool OpenGLManager::GLInfo::is_mesa() const
|
|
{
|
|
return boost::icontains(m_version, "mesa");
|
|
}
|
|
|
|
int OpenGLManager::GLInfo::get_max_tex_size() const
|
|
{
|
|
if (!m_detected)
|
|
detect();
|
|
|
|
// clamp to avoid the texture generation become too slow and use too much GPU memory
|
|
#ifdef __APPLE__
|
|
// and use smaller texture for non retina systems
|
|
return (Slic3r::GUI::mac_max_scaling_factor() > 1.0) ? std::min(m_max_tex_size, 8192) : std::min(m_max_tex_size / 2, 4096);
|
|
#else
|
|
// and use smaller texture for older OpenGL versions
|
|
return is_version_greater_or_equal_to(3, 0) ? std::min(m_max_tex_size, 8192) : std::min(m_max_tex_size / 2, 4096);
|
|
#endif // __APPLE__
|
|
}
|
|
|
|
float OpenGLManager::GLInfo::get_max_anisotropy() const
|
|
{
|
|
if (!m_detected)
|
|
detect();
|
|
|
|
return m_max_anisotropy;
|
|
}
|
|
|
|
void OpenGLManager::GLInfo::detect() const
|
|
{
|
|
*const_cast<std::string*>(&m_version) = gl_get_string_safe(GL_VERSION, "N/A");
|
|
*const_cast<std::string*>(&m_glsl_version) = gl_get_string_safe(GL_SHADING_LANGUAGE_VERSION, "N/A");
|
|
*const_cast<std::string*>(&m_vendor) = gl_get_string_safe(GL_VENDOR, "N/A");
|
|
*const_cast<std::string*>(&m_renderer) = gl_get_string_safe(GL_RENDERER, "N/A");
|
|
|
|
BOOST_LOG_TRIVIAL(info) << boost::format("got opengl version %1%, glsl version %2%, vendor %3% , graphics card model %4%") % m_version % m_glsl_version % m_vendor % m_renderer << std::endl;
|
|
|
|
int* max_tex_size = const_cast<int*>(&m_max_tex_size);
|
|
glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, max_tex_size));
|
|
|
|
*max_tex_size /= 2;
|
|
|
|
if (Slic3r::total_physical_memory() / (1024 * 1024 * 1024) < 6)
|
|
*max_tex_size /= 2;
|
|
|
|
if (GLAD_GL_EXT_texture_filter_anisotropic) {
|
|
float* max_anisotropy = const_cast<float*>(&m_max_anisotropy);
|
|
glsafe(::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
|
|
}
|
|
|
|
if (!GLAD_GL_ARB_compatibility)
|
|
*const_cast<bool*>(&m_core_profile) = true;
|
|
|
|
*const_cast<bool*>(&m_detected) = true;
|
|
}
|
|
|
|
static bool version_greater_or_equal_to(const std::string& version, unsigned int major, unsigned int minor)
|
|
{
|
|
if (version == "N/A")
|
|
return false;
|
|
|
|
std::vector<std::string> tokens;
|
|
boost::split(tokens, version, boost::is_any_of(" "), boost::token_compress_on);
|
|
|
|
if (tokens.empty())
|
|
return false;
|
|
|
|
std::vector<std::string> numbers;
|
|
boost::split(numbers, tokens[0], boost::is_any_of("."), boost::token_compress_on);
|
|
|
|
unsigned int gl_major = 0;
|
|
unsigned int gl_minor = 0;
|
|
|
|
if (numbers.size() > 0)
|
|
gl_major = ::atoi(numbers[0].c_str());
|
|
|
|
if (numbers.size() > 1)
|
|
gl_minor = ::atoi(numbers[1].c_str());
|
|
|
|
if (gl_major < major)
|
|
return false;
|
|
else if (gl_major > major)
|
|
return true;
|
|
else
|
|
return gl_minor >= minor;
|
|
}
|
|
|
|
bool OpenGLManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const
|
|
{
|
|
if (!m_detected)
|
|
detect();
|
|
|
|
return version_greater_or_equal_to(m_version, major, minor);
|
|
}
|
|
|
|
bool OpenGLManager::GLInfo::is_glsl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const
|
|
{
|
|
if (!m_detected)
|
|
detect();
|
|
|
|
return version_greater_or_equal_to(m_glsl_version, major, minor);
|
|
}
|
|
|
|
// If formatted for github, plaintext with OpenGL extensions enclosed into <details>.
|
|
// Otherwise HTML formatted for the system info dialog.
|
|
std::string OpenGLManager::GLInfo::to_string(bool for_github) const
|
|
{
|
|
if (!m_detected)
|
|
detect();
|
|
|
|
std::stringstream out;
|
|
|
|
const bool format_as_html = ! for_github;
|
|
std::string h2_start = format_as_html ? "<b>" : "";
|
|
std::string h2_end = format_as_html ? "</b>" : "";
|
|
std::string b_start = format_as_html ? "<b>" : "";
|
|
std::string b_end = format_as_html ? "</b>" : "";
|
|
std::string line_end = format_as_html ? "<br>" : "\n";
|
|
|
|
out << h2_start << "OpenGL installation" << h2_end << line_end;
|
|
out << b_start << "GL version: " << b_end << m_version << line_end;
|
|
#if !SLIC3R_OPENGL_ES
|
|
out << b_start << "Profile: " << b_end << (is_core_profile() ? "Core" : "Compatibility") << line_end;
|
|
#endif // !SLIC3R_OPENGL_ES
|
|
out << b_start << "Vendor: " << b_end << m_vendor << line_end;
|
|
out << b_start << "Renderer: " << b_end << m_renderer << line_end;
|
|
out << b_start << "GLSL version: " << b_end << m_glsl_version << line_end;
|
|
|
|
{
|
|
std::vector<std::string> extensions_list;
|
|
std::string extensions_str = gl_get_string_safe(GL_EXTENSIONS, "");
|
|
boost::split(extensions_list, extensions_str, boost::is_any_of(" "), boost::token_compress_on);
|
|
|
|
if (!extensions_list.empty()) {
|
|
if (for_github)
|
|
out << "<details>\n<summary>Installed extensions:</summary>\n";
|
|
else
|
|
out << h2_start << "Installed extensions:" << h2_end << line_end;
|
|
|
|
std::sort(extensions_list.begin(), extensions_list.end());
|
|
for (const std::string& ext : extensions_list)
|
|
if (! ext.empty())
|
|
out << ext << line_end;
|
|
|
|
if (for_github)
|
|
out << "</details>\n";
|
|
}
|
|
}
|
|
|
|
return out.str();
|
|
}
|
|
|
|
OpenGLManager::GLInfo OpenGLManager::s_gl_info;
|
|
bool OpenGLManager::s_compressed_textures_supported = false;
|
|
bool OpenGLManager::s_force_power_of_two_textures = false;
|
|
OpenGLManager::EMultisampleState OpenGLManager::s_multisample = OpenGLManager::EMultisampleState::Unknown;
|
|
OpenGLManager::EFramebufferType OpenGLManager::s_framebuffers_type = OpenGLManager::EFramebufferType::Unknown;
|
|
|
|
OpenGLManager::~OpenGLManager()
|
|
{
|
|
m_shaders_manager.shutdown();
|
|
|
|
if (m_context != nullptr)
|
|
delete m_context;
|
|
}
|
|
|
|
bool OpenGLManager::init_gl(bool popup_error)
|
|
{
|
|
if (!m_gl_initialized) {
|
|
int version = 0;
|
|
#if defined(__WXGTK__) && defined(wxHAS_EGL)
|
|
if (is_running_on_wayland()) {
|
|
// On EGL/Wayland, gladLoaderLoadGL() dlopen's libGL.so then
|
|
// immediately dlclose's it. Since nothing else holds libGL.so
|
|
// open (unlike GLX where the context keeps it loaded), the
|
|
// library gets unmapped and all function pointers become invalid.
|
|
// Use eglGetProcAddress directly to avoid this.
|
|
version = gladLoadGL((GLADloadfunc)eglGetProcAddress);
|
|
} else
|
|
#endif
|
|
{
|
|
version = gladLoaderLoadGL();
|
|
}
|
|
if (version == 0) {
|
|
BOOST_LOG_TRIVIAL(error) << "Unable to init GLAD OpenGL loader";
|
|
return false;
|
|
}
|
|
BOOST_LOG_TRIVIAL(info) << "GLAD loaded OpenGL " << GLAD_VERSION_MAJOR(version) << "." << GLAD_VERSION_MINOR(version);
|
|
m_gl_initialized = true;
|
|
if (GLAD_GL_EXT_texture_compression_s3tc)
|
|
s_compressed_textures_supported = true;
|
|
else
|
|
s_compressed_textures_supported = false;
|
|
|
|
if (s_gl_info.is_version_greater_or_equal_to(3, 0)) {
|
|
// ARB framebuffer became a mandatory part of core OpenGL 3.0
|
|
s_framebuffers_type = EFramebufferType::Arb;
|
|
BOOST_LOG_TRIVIAL(info) << "Opengl version >= 30, FrameBuffer Type ARB." << std::endl;
|
|
}
|
|
else if (GLAD_GL_ARB_framebuffer_object) {
|
|
s_framebuffers_type = EFramebufferType::Arb;
|
|
BOOST_LOG_TRIVIAL(info) << "Found Framebuffer Type ARB."<< std::endl;
|
|
}
|
|
else if (GLAD_GL_EXT_framebuffer_object) {
|
|
BOOST_LOG_TRIVIAL(info) << "Found Framebuffer Type Ext."<< std::endl;
|
|
s_framebuffers_type = EFramebufferType::Ext;
|
|
}
|
|
else {
|
|
s_framebuffers_type = EFramebufferType::Unknown;
|
|
BOOST_LOG_TRIVIAL(warning) << "Found Framebuffer Type unknown!"<< std::endl;
|
|
}
|
|
|
|
bool valid_version = s_gl_info.is_version_greater_or_equal_to(2, 0);
|
|
if (!valid_version) {
|
|
BOOST_LOG_TRIVIAL(error) << "Found opengl version < 2.0"<< std::endl;
|
|
// Complain about the OpenGL version.
|
|
if (popup_error) {
|
|
wxString message = from_u8((boost::format(
|
|
_utf8(L("The application cannot run normally because OpenGL version is lower than 3.2.\n")))).str());
|
|
message += "\n";
|
|
message += _L("Please upgrade your graphics card driver.");
|
|
wxMessageBox(message, _L("Unsupported OpenGL version"), wxOK | wxICON_ERROR);
|
|
}
|
|
}
|
|
|
|
if (valid_version)
|
|
{
|
|
// load shaders
|
|
auto [result, error] = m_shaders_manager.init();
|
|
if (!result) {
|
|
BOOST_LOG_TRIVIAL(error) << "Unable to load shaders: "<<error<< std::endl;
|
|
if (popup_error) {
|
|
wxString message = from_u8((boost::format(
|
|
_utf8(L("Unable to load shaders:\n%s"))) % error).str());
|
|
wxMessageBox(message, _L("Error loading shaders"), wxOK | wxICON_ERROR);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
// Since AMD driver version 22.7.1, there is probably some bug in the driver that causes the issue with the missing
|
|
// texture of the bed (see: https://github.com/prusa3d/PrusaSlicer/issues/8417).
|
|
// It seems that this issue only triggers when mipmaps are generated manually
|
|
// (combined with a texture compression) with texture size not being power of two.
|
|
// When mipmaps are generated through OpenGL function glGenerateMipmap() the driver works fine,
|
|
// but the mipmap generation is quite slow on some machines.
|
|
// There is no an easy way to detect the driver version without using Win32 API because the strings returned by OpenGL
|
|
// have no standardized format, only some of them contain the driver version.
|
|
// Until we do not know that driver will be fixed (if ever) we force the use of power of two textures on all cards
|
|
// 1) containing the string 'Radeon' in the string returned by glGetString(GL_RENDERER)
|
|
// 2) containing the string 'Custom' in the string returned by glGetString(GL_RENDERER)
|
|
const auto& gl_info = OpenGLManager::get_gl_info();
|
|
if (boost::contains(gl_info.get_vendor(), "ATI Technologies Inc.") &&
|
|
(boost::contains(gl_info.get_renderer(), "Radeon") ||
|
|
boost::contains(gl_info.get_renderer(), "Custom")))
|
|
s_force_power_of_two_textures = true;
|
|
#endif // _WIN32
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
wxGLContext* OpenGLManager::init_glcontext(wxGLCanvas& canvas, const std::pair<int, int>& required_opengl_version, bool enable_compatibility_profile,
|
|
bool enable_debug)
|
|
{
|
|
if (m_context == nullptr) {
|
|
// m_debug_enabled = enable_debug;
|
|
|
|
const int gl_major = required_opengl_version.first;
|
|
const int gl_minor = required_opengl_version.second;
|
|
const bool supports_core_profile =
|
|
std::find(OpenGLVersions::core.begin(), OpenGLVersions::core.end(), std::make_pair(gl_major, gl_minor)) != OpenGLVersions::core.end();
|
|
|
|
if (gl_major == 0 && !enable_compatibility_profile) {
|
|
// search for highest supported core profile version
|
|
// disable wxWidgets logging to avoid showing the log dialog in case the following code fails generating a valid gl context
|
|
wxLogNull logNo;
|
|
for (auto v = OpenGLVersions::core.rbegin(); v != OpenGLVersions::core.rend(); ++v) {
|
|
wxGLContextAttrs attrs;
|
|
attrs.PlatformDefaults().MajorVersion(v->first).MinorVersion(v->second).CoreProfile().ForwardCompatible();
|
|
// if (m_debug_enabled)
|
|
// attrs.DebugCtx();
|
|
attrs.EndList();
|
|
m_context = new wxGLContext(&canvas, nullptr, &attrs);
|
|
if (m_context->IsOK())
|
|
break;
|
|
else {
|
|
delete m_context;
|
|
m_context = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_context == nullptr) {
|
|
// search for requested compatibility profile version
|
|
if (enable_compatibility_profile) {
|
|
// disable wxWidgets logging to avoid showing the log dialog in case the following code fails generating a valid gl context
|
|
wxLogNull logNo;
|
|
wxGLContextAttrs attrs;
|
|
attrs.PlatformDefaults().CompatibilityProfile();
|
|
// if (m_debug_enabled)
|
|
// attrs.DebugCtx();
|
|
attrs.EndList();
|
|
m_context = new wxGLContext(&canvas, nullptr, &attrs);
|
|
if (!m_context->IsOK()) {
|
|
delete m_context;
|
|
m_context = nullptr;
|
|
}
|
|
}
|
|
// search for requested core profile version
|
|
else if (supports_core_profile) {
|
|
// disable wxWidgets logging to avoid showing the log dialog in case the following code fails generating a valid gl context
|
|
wxLogNull logNo;
|
|
wxGLContextAttrs attrs;
|
|
attrs.PlatformDefaults().MajorVersion(gl_major).MinorVersion(gl_minor).CoreProfile().ForwardCompatible();
|
|
// if (m_debug_enabled)
|
|
// attrs.DebugCtx();
|
|
attrs.EndList();
|
|
m_context = new wxGLContext(&canvas, nullptr, &attrs);
|
|
if (!m_context->IsOK()) {
|
|
delete m_context;
|
|
m_context = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_context == nullptr) {
|
|
wxGLContextAttrs attrs;
|
|
attrs.PlatformDefaults();
|
|
// if (m_debug_enabled)
|
|
// attrs.DebugCtx();
|
|
attrs.EndList();
|
|
// if no valid context was created use the default one
|
|
m_context = new wxGLContext(&canvas, nullptr, &attrs);
|
|
}
|
|
|
|
}
|
|
return m_context;
|
|
}
|
|
|
|
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[] = {
|
|
WX_GL_RGBA,
|
|
WX_GL_DOUBLEBUFFER,
|
|
// RGB channels each should be allocated with 8 bit depth. One should almost certainly get these bit depths by default.
|
|
WX_GL_MIN_RED, 8,
|
|
WX_GL_MIN_GREEN, 8,
|
|
WX_GL_MIN_BLUE, 8,
|
|
// Requesting an 8 bit alpha channel. Interestingly, the NVIDIA drivers would most likely work with some alpha plane, but glReadPixels would not return
|
|
// the alpha channel on NVIDIA if not requested when the GL context is created.
|
|
WX_GL_MIN_ALPHA, 8,
|
|
WX_GL_DEPTH_SIZE, 24,
|
|
//BBS: turn on stencil buffer for outline
|
|
WX_GL_STENCIL_SIZE, 8,
|
|
WX_GL_SAMPLE_BUFFERS, antialiasing_samples > 0 ? GL_TRUE : GL_FALSE,
|
|
WX_GL_SAMPLES, antialiasing_samples,
|
|
0
|
|
};
|
|
|
|
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[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.
|
|
canvas->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
|
return canvas;
|
|
}
|
|
|
|
void OpenGLManager::detect_multisample(int* attribList)
|
|
{
|
|
int wxVersion = wxMAJOR_VERSION * 10000 + wxMINOR_VERSION * 100 + wxRELEASE_NUMBER;
|
|
bool enable_multisample = wxVersion >= 30003;
|
|
|
|
#if defined(__WXGTK__)
|
|
// On Wayland, wxGLCanvas::IsDisplaySupported() requires the EGL backend.
|
|
// If wxWidgets was built without EGL, the GLX backend will crash trying
|
|
// to access a non-existent X11 display. Disable multisample in that case.
|
|
if (is_running_on_wayland()) {
|
|
#if !defined(wxHAS_EGL) || !wxHAS_EGL
|
|
BOOST_LOG_TRIVIAL(warning) << "Wayland without EGL: disabling multisample detection";
|
|
s_multisample = EMultisampleState::Disabled;
|
|
return;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
s_multisample =
|
|
enable_multisample &&
|
|
// Disable multi-sampling on ChromeOS, as the OpenGL virtualization swaps Red/Blue channels with multi-sampling enabled,
|
|
// at least on some platforms.
|
|
platform_flavor() != PlatformFlavor::LinuxOnChromium &&
|
|
wxGLCanvas::IsDisplaySupported(attribList)
|
|
? EMultisampleState::Enabled : EMultisampleState::Disabled;
|
|
// Alternative method: it was working on previous version of wxWidgets but not with the latest, at least on Windows
|
|
// s_multisample = enable_multisample && wxGLCanvas::IsExtensionSupported("WGL_ARB_multisample");
|
|
}
|
|
|
|
} // namespace GUI
|
|
} // namespace Slic3r
|