Skip to content

Commit

Permalink
Re-encode dungeon tiles to remove bloat
Browse files Browse the repository at this point in the history
Triangles in the dungeon CEL data have two redundant 0x00 pixels every other row.
Re-encodes the dungeon CEL data to remove these pixels in order to save RAM and simplify the rendering code.

Example RAM savings:

```
VERBOSE: Re-encoding dungeon CELs: 1,119 frames, 738,836 bytes
VERBOSE:  Re-encoded dungeon CELs: 1,119 frames, 722,552 bytes
```

Performance remains the same. The rendering code is now a bit simpler.
  • Loading branch information
glebm committed Aug 18, 2024
1 parent a2961ea commit 451f1fd
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 52 deletions.
1 change: 1 addition & 0 deletions Source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ set(libdevilutionx_SRCS
levels/drlg_l3.cpp
levels/drlg_l4.cpp
levels/gendung.cpp
levels/reencode_dun_cels.cpp
levels/setmaps.cpp
levels/themes.cpp
levels/town.cpp
Expand Down
2 changes: 1 addition & 1 deletion Source/diablo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2856,8 +2856,8 @@ void LoadGameLevel(bool firstflag, lvl_entry lvldir)
SetRndSeed(DungeonSeeds[currlevel]);
IncProgress();
MakeLightTable();
SetDungeonMicros();
LoadLvlGFX();
SetDungeonMicros();
ClearClxDrawCache();
IncProgress();

Expand Down
70 changes: 22 additions & 48 deletions Source/engine/render/dun_render.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,19 +186,19 @@ template <LightType Light>
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderLineTransparent(uint8_t *DVL_RESTRICT dst, const uint8_t *DVL_RESTRICT src, uint_fast8_t n, const uint8_t *DVL_RESTRICT tbl);

template <>
void RenderLineTransparent<LightType::FullyDark>(uint8_t *DVL_RESTRICT dst, [[maybe_unused]] const uint8_t *DVL_RESTRICT src, uint_fast8_t n, [[maybe_unused]] const uint8_t *DVL_RESTRICT tbl)
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderLineTransparent<LightType::FullyDark>(uint8_t *DVL_RESTRICT dst, [[maybe_unused]] const uint8_t *DVL_RESTRICT src, uint_fast8_t n, [[maybe_unused]] const uint8_t *DVL_RESTRICT tbl)
{
BlitFillBlended(dst, n, 0);
}

template <>
void RenderLineTransparent<LightType::FullyLit>(uint8_t *DVL_RESTRICT dst, const uint8_t *DVL_RESTRICT src, uint_fast8_t n, [[maybe_unused]] const uint8_t *DVL_RESTRICT tbl)
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderLineTransparent<LightType::FullyLit>(uint8_t *DVL_RESTRICT dst, const uint8_t *DVL_RESTRICT src, uint_fast8_t n, [[maybe_unused]] const uint8_t *DVL_RESTRICT tbl)
{
BlitPixelsBlended(dst, src, n);
}

template <>
void RenderLineTransparent<LightType::PartiallyLit>(uint8_t *DVL_RESTRICT dst, const uint8_t *DVL_RESTRICT src, uint_fast8_t n, const uint8_t *DVL_RESTRICT tbl)
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderLineTransparent<LightType::PartiallyLit>(uint8_t *DVL_RESTRICT dst, const uint8_t *DVL_RESTRICT src, uint_fast8_t n, const uint8_t *DVL_RESTRICT tbl)
{
BlitPixelsBlendedWithMap(dst, src, n, tbl);
}
Expand Down Expand Up @@ -464,29 +464,24 @@ DVL_ALWAYS_INLINE DiamondClipY CalculateDiamondClipY(const Clip &clip)

DVL_ALWAYS_INLINE std::size_t CalculateTriangleSourceSkipLowerBottom(int_fast16_t numLines)
{
return XStep * numLines * (numLines + 1) / 2 + 2 * ((numLines + 1) / 2);
return XStep * numLines * (numLines + 1) / 2;
}

DVL_ALWAYS_INLINE std::size_t CalculateTriangleSourceSkipUpperBottom(int_fast16_t numLines)
{
return 2 * TriangleUpperHeight * numLines - numLines * (numLines - 1) + 2 * ((numLines + 1) / 2);
return 2 * TriangleUpperHeight * numLines - numLines * (numLines - 1);
}

template <LightType Light, bool Transparent>
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderLeftTriangleLower(uint8_t *DVL_RESTRICT &dst, uint16_t dstPitch, const uint8_t *DVL_RESTRICT &src, const uint8_t *DVL_RESTRICT tbl)
{
dst += XStep * (LowerHeight - 1);
unsigned width = 0;
for (unsigned i = 0; i < LowerHeight; i += 2) {
src += 2;
width += XStep;
unsigned width = XStep;
for (unsigned i = 0; i < LowerHeight; ++i) {
RenderLineTransparentOrOpaque<Light, Transparent>(dst, src, width, tbl);
dst -= dstPitch + XStep;
src += width;
width += XStep;
RenderLineTransparentOrOpaque<Light, Transparent>(dst, src, width, tbl);
dst -= dstPitch + XStep;
src += width;
width += XStep;
}
}

Expand All @@ -497,7 +492,6 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderLeftTriangleLowerClipVertical(con
dst += XStep * (LowerHeight - clipY.lowerBottom - 1);
const auto lowerMax = LowerHeight - clipY.lowerTop;
for (auto i = 1 + clipY.lowerBottom; i <= lowerMax; ++i, dst -= dstPitch + XStep) {
src += 2 * (i % 2);
const auto width = XStep * i;
RenderLineTransparentOrOpaque<Light, Transparent>(dst, src, width, tbl);
src += width;
Expand All @@ -511,7 +505,6 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderLeftTriangleLowerClipLeftAndVerti
dst += XStep * (LowerHeight - clipY.lowerBottom - 1) - clipLeft;
const auto lowerMax = LowerHeight - clipY.lowerTop;
for (auto i = 1 + clipY.lowerBottom; i <= lowerMax; ++i, dst -= dstPitch + XStep) {
src += 2 * (i % 2);
const auto width = XStep * i;
const auto startX = Width - XStep * i;
const auto skip = startX < clipLeft ? clipLeft - startX : 0;
Expand All @@ -528,7 +521,6 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderLeftTriangleLowerClipRightAndVert
dst += XStep * (LowerHeight - clipY.lowerBottom - 1);
const auto lowerMax = LowerHeight - clipY.lowerTop;
for (auto i = 1 + clipY.lowerBottom; i <= lowerMax; ++i, dst -= dstPitch + XStep) {
src += 2 * (i % 2);
const auto width = XStep * i;
if (width > clipRight)
RenderLineTransparentOrOpaque<Light, Transparent>(dst, src, width - clipRight, tbl);
Expand All @@ -541,19 +533,13 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderLeftTriangleFull(uint8_t *DVL_RES
{
RenderLeftTriangleLower<Light, Transparent>(dst, dstPitch, src, tbl);
dst += 2 * XStep;
unsigned width = Width;
for (unsigned i = 0; i < TriangleUpperHeight - 1; i += 2) {
src += 2;
width -= XStep;
unsigned width = Width - XStep;
for (unsigned i = 0; i < TriangleUpperHeight; ++i) {
RenderLineTransparentOrOpaque<Light, Transparent>(dst, src, width, tbl);
src += width;
dst -= dstPitch - XStep;
width -= XStep;
RenderLineTransparentOrOpaque<Light, Transparent>(dst, src, width, tbl);
src += width;
dst -= dstPitch - XStep;
}
RenderLineTransparentOrOpaque<Light, Transparent>(dst, src, width, tbl);
}

template <LightType Light, bool Transparent>
Expand All @@ -565,7 +551,6 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderLeftTriangleClipVertical(uint8_t
dst += 2 * XStep + XStep * clipY.upperBottom;
const auto upperMax = TriangleUpperHeight - clipY.upperTop;
for (auto i = 1 + clipY.upperBottom; i <= upperMax; ++i, dst -= dstPitch - XStep) {
src += 2 * (i % 2);
const auto width = Width - XStep * i;
RenderLineTransparentOrOpaque<Light, Transparent>(dst, src, width, tbl);
src += width;
Expand All @@ -582,7 +567,6 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderLeftTriangleClipLeftAndVertical(u
dst += 2 * XStep + XStep * clipY.upperBottom;
const auto upperMax = TriangleUpperHeight - clipY.upperTop;
for (auto i = 1 + clipY.upperBottom; i <= upperMax; ++i, dst -= dstPitch - XStep) {
src += 2 * (i % 2);
const auto width = Width - XStep * i;
const auto startX = XStep * i;
const auto skip = startX < clipLeft ? clipLeft - startX : 0;
Expand All @@ -601,7 +585,6 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderLeftTriangleClipRightAndVertical(
dst += 2 * XStep + XStep * clipY.upperBottom;
const auto upperMax = TriangleUpperHeight - clipY.upperTop;
for (auto i = 1 + clipY.upperBottom; i <= upperMax; ++i, dst -= dstPitch - XStep) {
src += 2 * (i % 2);
const auto width = Width - XStep * i;
if (width <= clipRight)
break;
Expand Down Expand Up @@ -629,16 +612,12 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderLeftTriangle(uint8_t *DVL_RESTRIC
template <LightType Light, bool Transparent>
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderRightTriangleLower(uint8_t *DVL_RESTRICT &dst, uint16_t dstPitch, const uint8_t *DVL_RESTRICT &src, const uint8_t *DVL_RESTRICT tbl)
{
unsigned width = 0;
for (unsigned i = 0; i < LowerHeight; i += 2) {
width += XStep;
RenderLineTransparentOrOpaque<Light, Transparent>(dst, src, width, tbl);
src += width + 2;
width += XStep;
dst -= dstPitch;
unsigned width = XStep;
for (unsigned i = 0; i < LowerHeight; ++i) {
RenderLineTransparentOrOpaque<Light, Transparent>(dst, src, width, tbl);
src += width;
dst -= dstPitch;
width += XStep;
}
}

Expand All @@ -650,7 +629,7 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderRightTriangleLowerClipVertical(co
for (auto i = 1 + clipY.lowerBottom; i <= lowerMax; ++i, dst -= dstPitch) {
const auto width = XStep * i;
RenderLineTransparentOrOpaque<Light, Transparent>(dst, src, width, tbl);
src += width + 2 * (i % 2);
src += width;
}
}

Expand All @@ -663,7 +642,7 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderRightTriangleLowerClipLeftAndVert
const auto width = XStep * i;
if (width > clipLeft)
RenderLineTransparentOrOpaque<Light, Transparent>(dst, src + clipLeft, width - clipLeft, tbl);
src += width + 2 * (i % 2);
src += width;
}
}

Expand All @@ -677,26 +656,21 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderRightTriangleLowerClipRightAndVer
const auto skip = Width - width < clipRight ? clipRight - (Width - width) : 0;
if (width > skip)
RenderLineTransparentOrOpaque<Light, Transparent>(dst, src, width - skip, tbl);
src += width + 2 * (i % 2);
src += width;
}
}

template <LightType Light, bool Transparent>
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderRightTriangleFull(uint8_t *DVL_RESTRICT dst, uint16_t dstPitch, const uint8_t *DVL_RESTRICT src, const uint8_t *DVL_RESTRICT tbl)
{
RenderRightTriangleLower<Light, Transparent>(dst, dstPitch, src, tbl);
unsigned width = Width;
for (unsigned i = 0; i < TriangleUpperHeight - 1; i += 2) {
width -= XStep;
RenderLineTransparentOrOpaque<Light, Transparent>(dst, src, width, tbl);
src += width + 2;
dst -= dstPitch;
width -= XStep;
unsigned width = Width - XStep;
for (unsigned i = 0; i < TriangleUpperHeight; ++i) {
RenderLineTransparentOrOpaque<Light, Transparent>(dst, src, width, tbl);
src += width;
dst -= dstPitch;
width -= XStep;
}
RenderLineTransparentOrOpaque<Light, Transparent>(dst, src, width, tbl);
}

template <LightType Light, bool Transparent>
Expand All @@ -709,7 +683,7 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderRightTriangleClipVertical(uint8_t
for (auto i = 1 + clipY.upperBottom; i <= upperMax; ++i, dst -= dstPitch) {
const auto width = Width - XStep * i;
RenderLineTransparentOrOpaque<Light, Transparent>(dst, src, width, tbl);
src += width + 2 * (i % 2);
src += width;
}
}

Expand All @@ -726,7 +700,7 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderRightTriangleClipLeftAndVertical(
if (width <= clipLeft)
break;
RenderLineTransparentOrOpaque<Light, Transparent>(dst, src + clipLeft, width - clipLeft, tbl);
src += width + 2 * (i % 2);
src += width;
}
}

Expand All @@ -742,7 +716,7 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void RenderRightTriangleClipRightAndVertical
const auto width = Width - XStep * i;
const auto skip = Width - width < clipRight ? clipRight - (Width - width) : 0;
RenderLineTransparentOrOpaque<Light, Transparent>(dst, src, width > skip ? width - skip : 0, tbl);
src += width + 2 * (i % 2);
src += width;
}
}

Expand Down
6 changes: 4 additions & 2 deletions Source/levels/dun_tile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ enum class TileType : uint8_t {

/**
*🭮 Left-pointing 32x31 triangle. Encoded as 31 varying-width rows with 2 padding bytes before every even row.
* We remove the padding bytes in `ReencodeDungeonCels`.
*
* The smallest rows (bottom and top) are 2px wide, the largest row is 16px wide (middle row).
* The smallest rows (bottom and top) are 2px wide, the largest row is 32px wide (middle row).
*
* Encoding:
* for i in [0, 30]:
Expand All @@ -46,8 +47,9 @@ enum class TileType : uint8_t {

/**
* 🭬Right-pointing 32x31 triangle. Encoded as 31 varying-width rows with 2 padding bytes after every even row.
* We remove the padding bytes in `ReencodeDungeonCels`.
*
* The smallest rows (bottom and top) are 2px wide, the largest row is 16px wide (middle row).
* The smallest rows (bottom and top) are 2px wide, the largest row is 32px wide (middle row).
*
* Encoding:
* for i in [0, 30]:
Expand Down
22 changes: 21 additions & 1 deletion Source/levels/gendung.cpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
#include "levels/gendung.h"

#include <cstddef>
#include <cstdint>
#include <memory>
#include <stack>
#include <utility>
#include <vector>

#include <ankerl/unordered_dense.h>

#include "engine/clx_sprite.hpp"
#include "engine/load_file.hpp"
#include "engine/random.hpp"
#include "engine/world_tile.hpp"
#include "init.h"
#include "levels/drlg_l1.h"
#include "levels/drlg_l2.h"
#include "levels/drlg_l3.h"
#include "levels/drlg_l4.h"
#include "levels/reencode_dun_cels.hpp"
#include "levels/town.h"
#include "lighting.h"
#include "options.h"
#include "utils/bitset2d.hpp"

namespace devilution {

Expand Down Expand Up @@ -490,12 +499,23 @@ void SetDungeonMicros()
size_t tileCount;
std::unique_ptr<uint16_t[]> levelPieces = LoadMinData(tileCount);

ankerl::unordered_dense::map<uint16_t, TileType> frameToTypeMap;
frameToTypeMap.reserve(4096);
for (size_t i = 0; i < tileCount / blocks; i++) {
uint16_t *pieces = &levelPieces[blocks * i];
for (size_t block = 0; block < blocks; block++) {
DPieceMicros[i].mt[block] = LevelCelBlock { SDL_SwapLE16(pieces[blocks - 2 + (block & 1) - (block & 0xE)]) };
const LevelCelBlock levelCelBlock { SDL_SwapLE16(pieces[blocks - 2 + (block & 1) - (block & 0xE)]) };
DPieceMicros[i].mt[block] = levelCelBlock;
if (levelCelBlock.hasValue()) {
if (const auto it = frameToTypeMap.find(levelCelBlock.frame()); it == frameToTypeMap.end()) {
frameToTypeMap.emplace_hint(it, levelCelBlock.frame(), levelCelBlock.type());
}
}
}
}
std::vector<std::pair<uint16_t, TileType>> frameToTypeList = std::move(frameToTypeMap).extract();
c_sort(frameToTypeList);
ReencodeDungeonCels(pDungeonCels, frameToTypeList);
}

void DRLG_InitTrans()
Expand Down
Loading

0 comments on commit 451f1fd

Please sign in to comment.