diff --git a/cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake b/cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake index 6e8c8e42b0..a542ba8ddf 100644 --- a/cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake +++ b/cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake @@ -17,6 +17,8 @@ macro(InitializeFreeCADBuildOptions) option(FREECAD_PARALLEL_LINK_JOBS "Linkage jobs pool size to fit memory limitations.") option(BUILD_WITH_CONDA "Set ON if you build FreeCAD with conda" OFF) option(BUILD_DYNAMIC_LINK_PYTHON "If OFF extension-modules do not link against python-libraries" ON) + option(BUILD_TRACY_FRAME_PROFILER "If ON then enables support for the Tracy frame profiler" OFF) + option(INSTALL_TO_SITEPACKAGES "If ON the freecad root namespace (python) is installed into python's site-packages" ON) option(INSTALL_PREFER_SYMLINKS "If ON then fc_copy_sources macro will create symlinks instead of copying files" OFF) option(OCCT_CMAKE_FALLBACK "disable usage of occt-config files" OFF) diff --git a/src/3rdParty/CMakeLists.txt b/src/3rdParty/CMakeLists.txt index e5dec18854..71b368b902 100644 --- a/src/3rdParty/CMakeLists.txt +++ b/src/3rdParty/CMakeLists.txt @@ -8,8 +8,17 @@ add_subdirectory(libE57Format) if (BUILD_ASSEMBLY AND NOT FREECAD_USE_EXTERNAL_ONDSELSOLVER) if( NOT EXISTS "${CMAKE_SOURCE_DIR}/src/3rdParty/OndselSolver/CMakeLists.txt" ) - message( SEND_ERROR "The OndselSolver git submodule is not available. Please run + message(FATAL_ERROR "The OndselSolver git submodule is not available. Please run git submodule update --init" ) endif() add_subdirectory(OndselSolver) endif() + +if (BUILD_TRACY_FRAME_PROFILER) + if( NOT EXISTS "${CMAKE_SOURCE_DIR}/src/3rdParty/tracy/CMakeLists.txt" ) + message(FATAL_ERROR "The Tracy git directory is not available. Please clone it manually." ) + endif() + + set(TRACY_STATIC OFF) + add_subdirectory(tracy) +endif() diff --git a/src/App/CMakeLists.txt b/src/App/CMakeLists.txt index 697512470b..519d8f01ec 100644 --- a/src/App/CMakeLists.txt +++ b/src/App/CMakeLists.txt @@ -3,6 +3,10 @@ if(WIN32) add_definitions(-DBOOST_DYN_LINK) endif(WIN32) +if(BUILD_TRACY_FRAME_PROFILER) + add_definitions(-DBUILD_TRACY_FRAME_PROFILER) +endif() + if(FREECAD_RELEASE_SEH) add_definitions(-DHAVE_SEH) endif(FREECAD_RELEASE_SEH) @@ -71,6 +75,10 @@ set(FreeCADApp_LIBS fmt::fmt ) +if(BUILD_TRACY_FRAME_PROFILER) + list(APPEND FreeCADApp_LIBS TracyClient) +endif() + include_directories( ${QtCore_INCLUDE_DIRS} ${QtXml_INCLUDE_DIRS} diff --git a/src/App/Document.cpp b/src/App/Document.cpp index d843343c9b..158fa6d9db 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -94,6 +94,7 @@ recompute path. Also, it enables more complicated dependencies beyond trees. #include #include #include +#include #include #include #include @@ -2963,6 +2964,8 @@ int Document::recompute(const std::vector& objs, bool* hasError, int options) { + ZoneScoped; + if (d->undoing || d->rollback) { if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { FC_WARN("Ignore document recompute on undo/redo"); diff --git a/src/Base/CMakeLists.txt b/src/Base/CMakeLists.txt index d8d3868f6b..5f894d5be8 100644 --- a/src/Base/CMakeLists.txt +++ b/src/Base/CMakeLists.txt @@ -5,6 +5,10 @@ if(WIN32) add_definitions(-DZIPIOS_UTF8) endif(WIN32) +if(BUILD_TRACY_FRAME_PROFILER) + add_definitions(-DBUILD_TRACY_FRAME_PROFILER) +endif() + include_directories( ${CMAKE_BINARY_DIR}/src ${CMAKE_SOURCE_DIR}/src @@ -65,6 +69,11 @@ endif(MSVC) include_directories( ${QtCore_INCLUDE_DIRS} ) + +if(BUILD_TRACY_FRAME_PROFILER) + list(APPEND FreeCADBase_LIBS TracyClient) +endif() + list(APPEND FreeCADBase_LIBS ${QtCore_LIBRARIES}) list(APPEND FreeCADBase_LIBS fmt::fmt) diff --git a/src/Base/Profiler.h b/src/Base/Profiler.h new file mode 100644 index 0000000000..3a517c2e97 --- /dev/null +++ b/src/Base/Profiler.h @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2025 Joao Matos * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + ***************************************************************************/ + +#ifdef TRACY_ENABLE +#include +#else +#define TracyNoop + +#define ZoneNamed(x, y) +#define ZoneNamedN(x, y, z) +#define ZoneNamedC(x, y, z) +#define ZoneNamedNC(x, y, z, w) + +#define ZoneTransient(x, y) +#define ZoneTransientN(x, y, z) + +#define ZoneScoped +#define ZoneScopedN(x) +#define ZoneScopedC(x) +#define ZoneScopedNC(x, y) + +#define ZoneText(x, y) +#define ZoneTextV(x, y, z) +#define ZoneTextF(x, ...) +#define ZoneTextVF(x, y, ...) +#define ZoneName(x, y) +#define ZoneNameV(x, y, z) +#define ZoneNameF(x, ...) +#define ZoneNameVF(x, y, ...) +#define ZoneColor(x) +#define ZoneColorV(x, y) +#define ZoneValue(x) +#define ZoneValueV(x, y) +#define ZoneIsActive false +#define ZoneIsActiveV(x) false + +#define FrameMark +#define FrameMarkNamed(x) +#define FrameMarkStart(x) +#define FrameMarkEnd(x) + +#define FrameImage(x, y, z, w, a) + +#define TracyLockable(type, varname) type varname +#define TracyLockableN(type, varname, desc) type varname +#define TracySharedLockable(type, varname) type varname +#define TracySharedLockableN(type, varname, desc) type varname +#define LockableBase(type) type +#define SharedLockableBase(type) type +#define LockMark(x) (void)x +#define LockableName(x, y, z) + +#define TracyPlot(x, y) +#define TracyPlotConfig(x, y, z, w, a) + +#define TracyMessage(x, y) +#define TracyMessageL(x) +#define TracyMessageC(x, y, z) +#define TracyMessageLC(x, y) +#define TracyAppInfo(x, y) + +#define TracyAlloc(x, y) +#define TracyFree(x) +#define TracyMemoryDiscard(x) +#define TracySecureAlloc(x, y) +#define TracySecureFree(x) +#define TracySecureMemoryDiscard(x) + +#define TracyAllocN(x, y, z) +#define TracyFreeN(x, y) +#define TracySecureAllocN(x, y, z) +#define TracySecureFreeN(x, y) + +#define ZoneNamedS(x, y, z) +#define ZoneNamedNS(x, y, z, w) +#define ZoneNamedCS(x, y, z, w) +#define ZoneNamedNCS(x, y, z, w, a) + +#define ZoneTransientS(x, y, z) +#define ZoneTransientNS(x, y, z, w) + +#define ZoneScopedS(x) +#define ZoneScopedNS(x, y) +#define ZoneScopedCS(x, y) +#define ZoneScopedNCS(x, y, z) + +#define TracyAllocS(x, y, z) +#define TracyFreeS(x, y) +#define TracyMemoryDiscardS(x, y) +#define TracySecureAllocS(x, y, z) +#define TracySecureFreeS(x, y) +#define TracySecureMemoryDiscardS(x, y) + +#define TracyAllocNS(x, y, z, w) +#define TracyFreeNS(x, y, z) +#define TracySecureAllocNS(x, y, z, w) +#define TracySecureFreeNS(x, y, z) + +#define TracyMessageS(x, y, z) +#define TracyMessageLS(x, y) +#define TracyMessageCS(x, y, z, w) +#define TracyMessageLCS(x, y, z) + +#define TracySourceCallbackRegister(x, y) +#define TracyParameterRegister(x, y) +#define TracyParameterSetup(x, y, z, w) +#define TracyIsConnected false +#define TracyIsStarted false +#define TracySetProgramName(x) + +#define TracyFiberEnter(x) +#define TracyFiberEnterHint(x, y) +#define TracyFiberLeave +#endif diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index 0bc809964d..d69188b1e9 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -136,6 +136,9 @@ #include "WidgetFactory.h" #include "3Dconnexion/navlib/NavlibInterface.h" +#ifdef BUILD_TRACY_FRAME_PROFILER +#include +#endif using namespace Gui; using namespace Gui::DockWnd; diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index cbd20fd5b3..4554222cba 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -44,6 +44,10 @@ if (BUILD_ADDONMGR) add_definitions(-DBUILD_ADDONMGR ) endif(BUILD_ADDONMGR) +if (BUILD_TRACY_FRAME_PROFILER) + add_definitions(-DBUILD_TRACY_FRAME_PROFILER) +endif() + include_directories( ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} @@ -99,6 +103,10 @@ else(MSVC) ) endif(MSVC) +if(BUILD_TRACY_FRAME_PROFILER) + list(APPEND FreeCADGui_LIBS TracyClient) +endif() + if (TARGET Coin::Coin) list(APPEND FreeCADGui_LIBS Coin::Coin) else() diff --git a/src/Gui/Quarter/QuarterWidget.cpp b/src/Gui/Quarter/QuarterWidget.cpp index 69d4a12459..3560d595de 100644 --- a/src/Gui/Quarter/QuarterWidget.cpp +++ b/src/Gui/Quarter/QuarterWidget.cpp @@ -93,6 +93,8 @@ #include #include +#include + #include "QuarterWidget.h" #include "InteractionMode.h" #include "QuarterP.h" @@ -839,6 +841,8 @@ void QuarterWidget::resizeEvent(QResizeEvent* event) */ void QuarterWidget::paintEvent(QPaintEvent* event) { + ZoneScoped; + if (updateDevicePixelRatio()) { qreal dev_pix_ratio = devicePixelRatio(); int width = static_cast(dev_pix_ratio * this->width()); @@ -986,6 +990,7 @@ QuarterWidget::redraw() void QuarterWidget::actualRedraw() { + ZoneScoped; PRIVATE(this)->sorendermanager->render(PRIVATE(this)->clearwindow, PRIVATE(this)->clearzbuffer); } diff --git a/src/Gui/Quarter/SoQTQuarterAdaptor.cpp b/src/Gui/Quarter/SoQTQuarterAdaptor.cpp index aae4454ebd..ecfbf9a142 100644 --- a/src/Gui/Quarter/SoQTQuarterAdaptor.cpp +++ b/src/Gui/Quarter/SoQTQuarterAdaptor.cpp @@ -47,6 +47,10 @@ #include "SoQTQuarterAdaptor.h" +#ifdef BUILD_TRACY_FRAME_PROFILER +#include +#endif + // NOLINTBEGIN // clang-format off static unsigned char fps2dfont[][12] = { @@ -766,6 +770,10 @@ void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::paintEvent(QPaintEvent* event) double start = SbTime::getTimeOfDay().getValue(); QuarterWidget::paintEvent(event); this->framesPerSecond = addFrametime(start); + +#ifdef BUILD_TRACY_FRAME_PROFILER + FrameMark; +#endif } void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::resetFrameCounter() diff --git a/src/Gui/View3DInventorViewer.cpp b/src/Gui/View3DInventorViewer.cpp index eae47b843d..eec3f67303 100644 --- a/src/Gui/View3DInventorViewer.cpp +++ b/src/Gui/View3DInventorViewer.cpp @@ -45,6 +45,8 @@ # include # include # include +# include +# include # include # include # include @@ -94,6 +96,7 @@ #include #include #include +#include #include #include #include @@ -430,6 +433,11 @@ void View3DInventorViewer::init() // setting up the defaults for the spin rotation initialize(); +#ifdef TRACY_ENABLE + SoProfiler::init(); + SoProfiler::enable(TRUE); +#endif + // NOLINTBEGIN auto cam = new SoOrthographicCamera; cam->position = SbVec3f(0, 0, 1); @@ -571,9 +579,14 @@ void View3DInventorViewer::init() // the fix and some details what happens behind the scene have a look at this // https://forum.freecad.org/viewtopic.php?f=10&t=7486&p=74777#p74736 uint32_t id = this->getSoRenderManager()->getGLRenderAction()->getCacheContext(); - this->getSoRenderManager()->setGLRenderAction(new SoBoxSelectionRenderAction); + auto boxSelectionAction = new SoBoxSelectionRenderAction; + this->getSoRenderManager()->setGLRenderAction(boxSelectionAction); this->getSoRenderManager()->getGLRenderAction()->setCacheContext(id); +#ifdef TRACY_ENABLE + boxSelectionAction->enableElement(SoProfilerElement::getClassTypeId(), SoProfilerElement::getClassStackIndex()); +#endif + // set the transparency and antialiasing settings getSoRenderManager()->getGLRenderAction()->setTransparencyType(SoGLRenderAction::SORTED_OBJECT_SORTED_TRIANGLE_BLEND); @@ -2415,6 +2428,8 @@ void View3DInventorViewer::renderGLImage() // upon spin. void View3DInventorViewer::renderScene() { + ZoneScoped; + // Must set up the OpenGL viewport manually, as upon resize // operations, Coin won't set it up until the SoGLRenderAction is // applied again. And since we need to do glClear() before applying @@ -2434,15 +2449,19 @@ void View3DInventorViewer::renderScene() glDepthRange(0.1,1.0); #endif - // Render our scenegraph with the image. SoGLRenderAction* glra = this->getSoRenderManager()->getGLRenderAction(); SoState* state = glra->getState(); - SoDevicePixelRatioElement::set(state, devicePixelRatio()); - SoGLWidgetElement::set(state, qobject_cast(this->getGLWidget())); - SoGLRenderActionElement::set(state, glra); - SoGLVBOActivatedElement::set(state, this->vboEnabled); - drawSingleBackground(col); - glra->apply(this->backgroundroot); + + // Render our scenegraph with the image. + { + ZoneScopedN("Background"); + SoDevicePixelRatioElement::set(state, devicePixelRatio()); + SoGLWidgetElement::set(state, qobject_cast(this->getGLWidget())); + SoGLRenderActionElement::set(state, glra); + SoGLVBOActivatedElement::set(state, this->vboEnabled); + drawSingleBackground(col); + glra->apply(this->backgroundroot); + } if (!this->shading) { state->push(); @@ -2480,7 +2499,10 @@ void View3DInventorViewer::renderScene() #endif // Render overlay front scenegraph. - glra->apply(this->foregroundroot); + { + ZoneScopedN("Foreground"); + glra->apply(this->foregroundroot); + } if (this->axiscrossEnabled) { this->drawAxisCross(); @@ -2498,8 +2520,11 @@ void View3DInventorViewer::renderScene() printDimension(); - for (auto it : this->graphicsItems) { - it->paintGL(); + { + ZoneScopedN("Graphics items"); + for (auto it : this->graphicsItems) { + it->paintGL(); + } } //fps rendering @@ -2661,6 +2686,8 @@ void View3DInventorViewer::selectAll() bool View3DInventorViewer::processSoEvent(const SoEvent* ev) { + ZoneScoped; + if (naviCubeEnabled && naviCube->processSoEvent(ev)) { return true; } diff --git a/src/Mod/Part/CMakeLists.txt b/src/Mod/Part/CMakeLists.txt index 6e916a0380..fff413f307 100644 --- a/src/Mod/Part/CMakeLists.txt +++ b/src/Mod/Part/CMakeLists.txt @@ -1,3 +1,6 @@ +if(BUILD_TRACY_FRAME_PROFILER) + add_definitions(-DBUILD_TRACY_FRAME_PROFILER) +endif() add_subdirectory(App) if(BUILD_GUI) diff --git a/src/Mod/Part/Gui/CMakeLists.txt b/src/Mod/Part/Gui/CMakeLists.txt index ae9d47f00d..a802d7911b 100644 --- a/src/Mod/Part/Gui/CMakeLists.txt +++ b/src/Mod/Part/Gui/CMakeLists.txt @@ -24,6 +24,10 @@ set(PartGui_LIBS MatGui ) +if(BUILD_TRACY_FRAME_PROFILER) + list(APPEND PartGui_LIBS TracyClient) +endif() + if(MSVC) include_directories( ${CMAKE_SOURCE_DIR}/src/3rdParty/OpenGL/api diff --git a/src/Mod/Part/Gui/SoBrepFaceSet.cpp b/src/Mod/Part/Gui/SoBrepFaceSet.cpp index fe93c46f7b..1cad8eeeae 100644 --- a/src/Mod/Part/Gui/SoBrepFaceSet.cpp +++ b/src/Mod/Part/Gui/SoBrepFaceSet.cpp @@ -68,6 +68,8 @@ # include #endif +#include + #include #include #include @@ -493,6 +495,8 @@ void SoBrepFaceSet::renderColoredArray(SoMaterialBundle *const materials) void SoBrepFaceSet::GLRender(SoGLRenderAction *action) { + ZoneScoped; + //SoBase::staticDataLock(); static bool init = false; if (!init) {