Skip to content

Commit

Permalink
Simplify CLX rendering
Browse files Browse the repository at this point in the history
Inlines blit command parsing.

We previously had blit commands because we supported rendering multiple
formats (CEL, CL2, CLX) but now we only ever render CLX, so this is
no longer necessary.
  • Loading branch information
glebm committed Jun 10, 2024
1 parent ee76c86 commit bae4030
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 112 deletions.
64 changes: 18 additions & 46 deletions Source/engine/render/blit_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,6 @@ namespace devilution {
#define DEVILUTIONX_BLIT_EXECUTION_POLICY
#endif

enum class BlitType : uint8_t {
Transparent,
Pixels,
Fill
};

struct BlitCommand {
BlitType type;
const uint8_t *srcEnd; // Pointer past the end of the command.
unsigned length; // Number of pixels this command will write.
uint8_t color; // For `BlitType::Pixel` and `BlitType::Fill` only.
};

DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void BlitFillDirect(uint8_t *dst, unsigned length, uint8_t color)
{
std::memset(dst, color, length);
Expand All @@ -40,18 +27,13 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void BlitPixelsDirect(uint8_t *DVL_RESTRICT
}

struct BlitDirect {
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void operator()(BlitCommand cmd, uint8_t *DVL_RESTRICT dst, const uint8_t *DVL_RESTRICT src)
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void operator()(unsigned length, uint8_t *DVL_RESTRICT dst, const uint8_t *DVL_RESTRICT src) const
{
BlitPixelsDirect(dst, src, length);
}
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void operator()(unsigned length, uint8_t color, uint8_t *DVL_RESTRICT dst) const
{
switch (cmd.type) {
case BlitType::Fill:
BlitFillDirect(dst, cmd.length, cmd.color);
return;
case BlitType::Pixels:
BlitPixelsDirect(dst, src, cmd.length);
return;
case BlitType::Transparent:
return;
}
BlitFillDirect(dst, length, color);
}
};

Expand Down Expand Up @@ -86,18 +68,13 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void BlitPixelsBlended(uint8_t *DVL_RESTRICT
struct BlitWithMap {
const uint8_t *DVL_RESTRICT colorMap;

DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void operator()(BlitCommand cmd, uint8_t *DVL_RESTRICT dst, const uint8_t *DVL_RESTRICT src) const
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void operator()(unsigned length, uint8_t *DVL_RESTRICT dst, const uint8_t *DVL_RESTRICT src) const
{
BlitPixelsWithMap(dst, src, length, colorMap);
}
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void operator()(unsigned length, uint8_t color, uint8_t *DVL_RESTRICT dst) const
{
switch (cmd.type) {
case BlitType::Fill:
BlitFillWithMap(dst, cmd.length, cmd.color, colorMap);
return;
case BlitType::Pixels:
BlitPixelsWithMap(dst, src, cmd.length, colorMap);
return;
case BlitType::Transparent:
return;
}
BlitFillWithMap(dst, length, color, colorMap);
}
};

Expand All @@ -112,18 +89,13 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void BlitPixelsBlendedWithMap(uint8_t *DVL_R
struct BlitBlendedWithMap {
const uint8_t *DVL_RESTRICT colorMap;

DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void operator()(BlitCommand cmd, uint8_t *DVL_RESTRICT dst, const uint8_t *DVL_RESTRICT src) const
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void operator()(unsigned length, uint8_t *DVL_RESTRICT dst, const uint8_t *DVL_RESTRICT src) const
{
BlitPixelsBlendedWithMap(dst, src, length, colorMap);
}
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void operator()(unsigned length, uint8_t color, uint8_t *DVL_RESTRICT dst) const
{
switch (cmd.type) {
case BlitType::Fill:
BlitFillBlended(dst, cmd.length, colorMap[cmd.color]);
return;
case BlitType::Pixels:
BlitPixelsBlendedWithMap(dst, src, cmd.length, colorMap);
return;
case BlitType::Transparent:
return;
}
BlitFillBlended(dst, length, colorMap[color]);
}
};

Expand Down
112 changes: 77 additions & 35 deletions Source/engine/render/clx_render.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,24 @@ namespace {
* indicates a fill-N command.
*/

struct BlitCommandInfo {
const uint8_t *srcEnd;
unsigned length;
};

BlitCommandInfo ClxBlitInfo(const uint8_t *src)
{
const uint8_t control = *src;
if (!IsClxOpaque(control))
return { src + 1, control };
if (IsClxOpaqueFill(control)) {
const uint8_t width = GetClxOpaqueFillWidth(control);
return { src + 2, width };
}
const uint8_t width = GetClxOpaquePixelsWidth(control);
return { src + 1 + width, width };
}

struct ClipX {
int_fast16_t left;
int_fast16_t right;
Expand Down Expand Up @@ -76,9 +94,9 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT const uint8_t *SkipRestOfLineWithOverrun(
{
int_fast16_t remainingWidth = srcWidth - skipSize.xOffset;
while (remainingWidth > 0) {
const BlitCommand cmd = ClxGetBlitCommand(src);
src = cmd.srcEnd;
remainingWidth -= cmd.length;
const auto [srcEnd, length] = ClxBlitInfo(src);
src = srcEnd;
remainingWidth -= length;
}
skipSize = GetSkipSize(remainingWidth, srcWidth);
return src;
Expand Down Expand Up @@ -113,11 +131,20 @@ void DoRenderBackwardsClipY(
auto remainingWidth = static_cast<int_fast16_t>(src.width) - xOffset;
dst += xOffset;
while (remainingWidth > 0) {
BlitCommand cmd = ClxGetBlitCommand(src.begin);
blitFn(cmd, dst, src.begin + 1);
src.begin = cmd.srcEnd;
dst += cmd.length;
remainingWidth -= cmd.length;
uint8_t v = *src.begin++;
if (IsClxOpaque(v)) {
if (IsClxOpaqueFill(v)) {
v = GetClxOpaqueFillWidth(v);
const uint8_t color = *src.begin++;
blitFn(v, color, dst);
} else {
v = GetClxOpaquePixelsWidth(v);
blitFn(v, dst, src.begin);
src.begin += v;
}
}
dst += v;
remainingWidth -= v;
}

const SkipSize skipSize = GetSkipSize(remainingWidth, static_cast<int_fast16_t>(src.width));
Expand Down Expand Up @@ -150,26 +177,40 @@ void DoRenderBackwardsClipXY(
remainingWidth += remainingLeftClip;
}
while (remainingLeftClip > 0) {
BlitCommand cmd = ClxGetBlitCommand(src.begin);
if (static_cast<int_fast16_t>(cmd.length) > remainingLeftClip) {
const auto overshoot = static_cast<int>(cmd.length - remainingLeftClip);
cmd.length = std::min<unsigned>(remainingWidth, overshoot);
blitFn(cmd, dst, src.begin + 1 + remainingLeftClip);
dst += cmd.length;
auto [srcEnd, length] = ClxBlitInfo(src.begin);
if (static_cast<int_fast16_t>(length) > remainingLeftClip) {
const uint8_t control = *src.begin;
const auto overshoot = static_cast<int>(length - remainingLeftClip);
length = std::min<unsigned>(remainingWidth, overshoot);
if (IsClxOpaque(control)) {
if (IsClxOpaqueFill(control)) {
blitFn(length, src.begin[1], dst);
} else {
blitFn(length, dst, src.begin + 1 + remainingLeftClip);
}
}
dst += length;
remainingWidth -= overshoot;
src.begin = cmd.srcEnd;
src.begin = srcEnd;
break;
}
src.begin = cmd.srcEnd;
remainingLeftClip -= cmd.length;
src.begin = srcEnd;
remainingLeftClip -= length;
}
while (remainingWidth > 0) {
BlitCommand cmd = ClxGetBlitCommand(src.begin);
const unsigned unclippedLength = cmd.length;
cmd.length = std::min<unsigned>(remainingWidth, cmd.length);
blitFn(cmd, dst, src.begin + 1);
src.begin = cmd.srcEnd;
dst += cmd.length;
auto [srcEnd, length] = ClxBlitInfo(src.begin);
const uint8_t control = *src.begin;
const unsigned unclippedLength = length;
length = std::min<unsigned>(remainingWidth, length);
if (IsClxOpaque(control)) {
if (IsClxOpaqueFill(control)) {
blitFn(length, src.begin[1], dst);
} else {
blitFn(length, dst, src.begin + 1);
}
}
src.begin = srcEnd;
dst += length;
remainingWidth -= unclippedLength; // result can be negative
}

Expand Down Expand Up @@ -763,19 +804,20 @@ std::string ClxDescribe(ClxSprite clx)
const uint8_t *src = clx.pixelData();
const uint8_t *end = src + clx.pixelDataSize();
while (src < end) {
BlitCommand cmd = ClxGetBlitCommand(src);
switch (cmd.type) {
case BlitType::Transparent:
out.append(fmt::format("Transp. | {:>5} | {:>5} |\n", cmd.length, cmd.srcEnd - src));
break;
case BlitType::Fill:
out.append(fmt::format("Fill | {:>5} | {:>5} | {}\n", cmd.length, cmd.srcEnd - src, cmd.color));
break;
case BlitType::Pixels:
out.append(fmt::format("Pixels | {:>5} | {:>5} | {}\n", cmd.length, cmd.srcEnd - src, fmt::join(src + 1, src + 1 + cmd.length, " ")));
break;
const uint8_t control = *src++;
if (IsClxOpaque(control)) {
if (IsClxOpaqueFill(control)) {
const uint8_t length = GetClxOpaqueFillWidth(control);
out.append(fmt::format("Fill | {:>5} | {:>5} | {}\n", length, 2, src[1]));
++src;
} else {
const uint8_t length = GetClxOpaquePixelsWidth(control);
out.append(fmt::format("Pixels | {:>5} | {:>5} | {}\n", length, length + 1, fmt::join(src + 1, src + 1 + length, " ")));
src += length;
}
} else {
out.append(fmt::format("Transp. | {:>5} | {:>5} |\n", control, 1));
}
src = cmd.srcEnd;
}
return out;
}
Expand Down
33 changes: 16 additions & 17 deletions Source/utils/cl2_to_clx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,30 +85,29 @@ uint16_t Cl2ToClx(const uint8_t *data, size_t size,
while (src != frameEnd) {
auto remainingWidth = static_cast<int_fast16_t>(frameWidth) - xOffset;
while (remainingWidth > 0) {
const BlitCommand cmd = ClxGetBlitCommand(src);
switch (cmd.type) {
case BlitType::Transparent:
const uint8_t control = *src++;
if (!IsClxOpaque(control)) {
if (!pixels.empty()) {
AppendClxPixelsOrFillRun(pixels.data(), pixels.size(), clxData);
pixels.clear();
}

transparentRunWidth += cmd.length;
break;
case BlitType::Fill:
case BlitType::Pixels:
transparentRunWidth += control;
remainingWidth -= control;
} else if (IsClxOpaqueFill(control)) {
AppendClxTransparentRun(transparentRunWidth, clxData);
transparentRunWidth = 0;

if (cmd.type == BlitType::Fill) {
pixels.insert(pixels.end(), cmd.length, cmd.color);
} else { // BlitType::Pixels
pixels.insert(pixels.end(), src + 1, cmd.srcEnd);
}
break;
const uint8_t width = GetClxOpaqueFillWidth(control);
const uint8_t color = *src++;
pixels.insert(pixels.end(), width, color);
remainingWidth -= width;
} else {
AppendClxTransparentRun(transparentRunWidth, clxData);
transparentRunWidth = 0;
const uint8_t width = GetClxOpaquePixelsWidth(control);
pixels.insert(pixels.end(), src, src + width);
src += width;
remainingWidth -= width;
}
src = cmd.srcEnd;
remainingWidth -= cmd.length;
}

++frameHeight;
Expand Down
14 changes: 0 additions & 14 deletions Source/utils/clx_decode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,4 @@ namespace devilution {
return static_cast<int_fast16_t>(ClxFillEnd - control);
}

[[nodiscard]] constexpr BlitCommand ClxGetBlitCommand(const uint8_t *src)
{
const uint8_t control = *src++;
if (!IsClxOpaque(control))
return BlitCommand { BlitType::Transparent, src, control, 0 };
if (IsClxOpaqueFill(control)) {
const uint8_t width = GetClxOpaqueFillWidth(control);
const uint8_t color = *src++;
return BlitCommand { BlitType::Fill, src, width, color };
}
const uint8_t width = GetClxOpaquePixelsWidth(control);
return BlitCommand { BlitType::Pixels, src + width, width, 0 };
}

} // namespace devilution

0 comments on commit bae4030

Please sign in to comment.