From a5c7ea04e5527075ade72f606394af512c142423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Mon, 7 Jan 2019 21:39:38 +0100 Subject: [PATCH 1/7] Update MSVC libraries to v19 to add Google benchmark --- openrct2.proj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openrct2.proj b/openrct2.proj index 9de93004c9..082acfd2f3 100644 --- a/openrct2.proj +++ b/openrct2.proj @@ -61,10 +61,10 @@ $(RootDir).dependencies - https://github.com/OpenRCT2/Dependencies/releases/download/v18/openrct2-libs-v18-x86-windows-static-winssl.zip - 5266545ca3034f9718729c7841d0352ed866db39 - https://github.com/OpenRCT2/Dependencies/releases/download/v18/openrct2-libs-v18-x64-windows-static-winssl.zip - 4a122221e000b26067c8a4d06d2f053f27fe3f39 + https://github.com/OpenRCT2/Dependencies/releases/download/v19/openrct2-libs-v19-x86-windows-static-winssl.zip + c0a49dfb7a0b4175c5b5003922f8a5c4b4589132 + https://github.com/OpenRCT2/Dependencies/releases/download/v19/openrct2-libs-v19-x64-windows-static-winssl.zip + 623cb9199da328e55ee9c7f5e1135ceab43cc6b9 2fe3bd994b3189899d93f1d5a881e725e046fdc2 https://github.com/google/googletest/archive/$(GtestVersion).zip 058b9df80244c03f1633cb06e9f70471a29ebb8e From f2233d3cc38a0105f7061eda8fc49b5d0f124a1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Mon, 7 Jan 2019 21:45:15 +0100 Subject: [PATCH 2/7] Update MSVC project to use Google benchmark --- openrct2.common.props | 6 +++--- src/openrct2/libopenrct2.vcxproj | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openrct2.common.props b/openrct2.common.props index d8b5aaa6f4..764f0dd8d9 100644 --- a/openrct2.common.props +++ b/openrct2.common.props @@ -61,7 +61,7 @@ /utf-8 /std:c++17 /permissive- /Zc:externConstexpr - wininet.lib;imm32.lib;version.lib;winmm.lib;crypt32.lib;wldap32.lib;%(AdditionalDependencies) + wininet.lib;imm32.lib;version.lib;winmm.lib;crypt32.lib;wldap32.lib;shlwapi.lib;%(AdditionalDependencies) /OPT:NOLBR /ignore:4099 %(AdditionalOptions) @@ -76,7 +76,7 @@ DebugFull - bz2d.lib;discord-rpc.lib;freetyped.lib;jansson_d.lib;libcurl.lib;libeay32.lib;libpng16d.lib;libspeexdsp.lib;SDL2d.lib;ssleay32.lib;zip.lib;zlibd.lib;libbreakpadd.lib;libbreakpad_clientd.lib;%(AdditionalDependencies) + bz2d.lib;discord-rpc.lib;freetyped.lib;jansson_d.lib;libcurl.lib;libeay32.lib;libpng16d.lib;libspeexdsp.lib;SDL2d.lib;ssleay32.lib;zip.lib;zlibd.lib;libbreakpadd.lib;libbreakpad_clientd.lib;benchmark.lib;%(AdditionalDependencies) @@ -94,7 +94,7 @@ DebugFull true true - bz2.lib;discord-rpc.lib;freetype.lib;jansson.lib;libcurl.lib;libeay32.lib;libpng16.lib;libspeexdsp.lib;SDL2.lib;ssleay32.lib;zip.lib;zlib.lib;libbreakpad.lib;libbreakpad_client.lib;%(AdditionalDependencies) + bz2.lib;discord-rpc.lib;freetype.lib;jansson.lib;libcurl.lib;libeay32.lib;libpng16.lib;libspeexdsp.lib;SDL2.lib;ssleay32.lib;zip.lib;zlib.lib;libbreakpad.lib;libbreakpad_client.lib;benchmark.lib;%(AdditionalDependencies) diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj index e0479d8f80..b7941709e9 100644 --- a/src/openrct2/libopenrct2.vcxproj +++ b/src/openrct2/libopenrct2.vcxproj @@ -32,7 +32,7 @@ - __ENABLE_DISCORD__;__ENABLE_LIGHTFX__;%(PreprocessorDefinitions) + __ENABLE_DISCORD__;__ENABLE_LIGHTFX__;USE_BENCHMARK;%(PreprocessorDefinitions) USE_BREAKPAD;%(PreprocessorDefinitions) From 304840069d021835051614b8ad2dd7ecfe6284f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Sat, 15 Dec 2018 23:41:38 +0100 Subject: [PATCH 3/7] Add sprite sorting benchmark --- src/openrct2/CMakeLists.txt | 11 ++ src/openrct2/cmdline/BenchSpriteSort.cpp | 226 +++++++++++++++++++++++ src/openrct2/cmdline/CommandLine.hpp | 1 + src/openrct2/cmdline/RootCommands.cpp | 9 +- src/openrct2/interface/Viewport.cpp | 50 +++-- src/openrct2/interface/Viewport.h | 10 +- 6 files changed, 289 insertions(+), 18 deletions(-) create mode 100644 src/openrct2/cmdline/BenchSpriteSort.cpp diff --git a/src/openrct2/CMakeLists.txt b/src/openrct2/CMakeLists.txt index b7bfd67ba6..bc54c296c1 100644 --- a/src/openrct2/CMakeLists.txt +++ b/src/openrct2/CMakeLists.txt @@ -47,6 +47,8 @@ if (NOT DISABLE_NETWORK) find_package(OpenSSL 1.0.0 REQUIRED) endif () +find_package(benchmark) + if (NOT DISABLE_TTF) if (UNIX AND NOT APPLE AND NOT MSVC) PKG_CHECK_MODULES(FONTCONFIG REQUIRED fontconfig) @@ -73,6 +75,15 @@ project(${PROJECT} CXX) add_library(${PROJECT} ${OPENRCT2_CORE_SOURCES} ${OPENRCT2_CORE_MM_SOURCES} ${RCT2_SECTIONS}) set_target_properties(${PROJECT} PROPERTIES PREFIX "") +if (benchmark_FOUND) + message("Found Google benchmark, enabling support") + set_target_properties(${PROJECT} PROPERTIES COMPILE_DEFINITIONS USE_BENCHMARK) + target_link_libraries(${PROJECT} benchmark::benchmark) + target_include_directories(${PROJECT} PRIVATE ${benchmark_INCLUDE_DIRS}) +else () + message("Google benchmark not found, disabling support") +endif () + # Libraries if (STATIC) target_link_libraries(${PROJECT} ${JANSSON_STATIC_LIBRARIES} diff --git a/src/openrct2/cmdline/BenchSpriteSort.cpp b/src/openrct2/cmdline/BenchSpriteSort.cpp new file mode 100644 index 0000000000..325a0089d3 --- /dev/null +++ b/src/openrct2/cmdline/BenchSpriteSort.cpp @@ -0,0 +1,226 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#include "CommandLine.hpp" + +#ifdef USE_BENCHMARK + +# include "../Context.h" +# include "../Game.h" +# include "../Intro.h" +# include "../OpenRCT2.h" +# include "../audio/audio.h" +# include "../core/Console.hpp" +# include "../core/Imaging.h" +# include "../drawing/Drawing.h" +# include "../interface/Viewport.h" +# include "../localisation/Localisation.h" +# include "../paint/Paint.h" +# include "../platform/platform.h" +# include "../util/Util.h" +# include "../world/Climate.h" +# include "../world/Map.h" +# include "../world/Park.h" +# include "../world/Surface.h" + +# include +# include +# include +# include + +static void fixup_pointers(paint_session* s, size_t paint_session_entries, size_t paint_struct_entries, size_t quadrant_entries) +{ + for (size_t i = 0; i < paint_session_entries; i++) + { + for (size_t j = 0; j < paint_struct_entries; j++) + { + if (s[i].PaintStructs[j].basic.next_quadrant_ps == (paint_struct*)paint_struct_entries) + { + s[i].PaintStructs[j].basic.next_quadrant_ps = nullptr; + } + else + { + s[i].PaintStructs[j].basic.next_quadrant_ps = &s[i].PaintStructs + [(uintptr_t)s[i].PaintStructs[j].basic.next_quadrant_ps] + .basic; + } + } + for (size_t j = 0; j < quadrant_entries; j++) + { + if (s[i].Quadrants[j] == (paint_struct*)quadrant_entries) + { + s[i].Quadrants[j] = nullptr; + } + else + { + s[i].Quadrants[j] = &s[i].PaintStructs[(size_t)s[i].Quadrants[j]].basic; + } + } + } +} + +// This function is based on benchgfx_render_screenshots +static void BM_paint_session_arrange(benchmark::State& state) +{ + core_init(); + gOpenRCT2Headless = true; + auto context = OpenRCT2::CreateContext(); + std::vector sessions; + log_info("Starting..."); + if (context->Initialise()) + { + drawing_engine_init(); + if (!context->LoadParkFromFile("data.sv6")) + { + return; + } + + gIntroState = INTRO_STATE_NONE; + gScreenFlags = SCREEN_FLAGS_PLAYING; + + int32_t mapSize = gMapSize; + int32_t resolutionWidth = (mapSize * 32 * 2); + int32_t resolutionHeight = (mapSize * 32 * 1); + + resolutionWidth += 8; + resolutionHeight += 128; + + rct_viewport viewport; + viewport.x = 0; + viewport.y = 0; + viewport.width = resolutionWidth; + viewport.height = resolutionHeight; + viewport.view_width = viewport.width; + viewport.view_height = viewport.height; + viewport.var_11 = 0; + viewport.flags = 0; + + int32_t customX = (gMapSize / 2) * 32 + 16; + int32_t customY = (gMapSize / 2) * 32 + 16; + + int32_t x = 0, y = 0; + int32_t z = tile_element_height(customX, customY) & 0xFFFF; + x = customY - customX; + y = ((customX + customY) / 2) - z; + + viewport.view_x = x - ((viewport.view_width) / 2); + viewport.view_y = y - ((viewport.view_height) / 2); + viewport.zoom = 0; + gCurrentRotation = 0; + + // Ensure sprites appear regardless of rotation + reset_all_sprite_quadrant_placements(); + + rct_drawpixelinfo dpi; + dpi.x = 0; + dpi.y = 0; + dpi.width = resolutionWidth; + dpi.height = resolutionHeight; + dpi.pitch = 0; + dpi.bits = (uint8_t*)malloc(dpi.width * dpi.height); + + log_info("Obtaining sprite data..."); + viewport_render(&dpi, &viewport, 0, 0, viewport.width, viewport.height, &sessions); + + free(dpi.bits); + drawing_engine_dispose(); + } + log_info("Got %u paint sessions.", std::size(sessions)); + // Fixing up the pointers continuously is wasteful. Fix it up once for `sessions` and store a copy. + // Keep in mind we need bit-exact copy, as the lists use pointers. + // Once sorted, just restore the copy with the original fixed-up version. + paint_session* local_s = new paint_session[std::size(sessions)]; + fixup_pointers(&sessions[0], std::size(sessions), std::size(local_s->PaintStructs), std::size(local_s->Quadrants)); + std::copy_n(sessions.cbegin(), std::size(sessions), local_s); + for (auto _ : state) + { + state.PauseTiming(); + std::copy_n(local_s, std::size(sessions), sessions.begin()); + state.ResumeTiming(); + paint_session_arrange(&sessions[0]); + benchmark::DoNotOptimize(sessions); + } + state.SetItemsProcessed(state.iterations() * std::size(sessions)); + delete[] local_s; +} +BENCHMARK(BM_paint_session_arrange); + +// Provide some baseline performing similar things as the actual version of the benchmark does. +static void BM_baseline(benchmark::State& state) +{ + std::vector sessions(1); + paint_session* local_s = new paint_session[std::size(sessions)]; + std::copy_n(sessions.cbegin(), std::size(sessions), local_s); + for (auto _ : state) + { + state.PauseTiming(); + std::copy_n(local_s, std::size(sessions), sessions.begin()); + state.ResumeTiming(); + paint_session_arrange(local_s); + benchmark::DoNotOptimize(local_s); + } + state.SetItemsProcessed(state.iterations() * std::size(sessions)); + delete[] local_s; +} +BENCHMARK(BM_baseline); + +static int cmdline_for_bench_sprite_sort(int argc, const char** argv) +{ + // Google benchmark does stuff to argv. It doesn't modify the pointees, + // but it wants to reorder the pointers, so present a copy of them. + std::vector argv_for_benchmark; + // argv[0] is expected to contain the binary name. Don't bother. + argv_for_benchmark.push_back(nullptr); + for (int i = 0; i < argc; i++) + { + argv_for_benchmark.push_back((char*)argv[i]); + } + // Account for argv[0] + argc++; + ::benchmark::Initialize(&argc, &argv_for_benchmark[0]); + if (::benchmark::ReportUnrecognizedArguments(argc, &argv_for_benchmark[0])) + return -1; + ::benchmark::RunSpecifiedBenchmarks(); + return 0; +} + +static exitcode_t HandleBenchSpriteSort(CommandLineArgEnumerator* argEnumerator) +{ + const char** argv = (const char**)argEnumerator->GetArguments() + argEnumerator->GetIndex(); + int32_t argc = argEnumerator->GetCount() - argEnumerator->GetIndex(); + int32_t result = cmdline_for_bench_sprite_sort(argc, argv); + if (result < 0) + { + return EXITCODE_FAIL; + } + return EXITCODE_OK; +} + +#else +static exitcode_t HandleBenchSpriteSort(CommandLineArgEnumerator* argEnumerator) +{ + log_error("Sorry, Google benchmark not enabled in this build"); + return EXITCODE_FAIL; +} +#endif // USE_BENCHMARK + +const CommandLineCommand CommandLine::BenchSpriteSortCommands[]{ +#ifdef USE_BENCHMARK + DefineCommand( + "", + " [--benchmark_list_tests={true|false}] [--benchmark_filter=] [--benchmark_min_time=] " + "[--benchmark_repetitions=] [--benchmark_report_aggregates_only={true|false}] " + "[--benchmark_format=] [--benchmark_out=] [--benchmark_out_format=] " + "[--benchmark_color={auto|true|false}] [--benchmark_counters_tabular={true|false}] [--v=]", + nullptr, HandleBenchSpriteSort), + CommandTableEnd +#else + DefineCommand("", "*** SORRY NOT ENABLED IN THIS BUILD ***", nullptr, HandleBenchSpriteSort), CommandTableEnd +#endif // USE_BENCHMARK +}; diff --git a/src/openrct2/cmdline/CommandLine.hpp b/src/openrct2/cmdline/CommandLine.hpp index 6d7797086e..bba00a04cd 100644 --- a/src/openrct2/cmdline/CommandLine.hpp +++ b/src/openrct2/cmdline/CommandLine.hpp @@ -117,6 +117,7 @@ namespace CommandLine extern const CommandLineCommand ScreenshotCommands[]; extern const CommandLineCommand SpriteCommands[]; extern const CommandLineCommand BenchGfxCommands[]; + extern const CommandLineCommand BenchSpriteSortCommands[]; extern const CommandLineCommand SimulateCommands[]; extern const CommandLineExample RootExamples[]; diff --git a/src/openrct2/cmdline/RootCommands.cpp b/src/openrct2/cmdline/RootCommands.cpp index 5b866ef2c8..b2ade7507a 100644 --- a/src/openrct2/cmdline/RootCommands.cpp +++ b/src/openrct2/cmdline/RootCommands.cpp @@ -135,10 +135,11 @@ const CommandLineCommand CommandLine::RootCommands[] #endif // Sub-commands - DefineSubCommand("screenshot", CommandLine::ScreenshotCommands), - DefineSubCommand("sprite", CommandLine::SpriteCommands ), - DefineSubCommand("benchgfx", CommandLine::BenchGfxCommands ), - DefineSubCommand("simulate", CommandLine::SimulateCommands ), + DefineSubCommand("screenshot", CommandLine::ScreenshotCommands ), + DefineSubCommand("sprite", CommandLine::SpriteCommands ), + DefineSubCommand("benchgfx", CommandLine::BenchGfxCommands ), + DefineSubCommand("benchspritesort", CommandLine::BenchSpriteSortCommands ), + DefineSubCommand("simulate", CommandLine::SimulateCommands ), CommandTableEnd }; diff --git a/src/openrct2/interface/Viewport.cpp b/src/openrct2/interface/Viewport.cpp index d25bcc9e4b..f612b4ed36 100644 --- a/src/openrct2/interface/Viewport.cpp +++ b/src/openrct2/interface/Viewport.cpp @@ -60,7 +60,7 @@ static int16_t _interactionMapX; static int16_t _interactionMapY; static uint16_t _unk9AC154; -static void viewport_paint_column(rct_drawpixelinfo* dpi, uint32_t viewFlags); +static void viewport_paint_column(rct_drawpixelinfo* dpi, uint32_t viewFlags, std::vector* sessions); static void viewport_paint_weather_gloom(rct_drawpixelinfo* dpi); /** @@ -791,7 +791,9 @@ void viewport_update_smart_vehicle_follow(rct_window* window) * edi: dpi * ebp: bottom */ -void viewport_render(rct_drawpixelinfo* dpi, rct_viewport* viewport, int32_t left, int32_t top, int32_t right, int32_t bottom) +void viewport_render( + rct_drawpixelinfo* dpi, rct_viewport* viewport, int32_t left, int32_t top, int32_t right, int32_t bottom, + std::vector* sessions) { if (right <= viewport->x) return; @@ -821,7 +823,7 @@ void viewport_render(rct_drawpixelinfo* dpi, rct_viewport* viewport, int32_t lef top += viewport->view_y; bottom += viewport->view_y; - viewport_paint(viewport, dpi, left, top, right, bottom); + viewport_paint(viewport, dpi, left, top, right, bottom, sessions); #ifdef DEBUG_SHOW_DIRTY_BOX if (viewport != g_viewport_list) @@ -842,7 +844,9 @@ void viewport_render(rct_drawpixelinfo* dpi, rct_viewport* viewport, int32_t lef * edi: dpi * ebp: bottom */ -void viewport_paint(rct_viewport* viewport, rct_drawpixelinfo* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom) +void viewport_paint( + rct_viewport* viewport, rct_drawpixelinfo* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, + std::vector* sessions) { uint32_t viewFlags = viewport->flags; uint16_t width = right - left; @@ -878,32 +882,37 @@ void viewport_paint(rct_viewport* viewport, rct_drawpixelinfo* dpi, int16_t left int16_t rightBorder = dpi1.x + dpi1.width; // Splits the area into 32 pixel columns and renders them - for (x = floor2(dpi1.x, 32); x < rightBorder; x += 32) + int16_t start_x = floor2(dpi1.x, 32); + if (sessions != nullptr) + { + sessions->reserve((rightBorder - start_x) / 32); + } + for (int16_t columnx = start_x; columnx < rightBorder; columnx += 32) { rct_drawpixelinfo dpi2 = dpi1; - if (x >= dpi2.x) + if (columnx >= dpi2.x) { - int16_t leftPitch = x - dpi2.x; + int16_t leftPitch = columnx - dpi2.x; dpi2.width -= leftPitch; dpi2.bits += leftPitch >> dpi2.zoom_level; dpi2.pitch += leftPitch >> dpi2.zoom_level; - dpi2.x = x; + dpi2.x = columnx; } int16_t paintRight = dpi2.x + dpi2.width; - if (paintRight >= x + 32) + if (paintRight >= columnx + 32) { - int16_t rightPitch = paintRight - x - 32; + int16_t rightPitch = paintRight - columnx - 32; paintRight -= rightPitch; dpi2.pitch += rightPitch >> dpi2.zoom_level; } dpi2.width = paintRight - dpi2.x; - viewport_paint_column(&dpi2, viewFlags); + viewport_paint_column(&dpi2, viewFlags, sessions); } } -static void viewport_paint_column(rct_drawpixelinfo* dpi, uint32_t viewFlags) +static void viewport_paint_column(rct_drawpixelinfo* dpi, uint32_t viewFlags, std::vector* sessions) { if (viewFlags & (VIEWPORT_FLAG_HIDE_VERTICAL | VIEWPORT_FLAG_HIDE_BASE | VIEWPORT_FLAG_UNDERGROUND_INSIDE | VIEWPORT_FLAG_CLIP_VIEW)) @@ -918,6 +927,23 @@ static void viewport_paint_column(rct_drawpixelinfo* dpi, uint32_t viewFlags) paint_session* session = paint_session_alloc(dpi, viewFlags); paint_session_generate(session); + // Perform a deep copy of the paint session, use relative offsets. + // This is done to extract the session for benchmark. + if (sessions != nullptr) + { + sessions->push_back(*session); + paint_session* session_copy = &sessions->at(sessions->size() - 1); + + // Mind the offset needs to be calculated against the original `session`, not `session_copy` + for (auto& ps : session_copy->PaintStructs) + { + ps.basic.next_quadrant_ps = (paint_struct*)(ps.basic.next_quadrant_ps ? int(ps.basic.next_quadrant_ps - &session->PaintStructs[0].basic) : std::size(session->PaintStructs)); + } + for (auto& quad : session_copy->Quadrants) + { + quad = (paint_struct*)(quad ? int(quad - &session->PaintStructs[0].basic) : std::size(session->Quadrants)); + } + } paint_session_arrange(session); paint_draw_structs(session); paint_session_free(session); diff --git a/src/openrct2/interface/Viewport.h b/src/openrct2/interface/Viewport.h index b0759eb2c7..20fd725939 100644 --- a/src/openrct2/interface/Viewport.h +++ b/src/openrct2/interface/Viewport.h @@ -13,6 +13,8 @@ #include "../world/Location.hpp" #include "Window.h" +#include + struct paint_session; struct paint_struct; struct rct_drawpixelinfo; @@ -127,8 +129,12 @@ void viewport_update_smart_sprite_follow(rct_window* window); void viewport_update_smart_guest_follow(rct_window* window, rct_peep* peep); void viewport_update_smart_staff_follow(rct_window* window, rct_peep* peep); void viewport_update_smart_vehicle_follow(rct_window* window); -void viewport_render(rct_drawpixelinfo* dpi, rct_viewport* viewport, int32_t left, int32_t top, int32_t right, int32_t bottom); -void viewport_paint(rct_viewport* viewport, rct_drawpixelinfo* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom); +void viewport_render( + rct_drawpixelinfo* dpi, rct_viewport* viewport, int32_t left, int32_t top, int32_t right, int32_t bottom, + std::vector* sessions = nullptr); +void viewport_paint( + rct_viewport* viewport, rct_drawpixelinfo* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, + std::vector* sessions = nullptr); void viewport_adjust_for_map_height(int16_t* x, int16_t* y, int16_t* z); From d2e39392f3ac15787e9af3fe05ceb06fd6d15a09 Mon Sep 17 00:00:00 2001 From: Gymnasiast Date: Thu, 17 Jan 2019 13:33:18 +0100 Subject: [PATCH 4/7] Fix Xcode project --- OpenRCT2.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/OpenRCT2.xcodeproj/project.pbxproj b/OpenRCT2.xcodeproj/project.pbxproj index e0f3c2c6f6..ac85fdd530 100644 --- a/OpenRCT2.xcodeproj/project.pbxproj +++ b/OpenRCT2.xcodeproj/project.pbxproj @@ -24,6 +24,7 @@ 4C29DEB3218C6AE500E8707F /* RCT12.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C29DEB2218C6AE500E8707F /* RCT12.cpp */; }; 4C358E5221C445F700ADE6BC /* ReplayManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C358E5021C445F700ADE6BC /* ReplayManager.cpp */; }; 4C3B4236205914F7000C5BB7 /* InGameConsole.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C3B4234205914F7000C5BB7 /* InGameConsole.cpp */; }; + 4C724B2221F0AD790012ADD0 /* BenchSpriteSort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C724B2121F0AD790012ADD0 /* BenchSpriteSort.cpp */; }; 4C93F1AD1F8CD9F000A9330D /* Input.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C93F1AC1F8CD9F000A9330D /* Input.cpp */; }; 4C93F1AF1F8CD9F600A9330D /* KeyboardShortcut.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C93F1AE1F8CD9F600A9330D /* KeyboardShortcut.cpp */; }; 4CB1375621C2E9F80029FCDA /* SimulateCommands.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CB1375521C2E9F80029FCDA /* SimulateCommands.cpp */; }; @@ -643,6 +644,7 @@ 4C6AC20E1F9E1693004324AA /* Station.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Station.h; sourceTree = ""; }; 4C6AC2101F9E1CB3004324AA /* CableLift.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CableLift.cpp; sourceTree = ""; }; 4C6AC2111F9E1CB3004324AA /* CableLift.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CableLift.h; sourceTree = ""; }; + 4C724B2121F0AD790012ADD0 /* BenchSpriteSort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BenchSpriteSort.cpp; sourceTree = ""; }; 4C7B53A21FFC15ED00A52E21 /* ObjectLimits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectLimits.h; sourceTree = ""; }; 4C7B53A31FFC180400A52E21 /* ObjectList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ObjectList.cpp; sourceTree = ""; }; 4C7B53A41FFC180400A52E21 /* ObjectList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectList.h; sourceTree = ""; }; @@ -2411,6 +2413,7 @@ isa = PBXGroup; children = ( D48AFDB61EF78DBF0081C644 /* BenchGfxCommmands.cpp */, + 4C724B2121F0AD790012ADD0 /* BenchSpriteSort.cpp */, F76C83631EC4E7CC00FA49E2 /* CommandLine.cpp */, F76C83641EC4E7CC00FA49E2 /* CommandLine.hpp */, F76C83651EC4E7CC00FA49E2 /* ConvertCommand.cpp */, @@ -3647,6 +3650,7 @@ C6D2BEE61F9BAACE008B557C /* TrackList.cpp in Sources */, C666EE701F37ACB10061AA04 /* LandRights.cpp in Sources */, 93F6004D213DD7DD00EEB83E /* TerrainEdgeObject.cpp in Sources */, + 4C724B2221F0AD790012ADD0 /* BenchSpriteSort.cpp in Sources */, C666EE781F37ACB10061AA04 /* ServerList.cpp in Sources */, C654DF341F69C0430040F43D /* NewCampaign.cpp in Sources */, F76C887D1EC5324E00FA49E2 /* CursorData.cpp in Sources */, From b38e600b15d690f0a6c9adb6e2ba8343055e7b52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Sun, 20 Jan 2019 23:55:05 +0100 Subject: [PATCH 5/7] Update sprite sort benchmark to accept filename --- src/openrct2/cmdline/BenchSpriteSort.cpp | 72 ++++++++++++++---------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/src/openrct2/cmdline/BenchSpriteSort.cpp b/src/openrct2/cmdline/BenchSpriteSort.cpp index 325a0089d3..9d1d2c2898 100644 --- a/src/openrct2/cmdline/BenchSpriteSort.cpp +++ b/src/openrct2/cmdline/BenchSpriteSort.cpp @@ -65,8 +65,7 @@ static void fixup_pointers(paint_session* s, size_t paint_session_entries, size_ } } -// This function is based on benchgfx_render_screenshots -static void BM_paint_session_arrange(benchmark::State& state) +static std::vector extract_paint_session(const std::string parkFileName) { core_init(); gOpenRCT2Headless = true; @@ -76,9 +75,10 @@ static void BM_paint_session_arrange(benchmark::State& state) if (context->Initialise()) { drawing_engine_init(); - if (!context->LoadParkFromFile("data.sv6")) + if (!context->LoadParkFromFile(parkFileName)) { - return; + log_error("Failed to load park!"); + return {}; } gIntroState = INTRO_STATE_NONE; @@ -132,6 +132,13 @@ static void BM_paint_session_arrange(benchmark::State& state) drawing_engine_dispose(); } log_info("Got %u paint sessions.", std::size(sessions)); + return sessions; +} + +// This function is based on benchgfx_render_screenshots +static void BM_paint_session_arrange(benchmark::State& state, const std::vector inputSessions) +{ + std::vector sessions = inputSessions; // Fixing up the pointers continuously is wasteful. Fix it up once for `sessions` and store a copy. // Keep in mind we need bit-exact copy, as the lists use pointers. // Once sorted, just restore the copy with the original fixed-up version. @@ -149,40 +156,47 @@ static void BM_paint_session_arrange(benchmark::State& state) state.SetItemsProcessed(state.iterations() * std::size(sessions)); delete[] local_s; } -BENCHMARK(BM_paint_session_arrange); - -// Provide some baseline performing similar things as the actual version of the benchmark does. -static void BM_baseline(benchmark::State& state) -{ - std::vector sessions(1); - paint_session* local_s = new paint_session[std::size(sessions)]; - std::copy_n(sessions.cbegin(), std::size(sessions), local_s); - for (auto _ : state) - { - state.PauseTiming(); - std::copy_n(local_s, std::size(sessions), sessions.begin()); - state.ResumeTiming(); - paint_session_arrange(local_s); - benchmark::DoNotOptimize(local_s); - } - state.SetItemsProcessed(state.iterations() * std::size(sessions)); - delete[] local_s; -} -BENCHMARK(BM_baseline); static int cmdline_for_bench_sprite_sort(int argc, const char** argv) { + const char* inputFileName = nullptr; + int argStart = 0; + if (argc >= 1 && platform_file_exists(argv[0])) + { + inputFileName = argv[0]; + argStart = 1; // skip file name + } // Google benchmark does stuff to argv. It doesn't modify the pointees, // but it wants to reorder the pointers, so present a copy of them. std::vector argv_for_benchmark; - // argv[0] is expected to contain the binary name. Don't bother. + // argv[0] is expected to contain the binary name. It's only for logging purposes, don't bother. argv_for_benchmark.push_back(nullptr); - for (int i = 0; i < argc; i++) + for (int i = argStart; i < argc; i++) { argv_for_benchmark.push_back((char*)argv[i]); } - // Account for argv[0] - argc++; + // Update argc with all the changes made + argc = (int)argv_for_benchmark.size(); + { + // Register some basic "baseline" benchmark + std::vector sessions(1); + for (auto& ps : sessions[0].PaintStructs) + { + ps.basic.next_quadrant_ps = (paint_struct*)(std::size(sessions[0].PaintStructs)); + } + for (auto& quad : sessions[0].Quadrants) + { + quad = (paint_struct*)(std::size(sessions[0].Quadrants)); + } + benchmark::RegisterBenchmark("baseline", BM_paint_session_arrange, sessions); + } + if (inputFileName != nullptr) + { + // Register benchmark for sv6 if valid + std::vector sessions = extract_paint_session(inputFileName); + if (!sessions.empty()) + benchmark::RegisterBenchmark(inputFileName, BM_paint_session_arrange, sessions); + } ::benchmark::Initialize(&argc, &argv_for_benchmark[0]); if (::benchmark::ReportUnrecognizedArguments(argc, &argv_for_benchmark[0])) return -1; @@ -214,7 +228,7 @@ const CommandLineCommand CommandLine::BenchSpriteSortCommands[]{ #ifdef USE_BENCHMARK DefineCommand( "", - " [--benchmark_list_tests={true|false}] [--benchmark_filter=] [--benchmark_min_time=] " + "[] [--benchmark_list_tests={true|false}] [--benchmark_filter=] [--benchmark_min_time=] " "[--benchmark_repetitions=] [--benchmark_report_aggregates_only={true|false}] " "[--benchmark_format=] [--benchmark_out=] [--benchmark_out_format=] " "[--benchmark_color={auto|true|false}] [--benchmark_counters_tabular={true|false}] [--v=]", From cba2dd96ec7b8e80465fee9b940dac1d60a87fca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Tue, 22 Jan 2019 19:54:12 +0100 Subject: [PATCH 6/7] Allow specyfing more than one input file for sprite sort benchmark --- src/openrct2/cmdline/BenchSpriteSort.cpp | 48 ++++++++++++------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/openrct2/cmdline/BenchSpriteSort.cpp b/src/openrct2/cmdline/BenchSpriteSort.cpp index 9d1d2c2898..0c9776ece8 100644 --- a/src/openrct2/cmdline/BenchSpriteSort.cpp +++ b/src/openrct2/cmdline/BenchSpriteSort.cpp @@ -159,24 +159,6 @@ static void BM_paint_session_arrange(benchmark::State& state, const std::vector< static int cmdline_for_bench_sprite_sort(int argc, const char** argv) { - const char* inputFileName = nullptr; - int argStart = 0; - if (argc >= 1 && platform_file_exists(argv[0])) - { - inputFileName = argv[0]; - argStart = 1; // skip file name - } - // Google benchmark does stuff to argv. It doesn't modify the pointees, - // but it wants to reorder the pointers, so present a copy of them. - std::vector argv_for_benchmark; - // argv[0] is expected to contain the binary name. It's only for logging purposes, don't bother. - argv_for_benchmark.push_back(nullptr); - for (int i = argStart; i < argc; i++) - { - argv_for_benchmark.push_back((char*)argv[i]); - } - // Update argc with all the changes made - argc = (int)argv_for_benchmark.size(); { // Register some basic "baseline" benchmark std::vector sessions(1); @@ -190,13 +172,31 @@ static int cmdline_for_bench_sprite_sort(int argc, const char** argv) } benchmark::RegisterBenchmark("baseline", BM_paint_session_arrange, sessions); } - if (inputFileName != nullptr) + + // Google benchmark does stuff to argv. It doesn't modify the pointees, + // but it wants to reorder the pointers, so present a copy of them. + std::vector argv_for_benchmark; + + // argv[0] is expected to contain the binary name. It's only for logging purposes, don't bother. + argv_for_benchmark.push_back(nullptr); + + // Extract file names from argument list. If there is no such file, consider it benchmark option. + for (int i = 0; i < argc; i++) { - // Register benchmark for sv6 if valid - std::vector sessions = extract_paint_session(inputFileName); - if (!sessions.empty()) - benchmark::RegisterBenchmark(inputFileName, BM_paint_session_arrange, sessions); + if (platform_file_exists(argv[i])) + { + // Register benchmark for sv6 if valid + std::vector sessions = extract_paint_session(argv[i]); + if (!sessions.empty()) + benchmark::RegisterBenchmark(argv[i], BM_paint_session_arrange, sessions); + } + else + { + argv_for_benchmark.push_back((char*)argv[i]); + } } + // Update argc with all the changes made + argc = (int)argv_for_benchmark.size(); ::benchmark::Initialize(&argc, &argv_for_benchmark[0]); if (::benchmark::ReportUnrecognizedArguments(argc, &argv_for_benchmark[0])) return -1; @@ -228,7 +228,7 @@ const CommandLineCommand CommandLine::BenchSpriteSortCommands[]{ #ifdef USE_BENCHMARK DefineCommand( "", - "[] [--benchmark_list_tests={true|false}] [--benchmark_filter=] [--benchmark_min_time=] " + "[]... [--benchmark_list_tests={true|false}] [--benchmark_filter=] [--benchmark_min_time=] " "[--benchmark_repetitions=] [--benchmark_report_aggregates_only={true|false}] " "[--benchmark_format=] [--benchmark_out=] [--benchmark_out_format=] " "[--benchmark_color={auto|true|false}] [--benchmark_counters_tabular={true|false}] [--v=]", From 8e3170ca4d639a0bcaa9fe04932ef1b170acf9f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Tue, 22 Jan 2019 19:55:57 +0100 Subject: [PATCH 7/7] Add changelog entry for sprite sorting benchmark --- distribution/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 60427698c5..842cc312a5 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -15,6 +15,7 @@ - Feature: [#8259] Add say command to in-game console. - Feature: [#8374] Add replay system. - Feature: [#8377] Add option to adjust amount of autosaves to keep. +- Feature: [#8458] Add sprite sorting benchmark. - Feature: [#8583] Add boosters to water coaster. - Change: [#7961] Add new object types: station, terrain surface, and terrain edge. - Change: [#8222] The climate setting has been moved from objective options to scenario options.