mirror of
https://github.com/OpenRCT2/OpenRCT2.git
synced 2025-01-22 18:31:59 -05:00
Add sprite sorting benchmark
This commit is contained in:
parent
f2233d3cc3
commit
304840069d
6 changed files with 289 additions and 18 deletions
|
@ -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}
|
||||
|
|
226
src/openrct2/cmdline/BenchSpriteSort.cpp
Normal file
226
src/openrct2/cmdline/BenchSpriteSort.cpp
Normal 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
|
||||
};
|
|
@ -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[];
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue