Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dun_render_benchmark #7353

Merged
merged 2 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Source/diablo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2855,6 +2855,7 @@ void LoadGameLevel(bool firstflag, lvl_entry lvldir)
}
SetRndSeed(DungeonSeeds[currlevel]);
IncProgress();
LoadTrns();
MakeLightTable();
LoadLvlGFX();
SetDungeonMicros();
Expand Down
4 changes: 2 additions & 2 deletions Source/levels/gendung.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ extern WorldTileRectangle SetPiece;
extern OptionalOwnedClxSpriteList pSpecialCels;
/** Specifies the tile definitions of the active dungeon type; (e.g. levels/l1data/l1.til). */
extern DVL_API_FOR_TEST std::unique_ptr<MegaTile[]> pMegaTiles;
extern std::unique_ptr<std::byte[]> pDungeonCels;
extern DVL_API_FOR_TEST std::unique_ptr<std::byte[]> pDungeonCels;
/**
* List tile properties
*/
Expand All @@ -194,7 +194,7 @@ extern std::array<bool, 256> TransList;
/** Contains the piece IDs of each tile on the map. */
extern DVL_API_FOR_TEST uint16_t dPiece[MAXDUNX][MAXDUNY];
/** Map of micros that comprises a full tile for any given dungeon piece. */
extern MICROS DPieceMicros[MAXTILES];
extern DVL_API_FOR_TEST MICROS DPieceMicros[MAXTILES];
/** Specifies the transparency at each coordinate of the map. */
extern DVL_API_FOR_TEST int8_t dTransVal[MAXDUNX][MAXDUNY];
/** Current realtime lighting. Per tile. */
Expand Down
11 changes: 7 additions & 4 deletions Source/lighting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,13 @@ void DoVision(Point position, uint8_t radius, MapExplorationType doAutomap, bool
}
}

void LoadTrns()
{
LoadFileInMem("plrgfx\\infra.trn", InfravisionTable);
LoadFileInMem("plrgfx\\stone.trn", StoneTable);
LoadFileInMem("gendata\\pause.trn", PauseTable);
}

void MakeLightTable()
{
// Generate 16 gradually darker translation tables for doing lighting
Expand Down Expand Up @@ -319,10 +326,6 @@ void MakeLightTable()
assert((FullyLitLightTable != nullptr) == (LightTables[0][0] == 0 && std::adjacent_find(LightTables[0].begin(), LightTables[0].end() - 1, [](auto x, auto y) { return (x + 1) != y; }) == LightTables[0].end() - 1));
assert((FullyDarkLightTable != nullptr) == (std::all_of(LightTables[LightsMax].begin(), LightTables[LightsMax].end(), [](auto x) { return x == 0; })));

LoadFileInMem("plrgfx\\infra.trn", InfravisionTable);
LoadFileInMem("plrgfx\\stone.trn", StoneTable);
LoadFileInMem("gendata\\pause.trn", PauseTable);

// Generate light falloffs ranges
const float maxDarkness = 15;
const float maxBrightness = 0;
Expand Down
7 changes: 4 additions & 3 deletions Source/lighting.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ extern Light Lights[MAXLIGHTS];
extern std::array<uint8_t, MAXLIGHTS> ActiveLights;
extern int ActiveLightCount;
constexpr char LightsMax = 15;
extern std::array<std::array<uint8_t, 256>, NumLightingLevels> LightTables;
extern DVL_API_FOR_TEST std::array<std::array<uint8_t, 256>, NumLightingLevels> LightTables;
/** @brief Contains a pointer to a light table that is fully lit (no color mapping is required). Can be null in hell. */
extern uint8_t *FullyLitLightTable;
extern DVL_API_FOR_TEST uint8_t *FullyLitLightTable;
/** @brief Contains a pointer to a light table that is fully dark (every color result to 0/black). Can be null in hellfire levels. */
extern uint8_t *FullyDarkLightTable;
extern DVL_API_FOR_TEST uint8_t *FullyDarkLightTable;
extern std::array<uint8_t, 256> InfravisionTable;
extern std::array<uint8_t, 256> StoneTable;
extern std::array<uint8_t, 256> PauseTable;
Expand All @@ -65,6 +65,7 @@ void DoUnLight(Point position, uint8_t radius);
void DoLighting(Point position, uint8_t radius, DisplacementOf<int8_t> offset);
void DoUnVision(Point position, uint8_t radius);
void DoVision(Point position, uint8_t radius, MapExplorationType doAutomap, bool visible);
void LoadTrns();
void MakeLightTable();
#ifdef _DEBUG
void ToggleLighting();
Expand Down
1 change: 1 addition & 0 deletions Source/mpq/mpq_reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ MpqArchive &MpqArchive::operator=(MpqArchive &&other) noexcept
if (archive_ != nullptr)
libmpq__archive_close(archive_);
archive_ = other.archive_;
other.archive_ = nullptr;
tmp_buf_ = std::move(other.tmp_buf_);
return *this;
}
Expand Down
31 changes: 31 additions & 0 deletions docs/benchmarking.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,34 @@ tools/linux_reduced_cpu_variance_run.sh build-reld/clx_render_benchmark
See `tools/build_and_run_benchmark.py --help` for more information.

You can also [profile](profiling-linux.md) your benchmarks.


## Comparing benchmark runs

You can use [compare.py from Google Benchmark](https://github.com/google/benchmark/blob/main/docs/tools.md) to compare 2 benchmarks.

First, install the tool:

```bash
git clone git@github.com:google/benchmark.git ~/google-benchmark
cd ~/google-benchmark/tools
pip3 install -r requirements.txt
cd -
```

Then, build the 2 binaries that you'd like to compare. For example:

```bash
BASELINE=master
BENCHMARK=dun_render_benchmark

git checkout "$BASELINE"
tools/build_and_run_benchmark.py -B "build-reld-${BASELINE}" --no-run "$BENCHMARK"
git checkout -

tools/build_and_run_benchmark.py --no-run "$BENCHMARK"

tools/linux_reduced_cpu_variance_run.sh ~/google-benchmark/tools/compare.py -a benchmarks \
"build-reld-${BASELINE}/${BENCHMARK}" "build-reld/${BENCHMARK}" \
--benchmark_repetitions=10
```
4 changes: 3 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ set(standalone_tests
)
set(benchmarks
clx_render_benchmark
crawl_benchmark)
crawl_benchmark
dun_render_benchmark)

include(Fixtures.cmake)

Expand Down Expand Up @@ -88,6 +89,7 @@ target_link_libraries(codec_test PRIVATE libdevilutionx_codec app_fatal_for_test
target_link_libraries(clx_render_benchmark PRIVATE libdevilutionx_so)
target_link_libraries(crawl_test PRIVATE libdevilutionx_crawl)
target_link_libraries(crawl_benchmark PRIVATE libdevilutionx_crawl)
target_link_libraries(dun_render_benchmark PRIVATE libdevilutionx_so)
target_link_libraries(file_util_test PRIVATE libdevilutionx_file_util app_fatal_for_testing)
target_link_libraries(format_int_test PRIVATE libdevilutionx_format_int language_for_testing)
target_link_libraries(parse_int_test PRIVATE libdevilutionx_parse_int)
Expand Down
116 changes: 116 additions & 0 deletions test/dun_render_benchmark.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#include <cstddef>
#include <span>

#include <ankerl/unordered_dense.h>
#include <benchmark/benchmark.h>

#include "diablo.h"
#include "engine/clx_sprite.hpp"
#include "engine/displacement.hpp"
#include "engine/load_file.hpp"
#include "engine/render/dun_render.hpp"
#include "engine/surface.hpp"
#include "init.h"
#include "levels/dun_tile.hpp"
#include "levels/gendung.h"
#include "lighting.h"
#include "options.h"
#include "utils/log.hpp"
#include "utils/sdl_wrap.h"

namespace devilution {
namespace {

SDLSurfaceUniquePtr SdlSurface;
ankerl::unordered_dense::map<TileType, std::vector<LevelCelBlock>> Tiles;

void InitOnce()
{
[[maybe_unused]] static const bool GlobalInitDone = []() {
LoadCoreArchives();
LoadGameArchives();
if (!HaveSpawn() && !HaveDiabdat()) {
LogError("This benchmark needs spawn.mpq or diabdat.mpq");
exit(1);
}

leveltype = DTYPE_CATHEDRAL;
pDungeonCels = LoadFileInMem("levels\\l1data\\l1.cel");
SetDungeonMicros();
MakeLightTable();

SdlSurface = SDLWrap::CreateRGBSurfaceWithFormat(
/*flags=*/0, /*width=*/640, /*height=*/480, /*depth=*/8, SDL_PIXELFORMAT_INDEX8);
if (SdlSurface == nullptr) {
LogError("Failed to create SDL Surface: {}", SDL_GetError());
exit(1);
}

for (size_t i = 0; i < 700; ++i) {
for (size_t j = 0; j < 10; ++j) {
if (const LevelCelBlock levelCelBlock = DPieceMicros[i].mt[j]; levelCelBlock.hasValue()) {
Tiles[levelCelBlock.type()].push_back(levelCelBlock);
}
}
}
return true;
}();
}

void RunForTileMaskLight(benchmark::State &state, TileType tileType, MaskType maskType, const uint8_t *lightTable)
{
Surface out = Surface(SdlSurface.get());
size_t numItemsProcessed = 0;
const std::span<const LevelCelBlock> tiles = Tiles[tileType];
for (auto _ : state) {
for (const LevelCelBlock &levelCelBlock : tiles) {
RenderTile(out, Point { 320, 240 }, levelCelBlock, maskType, lightTable);
uint8_t color = out[Point { 310, 200 }];
benchmark::DoNotOptimize(color);
}
numItemsProcessed += tiles.size();
}
state.SetItemsProcessed(numItemsProcessed);
}

using GetLightTableFn = const uint8_t *();

const uint8_t *FullyLit() { return FullyLitLightTable; }
const uint8_t *FullyDark() { return FullyDarkLightTable; }
const uint8_t *PartiallyLit() { return LightTables[5].data(); }

template <TileType TileT, MaskType MaskT, GetLightTableFn GetLightTableFnT>
void Render(benchmark::State &state)
{
InitOnce();
RunForTileMaskLight(state, TileT, MaskT, GetLightTableFnT());
}

// Define aliases in order to have shorter benchmark names.
constexpr auto LeftTriangle = TileType::LeftTriangle;
constexpr auto RightTriangle = TileType::RightTriangle;
constexpr auto TransparentSquare = TileType::TransparentSquare;
constexpr auto Square = TileType::Square;
constexpr auto LeftTrapezoid = TileType::LeftTrapezoid;
constexpr auto RightTrapezoid = TileType::RightTrapezoid;
constexpr auto Transparent = MaskType::Transparent;
constexpr auto Solid = MaskType::Solid;

#define DEFINE_FOR_TILE_AND_MASK_TYPE(TILE_TYPE, MASK_TYPE) \
BENCHMARK_TEMPLATE(Render, TILE_TYPE, MASK_TYPE, FullyLit); \
BENCHMARK_TEMPLATE(Render, TILE_TYPE, MASK_TYPE, FullyDark); \
BENCHMARK_TEMPLATE(Render, TILE_TYPE, MASK_TYPE, PartiallyLit);

#define DEFINE_FOR_TILE_TYPE(TILE_TYPE) \
DEFINE_FOR_TILE_AND_MASK_TYPE(TILE_TYPE, Solid) \
DEFINE_FOR_TILE_AND_MASK_TYPE(TILE_TYPE, Transparent)

DEFINE_FOR_TILE_TYPE(LeftTriangle)
DEFINE_FOR_TILE_TYPE(RightTriangle)
DEFINE_FOR_TILE_TYPE(TransparentSquare)
DEFINE_FOR_TILE_TYPE(Square)
DEFINE_FOR_TILE_TYPE(LeftTrapezoid)
DEFINE_FOR_TILE_TYPE(RightTrapezoid)

} // namespace
} // namespace devilution
8 changes: 5 additions & 3 deletions tools/build_and_run_benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def main():
nargs="*",
help="arguments passed to the benchmark binary",
)
parser.add_argument("--run", action=argparse.BooleanOptionalAction, default=True, help="If false, only builds the target")
args = parser.parse_args()
build = args.build
if not build:
Expand All @@ -81,9 +82,10 @@ def main():
try:
maybe_create_build_dir(build, configure_args)
build_target(build, args.target)
run_benchmark(build, args.target, args.benchmark_args, args.gperf)
if args.gperf:
run_pprof(build, args.target, args.port)
if args.run:
run_benchmark(build, args.target, args.benchmark_args, args.gperf)
if args.gperf:
run_pprof(build, args.target, args.port)
except subprocess.CalledProcessError as e:
print("Error:", e.cmd[0], "failed", file=sys.stderr)
return e.returncode
Expand Down
Loading