From 098e9a34ca87ac0eb085f48c6dd631af766f679c Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Mon, 18 Sep 2023 21:42:04 +0200 Subject: [PATCH] Poormans profiler --- Source/CMakeLists.txt | 1 + Source/diablo.cpp | 10 +++++ Source/engine/render/clx_render.cpp | 6 +++ Source/engine/render/dun_render.cpp | 7 ++++ Source/engine/render/scrollrt.cpp | 39 ++++++++++++++++++ Source/engine/render/scrollrt.h | 1 + Source/engine/render/text_render.cpp | 7 ++++ Source/utils/profiler.cpp | 59 ++++++++++++++++++++++++++++ Source/utils/profiler.h | 22 +++++++++++ 9 files changed, 152 insertions(+) create mode 100644 Source/utils/profiler.cpp create mode 100644 Source/utils/profiler.h diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index f7adf835e..9d0cfd937 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -167,6 +167,7 @@ set(libdevilutionx_SRCS utils/str_cat.cpp utils/str_case.cpp utils/surface_to_clx.cpp + utils/profiler.cpp utils/timer.cpp utils/utf8.cpp) diff --git a/Source/diablo.cpp b/Source/diablo.cpp index e692bef64..9bea3635d 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -8,6 +8,7 @@ #include #include +#include #include @@ -697,6 +698,7 @@ bool HandleTextInput(std::string_view text) void GameEventHandler(const SDL_Event &event, uint16_t modState) { + FunctionProfiler profiler(__func__); StaticVector ctrlEvents = ToControllerButtonEvents(event); for (ControllerButtonEvent ctrlEvent : ctrlEvents) { GameAction action; @@ -825,6 +827,7 @@ void RunGameLoop(interface_mode uMsg) unsigned run_game_iteration = 0; #endif + FunctionProfiler profiler(__func__); while (gbRunGame) { #ifdef _DEBUG @@ -2454,6 +2457,12 @@ int DiabloMain(int argc, char **argv) DiabloSplash(); mainmenu_loop(); + + for (const auto &timing : FunctionProfiler::Dump()) { + fmt::print("{}: {}ms ({} calls)\n", timing.name, timing.ms, timing.count); + } + fmt::print("\n"); + DiabloDeinit(); return 0; @@ -3002,6 +3011,7 @@ void LoadGameLevel(bool firstflag, lvl_entry lvldir) bool game_loop(bool bStartup) { + FunctionProfiler profiler(__func__); uint16_t wait = bStartup ? sgGameInitInfo.nTickRate * 3 : 3; for (unsigned i = 0; i < wait; i++) { diff --git a/Source/engine/render/clx_render.cpp b/Source/engine/render/clx_render.cpp index cff554ee5..326ca0311 100644 --- a/Source/engine/render/clx_render.cpp +++ b/Source/engine/render/clx_render.cpp @@ -12,6 +12,7 @@ #include "engine/render/scrollrt.h" #include "utils/attributes.h" #include "utils/clx_decode.hpp" +#include "utils/profiler.h" #ifdef DEBUG_CLX #include @@ -194,6 +195,7 @@ void DoRenderBackwards( const Surface &out, Point position, const uint8_t *src, size_t srcSize, unsigned srcWidth, unsigned srcHeight, BlitFn &&blitFn) { + FunctionProfiler profiler(__func__); if (position.y < 0 || position.y + 1 >= static_cast(out.h() + srcHeight)) return; const ClipX clipX = CalculateClipX(position.x, srcWidth, out); @@ -615,6 +617,7 @@ template void RenderClxOutline(const Surface &out, Point position, const uint8_t *src, std::size_t srcSize, std::size_t srcWidth, uint8_t color) { + FunctionProfiler profiler(__func__); RenderSrc srcForBackwards { src, src + srcSize, static_cast(srcWidth) }; if (position.x > 0 && position.x + static_cast(srcWidth) < static_cast(out.w())) { RenderClxOutlineClippedY(out, position, srcForBackwards, color); @@ -625,6 +628,7 @@ void RenderClxOutline(const Surface &out, Point position, const uint8_t *src, st void ClxApplyTrans(ClxSprite sprite, const uint8_t *trn) { + FunctionProfiler profiler(__func__); // A bit of a hack but this is the only place in the code where we need mutable sprites. auto *dst = const_cast(sprite.pixelData()); uint16_t remaining = sprite.pixelDataSize(); @@ -666,6 +670,7 @@ void ClxApplyTrans(ClxSpriteSheet sheet, const uint8_t *trn) bool IsPointWithinClx(Point position, ClxSprite clx) { + FunctionProfiler profiler(__func__); const uint8_t *src = clx.pixelData(); const uint8_t *end = src + clx.pixelDataSize(); const uint16_t width = clx.width(); @@ -719,6 +724,7 @@ bool IsPointWithinClx(Point position, ClxSprite clx) std::pair ClxMeasureSolidHorizontalBounds(ClxSprite clx) { + FunctionProfiler profiler(__func__); const uint8_t *src = clx.pixelData(); const uint8_t *end = src + clx.pixelDataSize(); const uint16_t width = clx.width(); diff --git a/Source/engine/render/dun_render.cpp b/Source/engine/render/dun_render.cpp index 56b50c1e9..fc31913fb 100644 --- a/Source/engine/render/dun_render.cpp +++ b/Source/engine/render/dun_render.cpp @@ -18,6 +18,7 @@ #include #include +#include "utils/profiler.h" #include "engine/render/blit_impl.hpp" #include "lighting.h" #include "options.h" @@ -922,6 +923,7 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderTileType(TileType tile, uint8_t * template DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderTransparentSquareDispatch(uint8_t *DVL_RESTRICT dst, uint16_t dstPitch, const uint8_t *DVL_RESTRICT src, const uint8_t *DVL_RESTRICT tbl, Clip clip) { + FunctionProfiler profiler(__func__); if (tbl == LightTables[LightsMax].data()) { RenderTransparentSquare(dst, dstPitch, src, tbl, clip); } else if (tbl == LightTables[0].data()) { @@ -964,6 +966,7 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderRightTrapezoidOrTransparentSquare template DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderLeftTrapezoidOrTransparentSquareDispatch(TileType tile, uint8_t *DVL_RESTRICT dst, uint16_t dstPitch, const uint8_t *DVL_RESTRICT src, const uint8_t *DVL_RESTRICT tbl, Clip clip) { + FunctionProfiler profiler(__func__); if (tbl == LightTables[LightsMax].data()) { RenderLeftTrapezoidOrTransparentSquare(tile, dst, dstPitch, src, tbl, clip); } else if (tbl == LightTables[0].data()) { @@ -976,6 +979,7 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderLeftTrapezoidOrTransparentSquareD template DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderRightTrapezoidOrTransparentSquareDispatch(TileType tile, uint8_t *DVL_RESTRICT dst, uint16_t dstPitch, const uint8_t *DVL_RESTRICT src, const uint8_t *DVL_RESTRICT tbl, Clip clip) { + FunctionProfiler profiler(__func__); if (tbl == LightTables[LightsMax].data()) { RenderRightTrapezoidOrTransparentSquare(tile, dst, dstPitch, src, tbl, clip); } else if (tbl == LightTables[0].data()) { @@ -988,6 +992,7 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderRightTrapezoidOrTransparentSquare template DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderTileDispatch(TileType tile, uint8_t *DVL_RESTRICT dst, uint16_t dstPitch, const uint8_t *DVL_RESTRICT src, const uint8_t *DVL_RESTRICT tbl, Clip clip) { + FunctionProfiler profiler(__func__); if (tbl == LightTables[LightsMax].data()) { RenderTileType(tile, dst, dstPitch, src, tbl, clip); } else if (tbl == LightTables[0].data()) { @@ -1126,6 +1131,7 @@ std::string_view MaskTypeToString(MaskType maskType) void RenderTile(const Surface &out, Point position, LevelCelBlock levelCelBlock, MaskType maskType, const uint8_t *tbl) { + FunctionProfiler profiler(__func__); const TileType tile = levelCelBlock.type(); #ifdef DEBUG_RENDER_OFFSET_X @@ -1180,6 +1186,7 @@ void RenderTile(const Surface &out, Point position, void world_draw_black_tile(const Surface &out, int sx, int sy) { + FunctionProfiler profiler(__func__); #ifdef DEBUG_RENDER_OFFSET_X sx += DEBUG_RENDER_OFFSET_X; #endif diff --git a/Source/engine/render/scrollrt.cpp b/Source/engine/render/scrollrt.cpp index 71b122112..5cc33c436 100644 --- a/Source/engine/render/scrollrt.cpp +++ b/Source/engine/render/scrollrt.cpp @@ -86,6 +86,7 @@ std::unordered_multimap MissilesAtRenderingTile; */ bool CouldMissileCollide(Point tile, bool checkPlayerAndMonster) { + FunctionProfiler profiler(__func__); if (!InDungeonBounds(tile)) return true; if (checkPlayerAndMonster) { @@ -100,6 +101,7 @@ bool CouldMissileCollide(Point tile, bool checkPlayerAndMonster) void UpdateMissilePositionForRendering(Missile &m, int progress) { + FunctionProfiler profiler(__func__); DisplacementOf velocity = m.position.velocity; velocity *= progress; velocity /= AnimationInfo::baseValueFraction; @@ -113,6 +115,7 @@ void UpdateMissilePositionForRendering(Missile &m, int progress) void UpdateMissileRendererData(Missile &m) { + FunctionProfiler profiler(__func__); m.position.tileForRendering = m.position.tile; m.position.offsetForRendering = m.position.offset; @@ -155,6 +158,7 @@ void UpdateMissileRendererData(Missile &m) void UpdateMissilesRendererData() { + FunctionProfiler profiler(__func__); MissilesAtRenderingTile.clear(); for (auto &m : Missiles) { @@ -174,6 +178,7 @@ Rectangle PrevCursorRect; void BlitCursor(uint8_t *dst, uint32_t dstPitch, uint8_t *src, uint32_t srcPitch, uint32_t srcWidth, uint32_t srcHeight) { + FunctionProfiler profiler(__func__); for (std::uint32_t i = 0; i < srcHeight; ++i, src += srcPitch, dst += dstPitch) { memcpy(dst, src, srcWidth); } @@ -184,6 +189,7 @@ void BlitCursor(uint8_t *dst, uint32_t dstPitch, uint8_t *src, uint32_t srcPitch */ void UndrawCursor(const Surface &out) { + FunctionProfiler profiler(__func__); DrawnCursor &cursor = GetDrawnCursor(); BlitCursor(&out[cursor.rect.position], out.pitch(), cursor.behindBuffer, cursor.rect.size.width, cursor.rect.size.width, cursor.rect.size.height); PrevCursorRect = cursor.rect; @@ -191,6 +197,7 @@ void UndrawCursor(const Surface &out) bool ShouldShowCursor() { + FunctionProfiler profiler(__func__); if (ControlMode == ControlTypes::KeyboardAndMouse) return true; if (pcurs == CURSOR_TELEPORT) @@ -208,6 +215,7 @@ bool ShouldShowCursor() */ void DrawCursor(const Surface &out) { + FunctionProfiler profiler(__func__); DrawnCursor &cursor = GetDrawnCursor(); if (IsHardwareCursor()) { SetHardwareCursorVisible(ShouldShowCursor()); @@ -268,6 +276,7 @@ void DrawCursor(const Surface &out) */ void DrawMissilePrivate(const Surface &out, const Missile &missile, Point targetBufferPosition, bool pre) { + FunctionProfiler profiler(__func__); if (missile._miPreFlag != pre || !missile._miDrawFlag) return; @@ -290,6 +299,7 @@ void DrawMissilePrivate(const Surface &out, const Missile &missile, Point target */ void DrawMissile(const Surface &out, WorldTilePosition tilePosition, Point targetBufferPosition, bool pre) { + FunctionProfiler profiler(__func__); const auto [begin, end] = MissilesAtRenderingTile.equal_range(tilePosition); for (auto it = begin; it != end; ++it) { DrawMissilePrivate(out, *it->second, targetBufferPosition, pre); @@ -305,6 +315,7 @@ void DrawMissile(const Surface &out, WorldTilePosition tilePosition, Point targe */ void DrawMonster(const Surface &out, Point tilePosition, Point targetBufferPosition, const Monster &monster) { + FunctionProfiler profiler(__func__); if (!monster.animInfo.sprites) { Log("Draw Monster \"{}\": NULL Cel Buffer", monster.name()); return; @@ -334,6 +345,7 @@ void DrawMonster(const Surface &out, Point tilePosition, Point targetBufferPosit */ void DrawPlayerIconHelper(const Surface &out, MissileGraphicID missileGraphicId, Point position, const Player &player, bool infraVision) { + FunctionProfiler profiler(__func__); bool lighting = &player != MyPlayer; if (player.isWalking()) @@ -365,6 +377,7 @@ void DrawPlayerIconHelper(const Surface &out, MissileGraphicID missileGraphicId, */ void DrawPlayerIcons(const Surface &out, const Player &player, Point position, bool infraVision) { + FunctionProfiler profiler(__func__); if (player.pManaShield) DrawPlayerIconHelper(out, MissileGraphicID::ManaShield, position, player, infraVision); if (player.wReflections > 0) @@ -380,6 +393,7 @@ void DrawPlayerIcons(const Surface &out, const Player &player, Point position, b */ void DrawPlayer(const Surface &out, const Player &player, Point tilePosition, Point targetBufferPosition) { + FunctionProfiler profiler(__func__); if (!IsTileLit(tilePosition) && !MyPlayer->_pInfraFlag && !MyPlayer->isOnArenaLevel() && leveltype != DTYPE_TOWN) { return; } @@ -422,6 +436,7 @@ void DrawPlayer(const Surface &out, const Player &player, Point tilePosition, Po */ void DrawDeadPlayer(const Surface &out, Point tilePosition, Point targetBufferPosition) { + FunctionProfiler profiler(__func__); dFlags[tilePosition.x][tilePosition.y] &= ~DungeonFlag::DeadPlayer; for (Player &player : Players) { @@ -442,6 +457,7 @@ void DrawDeadPlayer(const Surface &out, Point tilePosition, Point targetBufferPo */ void DrawObject(const Surface &out, Point tilePosition, Point targetBufferPosition, bool pre) { + FunctionProfiler profiler(__func__); if (LightTableIndex >= LightsMax) { return; } @@ -480,6 +496,7 @@ static void DrawDungeon(const Surface & /*out*/, Point /*tilePosition*/, Point / */ void DrawCell(const Surface &out, Point tilePosition, Point targetBufferPosition) { + FunctionProfiler profiler(__func__); const uint16_t levelPieceId = dPiece[tilePosition.x][tilePosition.y]; const MICROS *pMap = &DPieceMicros[levelPieceId]; @@ -593,6 +610,7 @@ void DrawCell(const Surface &out, Point tilePosition, Point targetBufferPosition */ void DrawFloor(const Surface &out, Point tilePosition, Point targetBufferPosition) { + FunctionProfiler profiler(__func__); LightTableIndex = dLight[tilePosition.x][tilePosition.y]; const uint8_t *tbl = LightTables[LightTableIndex].data(); @@ -627,6 +645,7 @@ void DrawFloor(const Surface &out, Point tilePosition, Point targetBufferPositio */ void DrawItem(const Surface &out, Point tilePosition, Point targetBufferPosition, bool pre) { + FunctionProfiler profiler(__func__); int8_t bItem = dItem[tilePosition.x][tilePosition.y]; if (bItem <= 0) @@ -654,6 +673,7 @@ void DrawItem(const Surface &out, Point tilePosition, Point targetBufferPosition */ void DrawMonsterHelper(const Surface &out, Point tilePosition, Point targetBufferPosition) { + FunctionProfiler profiler(__func__); int mi = dMonster[tilePosition.x][tilePosition.y]; bool isNegativeMonster = mi < 0; mi = std::abs(mi) - 1; @@ -715,6 +735,7 @@ void DrawMonsterHelper(const Surface &out, Point tilePosition, Point targetBuffe */ void DrawPlayerHelper(const Surface &out, const Player &player, Point tilePosition, Point targetBufferPosition) { + FunctionProfiler profiler(__func__); DrawPlayer(out, player, tilePosition, targetBufferPosition); } @@ -726,6 +747,7 @@ void DrawPlayerHelper(const Surface &out, const Player &player, Point tilePositi */ void DrawDungeon(const Surface &out, Point tilePosition, Point targetBufferPosition) { + FunctionProfiler profiler(__func__); assert(InDungeonBounds(tilePosition)); if (dRendered.test(tilePosition.x, tilePosition.y)) @@ -814,6 +836,7 @@ void DrawDungeon(const Surface &out, Point tilePosition, Point targetBufferPosit */ void DrawFloor(const Surface &out, Point tilePosition, Point targetBufferPosition, int rows, int columns) { + FunctionProfiler profiler(__func__); for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { if (InDungeonBounds(tilePosition)) { @@ -845,6 +868,7 @@ void DrawFloor(const Surface &out, Point tilePosition, Point targetBufferPositio bool IsWall(Point position) { + FunctionProfiler profiler(__func__); return TileHasAny(dPiece[position.x][position.y], TileProperties::Solid) || dSpecial[position.x][position.y] != 0; } @@ -858,6 +882,7 @@ bool IsWall(Point position) */ void DrawTileContent(const Surface &out, Point tilePosition, Point targetBufferPosition, int rows, int columns) { + FunctionProfiler profiler(__func__); // Keep evaluating until MicroTiles can't affect screen rows += MicroTileLen; dRendered.reset(); @@ -907,6 +932,7 @@ void DrawTileContent(const Surface &out, Point tilePosition, Point targetBufferP */ void Zoom(const Surface &out) { + FunctionProfiler profiler(__func__); int viewportWidth = out.w(); int viewportOffsetX = 0; if (CanPanelsCoverView()) { @@ -964,6 +990,7 @@ int tileRows; void CalcFirstTilePosition(Point &position, Displacement &offset) { + FunctionProfiler profiler(__func__); // Adjust by player offset and tile grid alignment Player &myPlayer = *MyPlayer; offset = tileOffset; @@ -1015,6 +1042,7 @@ void CalcFirstTilePosition(Point &position, Displacement &offset) */ void DrawGame(const Surface &fullOut, Point position, Displacement offset) { + FunctionProfiler profiler(__func__); // Limit rendering to the view area const Surface &out = !*sgOptions.Graphics.zoom ? fullOut.subregionY(0, gnViewportHeight) @@ -1093,6 +1121,7 @@ void DrawGame(const Surface &fullOut, Point position, Displacement offset) */ void DrawView(const Surface &out, Point startPosition) { + FunctionProfiler profiler(__func__); #ifdef _DEBUG DebugCoordsMap.clear(); #endif @@ -1263,6 +1292,7 @@ void DrawFPS(const Surface &out) */ void DoBlitScreen(int x, int y, int w, int h) { + FunctionProfiler profiler(__func__); #ifdef DEBUG_DO_BLIT_SCREEN const Surface &out = GlobalBackBuffer(); const uint8_t debugColor = PAL8_RED; @@ -1291,6 +1321,7 @@ void DrawMain(const Surface &out, int dwHgt, bool drawDesc, bool drawHp, bool dr if (!gbActive || RenderDirectlyToOutputSurface) { return; } + FunctionProfiler profiler(__func__); assert(dwHgt >= 0 && dwHgt <= gnScreenHeight); @@ -1339,6 +1370,7 @@ void DrawMain(const Surface &out, int dwHgt, bool drawDesc, bool drawHp, bool dr Displacement GetOffsetForWalking(const AnimationInfo &animationInfo, const Direction dir, bool cameraMode /*= false*/) { + FunctionProfiler profiler(__func__); // clang-format off // South, SouthWest, West, NorthWest, North, NorthEast, East, SouthEast, constexpr Displacement StartOffset[8] = { { 0, -32 }, { 32, -16 }, { 64, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { -64, 0 }, { -32, -16 } }; @@ -1372,6 +1404,7 @@ void ShiftGrid(int *x, int *y, int horizontal, int vertical) int RowsCoveredByPanel() { + FunctionProfiler profiler(__func__); auto &mainPanelSize = GetMainPanel().size; if (GetScreenWidth() <= mainPanelSize.width) { return 0; @@ -1387,6 +1420,7 @@ int RowsCoveredByPanel() void CalcTileOffset(int *offsetX, int *offsetY) { + FunctionProfiler profiler(__func__); uint16_t screenWidth = GetScreenWidth(); uint16_t viewportHeight = GetViewportHeight(); @@ -1412,6 +1446,7 @@ void CalcTileOffset(int *offsetX, int *offsetY) void TilesInView(int *rcolumns, int *rrows) { + FunctionProfiler profiler(__func__); uint16_t screenWidth = GetScreenWidth(); uint16_t viewportHeight = GetViewportHeight(); @@ -1489,6 +1524,7 @@ void CalcViewportGeometry() Point GetScreenPosition(Point tile) { + FunctionProfiler profiler(__func__); Point firstTile = ViewPosition; Displacement offset = {}; CalcFirstTilePosition(firstTile, offset); @@ -1507,6 +1543,7 @@ void ClearScreenBuffer() { if (HeadlessMode) return; + FunctionProfiler profiler(__func__); assert(PalSurface != nullptr); SDL_FillRect(PalSurface, nullptr, 0); @@ -1583,6 +1620,7 @@ void scrollrt_draw_game_screen() { if (HeadlessMode) return; + FunctionProfiler profiler(__func__); int hgt = 0; @@ -1604,6 +1642,7 @@ void DrawAndBlit() if (!gbRunGame || HeadlessMode) { return; } + FunctionProfiler profiler(__func__); int hgt = 0; bool drawHealth = IsRedrawComponent(PanelDrawComponent::Health); diff --git a/Source/engine/render/scrollrt.h b/Source/engine/render/scrollrt.h index d13c8cf14..a945bce86 100644 --- a/Source/engine/render/scrollrt.h +++ b/Source/engine/render/scrollrt.h @@ -7,6 +7,7 @@ #include +#include #include "engine.h" #include "engine/animationinfo.h" #include "engine/point.hpp" diff --git a/Source/engine/render/text_render.cpp b/Source/engine/render/text_render.cpp index 8d6f9f95c..21a30f752 100644 --- a/Source/engine/render/text_render.cpp +++ b/Source/engine/render/text_render.cpp @@ -15,6 +15,7 @@ #include +#include "utils/profiler.h" #include "DiabloUI/diabloui.h" #include "DiabloUI/ui_item.h" #include "engine.h" @@ -469,6 +470,7 @@ void UnloadFonts() int GetLineWidth(std::string_view text, GameFontTables size, int spacing, int *charactersInLine) { + FunctionProfiler profiler(__func__); int lineWidth = 0; CurrentFont currentFont; uint32_t codepoints = 0; @@ -502,6 +504,7 @@ int GetLineWidth(std::string_view text, GameFontTables size, int spacing, int *c int GetLineWidth(std::string_view fmt, DrawStringFormatArg *args, std::size_t argsLen, size_t argsOffset, GameFontTables size, int spacing, int *charactersInLine) { + FunctionProfiler profiler(__func__); int lineWidth = 0; CurrentFont currentFont; @@ -555,6 +558,7 @@ int GetLineWidth(std::string_view fmt, DrawStringFormatArg *args, std::size_t ar int GetLineHeight(std::string_view text, GameFontTables fontIndex) { + FunctionProfiler profiler(__func__); if (fontIndex == GameFont12 && IsSmallFontTall() && ContainsSmallFontTallCodepoints(text)) { return SmallFontTallLineHeight; } @@ -574,6 +578,7 @@ int AdjustSpacingToFitHorizontally(int &lineWidth, int maxSpacing, int character std::string WordWrapString(std::string_view text, unsigned width, GameFontTables size, int spacing) { + FunctionProfiler profiler(__func__); std::string output; if (text.empty() || text[0] == '\0') return output; @@ -660,6 +665,7 @@ std::string WordWrapString(std::string_view text, unsigned width, GameFontTables */ uint32_t DrawString(const Surface &out, std::string_view text, const Rectangle &rect, UiFlags flags, int spacing, int lineHeight) { + FunctionProfiler profiler(__func__); GameFontTables size = GetSizeFromFlags(flags); text_color color = GetColorFromFlags(flags); @@ -710,6 +716,7 @@ uint32_t DrawString(const Surface &out, std::string_view text, const Rectangle & void DrawStringWithColors(const Surface &out, std::string_view fmt, DrawStringFormatArg *args, std::size_t argsLen, const Rectangle &rect, UiFlags flags, int spacing, int lineHeight) { + FunctionProfiler profiler(__func__); GameFontTables size = GetSizeFromFlags(flags); text_color color = GetColorFromFlags(flags); diff --git a/Source/utils/profiler.cpp b/Source/utils/profiler.cpp new file mode 100644 index 000000000..19087224b --- /dev/null +++ b/Source/utils/profiler.cpp @@ -0,0 +1,59 @@ +#include + +#include +#include + +#include +#include + +std::unordered_map timings; + +FunctionProfiler::FunctionProfiler(std::string name) + : _name(name) +{ + LARGE_INTEGER frequency; + if (QueryPerformanceFrequency(&frequency)) { + _frequency = frequency.QuadPart; + } else { + _frequency = 1000; + } + + LARGE_INTEGER ticks; + if (QueryPerformanceCounter(&ticks)) { + _start = ticks.QuadPart; + } else { + _start = SDL_GetTicks() * _frequency / 1000; + } +} + +FunctionProfiler::~FunctionProfiler() +{ + LARGE_INTEGER ticks; + uint64_t now; + if (QueryPerformanceCounter(&ticks)) { + now = ticks.QuadPart; + } else { + now = SDL_GetTicks() * _frequency / 1000; + } + double ms = (double)(now - _start) * 1000.0 / (double)_frequency; + + TimingInfo &timing = timings[_name]; + timing.ms += ms; + timing.count++; +} + +static bool CompareTiming(const TimingInfo &a, const TimingInfo &b) +{ + return a.ms > b.ms; +} + +std::vector FunctionProfiler::Dump() +{ + std::vector timingList; + for (auto it = timings.begin(); it != timings.end(); ++it) { + it->second.name = it->first; + timingList.push_back(it->second); + } + std::sort(timingList.begin(), timingList.end(), CompareTiming); + return timingList; +} diff --git a/Source/utils/profiler.h b/Source/utils/profiler.h new file mode 100644 index 000000000..644c11fa1 --- /dev/null +++ b/Source/utils/profiler.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include + +struct TimingInfo { + std::string name; + double ms; + int count; +}; + +class FunctionProfiler { +public: + FunctionProfiler(std::string name); + ~FunctionProfiler(); + static std::vector Dump(); +private: + std::string _name; + uint64_t _frequency; + uint64_t _start; +};