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 ecf12cc3a7..d40c999d58 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 df4eb45bc1..2903a9ed72 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 c8827f4b01..55437afa69 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 fb7a3e1640..f43d5bf37d 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 bcc76dd297..1b83e366da 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());
@@ -990,6 +994,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 078d9380b4..61ababca17 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) {