Add sprite sorting benchmark

This commit is contained in:
Michał Janiszewski 2018-12-15 23:41:38 +01:00
parent f2233d3cc3
commit 304840069d
6 changed files with 289 additions and 18 deletions

View file

@ -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}

View file

@ -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 <benchmark/benchmark.h>
# include <cstdint>
# include <iterator>
# include <vector>
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<paint_session> 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<paint_session> 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<char*> 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(
"",
"<file> [--benchmark_list_tests={true|false}] [--benchmark_filter=<regex>] [--benchmark_min_time=<min_time>] "
"[--benchmark_repetitions=<num_repetitions>] [--benchmark_report_aggregates_only={true|false}] "
"[--benchmark_format=<console|json|csv>] [--benchmark_out=<filename>] [--benchmark_out_format=<json|console|csv>] "
"[--benchmark_color={auto|true|false}] [--benchmark_counters_tabular={true|false}] [--v=<verbosity>]",
nullptr, HandleBenchSpriteSort),
CommandTableEnd
#else
DefineCommand("", "*** SORRY NOT ENABLED IN THIS BUILD ***", nullptr, HandleBenchSpriteSort), CommandTableEnd
#endif // USE_BENCHMARK
};

View file

@ -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[];

View file

@ -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
};

View file

@ -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<paint_session>* 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<paint_session>* 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<paint_session>* 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<paint_session>* 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);

View file

@ -13,6 +13,8 @@
#include "../world/Location.hpp"
#include "Window.h"
#include <vector>
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<paint_session>* 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<paint_session>* sessions = nullptr);
void viewport_adjust_for_map_height(int16_t* x, int16_t* y, int16_t* z);