diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 1dfc1e97f..a2a778452 100644 --- a/Source/CMakeLists.txt +++ b/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 diff --git a/Source/capture.cpp b/Source/capture.cpp index e63b6f0c3..2ad02a6d9 100644 --- a/Source/capture.cpp +++ b/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" diff --git a/Source/engine/lighting_defs.hpp b/Source/engine/lighting_defs.hpp new file mode 100644 index 000000000..b775d7f12 --- /dev/null +++ b/Source/engine/lighting_defs.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include + +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 diff --git a/Source/engine/palette.h b/Source/engine/palette.h index 05f8e225c..1936fef9f 100644 --- a/Source/engine/palette.h +++ b/Source/engine/palette.h @@ -11,7 +11,7 @@ #include -#include "levels/gendung.h" +#include "levels/gendung_defs.hpp" namespace devilution { diff --git a/Source/engine/render/light_render.cpp b/Source/engine/render/light_render.cpp index 05974cac8..4b7a982fe 100644 --- a/Source/engine/render/light_render.cpp +++ b/Source/engine/render/light_render.cpp @@ -2,15 +2,17 @@ #include #include +#include +#include +#include #include #include #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(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 lightmapBuffer) +Lightmap Lightmap::bleedUp(bool perPixelLighting, const Lightmap &source, Point targetBufferPosition, std::span lightmapBuffer) { assert(lightmapBuffer.size() >= TILE_WIDTH * TILE_HEIGHT); - if (!*GetOptions().Graphics.perPixelLighting) - return source; + if (!perPixelLighting) return source; const int sourceHeight = static_cast(source.lightmapBuffer.size() / source.lightmapPitch); const int clipLeft = std::max(0, -targetBufferPosition.x); diff --git a/Source/engine/render/light_render.hpp b/Source/engine/render/light_render.hpp index 3445a8ae8..aabc0b8da 100644 --- a/Source/engine/render/light_render.hpp +++ b/Source/engine/render/light_render.hpp @@ -1,8 +1,11 @@ #pragma once +#include +#include #include #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 lightmapBuffer); + static Lightmap bleedUp(bool perPixelLighting, const Lightmap &source, Point targetBufferPosition, std::span lightmapBuffer); private: const uint8_t *outBuffer; diff --git a/Source/engine/render/scrollrt.cpp b/Source/engine/render/scrollrt.cpp index a6d9cf281..542187d5c 100644 --- a/Source/engine/render/scrollrt.cpp +++ b/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); diff --git a/Source/engine/render/text_render.cpp b/Source/engine/render/text_render.cpp index f19445395..32fa9aced 100644 --- a/Source/engine/render/text_render.cpp +++ b/Source/engine/render/text_render.cpp @@ -16,8 +16,7 @@ #include #include -#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" diff --git a/Source/engine/render/text_render.hpp b/Source/engine/render/text_render.hpp index ef05c77c2..2bf746748 100644 --- a/Source/engine/render/text_render.hpp +++ b/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 { diff --git a/Source/levels/gendung.h b/Source/levels/gendung.h index 15c5401b2..f601c06a9 100644 --- a/Source/levels/gendung.h +++ b/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 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 diff --git a/Source/levels/gendung_defs.hpp b/Source/levels/gendung_defs.hpp new file mode 100644 index 000000000..2b24c4c79 --- /dev/null +++ b/Source/levels/gendung_defs.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +#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 diff --git a/Source/lighting.h b/Source/lighting.h index 2a0de57ad..d38d8d408 100644 --- a/Source/lighting.h +++ b/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 VisionActive; extern Light Lights[MAXLIGHTS]; extern std::array ActiveLights; extern int ActiveLightCount; -constexpr char LightsMax = 15; extern DVL_API_FOR_TEST std::array, 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; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4d72e5921..55feffd07 100644 --- a/test/CMakeLists.txt +++ b/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) diff --git a/test/light_render_benchmark.cpp b/test/light_render_benchmark.cpp index 7d7720bbd..4d0df0b9e 100644 --- a/test/light_render_benchmark.cpp +++ b/test/light_render_benchmark.cpp @@ -1,64 +1,66 @@ -#include - -#include - -#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 +#include +#include + +#include + +#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, 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