diff --git a/CMakePresets.json b/CMakePresets.json index fb1da4c5cc8..c6af9550ffe 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -110,7 +110,8 @@ "inherits": "win32", "displayName": "Windows 32bit Profile", "cacheVariables": { - "RTS_BUILD_OPTION_PROFILE": "ON" + "RTS_BUILD_OPTION_PROFILE": "ON", + "RTS_BUILD_OPTION_PROFILE_TRACY": "ON" } }, { diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt index 7c1269d1db9..49bd9f6a0b9 100644 --- a/Core/CMakeLists.txt +++ b/Core/CMakeLists.txt @@ -22,6 +22,7 @@ target_sources(corei_libraries_include PRIVATE ) target_link_libraries(corei_always INTERFACE core_config + core_profile_tracy core_utility corei_libraries_include resources diff --git a/Core/GameEngine/Source/Common/FrameRateLimit.cpp b/Core/GameEngine/Source/Common/FrameRateLimit.cpp index b8c7f1ff7a9..ffd76c23439 100644 --- a/Core/GameEngine/Source/Common/FrameRateLimit.cpp +++ b/Core/GameEngine/Source/Common/FrameRateLimit.cpp @@ -32,6 +32,7 @@ FrameRateLimit::FrameRateLimit() Real FrameRateLimit::wait(UnsignedInt maxFps) { + PROFILER_SECTION; LARGE_INTEGER tick; QueryPerformanceCounter(&tick); double elapsedSeconds = static_cast(tick.QuadPart - m_start) / m_freq; diff --git a/Core/GameEngine/Source/GameLogic/AI/AIPathfind.cpp b/Core/GameEngine/Source/GameLogic/AI/AIPathfind.cpp index 09886bb0a7b..0d8003df21a 100644 --- a/Core/GameEngine/Source/GameLogic/AI/AIPathfind.cpp +++ b/Core/GameEngine/Source/GameLogic/AI/AIPathfind.cpp @@ -6072,9 +6072,7 @@ void Pathfinder::processPathfindQueue() m_logicalExtent = bounds; m_cumulativeCellsAllocated = 0; // Number of pathfind cells examined. -#ifdef DEBUG_QPF Int pathsFound = 0; -#endif while (m_cumulativeCellsAllocated < PATHFIND_CELLS_PER_FRAME && m_queuePRTail!=m_queuePRHead) { Object *obj = TheGameLogic->findObjectByID(m_queuedPathfindRequests[m_queuePRHead]); @@ -6083,9 +6081,7 @@ void Pathfinder::processPathfindQueue() AIUpdateInterface *ai = obj->getAIUpdateInterface(); if (ai) { ai->doPathfind(this); -#ifdef DEBUG_QPF pathsFound++; -#endif } } m_queuePRHead = m_queuePRHead+1; @@ -6093,6 +6089,10 @@ void Pathfinder::processPathfindQueue() m_queuePRHead = 0; } } + if (pathsFound > 0) { + PROFILER_PLOT("PathfindCells", (double)m_cumulativeCellsAllocated); + PROFILER_PLOT("PathfindPaths", (double)pathsFound); + } #ifdef DEBUG_QPF if (pathsFound>0) { #ifdef DEBUG_LOGGING diff --git a/Core/GameEngineDevice/CMakeLists.txt b/Core/GameEngineDevice/CMakeLists.txt index 74b040200ae..0a4bbf7ab20 100644 --- a/Core/GameEngineDevice/CMakeLists.txt +++ b/Core/GameEngineDevice/CMakeLists.txt @@ -56,6 +56,7 @@ set(GAMEENGINEDEVICE_SRC Include/W3DDevice/GameClient/W3DMouse.h # Include/W3DDevice/GameClient/W3DParticleSys.h # Include/W3DDevice/GameClient/W3DPoly.h + Include/W3DDevice/GameClient/W3DProfilerFrameCapture.h # Include/W3DDevice/GameClient/W3DProjectedShadow.h Include/W3DDevice/GameClient/W3DPropBuffer.h # Include/W3DDevice/GameClient/W3DRoadBuffer.h @@ -159,6 +160,7 @@ set(GAMEENGINEDEVICE_SRC Source/W3DDevice/GameClient/W3DMouse.cpp # Source/W3DDevice/GameClient/W3DParticleSys.cpp # Source/W3DDevice/GameClient/W3DPoly.cpp + Source/W3DDevice/GameClient/W3DProfilerFrameCapture.cpp Source/W3DDevice/GameClient/W3DPropBuffer.cpp # Source/W3DDevice/GameClient/W3DRoadBuffer.cpp # Source/W3DDevice/GameClient/W3DScene.cpp diff --git a/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DProfilerFrameCapture.h b/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DProfilerFrameCapture.h new file mode 100644 index 00000000000..62c42ab9bfe --- /dev/null +++ b/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DProfilerFrameCapture.h @@ -0,0 +1,43 @@ +/* +** Command & Conquer Generals Zero Hour(tm) +** Copyright 2026 TheSuperHackers +** +** This program 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 3 of the License, or +** (at your option) any later version. +** +** This program 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 this program. If not, see . +*/ + +#pragma once + +#ifdef PROFILER_ENABLED + +#include "Lib/BaseType.h" +#include + +class W3DProfilerFrameCapture +{ +public: + W3DProfilerFrameCapture(); + ~W3DProfilerFrameCapture(); + + void Capture(UnsignedInt displayWidth, UnsignedInt displayHeight); + +private: + bool ShouldReuseLastCapture(UnsignedInt currentTimeMs) const; + + DWORD m_swizzleShader = 0; + UnsignedInt m_lastCaptureTimeMs = 0; + UnsignedInt m_lastCaptureHeight = 0; + std::vector m_lastCapturePixels; +}; + +#endif // PROFILER_ENABLED diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DProfilerFrameCapture.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DProfilerFrameCapture.cpp new file mode 100644 index 00000000000..90a8d931982 --- /dev/null +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DProfilerFrameCapture.cpp @@ -0,0 +1,255 @@ +/* +** Command & Conquer Generals Zero Hour(tm) +** Copyright 2026 TheSuperHackers +** +** This program 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 3 of the License, or +** (at your option) any later version. +** +** This program 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 this program. If not, see . +*/ + +#ifdef PROFILER_ENABLED + +#include "../../../Include/W3DDevice/GameClient/W3DProfilerFrameCapture.h" + +#include "WW3D2/dx8wrapper.h" +#include "WW3D2/surfaceclass.h" +#include "WW3D2/texture.h" +#include "WW3D2/ww3d.h" +#include "WW3D2/ww3dformat.h" +#include "WWMath/wwmath.h" +#include +#include + +W3DProfilerFrameCapture::W3DProfilerFrameCapture() +{ +} + +W3DProfilerFrameCapture::~W3DProfilerFrameCapture() +{ + if (m_swizzleShader) + { + DX8Wrapper::_Get_D3D_Device8()->DeletePixelShader(m_swizzleShader); + m_swizzleShader = 0; + } +} + +bool W3DProfilerFrameCapture::ShouldReuseLastCapture(UnsignedInt currentTimeMs) const +{ + return PROFILER_FRAME_IMAGE_INTERVAL_MS > 0 + && currentTimeMs - m_lastCaptureTimeMs < PROFILER_FRAME_IMAGE_INTERVAL_MS + && !m_lastCapturePixels.empty(); +} + +void W3DProfilerFrameCapture::Capture(UnsignedInt displayWidth, UnsignedInt displayHeight) +{ + if (!PROFILER_IS_CONNECTED) + return; + + // the profiler expects an image every render frame. resend the last capture if we're inside the capture interval. + const UnsignedInt currentTimeMs = WW3D::Get_Logic_Time_Milliseconds(); + if (ShouldReuseLastCapture(currentTimeMs)) + { + PROFILER_FRAME_IMAGE(m_lastCapturePixels.data(), PROFILER_FRAME_IMAGE_SIZE, m_lastCaptureHeight, 0, false); + return; + } + + // compile swizzle shader convert BGRA to RGBA + // TheSuperHackers @todo In DX9 with ps2.0 this shader will be much simpler + if (!m_swizzleShader) + { + ID3DXBuffer *compiledShader = nullptr; + const char *shader = + "ps.1.4\n" + "texld r0, t0\n" + "mov r1.a, r0.r\n" + "mov r2.a, r0.g\n" + "mov r3.a, r0.b\n" + "mul r0.rgb, r3.a, c0\n" + "mad r0.rgb, r2.a, c1, r0\n" + "mad r0.rgb, r1.a, c2, r0\n"; + + HRESULT hr = D3DXAssembleShader(shader, strlen(shader), 0, nullptr, &compiledShader, nullptr); + if (FAILED(hr)) + return; + + hr = DX8Wrapper::_Get_D3D_Device8()->CreatePixelShader((DWORD *)compiledShader->GetBufferPointer(), &m_swizzleShader); + compiledShader->Release(); + + if (FAILED(hr)) + return; + } + + // allocate render target + TextureClass *renderTarget = DX8Wrapper::Create_Render_Target(PROFILER_FRAME_IMAGE_SIZE, PROFILER_FRAME_IMAGE_SIZE, WW3D_FORMAT_A8R8G8B8); + if (!renderTarget) + return; + + // allocate surface class + const Real aspectRatio = (Real)displayHeight / (Real)displayWidth; + unsigned int profilerImageHeight = min((int)WWMath::Round(PROFILER_FRAME_IMAGE_SIZE * aspectRatio), PROFILER_FRAME_IMAGE_SIZE); + SurfaceClass *surfaceClass = NEW_REF(SurfaceClass, (PROFILER_FRAME_IMAGE_SIZE, profilerImageHeight, WW3D_FORMAT_A8R8G8B8)); + if (!surfaceClass) + { + REF_PTR_RELEASE(renderTarget); + return; + } + + // get the backbuffer + SurfaceClass *backBuffer = DX8Wrapper::_Get_DX8_Back_Buffer(); + if (!backBuffer) + { + REF_PTR_RELEASE(surfaceClass); + REF_PTR_RELEASE(renderTarget); + return; + } + + IDirect3DSurface8 *backBufferSurface = backBuffer->Peek_D3D_Surface(); + D3DSURFACE_DESC backBufferSurfaceDesc; + HRESULT hr = backBufferSurface->GetDesc(&backBufferSurfaceDesc); + if (FAILED(hr)) + { + REF_PTR_RELEASE(backBuffer); + REF_PTR_RELEASE(surfaceClass); + REF_PTR_RELEASE(renderTarget); + return; + } + + // allocate intermediate texture + IDirect3DTexture8 *intermediateTexture = nullptr; + hr = DX8Wrapper::_Get_D3D_Device8()->CreateTexture( + backBufferSurfaceDesc.Width, + backBufferSurfaceDesc.Height, + 1, + D3DUSAGE_RENDERTARGET, + backBufferSurfaceDesc.Format, + D3DPOOL_DEFAULT, + &intermediateTexture); + if (FAILED(hr)) + { + REF_PTR_RELEASE(backBuffer); + REF_PTR_RELEASE(surfaceClass); + REF_PTR_RELEASE(renderTarget); + return; + } + + // draw backbuffer to intermediate texture + IDirect3DSurface8 *intermediateTextureSurface; + hr = intermediateTexture->GetSurfaceLevel(0, &intermediateTextureSurface); + if (FAILED(hr)) + { + REF_PTR_RELEASE(backBuffer); + REF_PTR_RELEASE(surfaceClass); + REF_PTR_RELEASE(renderTarget); + intermediateTexture->Release(); + return; + } + DX8Wrapper::_Copy_DX8_Rects(backBufferSurface, nullptr, 0, intermediateTextureSurface, nullptr); + intermediateTextureSurface->Release(); + intermediateTextureSurface = nullptr; + + // release the backbuffer + backBufferSurface = nullptr; + REF_PTR_RELEASE(backBuffer); + + // set render target to a small surface + IDirect3DSurface8 *smallRenderTargetSurface = renderTarget->Get_D3D_Surface_Level(); + WWASSERT(smallRenderTargetSurface != nullptr); + DX8Wrapper::Set_Render_Target(smallRenderTargetSurface, false); + + // set viewport + IDirect3DDevice8 *device = DX8Wrapper::_Get_D3D_Device8(); + D3DVIEWPORT8 restoreViewport; + device->GetViewport(&restoreViewport); + + SurfaceClass::SurfaceDescription smallRenderDesc; + surfaceClass->Get_Description(smallRenderDesc); + + D3DVIEWPORT8 viewport; + viewport.X = 0; + viewport.Y = 0; + viewport.Width = PROFILER_FRAME_IMAGE_SIZE; + viewport.Height = smallRenderDesc.Height; + viewport.MinZ = 0.0f; + viewport.MaxZ = 1.0f; + DX8Wrapper::Set_Viewport(&viewport); + + // bind swizzle shader + DX8Wrapper::Set_Pixel_Shader(m_swizzleShader); + static const Real kMaskR[4] = {1.0f, 0.0f, 0.0f, 0.0f}; + static const Real kMaskG[4] = {0.0f, 1.0f, 0.0f, 0.0f}; + static const Real kMaskB[4] = {0.0f, 0.0f, 1.0f, 0.0f}; + device->SetPixelShaderConstant(0, kMaskR, 1); + device->SetPixelShaderConstant(1, kMaskG, 1); + device->SetPixelShaderConstant(2, kMaskB, 1); + + // draw texture scaled-down onto a small surface + struct QuadVertex + { + Real x, y, z, rhw; + Real u, v; + } vtx[4]; + const Real left = -0.5f; + const Real top = -0.5f; + const Real right = (Real)PROFILER_FRAME_IMAGE_SIZE - 0.5f; + const Real bottom = (Real)smallRenderDesc.Height - 0.5f; + vtx[0] = {right, bottom, 0.0f, 1.0f, 1.0f, 1.0f}; + vtx[1] = {right, top, 0.0f, 1.0f, 1.0f, 0.0f}; + vtx[2] = {left, bottom, 0.0f, 1.0f, 0.0f, 1.0f}; + vtx[3] = {left, top, 0.0f, 1.0f, 0.0f, 0.0f}; + DX8Wrapper::Set_DX8_Texture(0, intermediateTexture); + DX8Wrapper::Set_Vertex_Shader(D3DFVF_XYZRHW | D3DFVF_TEX1); + device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, vtx, sizeof(QuadVertex)); + DX8Wrapper::Set_Pixel_Shader(0); + DX8Wrapper::Set_DX8_Texture(0, nullptr); + DX8Wrapper::Set_Viewport(&restoreViewport); + DX8Wrapper::Set_Render_Target(static_cast(nullptr)); + + // copy the small surface pixels from GPU to CPU + RECT srcRect = { 0, 0, PROFILER_FRAME_IMAGE_SIZE, smallRenderDesc.Height }; + POINT dstPoint = { 0, 0 }; + DX8Wrapper::_Copy_DX8_Rects( + smallRenderTargetSurface, + &srcRect, + 1, + surfaceClass->Peek_D3D_Surface(), + &dstPoint); + smallRenderTargetSurface->Release(); + + // send pixels to the profiler backend + int pitch = 0; + void *bits = surfaceClass->Lock(&pitch); + if (bits) + { + const size_t rowBytes = (size_t)PROFILER_FRAME_IMAGE_SIZE * 4; + m_lastCaptureHeight = smallRenderDesc.Height; + m_lastCapturePixels.resize(rowBytes * m_lastCaptureHeight); + + const UnsignedByte *source = static_cast(bits); + UnsignedByte *destination = m_lastCapturePixels.data(); + for (UnsignedInt row = 0; row < m_lastCaptureHeight; ++row) + { + std::memcpy(destination + row * rowBytes, source + row * pitch, rowBytes); + } + + PROFILER_FRAME_IMAGE(m_lastCapturePixels.data(), PROFILER_FRAME_IMAGE_SIZE, m_lastCaptureHeight, 0, false); + surfaceClass->Unlock(); + m_lastCaptureTimeMs = currentTimeMs; + } + + // cleanup + intermediateTexture->Release(); + intermediateTexture = nullptr; + REF_PTR_RELEASE(surfaceClass); + REF_PTR_RELEASE(renderTarget); +} + +#endif // PROFILER_ENABLED diff --git a/Core/Libraries/Include/rts/profile.h b/Core/Libraries/Include/rts/profile.h index 06faf42d045..97b50c93261 100644 --- a/Core/Libraries/Include/rts/profile.h +++ b/Core/Libraries/Include/rts/profile.h @@ -27,6 +27,43 @@ // Proxy header for profile module ////////////////////////////////////////////////////////////////////////////// -# pragma once +#pragma once +#if defined(RTS_PROFILE_LEGACY) #include "../../Source/profile/profile.h" +#endif + +#if defined(RTS_PROFILE_TRACY) + +#include + +#define PROFILER_ENABLED +#define PROFILER_FRAME_IMAGE_SIZE 256 // Horizontal size of the frame image in pixels. +#define PROFILER_FRAME_IMAGE_INTERVAL_MS 500 // Will capture every render frame if set to 0 +#define PROFILER_SECTION ZoneScoped +#define PROFILER_SECTION_NAME(name) ZoneScopedN(name) +#define PROFILER_SECTION_COLOR(color) ZoneScopedC(color) +#define PROFILER_SECTION_NAMECOLOR(name, color) ZoneScopedNC(name, color) +#define PROFILER_FRAME_MARK FrameMark +#define PROFILER_FRAME_MARK_NAME(name) FrameMarkNamed(name) +#define PROFILER_FRAME_IMAGE(image, width, height, offset, flip) FrameImage(image, width, height, offset, flip) +#define PROFILER_MSG(txt, size) TracyMessage(txt, size) +#define PROFILER_PLOT(name, value) TracyPlot(name, value) +#define PROFILER_IS_CONNECTED TracyIsConnected + +#else + +#define PROFILER_FRAME_IMAGE_SIZE 0 +#define PROFILER_FRAME_IMAGE_INTERVAL_MS 0 +#define PROFILER_SECTION ((void)0) +#define PROFILER_SECTION_NAME(name) ((void)0) +#define PROFILER_SECTION_COLOR(color) ((void)0) +#define PROFILER_SECTION_NAMECOLOR(name, color) ((void)0) +#define PROFILER_FRAME_MARK ((void)0) +#define PROFILER_FRAME_MARK_NAME(name) ((void)0) +#define PROFILER_FRAME_IMAGE(image, width, height, offset, flip) ((void)0) +#define PROFILER_MSG(txt, size) ((void)0) +#define PROFILER_PLOT(name, value) ((void)0) +#define PROFILER_IS_CONNECTED false + +#endif diff --git a/Core/Libraries/Source/WWVegas/WWLib/WWCommon.h b/Core/Libraries/Source/WWVegas/WWLib/WWCommon.h index 2d67c465d92..b97ec772535 100644 --- a/Core/Libraries/Source/WWVegas/WWLib/WWCommon.h +++ b/Core/Libraries/Source/WWVegas/WWLib/WWCommon.h @@ -22,6 +22,7 @@ #include "STLUtils.h" #include "stringex.h" #include +#include #ifndef SAFE_RELEASE #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=nullptr; } } diff --git a/Core/Libraries/Source/debug/debug.h b/Core/Libraries/Source/debug/debug.h index 11b75830a11..414f185be5d 100644 --- a/Core/Libraries/Source/debug/debug.h +++ b/Core/Libraries/Source/debug/debug.h @@ -81,7 +81,7 @@ */ // Define which libraries to use. -#if defined(RTS_DEBUG) || defined(RTS_PROFILE) +#if defined(RTS_DEBUG) || defined(RTS_PROFILE_LEGACY) # define HAS_ASSERTS # define HAS_LOGS #endif @@ -90,7 +90,7 @@ # define HAS_OPT #endif -#if defined(RTS_PROFILE) +#if defined(RTS_PROFILE_LEGACY) # define HAS_PROFILE #endif diff --git a/Core/Libraries/Source/debug/debug_debug.cpp b/Core/Libraries/Source/debug/debug_debug.cpp index 232125caca4..18adcc0cceb 100644 --- a/Core/Libraries/Source/debug/debug_debug.cpp +++ b/Core/Libraries/Source/debug/debug_debug.cpp @@ -1498,7 +1498,7 @@ void Debug::WriteBuildInfo() (*this) << " internal " << m_intVersion; #if defined(RTS_DEBUG) operator<<(" debug"); - #elif defined(RTS_PROFILE) + #elif defined(RTS_PROFILE_LEGACY) || defined(RTS_PROFILE_TRACY) operator<<(" profile"); #else operator<<(" release"); diff --git a/Core/Libraries/Source/profile/CMakeLists.txt b/Core/Libraries/Source/profile/CMakeLists.txt index 5fee2d17b0e..b0db6bd2dc9 100644 --- a/Core/Libraries/Source/profile/CMakeLists.txt +++ b/Core/Libraries/Source/profile/CMakeLists.txt @@ -16,22 +16,22 @@ set(PROFILE_SRC "profile.h" ) -add_library(core_profile STATIC) +add_library(core_profile_legacy STATIC) -target_sources(core_profile PRIVATE ${PROFILE_SRC}) +target_sources(core_profile_legacy PRIVATE ${PROFILE_SRC}) -target_include_directories(core_profile INTERFACE +target_include_directories(core_profile_legacy INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} ) -target_precompile_headers(core_profile PRIVATE +target_precompile_headers(core_profile_legacy PRIVATE [["Utility/CppMacros.h"]] # Must be first, to be removed when abandoning VC6 "profile.h" "internal.h" ) -target_link_libraries(core_profile PRIVATE +target_link_libraries(core_profile_legacy PRIVATE core_wwcommon corei_always ) diff --git a/Core/Libraries/Source/profile/profile.cpp b/Core/Libraries/Source/profile/profile.cpp index e43bda74273..f0a2265166f 100644 --- a/Core/Libraries/Source/profile/profile.cpp +++ b/Core/Libraries/Source/profile/profile.cpp @@ -199,7 +199,7 @@ void Profile::StartRange(const char *range) if (active) { -#ifdef RTS_PROFILE +#ifdef RTS_PROFILE_LEGACY m_frameNames[k].funcIndex=ProfileFuncLevelTracer::FrameStart(); DASSERT(m_frameNames[k].funcIndex>=0); #endif @@ -250,7 +250,7 @@ void Profile::AppendRange(const char *range) if (active) { -#ifdef RTS_PROFILE +#ifdef RTS_PROFILE_LEGACY m_frameNames[k].funcIndex=ProfileFuncLevelTracer::FrameStart(); DASSERT(m_frameNames[k].funcIndex>=0); #endif @@ -281,7 +281,7 @@ void Profile::StopRange(const char *range) // stop recording m_frameNames[k].isRecording=false; if ( -#ifdef RTS_PROFILE +#ifdef RTS_PROFILE_LEGACY m_frameNames[k].funcIndex>=0 || #endif m_frameNames[k].highIndex>=0 @@ -300,7 +300,7 @@ void Profile::StopRange(const char *range) } else atIndex=m_frameNames[k].lastGlobalIndex; -#ifdef RTS_PROFILE +#ifdef RTS_PROFILE_LEGACY if (m_frameNames[k].funcIndex>=0) ProfileFuncLevelTracer::FrameEnd(m_frameNames[k].funcIndex,atIndex); if (m_frameNames[k].highIndex>=0) @@ -329,7 +329,7 @@ const char *Profile::GetFrameName(unsigned frame) void Profile::ClearTotals() { -#ifdef RTS_PROFILE +#ifdef RTS_PROFILE_LEGACY ProfileFuncLevelTracer::ClearTotals(); #endif ProfileId::ClearTotals(); @@ -372,7 +372,7 @@ bool Profile::SimpleMatch(const char *str, const char *pattern) static void ProfileShutdown() { -#ifdef RTS_PROFILE +#ifdef RTS_PROFILE_LEGACY ProfileFuncLevelTracer::Shutdown(); #endif ProfileId::Shutdown(); diff --git a/Core/Libraries/Source/profile/profile_funclevel.h b/Core/Libraries/Source/profile/profile_funclevel.h index 8f6517065da..a153a5f9e28 100644 --- a/Core/Libraries/Source/profile/profile_funclevel.h +++ b/Core/Libraries/Source/profile/profile_funclevel.h @@ -33,7 +33,7 @@ \brief The function level profiler. Note that this class exists even if the current build configuration - is not RTS_PROFILE. In these cases all calls will simply return + is not RTS_PROFILE_LEGACY. In these cases all calls will simply return empty data. */ class ProfileFuncLevel diff --git a/Core/Tools/ImagePacker/CMakeLists.txt b/Core/Tools/ImagePacker/CMakeLists.txt index db8aaf2d157..bcf49077428 100644 --- a/Core/Tools/ImagePacker/CMakeLists.txt +++ b/Core/Tools/ImagePacker/CMakeLists.txt @@ -28,7 +28,7 @@ target_include_directories(corei_imagepacker INTERFACE target_link_libraries(corei_imagepacker INTERFACE comctl32 core_debug - core_profile + core_profile_legacy core_wwcommon imm32 vfw32 diff --git a/Core/Tools/MapCacheBuilder/CMakeLists.txt b/Core/Tools/MapCacheBuilder/CMakeLists.txt index bdb38951a54..06d17b28b3e 100644 --- a/Core/Tools/MapCacheBuilder/CMakeLists.txt +++ b/Core/Tools/MapCacheBuilder/CMakeLists.txt @@ -16,7 +16,7 @@ target_include_directories(corei_mapcachebuilder INTERFACE target_link_libraries(corei_mapcachebuilder INTERFACE comctl32 core_debug - core_profile + core_profile_legacy imm32 vfw32 winmm diff --git a/Core/Tools/PATCHGET/CMakeLists.txt b/Core/Tools/PATCHGET/CMakeLists.txt index bd271598c5f..7682d62f5b0 100644 --- a/Core/Tools/PATCHGET/CMakeLists.txt +++ b/Core/Tools/PATCHGET/CMakeLists.txt @@ -24,7 +24,7 @@ target_sources(corei_patchgrabber INTERFACE ${PATCHGET_SRC}) target_link_libraries(corei_patchgrabber INTERFACE comctl32 core_debug - core_profile + core_profile_legacy gamespy::gamespy imm32 vfw32 diff --git a/Core/Tools/ParticleEditor/CMakeLists.txt b/Core/Tools/ParticleEditor/CMakeLists.txt index 39c5735bb72..a67e9c55480 100644 --- a/Core/Tools/ParticleEditor/CMakeLists.txt +++ b/Core/Tools/ParticleEditor/CMakeLists.txt @@ -38,7 +38,7 @@ target_include_directories(core_particleeditor PRIVATE target_link_libraries(core_particleeditor PRIVATE core_debug - core_profile + core_profile_legacy corei_always_no_pch corei_gameengine_include corei_libraries_source_wwvegas diff --git a/Generals/Code/GameEngine/Include/GameLogic/ScriptEngine.h b/Generals/Code/GameEngine/Include/GameLogic/ScriptEngine.h index 266f4d7a362..ef943a40cc5 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/ScriptEngine.h +++ b/Generals/Code/GameEngine/Include/GameLogic/ScriptEngine.h @@ -47,7 +47,7 @@ class Player; class PolygonTrigger; class ObjectTypes; -#ifdef RTS_PROFILE +#ifdef RTS_PROFILE_LEGACY #define SPECIAL_SCRIPT_PROFILING #endif diff --git a/Generals/Code/GameEngine/Source/GameClient/GameClient.cpp b/Generals/Code/GameEngine/Source/GameClient/GameClient.cpp index ad59f27d9e9..5f45c3d3c4c 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GameClient.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GameClient.cpp @@ -490,6 +490,8 @@ DECLARE_PERF_TIMER(GameClient_draw) void GameClient::update() { USE_PERF_TIMER(GameClient_update) + PROFILER_FRAME_MARK; + PROFILER_SECTION_COLOR(0x2196F3); // create the FRAME_TICK message GameMessage *frameMsg = TheMessageStream->appendMessage( GameMessage::MSG_FRAME_TICK ); frameMsg->appendTimestampArgument( getFrame() ); diff --git a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index 780bdfff8a2..790225d6a25 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -2106,7 +2106,11 @@ void GameLogic::startNewGame( Bool loadingSaveGame ) TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE( "GUI:FastForwardInstructions", L"Press F to toggle Fast Forward" ) ); } - +#ifdef PROFILER_ENABLED + AsciiString message; + message.format("GameStart: %s", TheGlobalData->m_mapName.str()); + PROFILER_MSG(message.str(), message.getLength()); +#endif } //----------------------------------------------------------------------------------------- @@ -3120,6 +3124,7 @@ extern __int64 Total_Load_3D_Assets; void GameLogic::update() { USE_PERF_TIMER(GameLogic_update) + PROFILER_SECTION_COLOR(0x4CAF50); LatchRestore inUpdateLatch(m_isInUpdate, TRUE); #ifdef DO_UNIT_TIMINGS @@ -3160,6 +3165,8 @@ void GameLogic::update() UnsignedInt now = getFrame(); TheGameClient->setFrame(now); + PROFILER_PLOT("LogicFrame", static_cast(now)); + // update (execute) scripts { TheScriptEngine->UPDATE(); @@ -3656,6 +3663,12 @@ void GameLogic::exitGame() TheScriptEngine->doUnfreezeTime(); TheMessageStream->appendMessage(GameMessage::MSG_CLEAR_GAME_DATA); + +#ifdef PROFILER_ENABLED + AsciiString message; + message.format("GameEnd: %s", TheGlobalData->m_mapName.str()); + PROFILER_MSG(message.str(), message.getLength()); +#endif } // ------------------------------------------------------------------------------------------------ diff --git a/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DDisplay.h b/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DDisplay.h index 937e2d756ba..a1ae9bcc298 100644 --- a/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DDisplay.h +++ b/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DDisplay.h @@ -35,6 +35,7 @@ #include "GameClient/Display.h" #include "WW3D2/lightenvironment.h" +#include "W3DDevice/GameClient/W3DProfilerFrameCapture.h" class VideoBuffer; class W3DDebugDisplay; @@ -183,6 +184,10 @@ class W3DDisplay : public Display Int64 m_timerAtCumuFPSStart; #endif +#ifdef PROFILER_ENABLED + W3DProfilerFrameCapture *m_profilerFrameCapture; +#endif + enum { FPS, ///< debug display frames per second diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp index 09a31378bf6..85b45a94609 100644 --- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp +++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp @@ -70,6 +70,7 @@ static void drawFramerateBar(); #include "W3DDevice/GameClient/W3DGameClient.h" #include "W3DDevice/GameClient/W3DFileSystem.h" #include "W3DDevice/GameClient/W3DDynamicLight.h" +#include "W3DDevice/GameClient/W3DProfilerFrameCapture.h" #include "W3DDevice/GameClient/HeightMap.h" #include "W3DDevice/GameClient/WorldHeightMap.h" #include "W3DDevice/GameClient/W3DScene.h" @@ -358,6 +359,10 @@ W3DDisplay::W3DDisplay() m_batchMode = DRAW_IMAGE_ALPHA; m_batchGrayscale = FALSE; m_batchNeedsInit = FALSE; + +#ifdef PROFILER_ENABLED + m_profilerFrameCapture = NEW W3DProfilerFrameCapture(); +#endif } // W3DDisplay::~W3DDisplay ==================================================== @@ -365,6 +370,10 @@ W3DDisplay::W3DDisplay() //============================================================================= W3DDisplay::~W3DDisplay() { +#ifdef PROFILER_ENABLED + delete m_profilerFrameCapture; + m_profilerFrameCapture = nullptr; +#endif // get rid of the debug display delete m_debugDisplay; @@ -1968,6 +1977,13 @@ void W3DDisplay::draw() TheGraphDraw->render(); TheGraphDraw->clear(); #endif + +#ifdef PROFILER_ENABLED + if (m_profilerFrameCapture && !TheGlobalData->m_headless) + { + m_profilerFrameCapture->Capture(getWidth(), getHeight()); + } +#endif // render is all done! WW3D::End_Render(); } diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/ScriptEngine.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/ScriptEngine.h index a7400c4d563..a6ce0c38a7d 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/ScriptEngine.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/ScriptEngine.h @@ -47,7 +47,7 @@ class Player; class PolygonTrigger; class ObjectTypes; -#ifdef RTS_PROFILE +#ifdef RTS_PROFILE_LEGACY #define SPECIAL_SCRIPT_PROFILING #endif diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/MainMenu.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/MainMenu.cpp index 9e4cdbbe79f..f51953e454f 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/MainMenu.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/MainMenu.cpp @@ -92,7 +92,7 @@ enum static Bool raiseMessageBoxes = TRUE; static Bool campaignSelected = FALSE; -#if defined(RTS_DEBUG) || defined RTS_PROFILE +#if defined(RTS_DEBUG) || defined RTS_PROFILE_LEGACY static NameKeyType campaignID = NAMEKEY_INVALID; static GameWindow *buttonCampaign = nullptr; #ifdef TEST_COMPRESSION @@ -529,7 +529,7 @@ void MainMenuInit( WindowLayout *layout, void *userData ) showSelectiveButtons(SHOW_NONE); // Set up the version number -#if defined(RTS_DEBUG) || defined RTS_PROFILE +#if defined(RTS_DEBUG) || defined RTS_PROFILE_LEGACY WinInstanceData instData; #ifdef TEST_COMPRESSION instData.init(); @@ -1255,7 +1255,7 @@ WindowMsgHandledType MainMenuSystem( GameWindow *window, UnsignedInt msg, if(buttonPushed) break; -#if defined(RTS_DEBUG) || defined RTS_PROFILE +#if defined(RTS_DEBUG) || defined RTS_PROFILE_LEGACY if( control == buttonCampaign ) { buttonPushed = TRUE; diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp index bf9eb5f3387..e7d1333905b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp @@ -42,8 +42,6 @@ #include "GameNetwork/GameSpyOverlay.h" #include "GameNetwork/GameSpy/PeerDefsImplementation.h" -#include - // PUBLIC DATA //////////////////////////////////////////////////////////////////////////////////// Shell *TheShell = nullptr; ///< the shell singleton definition @@ -518,7 +516,7 @@ void Shell::showShell( Bool runInit ) if (!TheGlobalData->m_shellMapOn && m_screenCount == 0) { -#ifdef RTS_PROFILE +#ifdef RTS_PROFILE_LEGACY Profile::StopRange("init"); #endif //else diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp index 786395fbffb..aeeae11e342 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp @@ -511,6 +511,8 @@ DECLARE_PERF_TIMER(GameClient_draw) void GameClient::update() { USE_PERF_TIMER(GameClient_update) + PROFILER_FRAME_MARK; + PROFILER_SECTION_COLOR(0x2196F3); // create the FRAME_TICK message GameMessage *frameMsg = TheMessageStream->appendMessage( GameMessage::MSG_FRAME_TICK ); frameMsg->appendTimestampArgument( getFrame() ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index b4e678aa1d6..3e06e11a522 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -111,8 +111,6 @@ #include "GameNetwork/NetworkInterface.h" #include "GameNetwork/GameSpy/PersistentStorageThread.h" -#include - DECLARE_PERF_TIMER(SleepyMaintenance) #include "Common/UnitTimings.h" //Contains the DO_UNIT_TIMINGS define jba. @@ -2406,7 +2404,11 @@ void GameLogic::startNewGame( Bool loadingSaveGame ) TheInGameUI->messageNoFormat( TheGameText->FETCH_OR_SUBSTITUTE( "GUI:FastForwardInstructions", L"Press F to toggle Fast Forward" ) ); } - +#ifdef PROFILER_ENABLED + AsciiString message; + message.format("GameStart: %s", TheGlobalData->m_mapName.str()); + PROFILER_MSG(message.str(), message.getLength()); +#endif } //----------------------------------------------------------------------------------------- @@ -3652,6 +3654,7 @@ extern __int64 Total_Load_3D_Assets; void GameLogic::update() { USE_PERF_TIMER(GameLogic_update) + PROFILER_SECTION_COLOR(0x4CAF50); LatchRestore inUpdateLatch(m_isInUpdate, TRUE); #ifdef DO_UNIT_TIMINGS @@ -3670,11 +3673,11 @@ void GameLogic::update() Total_Load_3D_Assets=0; #endif -#ifdef RTS_PROFILE +#ifdef RTS_PROFILE_LEGACY Profile::StartRange("map_load"); #endif startNewGame( FALSE ); -#ifdef RTS_PROFILE +#ifdef RTS_PROFILE_LEGACY Profile::StopRange("map_load"); #endif m_startNewGame = FALSE; @@ -3698,6 +3701,8 @@ void GameLogic::update() UnsignedInt now = getFrame(); TheGameClient->setFrame(now); + PROFILER_PLOT("LogicFrame", static_cast(now)); + // update (execute) scripts { TheScriptEngine->UPDATE(); @@ -4215,6 +4220,12 @@ void GameLogic::exitGame() TheScriptEngine->doUnfreezeTime(); TheMessageStream->appendMessage(GameMessage::MSG_CLEAR_GAME_DATA); + +#ifdef PROFILER_ENABLED + AsciiString message; + message.format("GameEnd: %s", TheGlobalData->m_mapName.str()); + PROFILER_MSG(message.str(), message.getLength()); +#endif } // ------------------------------------------------------------------------------------------------ diff --git a/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DDisplay.h b/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DDisplay.h index 476fc14342c..cf2e537f9e1 100644 --- a/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DDisplay.h +++ b/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DDisplay.h @@ -35,6 +35,7 @@ #include "GameClient/Display.h" #include "WW3D2/lightenvironment.h" +#include "W3DDevice/GameClient/W3DProfilerFrameCapture.h" class VideoBuffer; class W3DDebugDisplay; @@ -183,6 +184,10 @@ class W3DDisplay : public Display Int64 m_timerAtCumuFPSStart; #endif +#ifdef PROFILER_ENABLED + W3DProfilerFrameCapture *m_profilerFrameCapture; +#endif + enum { FPS, ///< debug display frames per second diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp index 833f70740a4..7d083f3393e 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp @@ -71,6 +71,7 @@ static void drawFramerateBar(); #include "W3DDevice/GameClient/W3DGameClient.h" #include "W3DDevice/GameClient/W3DFileSystem.h" #include "W3DDevice/GameClient/W3DDynamicLight.h" +#include "W3DDevice/GameClient/W3DProfilerFrameCapture.h" #include "W3DDevice/GameClient/HeightMap.h" #include "W3DDevice/GameClient/WorldHeightMap.h" #include "W3DDevice/GameClient/W3DScene.h" @@ -408,6 +409,10 @@ W3DDisplay::W3DDisplay() m_batchMode = DRAW_IMAGE_ALPHA; m_batchGrayscale = FALSE; m_batchNeedsInit = FALSE; + +#ifdef PROFILER_ENABLED + m_profilerFrameCapture = NEW W3DProfilerFrameCapture(); +#endif } // W3DDisplay::~W3DDisplay ==================================================== @@ -415,6 +420,10 @@ W3DDisplay::W3DDisplay() //============================================================================= W3DDisplay::~W3DDisplay() { +#ifdef PROFILER_ENABLED + delete m_profilerFrameCapture; + m_profilerFrameCapture = nullptr; +#endif // get rid of the debug display delete m_debugDisplay; @@ -2057,6 +2066,13 @@ void W3DDisplay::draw() TheGraphDraw->render(); TheGraphDraw->clear(); #endif + +#ifdef PROFILER_ENABLED + if (m_profilerFrameCapture && !TheGlobalData->m_headless) + { + m_profilerFrameCapture->Capture(getWidth(), getHeight()); + } +#endif // render is all done! WW3D::End_Render(); } diff --git a/GeneralsMD/Code/Main/CMakeLists.txt b/GeneralsMD/Code/Main/CMakeLists.txt index be2e55e8fe4..0aa9b6df91d 100644 --- a/GeneralsMD/Code/Main/CMakeLists.txt +++ b/GeneralsMD/Code/Main/CMakeLists.txt @@ -11,7 +11,7 @@ target_link_libraries(z_generals PRIVATE binkstub comctl32 core_debug - core_profile + core_profile_legacy d3d8 d3dx8 dinput8 diff --git a/GeneralsMD/Code/Main/WinMain.cpp b/GeneralsMD/Code/Main/WinMain.cpp index d285cb8ad46..b27c8b39067 100644 --- a/GeneralsMD/Code/Main/WinMain.cpp +++ b/GeneralsMD/Code/Main/WinMain.cpp @@ -66,7 +66,6 @@ #include "GeneratedVersion.h" #include "resource.h" -#include #ifdef RTS_ENABLE_CRASHDUMP #include "Common/MiniDumper.h" #endif @@ -793,7 +792,7 @@ Int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, { Int exitcode = 1; -#ifdef RTS_PROFILE +#ifdef RTS_PROFILE_LEGACY Profile::StartRange("init"); #endif @@ -843,7 +842,7 @@ Int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, // Force "splash image" to be loaded from a file, not a resource so same exe can be used in different localizations. -#if defined(RTS_DEBUG) || defined RTS_PROFILE +#if defined(RTS_DEBUG) || defined RTS_PROFILE_LEGACY // check both localized directory and root dir char filePath[_MAX_PATH]; diff --git a/GeneralsMD/Code/Tools/GUIEdit/CMakeLists.txt b/GeneralsMD/Code/Tools/GUIEdit/CMakeLists.txt index 71671854f42..94889705e34 100644 --- a/GeneralsMD/Code/Tools/GUIEdit/CMakeLists.txt +++ b/GeneralsMD/Code/Tools/GUIEdit/CMakeLists.txt @@ -48,7 +48,7 @@ target_include_directories(z_guiedit PRIVATE target_link_libraries(z_guiedit PRIVATE comctl32 core_debug - core_profile + core_profile_legacy d3d8lib imm32 stlport diff --git a/GeneralsMD/Code/Tools/WorldBuilder/CMakeLists.txt b/GeneralsMD/Code/Tools/WorldBuilder/CMakeLists.txt index fa1841b3997..e8ab25bc24e 100644 --- a/GeneralsMD/Code/Tools/WorldBuilder/CMakeLists.txt +++ b/GeneralsMD/Code/Tools/WorldBuilder/CMakeLists.txt @@ -217,7 +217,7 @@ target_precompile_headers(z_worldbuilder PRIVATE target_link_libraries(z_worldbuilder PRIVATE core_debug - core_profile + core_profile_legacy d3d8lib imm32 vfw32 diff --git a/README.md b/README.md index 84093136b79..71ef5135ad0 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,12 @@ through `VCPKG_BINARY_SOURCES=clear;files,/vcpkg-bincache,readwrite` that folder), so the first CI build warms the cache and subsequent builds pull prebuilt binaries instead of re-compiling everything. +### Profiling + +Tracy profiling is supported in the CMake preset `win32-profile`. +Use `tracy-profiler.exe` from [Tracy v0.13.1](https://github.com/wolfpld/tracy/releases/tag/v0.13.1). +If you get an error when using Tracy, try removing `dbghelp.dll` from the game binary directory. + ## Contributing We welcome contributions to the project! If you’re interested in contributing, you need to have knowledge of C++. Join diff --git a/cmake/config-build.cmake b/cmake/config-build.cmake index b28f5c0b760..7973f5a1f03 100644 --- a/cmake/config-build.cmake +++ b/cmake/config-build.cmake @@ -4,6 +4,7 @@ option(RTS_BUILD_CORE_EXTRAS "Build core extra tools/tests" OFF) option(RTS_BUILD_ZEROHOUR "Build Zero Hour code." ON) option(RTS_BUILD_GENERALS "Build Generals code." ON) option(RTS_BUILD_OPTION_PROFILE "Build code with the \"Profile\" configuration." OFF) +option(RTS_BUILD_OPTION_PROFILE_TRACY "Build code with Tracy profiling enabled." OFF) option(RTS_BUILD_OPTION_DEBUG "Build code with the \"Debug\" configuration." OFF) option(RTS_BUILD_OPTION_ASAN "Build code with Address Sanitizer." OFF) option(RTS_BUILD_OPTION_VC6_FULL_DEBUG "Build VC6 with full debug info." OFF) @@ -73,5 +74,12 @@ else() endif() if(RTS_BUILD_OPTION_PROFILE) - target_compile_definitions(core_config INTERFACE RTS_PROFILE) + target_compile_definitions(core_config INTERFACE RTS_PROFILE_LEGACY) +endif() + +# Define a dummy Tracy target when the build option is disabled. +if(RTS_BUILD_OPTION_PROFILE_TRACY) + include(cmake/tracy.cmake) +else() + add_library(core_profile_tracy INTERFACE) endif() diff --git a/cmake/tracy.cmake b/cmake/tracy.cmake new file mode 100644 index 00000000000..09f2245ccec --- /dev/null +++ b/cmake/tracy.cmake @@ -0,0 +1,19 @@ +find_package(Tracy CONFIG QUIET) +if(NOT Tracy_FOUND) + FetchContent_Declare( + tracy + GIT_REPOSITORY https://github.com/TheSuperHackers/tracy + GIT_TAG 05cceee0df3b8d7c6fa87e9638af311dbabc63cb # 0.13.1 + ) + FetchContent_MakeAvailable(tracy) +endif() + +if(NOT TARGET TracyClient) + message(FATAL_ERROR "Tracy is enabled but TracyClient was not found.") +endif() + +target_compile_definitions(TracyClient INTERFACE + RTS_PROFILE_TRACY +) + +add_library(core_profile_tracy ALIAS TracyClient)