Files
OrcaSlicer/src/slic3r/GUI/SceneBenchmark.cpp
T
HanifKoh 4895bc03b4 Remove Unused Project Includes and Forward-Declare Where a Type Is Only Referenced (#16099)
* 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
2026-10-05 16:47:17 +08:00

580 lines
22 KiB
C++

#include "SceneBenchmark.hpp"
#include "3DScene.hpp"
#include "BuildCommit.hpp"
#include "Camera.hpp"
#include "GLCanvas3D.hpp"
#include "GUI.hpp"
#include "GUI_App.hpp"
#include "GUI_Factories.hpp"
#include "I18N.hpp"
#include "IMSlider.hpp"
#include "MainFrame.hpp"
#include "MsgDialog.hpp"
#include "NotificationManager.hpp"
#include "OpenGLManager.hpp"
#include "Plater.hpp"
#include "Widgets/DialogButtons.hpp"
#include "Widgets/Label.hpp"
#include "Widgets/ProgressBar.hpp"
#include "Widgets/StateColor.hpp"
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/libslic3r.h"
#include "libslic3r_version.h"
#include <array>
#include <glad/gl.h>
#include <vector>
#include <string>
#include "slic3r/GUI/FrameProfiler.hpp"
#include "slic3r/GUI/GUI_Utils.hpp"
#include "libslic3r/Point.hpp"
#include "libslic3r/BoundingBox.hpp"
#include <ratio>
#include "libslic3r/Preset.hpp"
#include <ios>
#include <wx/clipbrd.h>
#include <wx/gdicmn.h>
#include <wx/colour.h>
#include <wx/event.h>
#include <wx/dcclient.h>
#include <wx/dataobj.h>
#include <wx/glcanvas.h>
#include <wx/string.h>
#include <wx/textctrl.h>
#include <wx/timer.h>
#include <wx/toplevel.h>
#include <wx/utils.h>
#include <algorithm>
#include <chrono>
#include <cmath>
#include <cstring>
#include <iomanip>
#include <memory>
#include <numeric>
#include <sstream>
#include "libslic3r/PresetBundle.hpp"
#include "slic3r/GUI/GCodeViewer.hpp"
#include "slic3r/GUI/Jobs/Worker.hpp"
#include "slic3r/GUI/Widgets/Button.hpp"
class wxWindow;
namespace Slic3r {
namespace GUI {
FrameTimeStats frame_time_stats(std::vector<double> frame_ms)
{
FrameTimeStats stats;
if (frame_ms.empty())
return stats;
std::sort(frame_ms.begin(), frame_ms.end());
const double total_ms = std::accumulate(frame_ms.begin(), frame_ms.end(), 0.0);
auto percentile = [&frame_ms](double p) {
const size_t rank = size_t(std::ceil(p * double(frame_ms.size()) / 100.0));
return frame_ms[std::clamp<size_t>(rank, 1, frame_ms.size()) - 1];
};
stats.fps = total_ms > 0.0 ? 1000.0 * double(frame_ms.size()) / total_ms : 0.0;
stats.average_ms = total_ms / double(frame_ms.size());
stats.median_ms = percentile(50.0);
stats.p95_ms = percentile(95.0);
stats.p99_ms = percentile(99.0);
stats.max_ms = frame_ms.back();
return stats;
}
namespace {
constexpr size_t WARMUP_FRAMES = 30;
// A scene runs the camera path twice: timing the frames, then averaging the render timings.
constexpr size_t PASS_FRAMES = 360;
constexpr size_t SCENE_FRAMES = WARMUP_FRAMES + 2 * PASS_FRAMES;
struct SceneResult
{
std::string name;
std::vector<double> frame_ms;
std::vector<FrameProfiler::Section> sections;
};
class SceneBenchmarkDialog : public DPIDialog
{
public:
SceneBenchmarkDialog();
~SceneBenchmarkDialog() override;
bool is_running() const { return m_stage != Stage::Done; }
protected:
void on_dpi_changed(const wxRect& suggested_rect) override;
private:
enum class Stage { Loading, Prepare, Slicing, Preview, Layers, Done };
void on_timer(wxTimerEvent& evt);
void on_idle(wxIdleEvent& evt);
void on_first_frame();
void begin_scene(GLCanvas3D* canvas, Stage stage);
void end_scene();
void stop_scene();
void start_slicing();
void set_camera(size_t pass_frame);
void set_top_layer(size_t pass_frame);
void set_status(const wxString& status);
void finish(const wxString& error = wxString());
void show_report(const wxString& error);
std::string report() const;
Stage m_stage{ Stage::Loading };
std::unique_ptr<wxWindowDisabler> m_disabler;
wxTimer m_timer;
int m_polls{ 0 };
Label* m_status{ nullptr };
ProgressBar* m_progress{ nullptr };
GLCanvas3D* m_canvas{ nullptr };
Camera m_saved_camera;
Vec3d m_target{ Vec3d::Zero() };
double m_zoom{ 1.0 };
size_t m_frame{ 0 };
std::chrono::steady_clock::time_point m_last_frame;
// The layer slider's top before the Layers scene moved it, -1 outside that scene.
int m_saved_top_layer{ -1 };
int m_swap_interval{ wxGLCanvas::DefaultSwapInterval };
bool m_restore_swap_interval{ false };
std::vector<SceneResult> m_results;
// Read on the first frame, with the context current.
std::string m_vsync;
int m_msaa_samples{ 0 };
int m_viewport_width{ 0 };
int m_viewport_height{ 0 };
std::string m_camera_type;
};
SceneBenchmarkDialog* s_benchmark = nullptr;
SceneBenchmarkDialog::SceneBenchmarkDialog()
: DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, _L("Benchmark 3D Scene"), wxDefaultPosition, wxDefaultSize,
wxCAPTION | wxCLOSE_BOX)
, m_timer(this)
{
s_benchmark = this;
SetBackgroundColour(*wxWHITE);
SetFont(Label::Body_14);
auto* sizer = new wxBoxSizer(wxVERTICAL);
sizer->SetMinSize(wxSize(FromDIP(360), -1));
m_status = new Label(this, _L("Loading the benchmark model") + dots);
m_progress = new ProgressBar(this, wxID_ANY, 100);
m_progress->SetHeight(FromDIP(8));
m_progress->SetMaxSize(wxSize(-1, FromDIP(8)));
m_progress->SetProgressForedColour(StateColor::darkModeColorFor(wxColour("#DFDFDF")));
sizer->Add(m_status, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, FromDIP(15));
sizer->Add(m_progress, 0, wxEXPAND | wxALL, FromDIP(15));
auto* buttons = new DialogButtons(this, {"Cancel"});
buttons->GetCANCEL()->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Close(); });
sizer->Add(buttons, 0, wxEXPAND);
SetSizerAndFit(sizer);
wxGetApp().UpdateDlgDarkUI(this);
Bind(wxEVT_TIMER, &SceneBenchmarkDialog::on_timer, this);
Bind(wxEVT_IDLE, &SceneBenchmarkDialog::on_idle, this);
Bind(wxEVT_CHAR_HOOK, [this](wxKeyEvent& evt) {
if (evt.GetKeyCode() == WXK_ESCAPE)
Close();
else
evt.Skip();
});
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent&) {
m_timer.Stop();
stop_scene();
m_disabler.reset();
Destroy();
});
// A corner of the 3D view, away from the model.
const wxRect view = wxGetApp().plater()->get_current_canvas3D()->get_wxglcanvas()->GetScreenRect();
SetPosition(wxPoint(view.GetLeft() + FromDIP(20), view.GetBottom() - GetSize().GetHeight() - FromDIP(20)));
m_disabler = std::make_unique<wxWindowDisabler>(this);
m_timer.Start(100);
}
SceneBenchmarkDialog::~SceneBenchmarkDialog()
{
// At shutdown the windows it disabled may be gone already.
if (wxGetApp().is_closing())
(void) m_disabler.release();
if (s_benchmark == this)
s_benchmark = nullptr;
}
void SceneBenchmarkDialog::on_dpi_changed(const wxRect&)
{
Fit();
Refresh();
}
void SceneBenchmarkDialog::on_timer(wxTimerEvent&)
{
Plater* plater = wxGetApp().plater();
if (wxGetApp().is_closing() || plater == nullptr) {
m_timer.Stop();
return;
}
if (m_stage == Stage::Loading) {
if (!plater->get_ui_job_worker().is_idle())
return;
m_timer.Stop();
GLCanvas3D* canvas = plater->get_view3D_canvas3D();
const BoundingBoxf3 box = canvas->volumes_bounding_box(true);
if (!box.defined) {
finish(_L("The benchmark model could not be loaded."));
return;
}
m_target = box.center();
const Size size = canvas->get_canvas_size();
m_zoom = double(std::min(size.get_width(), size.get_height())) / (1.1 * box.size().norm());
plater->deselect_all();
begin_scene(canvas, Stage::Prepare);
} else if (m_stage == Stage::Slicing) {
if (plater->is_background_process_slicing())
return;
GLCanvas3D* canvas = plater->get_preview_canvas3D();
if (canvas->get_gcode_viewer().has_data() && canvas->get_wxglcanvas()->IsShownOnScreen()) {
m_timer.Stop();
plater->get_notification_manager()->set_slicing_progress_hidden();
begin_scene(canvas, Stage::Preview);
} else if (++m_polls > 30)
finish(_L("Slicing failed, so only Prepare was measured."));
}
}
void SceneBenchmarkDialog::on_idle(wxIdleEvent& evt)
{
evt.Skip();
if (m_canvas == nullptr || wxGetApp().is_closing())
return;
if (!m_canvas->get_wxglcanvas()->IsShownOnScreen()) {
finish(_L("The benchmark stopped because the 3D view was hidden."));
return;
}
const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
if (m_frame > WARMUP_FRAMES && m_frame <= WARMUP_FRAMES + PASS_FRAMES)
m_results.back().frame_ms.push_back(std::chrono::duration<double, std::milli>(now - m_last_frame).count());
m_last_frame = now;
FrameProfiler& profiler = m_canvas->get_frame_profiler();
if (m_frame == WARMUP_FRAMES + PASS_FRAMES)
profiler.start_averaging();
if (m_stage == Stage::Layers)
// The warm-up ends where the path starts, so the slider moves in every frame.
set_top_layer(m_frame < WARMUP_FRAMES ? PASS_FRAMES - WARMUP_FRAMES + m_frame : (m_frame - WARMUP_FRAMES) % PASS_FRAMES);
else
set_camera(m_frame < WARMUP_FRAMES ? 0 : (m_frame - WARMUP_FRAMES) % PASS_FRAMES);
m_canvas->render();
if (m_frame == 0)
on_first_frame();
if (++m_frame == SCENE_FRAMES) {
m_results.back().sections = profiler.finish_averaging(true);
end_scene();
// The next scene starts right away, with no timer to bring the next idle event.
if (m_canvas != nullptr)
evt.RequestMore();
return;
}
const int scene = m_stage == Stage::Prepare ? 0 : m_stage == Stage::Preview ? 1 : 2;
if (m_frame % 30 == 0)
m_progress->SetValue(int(100 * (scene * SCENE_FRAMES + m_frame) / (3 * SCENE_FRAMES)));
evt.RequestMore();
}
void SceneBenchmarkDialog::on_first_frame()
{
// A benchmark measures the frames the GPU can draw, not the display refresh rate.
wxGLCanvas* glcanvas = m_canvas->get_wxglcanvas();
m_swap_interval = glcanvas->GetSwapInterval();
const bool known = m_swap_interval != wxGLCanvas::DefaultSwapInterval;
if (known && m_swap_interval != 0)
m_restore_swap_interval = glcanvas->SetSwapInterval(0) != wxGLCanvas::SwapInterval::NotSet;
if (m_results.size() > 1)
return;
m_vsync = !known ? "unknown" : m_swap_interval == 0 || m_restore_swap_interval ? "off" : "on";
GLint samples = 0;
glsafe(::glGetIntegerv(GL_SAMPLES, &samples));
m_msaa_samples = samples;
const Camera& camera = wxGetApp().plater()->get_camera();
const std::array<int, 4>& viewport = camera.get_viewport();
m_viewport_width = viewport[2];
m_viewport_height = viewport[3];
m_camera_type = camera.get_type_as_string();
}
void SceneBenchmarkDialog::begin_scene(GLCanvas3D* canvas, Stage stage)
{
m_stage = stage;
m_canvas = canvas;
m_saved_camera = wxGetApp().plater()->get_camera();
m_frame = 0;
m_restore_swap_interval = false;
m_results.push_back({stage == Stage::Prepare ? "Prepare" : stage == Stage::Preview ? "Preview" : "Layers", {}, {}});
m_results.back().frame_ms.reserve(PASS_FRAMES);
set_status(stage == Stage::Prepare ? _L("Turning the camera in Prepare") :
stage == Stage::Preview ? _L("Turning the camera in Preview") :
_L("Moving through the layers in Preview"));
if (stage == Stage::Layers) {
m_saved_top_layer = canvas->get_gcode_viewer().get_layers_slider()->GetHigherValue();
set_camera(0);
}
canvas->set_benchmarking(true);
}
void SceneBenchmarkDialog::end_scene()
{
const Stage stage = m_stage;
GLCanvas3D* canvas = m_canvas;
stop_scene();
if (stage == Stage::Prepare)
start_slicing();
else if (stage == Stage::Preview)
begin_scene(canvas, Stage::Layers);
else
finish();
}
void SceneBenchmarkDialog::stop_scene()
{
if (m_canvas == nullptr)
return;
if (m_frame < SCENE_FRAMES)
m_results.pop_back();
FrameProfiler& profiler = m_canvas->get_frame_profiler();
if (profiler.is_averaging())
profiler.finish_averaging(false);
if (m_restore_swap_interval && m_canvas->make_current_for_postinit())
m_canvas->get_wxglcanvas()->SetSwapInterval(m_swap_interval);
if (m_saved_top_layer >= 0) {
m_canvas->get_gcode_viewer().get_layers_slider()->SetHigherValue(m_saved_top_layer);
m_saved_top_layer = -1;
}
m_canvas->set_benchmarking(false);
m_canvas->set_as_dirty();
wxGetApp().plater()->get_camera() = m_saved_camera;
m_canvas = nullptr;
}
void SceneBenchmarkDialog::start_slicing()
{
m_stage = Stage::Slicing;
set_status(_L("Slicing"));
m_progress->SetValue(33);
Plater* plater = wxGetApp().plater();
plater->reslice();
plater->select_view_3D("Preview", false);
wxGetApp().mainframe->select_tab(TAB_ID_PREVIEW);
m_polls = 0;
m_timer.Start(100);
}
void SceneBenchmarkDialog::set_camera(size_t pass_frame)
{
// Two turns around the model while the view goes three times from below the plate to nearly
// straight above it, zooming in and out twice.
const double t = double(pass_frame) / double(PASS_FRAMES);
const double azimuth = (-0.75 + 4.0 * t) * PI;
const double elevation = (30.0 + 55.0 * std::sin(6.0 * PI * t)) * PI / 180.0;
const Vec3d dir(std::cos(elevation) * std::cos(azimuth), std::cos(elevation) * std::sin(azimuth), std::sin(elevation));
Camera& camera = wxGetApp().plater()->get_camera();
camera.look_at(m_target + Camera::DefaultDistance * dir, m_target, Vec3d::UnitZ());
camera.set_zoom(m_zoom * (1.0 + 0.4 * std::sin(4.0 * PI * t)));
}
void SceneBenchmarkDialog::set_top_layer(size_t pass_frame)
{
// Down to the first layer and back up, as dragging the top of the layer slider does.
IMSlider* slider = m_canvas->get_gcode_viewer().get_layers_slider();
const double t = double(pass_frame) / double(PASS_FRAMES);
const int span = slider->GetMaxValue() - slider->GetMinValue();
slider->SetHigherValue(slider->GetMinValue() + int(std::lround(span * std::abs(1.0 - 2.0 * t))));
}
void SceneBenchmarkDialog::set_status(const wxString& status)
{
m_status->SetLabel(status + dots);
Fit();
}
void SceneBenchmarkDialog::finish(const wxString& error)
{
m_timer.Stop();
stop_scene();
m_disabler.reset();
m_stage = Stage::Done;
show_report(error);
}
void SceneBenchmarkDialog::show_report(const wxString& error)
{
DestroyChildren();
m_status = nullptr;
m_progress = nullptr;
auto* sizer = new wxBoxSizer(wxVERTICAL);
if (!error.IsEmpty()) {
auto* message = new Label(this, error);
message->Wrap(FromDIP(500));
sizer->Add(message, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, FromDIP(15));
}
std::string text;
if (!m_results.empty()) {
text = report();
auto* content = new wxTextCtrl(this, wxID_ANY, from_u8(text), wxDefaultPosition, wxDefaultSize,
wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP | wxBORDER_SIMPLE);
content->SetFont(wxGetApp().code_font());
wxClientDC dc(content);
dc.SetFont(content->GetFont());
const wxSize extent = dc.GetMultiLineTextExtent(content->GetValue()) + FromDIP(wxSize(40, 20));
content->SetMinSize(wxSize(extent.GetWidth(), std::min(extent.GetHeight(), FromDIP(560))));
sizer->Add(content, 1, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, FromDIP(15));
}
auto* buttons = new DialogButtons(this, text.empty() ? std::vector<wxString>{"OK"} : std::vector<wxString>{"Copy", "OK"});
if (Button* copy = buttons->GetButtonFromID(wxID_COPY)) {
copy->SetToolTip(_L("Copy the results to the clipboard"));
copy->Bind(wxEVT_BUTTON, [text](wxCommandEvent&) {
wxClipboardLocker lock;
if (!lock)
return;
wxTheClipboard->SetData(new wxTextDataObject(from_u8(text)));
});
}
buttons->GetOK()->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Close(); });
sizer->Add(buttons, 0, wxEXPAND);
SetSizerAndFit(sizer);
CenterOnParent();
wxGetApp().UpdateDlgDarkUI(this);
Raise();
}
std::string SceneBenchmarkDialog::report() const
{
const AppConfig& config = *wxGetApp().app_config;
const OpenGLManager::GLInfo& gl = OpenGLManager::get_gl_info();
auto on_off = [](bool on) { return on ? "on" : "off"; };
std::ostringstream out;
out << SLIC3R_APP_NAME << " 3D scene benchmark\n"
<< "Version: " << SoftFever_VERSION << " (" << build_commit_label << ")\n"
<< "GPU: " << gl.get_renderer() << '\n'
<< "OpenGL: " << gl.get_version() << '\n'
<< "Viewport: " << m_viewport_width << " x " << m_viewport_height << " px, " << m_camera_type << " camera\n"
<< "Settings: MSAA " << (m_msaa_samples > 1 ? std::to_string(m_msaa_samples) + "x" : "off")
<< ", FXAA " << on_off(config.get_bool(SETTING_OPENGL_FXAA_ENABLED))
<< ", scene cache " << on_off(config.get_bool(SETTING_OPENGL_SCENE_CACHE)) << ", VSync " << m_vsync << '\n'
<< "Realistic: ";
if (config.get_bool(SETTING_OPENGL_REALISTIC_MODE))
out << "on, Phong " << on_off(config.get_bool(SETTING_OPENGL_REALISTIC_PHONG)) << ", SSAO "
<< on_off(config.get_bool(SETTING_OPENGL_PHONG_SSAO)) << ", shadows " << config.get(SETTING_OPENGL_REALISTIC_SHADOWS)
<< ", in Preview " << on_off(config.get_bool(SETTING_OPENGL_REALISTIC_PREVIEW)) << '\n';
else
out << "off\n";
// The Preview scenes depend on the toolpaths, which depend on the presets it was sliced with.
const PresetBundle& presets = *wxGetApp().preset_bundle;
auto preset_name = [](const PresetCollection& collection) {
return collection.get_selected_preset_name() + (collection.get_edited_preset().is_dirty ? " (modified)" : "");
};
out << "Presets: " << preset_name(presets.printers) << ", " << preset_name(presets.prints) << '\n'
<< "Scene: OrcaSliced Combo, " << PASS_FRAMES << " frames per pass\n";
const GCodeViewer& gcode_viewer = wxGetApp().plater()->get_preview_canvas3D()->get_gcode_viewer();
if (gcode_viewer.has_data())
out << "Toolpaths: " << gcode_viewer.get_vertices_count() << " vertices, " << gcode_viewer.get_layers_count() << " layers\n";
out << '\n';
std::vector<FrameTimeStats> stats;
for (const SceneResult& result : m_results)
stats.push_back(frame_time_stats(result.frame_ms));
auto row = [&out, &stats](const char* label, double FrameTimeStats::*value) {
out << std::left << std::setw(22) << label << std::right;
for (const FrameTimeStats& s : stats)
out << std::setw(10) << s.*value;
out << '\n';
};
out << std::fixed << std::setprecision(1) << std::setw(22) << "";
for (const SceneResult& result : m_results)
out << std::right << std::setw(10) << result.name;
out << '\n';
row("Average FPS", &FrameTimeStats::fps);
out << std::setprecision(2) << "Frame time (ms)\n";
row(" average", &FrameTimeStats::average_ms);
row(" median", &FrameTimeStats::median_ms);
row(" 95th percentile", &FrameTimeStats::p95_ms);
row(" 99th percentile", &FrameTimeStats::p99_ms);
row(" maximum", &FrameTimeStats::max_ms);
for (const SceneResult& result : m_results) {
out << "\nRender timings, " << result.name << " (ms per frame)\n";
if (result.sections.empty()) {
out << " not supported by the graphics driver\n";
continue;
}
out << std::setw(22) << "" << std::setw(10) << "CPU" << std::setw(10) << "GPU" << '\n';
double cpu_ms = 0.0;
double gpu_ms = 0.0;
for (const FrameProfiler::Section& section : result.sections) {
out << " " << std::left << std::setw(20) << section.name << std::right << std::setw(10) << section.cpu_ms << std::setw(10)
<< section.gpu_ms << '\n';
cpu_ms += section.cpu_ms;
gpu_ms += section.gpu_ms;
}
out << " " << std::left << std::setw(20) << "total" << std::right << std::setw(10) << cpu_ms << std::setw(10) << gpu_ms << '\n';
}
return out.str();
}
} // namespace
void run_scene_benchmark()
{
Plater* plater = wxGetApp().plater();
if (plater == nullptr || !wxGetApp().is_editor())
return;
if (s_benchmark != nullptr) {
if (s_benchmark->is_running()) {
s_benchmark->Raise();
return;
}
s_benchmark->Close();
}
MessageDialog confirm(wxGetApp().mainframe,
_L("The benchmark replaces the current project with the OrcaSliced Combo and slices it. It turns the camera "
"around the model in Prepare and in Preview, then moves the layer slider through the sliced layers, timing "
"every frame, and shows the results.") + "\n\n" +
_L("It uses the current graphics settings and usually takes less than a minute. The main window stays "
"locked until it finishes."),
_L("Benchmark 3D Scene"), wxICON_INFORMATION | wxOK | wxCANCEL);
confirm.SetButtonLabel(wxID_OK, _L("Start"), true);
if (confirm.ShowModal() != wxID_OK || plater->new_project() == wxID_CANCEL)
return;
const std::vector<MenuFactory::HandyModel>& models = MenuFactory::handy_models();
const auto combo = std::find_if(models.begin(), models.end(),
[](const MenuFactory::HandyModel& model) { return std::strcmp(model.key, "orcasliced_combo") == 0; });
MenuFactory::load_handy_model(size_t(combo - models.begin()));
(new SceneBenchmarkDialog())->Show();
}
} // namespace GUI
} // namespace Slic3r