From bae4030d7b38dce2e0d04df79dc25fb04b6dae6c Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sun, 9 Jun 2024 16:31:39 +0100 Subject: [PATCH] Simplify CLX rendering 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. --- Source/engine/render/blit_impl.hpp | 64 +++++----------- Source/engine/render/clx_render.cpp | 112 +++++++++++++++++++--------- Source/utils/cl2_to_clx.cpp | 33 ++++---- Source/utils/clx_decode.hpp | 14 ---- 4 files changed, 111 insertions(+), 112 deletions(-) diff --git a/Source/engine/render/blit_impl.hpp b/Source/engine/render/blit_impl.hpp index 4d2e0d268..4c2fa8cbe 100644 --- a/Source/engine/render/blit_impl.hpp +++ b/Source/engine/render/blit_impl.hpp @@ -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); @@ -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); } }; @@ -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); } }; @@ -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]); } }; diff --git a/Source/engine/render/clx_render.cpp b/Source/engine/render/clx_render.cpp index e538749f6..9d75f3560 100644 --- a/Source/engine/render/clx_render.cpp +++ b/Source/engine/render/clx_render.cpp @@ -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; @@ -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; @@ -113,11 +131,20 @@ void DoRenderBackwardsClipY( auto remainingWidth = static_cast(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(src.width)); @@ -150,26 +177,40 @@ void DoRenderBackwardsClipXY( remainingWidth += remainingLeftClip; } while (remainingLeftClip > 0) { - BlitCommand cmd = ClxGetBlitCommand(src.begin); - if (static_cast(cmd.length) > remainingLeftClip) { - const auto overshoot = static_cast(cmd.length - remainingLeftClip); - cmd.length = std::min(remainingWidth, overshoot); - blitFn(cmd, dst, src.begin + 1 + remainingLeftClip); - dst += cmd.length; + auto [srcEnd, length] = ClxBlitInfo(src.begin); + if (static_cast(length) > remainingLeftClip) { + const uint8_t control = *src.begin; + const auto overshoot = static_cast(length - remainingLeftClip); + length = std::min(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(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(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 } @@ -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; } diff --git a/Source/utils/cl2_to_clx.cpp b/Source/utils/cl2_to_clx.cpp index 4c8204502..d1c4ae7cb 100644 --- a/Source/utils/cl2_to_clx.cpp +++ b/Source/utils/cl2_to_clx.cpp @@ -85,30 +85,29 @@ uint16_t Cl2ToClx(const uint8_t *data, size_t size, while (src != frameEnd) { auto remainingWidth = static_cast(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; diff --git a/Source/utils/clx_decode.hpp b/Source/utils/clx_decode.hpp index 9d7e5d80b..f9e6e8341 100644 --- a/Source/utils/clx_decode.hpp +++ b/Source/utils/clx_decode.hpp @@ -27,18 +27,4 @@ namespace devilution { return static_cast(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