Browse Source

Make light render benchmark standalone

pull/8057/head
Gleb Mazovetskiy 9 months ago
parent
commit
8c6b2853ca
  1. 17
      Source/CMakeLists.txt
  2. 1
      Source/capture.cpp
  3. 16
      Source/engine/lighting_defs.hpp
  4. 2
      Source/engine/palette.h
  5. 47
      Source/engine/render/light_render.cpp
  6. 11
      Source/engine/render/light_render.hpp
  7. 9
      Source/engine/render/scrollrt.cpp
  8. 5
      Source/engine/render/text_render.cpp
  9. 1
      Source/engine/render/text_render.hpp
  10. 31
      Source/levels/gendung.h
  11. 37
      Source/levels/gendung_defs.hpp
  12. 8
      Source/lighting.h
  13. 2
      test/CMakeLists.txt
  14. 130
      test/light_render_benchmark.cpp

17
Source/CMakeLists.txt

@ -190,7 +190,6 @@ endif()
add_devilutionx_object_library(libdevilutionx_assets
engine/assets.cpp
utils/paths.cpp
)
target_link_dependencies(libdevilutionx_assets PUBLIC
DevilutionX::SDL
@ -199,6 +198,7 @@ target_link_dependencies(libdevilutionx_assets PUBLIC
libdevilutionx_headless_mode
libdevilutionx_game_mode
libdevilutionx_mpq
libdevilutionx_paths
libdevilutionx_sdl2_to_1_2_backports
libdevilutionx_strings
${DEVILUTIONX_PLATFORM_ASSETS_LINK_LIBRARIES}
@ -245,6 +245,16 @@ target_link_dependencies(libdevilutionx_padmapper PUBLIC
libdevilutionx_options
)
add_devilutionx_object_library(libdevilutionx_paths
utils/paths.cpp
)
target_link_dependencies(libdevilutionx_paths PUBLIC
DevilutionX::SDL
libdevilutionx_file_util
libdevilutionx_log
libdevilutionx_sdl2_to_1_2_backports
)
add_devilutionx_object_library(libdevilutionx_crawl
crawl.cpp
)
@ -322,11 +332,6 @@ target_link_dependencies(libdevilutionx_init PUBLIC
add_devilutionx_object_library(libdevilutionx_light_render
engine/render/light_render.cpp
)
target_link_dependencies(libdevilutionx_light_render PUBLIC
DevilutionX::SDL
libdevilutionx_lighting
libdevilutionx_options
)
add_devilutionx_object_library(libdevilutionx_lighting
lighting.cpp

1
Source/capture.cpp

@ -26,6 +26,7 @@
#include "engine/backbuffer_state.hpp"
#include "engine/dx.h"
#include "engine/palette.h"
#include "engine/render/scrollrt.h"
#include "utils/file_util.h"
#include "utils/log.hpp"
#include "utils/paths.h"

16
Source/engine/lighting_defs.hpp

@ -0,0 +1,16 @@
#pragma once
#include <cstddef>
namespace devilution {
#define MAXLIGHTS 32
#define MAXVISION 4
#define NO_LIGHT -1
constexpr char LightsMax = 15;
/** @brief Number of supported light levels */
constexpr size_t NumLightingLevels = LightsMax + 1;
} // namespace devilution

2
Source/engine/palette.h

@ -11,7 +11,7 @@
#include <SDL.h>
#include "levels/gendung.h"
#include "levels/gendung_defs.hpp"
namespace devilution {

47
Source/engine/render/light_render.cpp

@ -2,15 +2,17 @@
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <span>
#include <vector>
#include "engine/displacement.hpp"
#include "engine/lighting_defs.hpp"
#include "engine/point.hpp"
#include "levels/dun_tile.hpp"
#include "levels/gendung.h"
#include "lighting.h"
#include "options.h"
#include "levels/gendung_defs.hpp"
namespace devilution {
@ -103,11 +105,11 @@ void RenderTriangle(Point p1, Point p2, Point p3, uint8_t lightLevel, uint8_t *l
}
}
uint8_t GetLightLevel(Point tile)
uint8_t GetLightLevel(const uint8_t tileLights[MAXDUNX][MAXDUNY], Point tile)
{
int x = std::clamp(tile.x, 0, MAXDUNX - 1);
int y = std::clamp(tile.y, 0, MAXDUNY - 1);
return dLight[x][y];
return tileLights[x][y];
}
uint8_t Interpolate(int q1, int q2, int lightLevel)
@ -392,15 +394,13 @@ void RenderCell(uint8_t quad[4], Point position, uint8_t lightLevel, uint8_t *li
}
}
void BuildLightmap(Point tilePosition, Point targetBufferPosition, uint16_t viewportWidth, uint16_t viewportHeight, int rows, int columns)
void BuildLightmap(Point tilePosition, Point targetBufferPosition, uint16_t viewportWidth, uint16_t viewportHeight,
int rows, int columns, const uint8_t tileLights[MAXDUNX][MAXDUNY], uint_fast8_t microTileLen)
{
if (!*GetOptions().Graphics.perPixelLighting)
return;
// Since light may need to bleed up to the top of wall tiles,
// expand the buffer space to include the full base diamond of the tallest tile graphics
const uint16_t bufferHeight = viewportHeight + TILE_HEIGHT * (MicroTileLen / 2 + 1);
rows += MicroTileLen + 2;
const uint16_t bufferHeight = viewportHeight + TILE_HEIGHT * (microTileLen / 2 + 1);
rows += microTileLen + 2;
const size_t totalPixels = static_cast<size_t>(viewportWidth) * bufferHeight;
LightmapBuffer.resize(totalPixels);
@ -424,10 +424,10 @@ void BuildLightmap(Point tilePosition, Point targetBufferPosition, uint16_t view
Point tile3 = tilePosition + Displacement { 0, 1 };
uint8_t quad[] = {
GetLightLevel(tile0),
GetLightLevel(tile1),
GetLightLevel(tile2),
GetLightLevel(tile3)
GetLightLevel(tileLights, tile0),
GetLightLevel(tileLights, tile1),
GetLightLevel(tileLights, tile2),
GetLightLevel(tileLights, tile3)
};
uint8_t maxLight = std::max({ quad[0], quad[1], quad[2], quad[3] });
@ -475,21 +475,24 @@ Lightmap::Lightmap(const uint8_t *outBuffer, uint16_t outPitch,
{
}
Lightmap Lightmap::build(Point tilePosition, Point targetBufferPosition,
Lightmap Lightmap::build(bool perPixelLighting, Point tilePosition, Point targetBufferPosition,
int viewportWidth, int viewportHeight, int rows, int columns,
const uint8_t *outBuffer, uint16_t outPitch,
const uint8_t *lightTables, size_t lightTableSize)
const uint8_t *lightTables, size_t lightTableSize,
const uint8_t tileLights[MAXDUNX][MAXDUNY],
uint_fast8_t microTileLen)
{
BuildLightmap(tilePosition, targetBufferPosition, viewportWidth, viewportHeight, rows, columns);
return Lightmap(outBuffer, outPitch, LightmapBuffer, gnScreenWidth, lightTables, lightTableSize);
if (perPixelLighting) {
BuildLightmap(tilePosition, targetBufferPosition, viewportWidth, viewportHeight, rows, columns, tileLights, microTileLen);
}
return Lightmap(outBuffer, outPitch, LightmapBuffer, viewportWidth, lightTables, lightTableSize);
}
Lightmap Lightmap::bleedUp(const Lightmap &source, Point targetBufferPosition, std::span<uint8_t> lightmapBuffer)
Lightmap Lightmap::bleedUp(bool perPixelLighting, const Lightmap &source, Point targetBufferPosition, std::span<uint8_t> lightmapBuffer)
{
assert(lightmapBuffer.size() >= TILE_WIDTH * TILE_HEIGHT);
if (!*GetOptions().Graphics.perPixelLighting)
return source;
if (!perPixelLighting) return source;
const int sourceHeight = static_cast<int>(source.lightmapBuffer.size() / source.lightmapPitch);
const int clipLeft = std::max(0, -targetBufferPosition.x);

11
Source/engine/render/light_render.hpp

@ -1,8 +1,11 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <span>
#include "engine/point.hpp"
#include "levels/gendung_defs.hpp"
namespace devilution {
@ -39,12 +42,14 @@ public:
return lightmapBuffer.data() + row * lightmapPitch + rowOffset;
}
static Lightmap build(Point tilePosition, Point targetBufferPosition,
static Lightmap build(bool perPixelLighting, Point tilePosition, Point targetBufferPosition,
int viewportWidth, int viewportHeight, int rows, int columns,
const uint8_t *outBuffer, uint16_t outPitch,
const uint8_t *lightTables, size_t lightTableSize);
const uint8_t *lightTables, size_t lightTableSize,
const uint8_t tileLights[MAXDUNX][MAXDUNY],
uint_fast8_t microTileLen);
static Lightmap bleedUp(const Lightmap &source, Point targetBufferPosition, std::span<uint8_t> lightmapBuffer);
static Lightmap bleedUp(bool perPixelLighting, const Lightmap &source, Point targetBufferPosition, std::span<uint8_t> lightmapBuffer);
private:
const uint8_t *outBuffer;

9
Source/engine/render/scrollrt.cpp

@ -538,7 +538,7 @@ void DrawCell(const Surface &out, const Lightmap lightmap, Point tilePosition, P
// Create a special lightmap buffer to bleed light up walls
uint8_t lightmapBuffer[TILE_WIDTH * TILE_HEIGHT];
Lightmap bleedLightmap = Lightmap::bleedUp(lightmap, targetBufferPosition, lightmapBuffer);
Lightmap bleedLightmap = Lightmap::bleedUp(*GetOptions().Graphics.perPixelLighting, lightmap, targetBufferPosition, lightmapBuffer);
// If the first micro tile is a floor tile, it may be followed
// by foliage which should be rendered now.
@ -846,7 +846,7 @@ void DrawDungeon(const Surface &out, const Lightmap &lightmap, Point tilePositio
if (perPixelLighting) {
// Create a special lightmap buffer to bleed light up walls
uint8_t lightmapBuffer[TILE_WIDTH * TILE_HEIGHT];
Lightmap bleedLightmap = Lightmap::bleedUp(lightmap, targetBufferPosition, lightmapBuffer);
Lightmap bleedLightmap = Lightmap::bleedUp(*GetOptions().Graphics.perPixelLighting, lightmap, targetBufferPosition, lightmapBuffer);
if (transparency)
ClxDrawBlendedWithLightmap(out, targetBufferPosition, (*pSpecialCels)[bArch], bleedLightmap);
@ -1131,9 +1131,10 @@ void DrawGame(const Surface &fullOut, Point position, Displacement offset)
DunRenderStats.clear();
#endif
Lightmap lightmap = Lightmap::build(position, Point {} + offset,
Lightmap lightmap = Lightmap::build(*GetOptions().Graphics.perPixelLighting, position, Point {} + offset,
gnScreenWidth, gnViewportHeight, rows, columns,
out.at(0, 0), out.pitch(), LightTables[0].data(), LightTables[0].size());
out.at(0, 0), out.pitch(), LightTables[0].data(), LightTables[0].size(),
dLight, MicroTileLen);
DrawFloor(out, lightmap, position, Point {} + offset, rows, columns);
DrawTileContent(out, lightmap, position, Point {} + offset, rows, columns);

5
Source/engine/render/text_render.cpp

@ -16,8 +16,7 @@
#include <ankerl/unordered_dense.h>
#include <fmt/core.h>
#include "DiabloUI/diabloui.h"
#include "DiabloUI/ui_item.h"
#include "DiabloUI/ui_flags.hpp"
#include "engine/load_cel.hpp"
#include "engine/load_clx.hpp"
#include "engine/load_file.hpp"
@ -26,12 +25,14 @@
#include "engine/point.hpp"
#include "engine/render/clx_render.hpp"
#include "engine/render/primitive_render.hpp"
#include "engine/surface.hpp"
#include "engine/ticks.hpp"
#include "options.h"
#include "utils/algorithm/container.hpp"
#include "utils/display.h"
#include "utils/is_of.hpp"
#include "utils/language.h"
#include "utils/log.hpp"
#include "utils/sdl_compat.h"
#include "utils/utf8.hpp"

1
Source/engine/render/text_render.hpp

@ -19,6 +19,7 @@
#include "engine/clx_sprite.hpp"
#include "engine/palette.h"
#include "engine/rectangle.hpp"
#include "engine/surface.hpp"
#include "utils/enum_traits.h"
namespace devilution {

31
Source/levels/gendung.h

@ -20,18 +20,13 @@
#include "engine/render/scrollrt.h"
#include "engine/world_tile.hpp"
#include "levels/dun_tile.hpp"
#include "levels/gendung_defs.hpp"
#include "utils/attributes.h"
#include "utils/bitset2d.hpp"
#include "utils/enum_traits.h"
namespace devilution {
#define DMAXX 40
#define DMAXY 40
#define MAXDUNX (16 + DMAXX * 2 + 16)
#define MAXDUNY (16 + DMAXY * 2 + 16)
#define MAXTHEMES 50
#define MAXTILES 1379
@ -63,32 +58,8 @@ inline bool IsArenaLevel(_setlevels setLevel)
}
}
enum dungeon_type : int8_t {
DTYPE_TOWN,
DTYPE_CATHEDRAL,
DTYPE_CATACOMBS,
DTYPE_CAVES,
DTYPE_HELL,
DTYPE_NEST,
DTYPE_CRYPT,
DTYPE_LAST = DTYPE_CRYPT,
DTYPE_NONE = -1,
};
tl::expected<dungeon_type, std::string> ParseDungeonType(std::string_view value);
enum lvl_entry : uint8_t {
ENTRY_MAIN,
ENTRY_PREV,
ENTRY_SETLVL,
ENTRY_RTNLVL,
ENTRY_LOAD,
ENTRY_WARPLVL,
ENTRY_TWARPDN,
ENTRY_TWARPUP,
};
enum class DungeonFlag : uint8_t {
// clang-format off
None = 0, // Only used by lighting/automap

37
Source/levels/gendung_defs.hpp

@ -0,0 +1,37 @@
#pragma once
#include <cstdint>
#define DMAXX 40
#define DMAXY 40
#define MAXDUNX (16 + DMAXX * 2 + 16)
#define MAXDUNY (16 + DMAXY * 2 + 16)
namespace devilution {
enum dungeon_type : int8_t {
DTYPE_TOWN,
DTYPE_CATHEDRAL,
DTYPE_CATACOMBS,
DTYPE_CAVES,
DTYPE_HELL,
DTYPE_NEST,
DTYPE_CRYPT,
DTYPE_LAST = DTYPE_CRYPT,
DTYPE_NONE = -1,
};
enum lvl_entry : uint8_t {
ENTRY_MAIN,
ENTRY_PREV,
ENTRY_SETLVL,
ENTRY_RTNLVL,
ENTRY_LOAD,
ENTRY_WARPLVL,
ENTRY_TWARPDN,
ENTRY_TWARPUP,
};
} // namespace devilution

8
Source/lighting.h

@ -12,18 +12,13 @@
#include "automap.h"
#include "engine/displacement.hpp"
#include "engine/lighting_defs.hpp"
#include "engine/point.hpp"
#include "engine/world_tile.hpp"
#include "utils/attributes.h"
namespace devilution {
#define MAXLIGHTS 32
#define MAXVISION 4
/** @brief Number of supported light levels */
constexpr size_t NumLightingLevels = 16;
#define NO_LIGHT -1
struct LightPosition {
WorldTilePosition tile;
/** Pixel offset from tile. */
@ -45,7 +40,6 @@ extern std::array<bool, MAXVISION> VisionActive;
extern Light Lights[MAXLIGHTS];
extern std::array<uint8_t, MAXLIGHTS> ActiveLights;
extern int ActiveLightCount;
constexpr char LightsMax = 15;
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 DVL_API_FOR_TEST uint8_t *FullyLitLightTable;

2
test/CMakeLists.txt

@ -107,7 +107,7 @@ target_link_dependencies(dun_render_benchmark PRIVATE libdevilutionx_so)
target_link_dependencies(file_util_test PRIVATE libdevilutionx_file_util app_fatal_for_testing)
target_link_dependencies(format_int_test PRIVATE libdevilutionx_format_int language_for_testing)
target_link_dependencies(ini_test PRIVATE libdevilutionx_ini app_fatal_for_testing)
target_link_dependencies(light_render_benchmark PRIVATE libdevilutionx_so)
target_link_dependencies(light_render_benchmark PRIVATE libdevilutionx_light_render DevilutionX::SDL libdevilutionx_surface libdevilutionx_paths app_fatal_for_testing)
target_link_dependencies(palette_blending_test PRIVATE libdevilutionx_palette_blending DevilutionX::SDL libdevilutionx_strings GTest::gmock app_fatal_for_testing)
target_link_dependencies(palette_blending_benchmark PRIVATE libdevilutionx_palette_blending DevilutionX::SDL app_fatal_for_testing)
target_link_dependencies(parse_int_test PRIVATE libdevilutionx_parse_int)

130
test/light_render_benchmark.cpp

@ -1,64 +1,66 @@
#include <cstddef>
#include <benchmark/benchmark.h>
#include "engine/render/light_render.hpp"
#include "engine/surface.hpp"
#include "lighting.h"
#include "utils/log.hpp"
#include "utils/paths.h"
#include "utils/sdl_wrap.h"
namespace devilution {
namespace {
void BM_BuildLightmap(benchmark::State &state)
{
std::string benchmarkDataPath = paths::BasePath() + "test/fixtures/light_render_benchmark/dLight.dmp";
FILE *lightFile = std::fopen(benchmarkDataPath.c_str(), "rb");
if (lightFile != nullptr) {
std::fread(&dLight[0][0], sizeof(uint8_t), MAXDUNX * MAXDUNY, lightFile);
std::fclose(lightFile);
}
SDLSurfaceUniquePtr sdl_surface = SDLWrap::CreateRGBSurfaceWithFormat(
/*flags=*/0, /*width=*/640, /*height=*/480, /*depth=*/8, SDL_PIXELFORMAT_INDEX8);
if (sdl_surface == nullptr) {
LogError("Failed to create SDL Surface: {}", SDL_GetError());
exit(1);
}
Surface out = Surface(sdl_surface.get());
Point tilePosition { 48, 44 };
Point targetBufferPosition { 0, -17 };
int viewportWidth = 640;
int viewportHeight = 352;
int rows = 25;
int columns = 10;
const uint8_t *outBuffer = out.at(0, 0);
uint16_t outPitch = out.pitch();
const uint8_t *lightTables = LightTables[0].data();
size_t lightTableSize = LightTables[0].size();
size_t numViewportTiles = rows * columns;
size_t numPixels = viewportWidth * viewportHeight;
size_t numBytesProcessed = 0;
size_t numItemsProcessed = 0;
for (auto _ : state) {
Lightmap lightmap = Lightmap::build(tilePosition, targetBufferPosition,
viewportWidth, viewportHeight, rows, columns,
outBuffer, outPitch, lightTables, lightTableSize);
uint8_t lightLevel = *lightmap.getLightingAt(outBuffer + outPitch * 120 + 120);
benchmark::DoNotOptimize(lightLevel);
numItemsProcessed += numViewportTiles;
numBytesProcessed += numPixels;
}
state.SetBytesProcessed(numBytesProcessed);
state.SetItemsProcessed(numItemsProcessed);
}
BENCHMARK(BM_BuildLightmap);
} // namespace
} // namespace devilution
#include <array>
#include <cstddef>
#include <cstdio>
#include <benchmark/benchmark.h>
#include "engine/lighting_defs.hpp"
#include "engine/render/light_render.hpp"
#include "engine/surface.hpp"
#include "levels/gendung_defs.hpp"
#include "utils/log.hpp"
#include "utils/paths.h"
#include "utils/sdl_wrap.h"
namespace devilution {
namespace {
void BM_BuildLightmap(benchmark::State &state)
{
const std::string benchmarkDataPath = paths::BasePath() + "test/fixtures/light_render_benchmark/dLight.dmp";
FILE *lightFile = std::fopen(benchmarkDataPath.c_str(), "rb");
uint8_t dLight[MAXDUNX][MAXDUNY];
std::array<std::array<uint8_t, 256>, LightsMax> lightTables;
if (lightFile != nullptr) {
if (std::fread(&dLight[0][0], sizeof(uint8_t) * MAXDUNX * MAXDUNY, 1, lightFile) != 1) {
std::perror("Failed to read dLight.dmp");
exit(1);
}
std::fclose(lightFile);
}
SDLSurfaceUniquePtr sdl_surface = SDLWrap::CreateRGBSurfaceWithFormat(
/*flags=*/0, /*width=*/640, /*height=*/480, /*depth=*/8, SDL_PIXELFORMAT_INDEX8);
if (sdl_surface == nullptr) {
std::fprintf(stderr, "Failed to create SDL Surface: %s\n", SDL_GetError());
exit(1);
}
Surface out = Surface(sdl_surface.get());
const Point tilePosition { 48, 44 };
const Point targetBufferPosition { 0, -17 };
const int viewportWidth = 640;
const int viewportHeight = 352;
const int rows = 25;
const int columns = 10;
const uint8_t *outBuffer = out.at(0, 0);
const uint16_t outPitch = out.pitch();
for (auto _ : state) {
Lightmap lightmap = Lightmap::build(/*perPixelLighting=*/true,
tilePosition, targetBufferPosition,
viewportWidth, viewportHeight, rows, columns,
outBuffer, outPitch, lightTables[0].data(), lightTables[0].size(),
dLight, /*microTileLen=*/10);
uint8_t lightLevel = *lightmap.getLightingAt(outBuffer + outPitch * 120 + 120);
benchmark::DoNotOptimize(lightLevel);
}
state.SetBytesProcessed(state.iterations() * viewportWidth * viewportHeight);
state.SetItemsProcessed(state.iterations() * rows * columns);
}
BENCHMARK(BM_BuildLightmap);
} // namespace
} // namespace devilution

Loading…
Cancel
Save