Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduced profiling instrumentation #3285

Merged
merged 6 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions 3rdparty/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,8 @@ if (WZ_ENABLE_BASIS_UNIVERSAL)
target_compile_definitions(basis_transcoder PRIVATE "-DBASISD_SUPPORT_ATC=0" "-DBASISD_SUPPORT_PVRTC1=0" "-DBASISD_SUPPORT_PVRTC2=0")

endif(WZ_ENABLE_BASIS_UNIVERSAL)

if (WZ_PROFILING_NVTX)
find_package(CUDAToolkit REQUIRED VERSION 5.0)
set(PROFILING_NVTX_INCLUDE ${CUDAToolkit_INCLUDE_DIRS} PARENT_SCOPE)
endif ()
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ OPTION(WZ_ENABLE_BASIS_UNIVERSAL "Enable Basis Universal texture support" ON)
OPTION(WZ_DEBUG_GFX_API_LEAKS "Enable debugging for graphics API leaks" ON)
OPTION(WZ_FORCE_MINIMAL_OPUSFILE "Force a minimal build of Opusfile, since WZ does not need (or want) HTTP stream support" ON)

# Dev options
OPTION(WZ_PROFILING_NVTX "Add NVTX-based profiling instrumentation to the code" OFF)

if(CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "Darwin" OR CMAKE_SYSTEM_NAME MATCHES "Linux")
# Only supported on Windows, macOS, and Linux
OPTION(ENABLE_DISCORD "Enable Discord presence / join integration" ON)
Expand Down Expand Up @@ -744,6 +747,12 @@ include(CheckCXXStdThread)
CHECK_CXX_STD_THREAD(HAVE_STD_THREAD)
cmake_reset_check_state()

if(WZ_PROFILING_NVTX)
set(WZ_PROFILING_INSTRUMENTATION ON)
else()
unset(WZ_PROFILING_INSTRUMENTATION)
endif()

set(WZ_BINDIR "${CMAKE_INSTALL_BINDIR}")
set(WZ_LOCALEDIR "${CMAKE_INSTALL_LOCALEDIR}")
message(STATUS "WZ_BINDIR=\"${WZ_BINDIR}\"")
Expand Down
2 changes: 1 addition & 1 deletion lib/framework/wzapp.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ bool wzBackendAttemptOpenURL(const char *url);
uint64_t wzGetCurrentSystemRAM(); // gets the system RAM in MiB

// Thread related
WZ_THREAD *wzThreadCreate(int (*threadFunc)(void *), void *data);
WZ_THREAD *wzThreadCreate(int (*threadFunc)(void *), void *data, const char* name = nullptr);
unsigned long wzThreadID(WZ_THREAD *thread);
WZ_DECL_NONNULL(1) int wzThreadJoin(WZ_THREAD *thread);
WZ_DECL_NONNULL(1) void wzThreadDetach(WZ_THREAD *thread);
Expand Down
7 changes: 5 additions & 2 deletions lib/sdl/main_sdl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -811,9 +811,12 @@ void wzDelay(unsigned int delay)
/**************************/
/*** Thread support ***/
/**************************/
WZ_THREAD *wzThreadCreate(int (*threadFunc)(void *), void *data)
WZ_THREAD *wzThreadCreate(int (*threadFunc)(void *), void *data, const char* name)
{
return (WZ_THREAD *)SDL_CreateThread(threadFunc, "wzThread", data);
const char* defaultName = "wzThread";
if (name == nullptr)
name = defaultName;
return (WZ_THREAD *)SDL_CreateThread(threadFunc, name, data);
}

unsigned long wzThreadID(WZ_THREAD *thread)
Expand Down
4 changes: 4 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ find_package(SQLite3 3.14 REQUIRED)
target_link_libraries(warzone2100 SQLite::SQLite3)
target_link_libraries(warzone2100 SQLiteCpp)

if (WZ_PROFILING_NVTX)
target_include_directories(warzone2100 PRIVATE ${PROFILING_NVTX_INCLUDE})
endif()

set(_curl_gnutls_thread_safe_fix FALSE)
if (DEFINED CURL_GNUTLS_REQUIRES_CALLBACKS)
if (CURL_GNUTLS_REQUIRES_CALLBACKS STREQUAL "YES")
Expand Down
7 changes: 7 additions & 0 deletions src/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,11 @@
#cmakedefine WZ_LOCALEDIR "@WZ_LOCALEDIR@"
#cmakedefine WZ_LOCALEDIR_ISABSOLUTE

/* Enables profiling instrumentation. */
#cmakedefine WZ_PROFILING_INSTRUMENTATION
/* Enables usage of NVTX-based instrumentation backend. */
#cmakedefine WZ_PROFILING_NVTX
/* Enables usage of VTune-based instrumentation backend. */
#cmakedefine WZ_PROFILING_VTUNE

#endif // __INCLUDED_WZ_GENERATED_CONFIG_H__
4 changes: 3 additions & 1 deletion src/fpath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "astar.h"

#include "fpath.h"
#include "profiling.h"

// If the path finding system is shutdown or not
static volatile bool fpathQuit = false;
Expand Down Expand Up @@ -86,6 +87,7 @@ static int fpathThreadFunc(void *)
continue;
}

WZ_PROFILE_SCOPE(fpathJob);
// Copy the first job from the queue.
packagedPathJob job = std::move(pathJobs.front());
pathJobs.pop_front();
Expand Down Expand Up @@ -114,7 +116,7 @@ bool fpathInitialise()
fpathMutex = wzMutexCreate();
fpathSemaphore = wzSemaphoreCreate(0);
waitingForResultSemaphore = wzSemaphoreCreate(0);
fpathThread = wzThreadCreate(fpathThreadFunc, nullptr);
fpathThread = wzThreadCreate(fpathThreadFunc, nullptr, "wzPath");
wzThreadStart(fpathThread);
}

Expand Down
5 changes: 5 additions & 0 deletions src/loop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
#include "scores.h"
#include "clparse.h"
#include "gamehistorylogger.h"
#include "profiling.h"

#include "warzoneconfig.h"

Expand Down Expand Up @@ -136,6 +137,7 @@ LEVEL_TYPE nextMissionType = LEVEL_TYPE::LDS_NONE;

static GAMECODE renderLoop()
{
WZ_PROFILE_SCOPE(renderLoop);
if (bMultiPlayer && !NetPlay.isHostAlive && NetPlay.bComms && !NetPlay.isHost)
{
intAddInGamePopup();
Expand Down Expand Up @@ -322,6 +324,7 @@ static GAMECODE renderLoop()
displayWorld();
}
wzPerfBegin(PERF_GUI, "User interface");
WZ_PROFILE_SCOPE(DrawUI);
/* Display the in game interface */
pie_SetFogStatus(false);

Expand Down Expand Up @@ -493,6 +496,7 @@ void countUpdate(bool synch)

static void gameStateUpdate()
{
WZ_PROFILE_SCOPE(gameStateUpdate);
syncDebug("map = \"%s\", pseudorandom 32-bit integer = 0x%08X, allocated = %d %d %d %d %d %d %d %d %d %d, position = %d %d %d %d %d %d %d %d %d %d", game.map, gameRandU32(),
NetPlay.players[0].allocated, NetPlay.players[1].allocated, NetPlay.players[2].allocated, NetPlay.players[3].allocated, NetPlay.players[4].allocated, NetPlay.players[5].allocated, NetPlay.players[6].allocated, NetPlay.players[7].allocated, NetPlay.players[8].allocated, NetPlay.players[9].allocated,
NetPlay.players[0].position, NetPlay.players[1].position, NetPlay.players[2].position, NetPlay.players[3].position, NetPlay.players[4].position, NetPlay.players[5].position, NetPlay.players[6].position, NetPlay.players[7].position, NetPlay.players[8].position, NetPlay.players[9].position
Expand Down Expand Up @@ -617,6 +621,7 @@ void setMaxFastForwardTicks(optional<size_t> value, bool fixedToNormalTickRate)
/* The main game loop */
GAMECODE gameLoop()
{
WZ_PROFILE_SCOPE(gameLoop);
static uint32_t lastFlushTime = 0;

static size_t numForcedUpdatesLastCall = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2106,7 +2106,7 @@ void mapInit()
lastDangerPlayer = 0;
dangerSemaphore = wzSemaphoreCreate(0);
dangerDoneSemaphore = wzSemaphoreCreate(0);
dangerThread = wzThreadCreate(dangerThreadFunc, nullptr);
dangerThread = wzThreadCreate(dangerThreadFunc, nullptr, "wzDanger");
wzThreadStart(dangerThread);
}
}
Expand Down
196 changes: 196 additions & 0 deletions src/profiling.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/*
This file is part of Warzone 2100.
Copyright (C) 2023 Warzone 2100 Project

Warzone 2100 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

Warzone 2100 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "profiling.h"

#if defined(WZ_PROFILING_INSTRUMENTATION)

#include <cstdio>
#include <string>

#ifdef WZ_PROFILING_NVTX
#include <nvtx3/nvToolsExt.h>
#endif

#ifdef WZ_PROFILING_VTUNE
#include <ittnotify.h>
#endif

namespace profiling
{

struct Domain::Internal
{
#ifdef WZ_PROFILING_NVTX
nvtxDomainHandle_t nvtxDomain = nullptr;
#endif

#ifdef WZ_PROFILING_VTUNE
__itt_domain* ittDomain = nullptr;
#endif
std::string name;

~Internal()
{
#ifdef WZ_PROFILING_NVTX
if (nvtxDomain)
{
nvtxDomainDestroy(nvtxDomain);
nvtxDomain = nullptr;
}
#endif
}
};

Domain::Domain(const char* name)
{
m_internal = new Internal();
m_internal->name = name ? name : "Unnamed";
#ifdef WZ_PROFILING_NVTX
m_internal->nvtxDomain = nvtxDomainCreateA(name);
#endif

#ifdef WZ_PROFILING_VTUNE
m_internal->ittDomain = __itt_domain_create(name);
#endif
}

Domain::~Domain()
{
if (m_internal)
{
#ifdef WZ_PROFILING_NVTX
nvtxDomainDestroy(m_internal->nvtxDomain);
m_internal->nvtxDomain = nullptr;
#endif
delete m_internal;
m_internal = nullptr;
}
}

// Global domain for warzone.
Domain wzRootDomain{"warzone2100"};

Scope::Scope(const Domain *domain, const char *name)
:m_domain(domain)
{
if (m_domain && name)
{
#ifdef WZ_PROFILING_NVTX
{
nvtxRangePushA(name);
}
#endif
#ifdef WZ_PROFILING_VTUNE
{
__itt_string_handle* task = __itt_string_handle_create(name);
auto ittDomain = m_domain ? m_domain->getInternal()->ittDomain : nullptr;
__itt_task_begin(ittDomain, __itt_null, __itt_null, task);
}
#endif
}
}

Scope::Scope(const Domain *domain, const char *object, const char *name)
:m_domain(domain)
{
if (m_domain && object && name)
{
static char tmpBuffer[255];
std::snprintf(tmpBuffer, sizeof(tmpBuffer), "%s::%s", object, name);
#ifdef WZ_PROFILING_NVTX
{
nvtxRangePushA(tmpBuffer);
}
#endif
#ifdef WZ_PROFILING_VTUNE
{
__itt_string_handle* task = __itt_string_handle_create(tmpBuffer);
auto ittDomain = m_domain ? m_domain->getInternal()->ittDomain : nullptr;
__itt_task_begin(ittDomain, __itt_null, __itt_null, task);
}
#endif
}
}

Scope::~Scope()
{
if (m_domain) {
#ifdef WZ_PROFILING_NVTX
nvtxRangePop();
#endif
#ifdef WZ_PROFILING_VTUNE
auto ittDomain = m_domain->getInternal()->ittDomain;
__itt_task_end(ittDomain);
#endif
}
}

void mark(const Domain *domain, const char *mark)
{
if (!domain || !mark)
return;
#ifdef WZ_PROFILING_NVTX
{
nvtxEventAttributes_t eventAttrib = {};
eventAttrib.version = NVTX_VERSION;
eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII;
eventAttrib.message.ascii = mark;
auto nvtxDomain = domain ? domain->getInternal()->nvtxDomain : nullptr;
nvtxDomainMarkEx(nvtxDomain, &eventAttrib);
}
#endif
#ifdef WZ_PROFILING_VTUNE
auto string = __itt_string_handle_create(mark);
auto ittDomain = domain ? domain->getInternal()->ittDomain : nullptr;
__itt_marker(ittDomain, __itt_null, string, __itt_scope::__itt_scope_task);
#endif
}

void mark(const Domain *domain, const char *object, const char *mark)
{
if (!domain || !object || !mark)
return;
static char tmpBuffer[255];
std::snprintf(tmpBuffer, sizeof(tmpBuffer), "%s::%s", object, mark);

#ifdef WZ_PROFILING_NVTX
{
nvtxEventAttributes_t eventAttrib = {};
eventAttrib.version = NVTX_VERSION;
eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII;
eventAttrib.message.ascii = tmpBuffer;
auto nvtxDomain = domain ? domain->getInternal()->nvtxDomain : nullptr;
nvtxDomainMarkEx(nvtxDomain, &eventAttrib);
}
#endif
#ifdef WZ_PROFILING_VTUNE
{
auto string = __itt_string_handle_create(msg.c_str());
auto ittDomain = domain ? domain->getInternal()->ittDomain : nullptr;
__itt_marker(ittDomain, __itt_null, string, __itt_scope::__itt_scope_task);
}
#endif
}

}

#endif // defined(WZ_PROFILING_INSTRUMENTATION)
Loading