3

So, I am converting my project from qmake to cmake, and I'm just about done! However, I'm getting more than double the runtime speed on my qmake build, at least in debug mode. I'd like to have the same compile flags set in both build configurations so that I'm comparing apples to apples.

Unfortunately, I haven't had much luck figuring out what the heck is accounting for the performance difference! My qmake flags resolve out to this:

/MP
 /GS
 /analyze-
 /W3
 /wd"4577"
 /wd"4467"
 /Zc:wchar_t
 /I"."
 /I"qt_generated"
 /I"./qt_generated/Debug"
 /I"..\third_party\Python38-32\include"
 /I"..\third_party\PythonQt\src"
 /I"..\third_party\pybind11\include"
 /I"src\third_party\eigen"
 /I"src\third_party\eigen\Eigen"
 /I"..\third_party\assimp\assimp-5.0.0\include"
 /I"..\third_party\assimp\assimp-5.0.0\build\include"
 /I"..\..\PhysX\physx\include"
 /I"..\..\PhysX\pxshared\include"
 /I"..\third_party\freetype-2.10.1\include"
 /I"..\third_party\soloud\include"
 /I"..\third_party\Visual Leak Detector\include"
 /I"C:\Qt\5.12.2\msvc2017\include"
 /I"C:\Qt\5.12.2\msvc2017\include\QtOpenGL"
 /I"C:\Qt\5.12.2\msvc2017\include\QtWidgets"
 /I"C:\Qt\5.12.2\msvc2017\include\QtOpenGLExtensions"
 /I"C:\Qt\5.12.2\msvc2017\include\QtMultimedia"
 /I"C:\Qt\5.12.2\msvc2017\include\QtGamepad"
 /I"C:\Qt\5.12.2\msvc2017\include\QtGui"
 /I"C:\Qt\5.12.2\msvc2017\include\QtANGLE"
 /I"C:\Qt\5.12.2\msvc2017\include\QtConcurrent"
 /I"C:\Qt\5.12.2\msvc2017\include\QtNetwork"
 /I"C:\Qt\5.12.2\msvc2017\include\QtCore"
 /I"qt_generated\moc"
 /I"qt_generated\ui"
 /I"C:\VulkanSDK\1.0.51.0\include"
 /I"C:\Qt\5.12.2\msvc2017\mkspecs\win32-msvc"
 /I"C:\Program Files (x86)\Visual Leak Detector\include"
 /Zi
 /Gm-
 /Od
 /Fd"qt_generated\obj\vc141.pdb"
 /Zc:inline
 /fp:precise
 /D "_WINDOWS"
 /D "UNICODE"
 /D "_UNICODE"
 /D "WIN32"
 /D "_ENABLE_EXTENDED_ALIGNED_STORAGE"
 /D "DEBUG_MODE"
 /D "WIN64"
 /D "QT_DLL"
 /D "QT_OPENGL_LIB"
 /D "QT_OPENGLEXTENSIONS_LIB"
 /D "QT_WIDGETS_LIB"
 /D "DEVELOP_MODE"
 /D "LINALG_USE_EIGEN"
 /D "PYTHONQT_USE_RELEASE_PYTHON_FALLBACK"
 /D "PX_PHYSX_STATIC_LIB"
 /D "QT_MULTIMEDIA_LIB"
 /D "QT_GAMEPAD_LIB"
 /D "QT_GUI_LIB"
 /D "QT_CONCURRENT_LIB"
 /D "QT_NETWORK_LIB"
 /D "QT_CORE_LIB"
 /errorReport:prompt
 /WX-
 /Zc:forScope
 /Gd
 /Oy-
 /MDd
 /FC
 /Fa"qt_generated\obj\"
 /EHsc
 /nologo
 /Fo"qt_generated\obj\"
 /Fp"qt_generated\obj\grand_blue_d.pch"
 /diagnostics:classic 

And my cmake flags resolve out to this, after trying to get as close a match as possible:

/MP
 /GS
 /analyze-
 /W3
 /wd"4577"
 /wd"4467"
 /Zc:wchar_t
 /I"C:\Users\me\Documents\Projects\my-engine\app"
 /I"C:\Users\me\Documents\Projects\my-engine\my_engine"
 /I"C:\Users\me\Documents\Projects\my-engine\app\reverie_autogen\include_Debug"
 /I"C:\Users\me\Documents\Projects\my-engine\third_party\Python38-32\include"
 /I"C:\Users\me\Documents\Projects\my-engine\my_engine\..\third_party\pybind11\include"
 /I"C:\Users\me\Documents\Projects\my-engine\my_engine\src\third_party\eigen"
 /I"C:\Users\me\Documents\Projects\my-engine\my_engine\..\third_party\assimp\assimp-5.0.0\include"
 /I"C:\Users\me\Documents\Projects\my-engine\my_engine\..\third_party\assimp\assimp-5.0.0\build\include"
 /I"C:\Users\me\Documents\Projects\my-engine\my_engine\..\third_party\physx\physx\include"
 /I"C:\Users\me\Documents\Projects\my-engine\my_engine\..\third_party\physx\pxshared\include"
 /I"C:\Users\me\Documents\Projects\my-engine\my_engine\..\third_party\freetype-2.10.1\include"
 /I"C:\Users\me\Documents\Projects\my-engine\my_engine\..\third_party\soloud\include"
 /I"C:\Users\me\Documents\Projects\my-engine\my_engine\..\third_party\Visual Leak Detector\include"
 /I"C:\Qt\5.12.2\msvc2017\include"
 /I"C:\Qt\5.12.2\msvc2017\include\QtCore"
 /I"C:\Qt\5.12.2\msvc2017\.\mkspecs\win32-msvc"
 /I"C:\Qt\5.12.2\msvc2017\include\QtConcurrent"
 /I"C:\Qt\5.12.2\msvc2017\include\QtGamepad"
 /I"C:\Qt\5.12.2\msvc2017\include\QtGui"
 /I"C:\Qt\5.12.2\msvc2017\include\QtANGLE"
 /I"C:\Qt\5.12.2\msvc2017\include\QtOpenGLExtensions"
 /I"C:\Qt\5.12.2\msvc2017\include\QtMultimedia"
 /I"C:\Qt\5.12.2\msvc2017\include\QtNetwork"
 /I"C:\Qt\5.12.2\msvc2017\include\QtMultimediaWidgets"
 /I"C:\Qt\5.12.2\msvc2017\include\QtWidgets"
 /I"C:\Program Files (x86)\Visual Leak Detector\include"
 /Zi
 /Gm-
 /Od
 /Fd"reverie.dir\Debug\vc141.pdb"
 /Zc:inline
 /fp:precise
 /D "WIN32"
 /D "_WINDOWS"
 /D "DEBUG_MODE"
 /D "_UNICODE"
 /D "_ENABLE_EXTENDED_ALIGNED_STORAGE"
 /D "WIN64"
 /D "QT_DLL"
 /D "QT_OPENGL_LIB"
 /D "DEVELOP_MODE"
 /D "LINALG_USE_EIGEN"
 /D "_CRT_SECURE_NO_WARNINGS"
 /D "PX_PHYSX_STATIC_LIB"
 /D "QT_CORE_LIB"
 /D "QT_CONCURRENT_LIB"
 /D "QT_GAMEPAD_LIB"
 /D "QT_GUI_LIB"
 /D "QT_OPENGLEXTENSIONS_LIB"
 /D "QT_MULTIMEDIA_LIB"
 /D "QT_NETWORK_LIB"
 /D "QT_MULTIMEDIAWIDGETS_LIB"
 /D "QT_WIDGETS_LIB"
 /D "CMAKE_INTDIR=\"Debug\""
 /D "UNICODE"
 /errorReport:prompt
 /WX-
 /Zc:forScope
 /Gd
 /Oy-
 /MDd
 /std:c++latest
 /Fa"reverie.dir\Debug\"
 /EHsc
 /nologo
 /Fo"reverie.dir\Debug\"
 /Fp"reverie.dir\Debug\reverie.pch"
 /diagnostics:classic 

As far as I can see, these should see identical performance! Any ideas at all as to how my qmake build could be so much faster?

Edit: For even more fun, here is my qmake project file:

# ----------------------------------------------------
# This file is generated by the Qt Visual Studio Tools.
# ------------------------------------------------------
# See: https://www.toptal.com/qt/vital-guide-qmake
# NOTE: Debug build should use /MDd runtime library, and release should use /MD (may be default for Qt)
# TODO: Convert to cmake: https://www.executionunit.com/blog/2014/01/22/moving-from-qmake-to-cmake/

message("Beginning qmake build of my_project.pro")

TEMPLATE = app
TARGET = my_project
QT += core \
      opengl \ 
      gui \
      widgets \
      concurrent \ # Mutexes/multithreading
      openglextensions \
      multimedia \
      gamepad \ # Controller support
      network # TCP/IP

# Set compiler flags /////////////////////////////////////////////////////////////
QMAKE_CXXFLAGS += /MP # Multiprocess compile, much faster

# MSVC versions after 15.3 are fickle with the flags required to use more modern c++ variants
QMAKE_CXXFLAGS *= /std:c++17 # Add if not there, this may be the ticket
# QMAKE_CXXFLAGS += -std=c++17 # For GCC/Clang
# QMAKE_CXXFLAGS += -std=c++1z

# Set general configuration options /////////////////////////////////////////////////
CONFIG += c++latest # Add support for c++17.
# CONFIG += c++1z # another attempt at C++17 support
CONFIG += qt # console # The target is a Qt application or library and requires the Qt library and header files
CONFIG += thread # Thread support is enabled. This is enabled when CONFIG includes qt, which is the default.
CONFIG += debug_and_release # Creates additional debug and release folders, but need it for debug

CONFIG(debug, debug|release){
    DESTDIR = ../app/debug
    DEFINES += DEBUG_MODE
} 
else {
    DESTDIR = ../app/release
}

# Replace O2 flag with O3 flag
#CONFIG(release, debug|release) {
#    QMAKE_CXXFLAGS_RELEASE -= -O1
#   QMAKE_CXXFLAGS_RELEASE -= -O2
#   QMAKE_CXXFLAGS_RELEASE *= -O3
#}

# Do not display debug output in release mode
CONFIG(debug, debug|release) : CONFIG += debug_info
CONFIG(release, debug|release) : DEFINES += QT_NO_DEBUG_OUTPUT

CONFIG += no_lflags_merge # Ensures that the list of libraries stored in the LIBS variable is not reduced to a list of unique values before it is used.
# CONFIG += CONSOLE # makes this a console application
CONFIG -= flat # flattens file hierarchy, subtract if this is not desired

# Defines //////////////////////////////////////////////////////////////////////////
DEFINES += _UNICODE _ENABLE_EXTENDED_ALIGNED_STORAGE WIN64 QT_DLL QT_OPENGL_LIB QT_OPENGLEXTENSIONS_LIB QT_WIDGETS_LIB
DEFINES += DEVELOP_MODE
DEFINES += LINALG_USE_EIGEN
INCLUDEPATH += ./qt_generated \
    . \
    ./qt_generated/$(ConfigurationName) 
    
LIBS += -lopengl32 \
    -lglu32 
DEPENDPATH += .


# Add Libraries ////////////////////////////////////////////////////////////////////
INCLUDEPATH += ../third_party/pybind11/include


# Compile against release version of python
CONFIG(debug, debug|release) : DEFINES += PYTHONQT_USE_RELEASE_PYTHON_FALLBACK

# Eigen
INCLUDEPATH += $$PWD/src/third_party/eigen \
               $$PWD/src/third_party/eigen/Eigen

# ASSIMP
# To be able to write <module.h>
INCLUDEPATH += ../third_party/assimp/assimp-5.0.0/include
INCLUDEPATH += ../third_party/assimp/assimp-5.0.0/build/include
CONFIG(debug, debug|release) : LIBS += -L$$PWD/lib/assimp -lassimp_d
CONFIG(release, debug|release) : LIBS += -L$$PWD/lib/assimp -lassimp

# PhysX
DEFINES += PX_PHYSX_STATIC_LIB
INCLUDEPATH += ../../PhysX/physx/include \
               ../../PhysX/pxshared/include
CONFIG(debug, debug|release) { 
    LIBS += -L$$PWD/lib/physx/debug -lPhysX_static_32
    LIBS += -L$$PWD/lib/physx/debug -lPhysXCharacterKinematic_static_32
    LIBS += -L$$PWD/lib/physx/debug -lPhysXCommon_static_32
    LIBS += -L$$PWD/lib/physx/debug -lPhysXCooking_static_32
    LIBS += -L$$PWD/lib/physx/debug -lPhysXExtensions_static_32
    LIBS += -L$$PWD/lib/physx/debug -lPhysXFoundation_static_32
    LIBS += -L$$PWD/lib/physx/debug -lPhysXPvdSDK_static_32
    LIBS += -L$$PWD/lib/physx/debug -lPhysXVehicle_static_32
}
CONFIG(release, debug|release) { 
    # Always needed
    LIBS += -L$$PWD/lib/physx/release -lPhysXCommon_static_32
    
    # Always needed
    LIBS += -L$$PWD/lib/physx/release -lPhysX_static_32
    
    # Always needed
    LIBS += -L$$PWD/lib/physx/release -lPhysXFoundation_static_32
    
    # To cook geometry data on the fly
    LIBS += -L$$PWD/lib/physx/release -lPhysXCooking_static_32
    
    # Other
    LIBS += -L$$PWD/lib/physx/release -lPhysXCharacterKinematic_static_32
    LIBS += -L$$PWD/lib/physx/release -lPhysXExtensions_static_32
    LIBS += -L$$PWD/lib/physx/release -lPhysXPvdSDK_static_32
    LIBS += -L$$PWD/lib/physx/release -lPhysXVehicle_static_32
}

# FreeType
INCLUDEPATH +=  ../third_party/freetype-2.10.1/include
CONFIG(debug, debug|release) : LIBS += -L$$PWD/lib/freetype/debug -lfreetype
CONFIG(release, debug|release) : LIBS += -L$$PWD/lib/freetype/release -lfreetype

# SoLoud
INCLUDEPATH += ../third_party/soloud/include
CONFIG(debug, debug|release) { 
    LIBS += -L$$PWD/lib/soloud/debug -lsoloud_x86_d
    LIBS += -L$$PWD/lib/soloud/debug -lsoloud_static_x86_d
}
CONFIG(release, debug|release) { 
    LIBS += -L$$PWD/lib/soloud/release -lsoloud_x86
    LIBS += -L$$PWD/lib/soloud/release -lsoloud_static_x86
}


# Include Visual Leak Detector //////////////////////////////////////////////////////////////////
INCLUDEPATH += "../third_party/Visual Leak Detector/include/"
LIBS        += -L"../third_party/Visual Leak Detector/lib/Win32"    
    
               
# Set directories //////////////////////////////////////////////////////////////////
MOC_DIR += ./qt_generated/moc
OBJECTS_DIR += ./qt_generated/obj
UI_DIR += ./qt_generated/ui
RCC_DIR += ./qt_generated

message("Loaded .pro files, now loading .pri")

# Load in library files for project
include(my_project.pri)

message("Loaded .pri files")

and my cmake project file:

# CMake build for the Reverie Engine
# To run cmake, add cmake/bin directory to system path
## TODO:
## Implement source_group, for organizing in IDE, see: https://stackoverflow.com/questions/31966135/cmake-source-group-not-working-correctly-with-hierarchical-project-setup
## TODO: Remove DLLs from this folder, as they are not needed

message( STATUS "---------Beginning cmake build of Reverie Engine---------")

# MACROS =================================================================
macro(remove_flag_from_target _target _flag)
    get_target_property(_target_cxx_flags ${_target} COMPILE_OPTIONS)
    if(_target_cxx_flags)
        list(REMOVE_ITEM _target_cxx_flags ${_flag})
        set_target_properties(${_target} PROPERTIES COMPILE_OPTIONS "${_target_cxx_flags}")
    endif()
endmacro()

# Project-level setup ====================================================
project( Reverie ) # TODO: Set version
cmake_minimum_required( VERSION 3.16 )
set(TARGET_NAME "reverie") # See: https://stackoverflow.com/questions/31037882/whats-the-cmake-syntax-to-set-and-use-variables

message (STATUS "--------- Set target ${TARGET_NAME}---------")

# Find includes in corresponding build directories
set(CMAKE_INCLUDE_CURRENT_DIR ON)

# Instruct CMake to run moc automatically when needed.
set(CMAKE_AUTOMOC ON) # Saves having to use QT5_WRAP_CPP
set(CMAKE_AUTORCC ON) # Saves having to use QT5_ADD_RESOURCES
set(CMAKE_AUTOUIC ON) # Saves having to use QT5_WRAP_UI


# Set compiler flags =====================================================
# Use C++20
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Multiprocess compiler, disable specific warnings
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /wd4577 /wd4467")

# Remove unwanted flags
STRING (REGEX REPLACE "[/|-]RTC(su|[1su])" "" CMAKE_CXX_FLAGS_DEBUG  "${CMAKE_CXX_FLAGS_DEBUG}")
STRING (REGEX REPLACE "[/|-]Ob0" "" CMAKE_CXX_FLAGS_DEBUG  "${CMAKE_CXX_FLAGS_DEBUG}")
STRING (REGEX REPLACE "[/|-]GR" "" CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS}")

# Set paths to find Qt ===================================================
# Find Qt itself
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
    set(CMAKE_PREFIX_PATH "C:\\Qt\\5.12.2\\msvc2017")
else()
    message (WARNING "-------- System not supported ---------")
endif()

# Find UI files
# See: https://stackoverflow.com/questions/40630820/how-to-place-header-and-ui-file-in-different-folders-using-autouic-in-cmake
set(CMAKE_AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/ui")

# Find Required Qt Libraries =============================================
find_package(Qt5 COMPONENTS Core REQUIRED)# Core stuff
find_package(Qt5 COMPONENTS Concurrent REQUIRED) # For threading
find_package(Qt5 COMPONENTS Gamepad REQUIRED) # Controller support
find_package(Qt5 COMPONENTS Gui REQUIRED) # For OpenGL integration
find_package(Qt5 COMPONENTS OpenGLExtensions REQUIRED)
# find_package(Qt5OpenGL) # OpenGL should come from GUI
find_package(Qt5 COMPONENTS Multimedia REQUIRED) # Multimedia capabilities
find_package(Qt5 COMPONENTS MultimediaWidgets REQUIRED)
find_package(Qt5 COMPONENTS Widgets REQUIRED) # For widgets

# OpenGL libraries
# See: https://stackoverflow.com/questions/65100749/converting-from-qmake-to-cmake-how-do-i-find-libraries-in-the-same-way/65106458#65106458
find_package(OpenGL REQUIRED) # enforces as a requirement
# find_library(OPENGL_LIB names opengl32) # Also valid
# find_library(GLU_LIB names glu32) # Also valid

# Set build mode directory options ========================================
# add_definitions(-DAPPLETTUTORIAL1_LIBRARY)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/debug)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/release)

# Set directories to find libraries, need to do this before add_executable for desired target
# link_directories(${CMAKE_SOURCE_DIR} "${CMAKE_SOURCE_DIR}/lib")
file(TO_CMAKE_PATH "$ENV{PYTHON_LIB}" ENV_PYTHON_LIB)
message(STATUS "Setting link directory ${ENV_PYTHON_LIB}")
link_directories(${ENV_PYTHON_LIB})

# Works as alternative, but does not solve any of my runtime problems
# set(PYTHON_QT_PATH "${CMAKE_SOURCE_DIR}/../third_party/PythonQt")
# set(PYTHON_QT_LIB_PATH "${PYTHON_QT_PATH}/lib")
# link_directories(${PYTHON_QT_LIB_PATH})

# Build EXE ===============================================================
# Get all source files, headers, resources, and UI (forms) from src subdirectory
# See: https://stackoverflow.com/questions/2110795/how-to-use-all-c-files-in-a-directory-with-the-cmake-build-system
## TODO: Maybe don't include headers as source?, see https://stackoverflow.com/questions/19866424/cmake-and-qt5-automoc-error
message (STATUS "-------- Finding source, header, resource, UI files relative to  ${CMAKE_SOURCE_DIR} ---------")
file(GLOB_RECURSE MY_SOURCES RELATIVE ${CMAKE_SOURCE_DIR} "src/*.cpp"  "src/*.h"
"resources/*.qrc" "ui/*.ui")

message (STATUS "=========== FOUND FILES =============")
message (STATUS "${MY_SOURCES}")
message (STATUS "=========== END FILES ===============")

# Generate release PDB
# https://stackoverflow.com/questions/28178978/how-to-generate-pdb-files-for-release-build-with-cmake-flags/31264946
add_compile_options("$<$<NOT:$<CONFIG:Debug>>:/Zi>")
add_link_options("$<$<NOT:$<CONFIG:Debug>>:/DEBUG>")
add_link_options("$<$<NOT:$<CONFIG:Debug>>:/OPT:REF>")
add_link_options("$<$<NOT:$<CONFIG:Debug>>:/OPT:ICF>")

# Add an executable to be built from sources variable
add_executable(${TARGET_NAME} ${MY_SOURCES})

# Add compile definitions to the target ===================================
# Set options based on debug or release mode
# Generator expression, see: 
# https://stackoverflow.com/questions/34490294/what-does-configdebugrelease-mean-in-cmake
# https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html
set(DEBUG_DEFINITIONS "DEBUG_MODE")
set(RELEASE_DEFINITIONS "QT_NO_DEBUG_OUTPUT")
target_compile_definitions(${TARGET_NAME} PUBLIC
    $<$<CONFIG:DEBUG>:${DEBUG_DEFINITIONS}>
    $<$<CONFIG:RELEASE>:${RELEASE_DEFINITIONS}>
)

# Remove unwanted flags ===================================================
# Doesn't work
# remove_flag_from_target(${TARGET_NAME} -RTC1)

# Pre-processor Defines ===================================================
add_definitions(-D_UNICODE -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DQT_DLL -DQT_OPENGL_LIB)
add_definitions(-DDEVELOP_MODE)
add_definitions(-DLINALG_USE_EIGEN)

# Disable warnings about deprecated things, like using strcpy (sorry, not sorry)
if(MSVC)
    add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif()

# Add compile options to the target =======================================
# target_compile_options(${TARGET_NAME} PUBLIC "$<$<CONFIG:DEBUG>:${MY_DEBUG_OPTIONS}>")
# target_compile_options(${TARGET_NAME} PUBLIC "$<$<CONFIG:RELEASE>:${MY_RELEASE_OPTIONS}>")

# Link Required Qt modules to main target =================================
# This MUST come after add_executable and find_package for each module
# Essentially, target_link_libraries links the two specified libraries
# https://stackoverflow.com/questions/25909943/getting-a-cmake-error-cannot-specify-link-libraries-for-target-which-is-not-bui/41262868
target_link_libraries(${TARGET_NAME} OpenGL::GL OpenGL::GLU)
target_link_libraries(${TARGET_NAME} Qt5::Core)
target_link_libraries(${TARGET_NAME} Qt5::Concurrent) 
target_link_libraries(${TARGET_NAME} Qt5::Gamepad)
target_link_libraries(${TARGET_NAME} Qt5::Gui)
target_link_libraries(${TARGET_NAME} Qt5::OpenGLExtensions) # TODO: Probably unnecessary, remove
target_link_libraries(${TARGET_NAME} Qt5::Multimedia)
target_link_libraries(${TARGET_NAME} Qt5::MultimediaWidgets)
target_link_libraries(${TARGET_NAME} Qt5::Widgets)

# Add additional libraries ================================================
# Add a default search locations
# TODO: link_directories takes generators, so maybe split into debug and release that way
message(STATUS "Default library search directories: ${CMAKE_SOURCE_DIR}/lib ${CMAKE_SOURCE_DIR}/lib/assimp")

# Include directories:
# https://stackoverflow.com/questions/13703647/how-to-properly-add-include-directories-with-cmake
# Linking libraries:
# **** https://stackoverflow.com/questions/48273378/qmake-to-cmake-transition-syntax-for-external-librairies
# https://stackoverflow.com/questions/60061299/convert-qmake-into-cmake

# Python
message (STATUS "=========== Including Python =============")
# To eliminate conflict with "slots" keyword: PyType_Slot *slots; /* terminated by slot==0. */
# Equivalent to CONFIG += no_keywords in qmake
# add_definitions(-DQT_NO_KEYWORDS)

# For system versions, see: 
# https://stackoverflow.com/questions/9160335/os-specific-instructions-in-cmake-how-to
if(DEFINED ENV{PYTHON_VERSION})
    set(PYTHON_VERSION $ENV{PYTHON_VERSION})
else()
    if(WIN32)
        # for Windows operating system in general
        # if(MSVC OR MSYS OR MINGW) # for detecting Windows compilers
        set(PYTHON_VERSION 38)
    elseif(APPLE)
        # for MacOS X or iOS, watchOS, tvOS (since 3.10.3)
    elseif(UNIX AND NOT APPLE)
        # For Linux, BSD, Solaris, Minix
        message(STATUS "Setting UNIX python version")
        set(PYTHON_VERSION 3.8)
    else()
        message( WARNING "Unrecognized operating system")
    endif()
endif()
message (STATUS "PYTHON VERSION $ENV{PYTHON_VERSION}")

# Set up include paths and libraries for Python itself
if(WIN32)
    # for Windows operating system in general
    # if(MSVC OR MSYS OR MINGW) # for detecting Windows compilers
    
    # Convert paths to CMAKE-friendly versions
    file(TO_CMAKE_PATH "$ENV{PYTHON_PATH}" ENV_PYTHON_PATH)
    
    # Link Python
    # Debug DLL fails to import third party modules, so always use release
    target_include_directories(${TARGET_NAME} PRIVATE "${ENV_PYTHON_PATH}/include")
    message(STATUS "Python include path for windows is ${ENV_PYTHON_PATH}/include")
    message(STATUS "Python path is ${ENV_PYTHON_PATH}")
    # find_library(PYTHON_DEBUG_LIB NAMES "python${PYTHON_VERSION}_d" HINTS ${ENV_PYTHON_LIB})
    find_library(PYTHON_RELEASE_LIB NAMES "python${PYTHON_VERSION}" HINTS ${ENV_PYTHON_LIB})
    # message(STATUS "Linking Python with debug path ${PYTHON_DEBUG_LIB} and release path ${PYTHON_RELEASE_LIB}")
    target_link_libraries(${TARGET_NAME}
        debug ${PYTHON_RELEASE_LIB} optimized ${PYTHON_RELEASE_LIB}
    )
elseif(APPLE)
    # for MacOS X or iOS, watchOS, tvOS (since 3.10.3)
    message (STATUS "BUILDING FOR APPLE")
    # Include library directory
    target_include_directories(${TARGET_NAME} PRIVATE "/System/Library/Frameworks/Python.framework/Headers")
    
    # Find library with name Python, and link
    find_library(PYTHON_LIBRARY_PATH NAMES "Python")
    target_link_libraries(${TARGET_NAME}
        ${PYTHON_LIBRARY_PATH}
    )
elseif(UNIX AND NOT APPLE)
    # For Linux, BSD, Solaris, Minix
    message (STATUS "BUILDING FOR UNIX")
    execute_process(COMMAND python${PYTHON_VERSION}-config --embed --libs
        OUTPUT_VARIABLE PYTHON_LIBRARY_PATH
        RESULT_VARIABLE FAILED_TO_DETECT_PYTHON
    )
    if(FAILED_TO_DETECT_PYTHON)
        message (WARNING "Failed to detect Python")
    else()
        # Set library path
        target_link_libraries(${TARGET_NAME} ${PYTHON_LIBRARY_PATH})
    endif()
else()
    message( WARNING "Unrecognized operating system")
endif()


# PYBIND
message (STATUS "=========== Including PYBIND =============")
target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_SOURCE_DIR}/../third_party/pybind11/include")

# EIGEN 
message (STATUS "=========== Including Eigen at ${CMAKE_SOURCE_DIR}/src/third_party/eigen =============")
target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_SOURCE_DIR}/src/third_party/eigen")


# ASSIMP
message (STATUS "=========== Including ASSIMP =============")
target_include_directories(${TARGET_NAME} PRIVATE 
"${CMAKE_SOURCE_DIR}/../third_party/assimp/assimp-5.0.0/include" 
"${CMAKE_SOURCE_DIR}/../third_party/assimp/assimp-5.0.0/build/include")
find_library(ASSIMP_DEBUG_LIB NAMES assimp_d HINTS "${CMAKE_SOURCE_DIR}/lib/assimp")
find_library(ASSIMP_LIB NAMES assimp HINTS "${CMAKE_SOURCE_DIR}/lib/assimp")
target_link_libraries(${TARGET_NAME} 
    debug ${ASSIMP_DEBUG_LIB} optimized ${ASSIMP_LIB})

# PhysX
message (STATUS "=========== Including PhysX as a static library =============")
add_definitions(-DPX_PHYSX_STATIC_LIB)
target_include_directories(${TARGET_NAME} PRIVATE 
"${CMAKE_SOURCE_DIR}/../third_party/physx/physx/include" 
"${CMAKE_SOURCE_DIR}/../third_party/physx/pxshared/include")
set( PHYSX_LIBRARY_DEBUG_DIR "${CMAKE_SOURCE_DIR}/lib/physx/debug")
set( PHYSX_LIBRARY_RELEASE_DIR "${CMAKE_SOURCE_DIR}/lib/physx/release")
message (STATUS "Debug directory is ${PHYSX_LIBRARY_DEBUG_DIR}")
target_link_libraries(${TARGET_NAME} 
    debug "${PHYSX_LIBRARY_DEBUG_DIR}/PhysX_static_32.lib" optimized "${PHYSX_LIBRARY_RELEASE_DIR}/PhysX_static_32.lib"
    debug "${PHYSX_LIBRARY_DEBUG_DIR}/PhysXCharacterKinematic_static_32.lib" optimized "${PHYSX_LIBRARY_RELEASE_DIR}/PhysXCharacterKinematic_static_32.lib" 
    debug "${PHYSX_LIBRARY_DEBUG_DIR}/PhysXCommon_static_32.lib" optimized "${PHYSX_LIBRARY_RELEASE_DIR}/PhysXCommon_static_32.lib"
    debug "${PHYSX_LIBRARY_DEBUG_DIR}/PhysXCooking_static_32.lib" optimized "${PHYSX_LIBRARY_RELEASE_DIR}/PhysXCooking_static_32.lib"
    debug "${PHYSX_LIBRARY_DEBUG_DIR}/PhysXExtensions_static_32.lib" optimized "${PHYSX_LIBRARY_RELEASE_DIR}/PhysXExtensions_static_32.lib"
    debug "${PHYSX_LIBRARY_DEBUG_DIR}/PhysXFoundation_static_32.lib" optimized "${PHYSX_LIBRARY_RELEASE_DIR}/PhysXFoundation_static_32.lib"
    debug "${PHYSX_LIBRARY_DEBUG_DIR}/PhysXPvdSDK_static_32.lib" optimized "${PHYSX_LIBRARY_RELEASE_DIR}/PhysXPvdSDK_static_32.lib"
    debug "${PHYSX_LIBRARY_DEBUG_DIR}/PhysXVehicle_static_32.lib" optimized "${PHYSX_LIBRARY_RELEASE_DIR}/PhysXVehicle_static_32.lib"
    )

# FreeType
message (STATUS "=========== Including FreeType =============")
target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_SOURCE_DIR}/../third_party/freetype-2.10.1/include")
find_library(FREETYPE_DEBUG_LIB NAMES freetype HINTS "${CMAKE_SOURCE_DIR}/lib/freetype/debug")
find_library(FREETYPE_RELEASE_LIB NAMES freetype HINTS "${CMAKE_SOURCE_DIR}/lib/freetype/release")
target_link_libraries(${TARGET_NAME} 
    debug ${FREETYPE_DEBUG_LIB} optimized ${FREETYPE_RELEASE_LIB})

# SoLoud
message (STATUS "=========== Including SoLoud =============")
target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_SOURCE_DIR}/../third_party/soloud/include")
find_library(SOLOUD_DEBUG_LIB NAMES soloud_x86_d HINTS "${CMAKE_SOURCE_DIR}/lib/soloud/debug")
find_library(SOLOUD_RELEASE_LIB NAMES soloud_x86 HINTS "${CMAKE_SOURCE_DIR}/lib/soloud/release")
find_library(SOLOUD_STATIC_DEBUG_LIB NAMES soloud_static_x86_d HINTS "${CMAKE_SOURCE_DIR}/lib/soloud/debug")
find_library(SOLOUD_STATIC_RELEASE_LIB NAMES soloud_static_x86 HINTS "${CMAKE_SOURCE_DIR}/lib/soloud/release")
target_link_libraries(${TARGET_NAME} 
    debug ${SOLOUD_DEBUG_LIB} optimized ${SOLOUD_RELEASE_LIB}
    debug ${SOLOUD_STATIC_DEBUG_LIB} optimized ${SOLOUD_STATIC_RELEASE_LIB}
    )
    
# Visual Leak Detector
# See: https://stackoverflow.com/questions/4029587/compiling-32-bit-vs-64-bit-project-using-cmake/15986042
target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_SOURCE_DIR}/../third_party/Visual Leak Detector/include/")
if( CMAKE_SIZEOF_VOID_P EQUAL 8 )
    message(STATUS "=========== Including 64 Bit VLD =============")
    # Is 64 bit
    find_library(VLD_LIB NAMES vld HINTS "${CMAKE_SOURCE_DIR}/../third_party/Visual Leak Detector/lib/Win64")
    target_link_libraries(${TARGET_NAME} ${VLD_LIB})
else()
    # Is 32 Bit
    message(STATUS "=========== Including 32 Bit VLD =============")
    find_library(VLD_LIB NAMES vld HINTS "${CMAKE_SOURCE_DIR}/../third_party/Visual Leak Detector/lib/Win32")
    target_link_libraries(${TARGET_NAME} ${VLD_LIB})
endif()

Edit: So, it's been a few weeks, and I've made absolutely no headway in figuring out what's going on here. I've generated some detailed reports for both build systems, which can be found here. So far, no clues that stand out to me. In fact, I don't even see anything in the reports that would suggest that my new build system is slower at all! If I'm reading things correctly, it should be faster.

Edit 2: Here are my linker options as well. The old way, and the new way.

Danny
  • 354
  • 3
  • 13
  • 2
    My two cents. You are setting CMake C++ version to 20, for qmake you are trying to set C++ 17, but you are using "QMAKE_CXXFLAGS *=", note asterisk. Resulting compiler settings are probably not identical. Why not aim for same C++ version? – talamaki Dec 09 '20 at 07:37
  • Great question! I'm using Qt 5.12, which I don't believe supports C++ 20, but I would like to use the latest and greatest. I just switched to C++ 17 for both, and am still seeing the performance difference. – Danny Dec 09 '20 at 16:07
  • Why are we concerned about speed in debug build? Nobody runs debug build for speed. And yes I know this does not help with the question. – stackoverblown Dec 20 '20 at 13:34
  • @stackoverblown Because I work in debug mode 95% of the time, and it's difficult to use the actual software when things are prohibitively slow – Danny Dec 20 '20 at 18:09
  • Couple of quick suggestions. First, fire up a profiler and compare the profiles, that should point you to *what* is slower and from there you'll better be able to figure out *why*. Second, make sure the `/std:c++latest` flag isn't causing a perf change, perhaps a language change is causing fewer/more copies and that's causing the issue. Third, what is the qtgenerated stuff and might that be the issue? My guess is #2 btw. – Tumbleweed53 Dec 20 '20 at 21:27
  • Unfortunately, when I profile both build versions, one of them is just slower across the board. There isn't one particular thing that is slower in one build than the other, which is very frustrating. I have also tried switching to /std::c++17 so that the language matches, but that also didn't make any appreciable difference in the performance of my cmake build. Lastly, qt generates a bunch of .objs and the like, but I don't know how any of that would affect runtime performance. – Danny Dec 22 '20 at 00:48
  • Did you check the linker options? Linker options can have a big impact on perf. Secondly build a barebones cmake/qmake project and compare their compile/linker options. Trying to debug this question without access to anything is very difficult. –  Dec 28 '20 at 01:40
  • @hdf89shfdfs I just added some pastebin links to my linker options in my old and new builds. Does anything look fishy to you? I understand the challenge of what I'm asking, but I'm unfortunately not at liberty to share the code base. – Danny Dec 28 '20 at 05:13
  • @Danny Quite interesting. What is your program's entry point? Do you have both main and WinMain defined? Also, from your comment, I understand that it might be related to logs. How do you print logs to console on a win32 app? – Shmuel H. Jan 01 '21 at 23:28

1 Answers1

1

I figured it out!! cmake was building my application as a console app, instead of a Windows app. Replacing add_executable(${TARGET_NAME} ${MY_SOURCES}) with add_executable(${TARGET_NAME} WIN32 ${MY_SOURCES}) in my cmake build makes the performance match. I learned something new today, thanks all.

Danny
  • 354
  • 3
  • 13
  • 1
    Why does this make a performance difference? –  Dec 29 '20 at 11:15
  • Because for some reason, the console app was much slower at handling my debug output. Pushing output to the MSVC std::out was much faster – Danny Dec 29 '20 at 21:05
  • This question is still marked as bountied despite having an answer. Please close the bounty. –  Jan 02 '21 at 01:40