diff options
| author | JosJuice <josjuice@gmail.com> | 2026-10-04 16:57:21 +0200 |
|---|---|---|
| committer | JosJuice <josjuice@gmail.com> | 2026-10-11 10:49:58 +0200 |
| commit | 178ec51fa63bdb041ff620e5230bed3d0141e3b5 (patch) | |
| tree | dc8874a1af4ab9530276cbd50671e0c9ad168ce2 | |
| parent | 6103e4f5c6215ce57d855d99ab714fc0f52e6fc5 (diff) | |
VideoCommon: Use GPR units more in TexDecoder_DecodeImpl_CMPR
This improves the time reported by the CMPR unit test. The lowest value
is about 14% lower, but there's more variance, with the highest value
being similar to before. The instruction count has increased.
I'm putting this in a separate commit so the older implementation is
available in the commit history for anyone wanting to look at it.
| -rw-r--r-- | Source/Core/VideoCommon/TextureDecoder_ARM64.cpp | 97 |
1 files changed, 51 insertions, 46 deletions
diff --git a/Source/Core/VideoCommon/TextureDecoder_ARM64.cpp b/Source/Core/VideoCommon/TextureDecoder_ARM64.cpp index 3943611d78..a09c3013d4 100644 --- a/Source/Core/VideoCommon/TextureDecoder_ARM64.cpp +++ b/Source/Core/VideoCommon/TextureDecoder_ARM64.cpp @@ -3,6 +3,8 @@ #include "VideoCommon/TextureDecoder.h" +#include <array> + #include <arm_neon.h> #include "Common/CommonTypes.h" @@ -365,24 +367,38 @@ TexDecoder_DecodeImpl_C14X2(u32* dst, const u8* src, int width, int height, int } } +static constexpr std::array<u32, 256> GenerateUnpack2BitTable() +{ + std::array<u32, 256> table; + + u32 index = 0; + for (u32 i = 0; i < 4; ++i) + for (u32 j = 0; j < 4; ++j) + for (u32 k = 0; k < 4; ++k) + for (u32 l = 0; l < 4; ++l) + table[index++] = (l << 24) | (k << 16) | (j << 8) | i; + + return table; +} + static DOLPHIN_FORCE_INLINE void TexDecoder_DecodeImpl_CMPR(u32* dst, const u8* src, int width, int height, int Wsteps4, int Wsteps8) { + static constexpr std::array<u32, 256> unpack_2bit_table = GenerateUnpack2BitTable(); + std::array<u32, 16> unpacked_indices; + uint8x8_t dxt_blend_factors_0 = vreinterpret_u8_u16(vdup_n_u16(0x0305)); uint8x8_t dxt_blend_factors_1 = vreinterpret_u8_u16(vdup_n_u16(0x0503)); - uint8x16_t table_offsets_0_1 = vreinterpretq_u8_u64(vdupq_n_u64(0x0404040400000000)); - uint8x16_t table_offsets_2_3 = vreinterpretq_u8_u64(vdupq_n_u64(0x0C0C0C0C08080808)); - for (int y = 0; y < height; y += 8) { for (int x = 0, yStep = (y / 8) * Wsteps8; x < width; x += 8, yStep++) { // Load four blocks at once - uint32x4x2_t values = vld2q_u32(reinterpret_cast<const u32*>(src + 32 * yStep)); + const u32* src2 = reinterpret_cast<const u32*>(src + 32 * yStep); + uint32x4x2_t values = vld2q_u32(src2); uint16x8_t rgb565 = vreinterpretq_u16_u32(values.val[0]); - uint8x16_t c2 = vreinterpretq_u8_u32(values.val[1]); // Unpack colors 0 and 1 @@ -448,56 +464,45 @@ static DOLPHIN_FORCE_INLINE void TexDecoder_DecodeImpl_CMPR(u32* dst, const u8* zip_colors(color_0_1.a, a_2_3), }; - // Unpack the indices so each one takes up one byte - // TODO: vluti2q_laneq_u8 should be faster, but it requires an optional extension - - uint8x16x4_t c8_interleaved; - c8_interleaved.val[0] = vshrq_n_u8(c2, 6); - c8_interleaved.val[1] = vandq_u8(vshrq_n_u8(c2, 4), vdupq_n_u8(0x03)); - c8_interleaved.val[2] = vandq_u8(vshrq_n_u8(c2, 2), vdupq_n_u8(0x03)); - c8_interleaved.val[3] = vandq_u8(c2, vdupq_n_u8(0x03)); - - uint16x8x4_t c8_partially_interleaved; - c8_partially_interleaved.val[0] = - vreinterpretq_u16_u8(vzip1q_u8(c8_interleaved.val[0], c8_interleaved.val[1])); - c8_partially_interleaved.val[1] = - vreinterpretq_u16_u8(vzip2q_u8(c8_interleaved.val[0], c8_interleaved.val[1])); - c8_partially_interleaved.val[2] = - vreinterpretq_u16_u8(vzip1q_u8(c8_interleaved.val[2], c8_interleaved.val[3])); - c8_partially_interleaved.val[3] = - vreinterpretq_u16_u8(vzip2q_u8(c8_interleaved.val[2], c8_interleaved.val[3])); - - uint32x4x4_t c8; - c8.val[0] = vreinterpretq_u32_u16( - vzip1q_u16(c8_partially_interleaved.val[0], c8_partially_interleaved.val[2])); - c8.val[1] = vreinterpretq_u32_u16( - vzip2q_u16(c8_partially_interleaved.val[0], c8_partially_interleaved.val[2])); - c8.val[2] = vreinterpretq_u32_u16( - vzip1q_u16(c8_partially_interleaved.val[1], c8_partially_interleaved.val[3])); - c8.val[3] = vreinterpretq_u32_u16( - vzip2q_u16(c8_partially_interleaved.val[1], c8_partially_interleaved.val[3])); - - // Rearrange the lines so every line from the first block is next to a line from the second + // Unpack the indices so each one takes up one byte. We could do this entirely using SIMD, + // but doing it using GPR units lets us offload the already busy FPR units. + // + // We rearrange the lines so every line from the first block is next to a line from the second // block, and every line from the third block is next to a line from the fourth block. // This lets us write 8 texels at a time to dst instead of just 4. + // + // TODO: vluti2q_laneq_u8 might be faster, but it requires an optional extension + + u32 first_block_indices = src2[1]; + u32 second_block_indices = src2[3]; + for (size_t i = 0; i < 4; ++i) + { + unpacked_indices[i * 2 + 0] = unpack_2bit_table[(first_block_indices >> i * 8) & 0xFF]; + unpacked_indices[i * 2 + 1] = + unpack_2bit_table[(second_block_indices >> i * 8) & 0xFF] | 0x04040404; + } - uint8x16x4_t c8_lines; - c8_lines.val[0] = vreinterpretq_u8_u32(vzip1q_u32(c8.val[0], c8.val[1])); - c8_lines.val[1] = vreinterpretq_u8_u32(vzip2q_u32(c8.val[0], c8.val[1])); - c8_lines.val[2] = vreinterpretq_u8_u32(vzip1q_u32(c8.val[2], c8.val[3])); - c8_lines.val[3] = vreinterpretq_u8_u32(vzip2q_u32(c8.val[2], c8.val[3])); + u32 third_block_indices = src2[5]; + u32 fourth_block_indices = src2[7]; + for (size_t i = 0; i < 4; ++i) + { + unpacked_indices[i * 2 + 8] = + unpack_2bit_table[(third_block_indices >> i * 8) & 0xFF] | 0x08080808; + unpacked_indices[i * 2 + 9] = + unpack_2bit_table[(fourth_block_indices >> i * 8) & 0xFF] | 0x0C0C0C0C; + } // Finally, do the table lookup and store the result + uint8x16x4_t table_indices = + vld1q_u8_x4(reinterpret_cast<const u8*>(unpacked_indices.data())); for (int i = 0; i < 4; ++i) { - uint8x16_t table_indices = - vaddq_u8(c8_lines.val[i], i < 2 ? table_offsets_0_1 : table_offsets_2_3); RGBAx16 rgba = { - vqtbl1q_u8(color.r, table_indices), - vqtbl1q_u8(color.g, table_indices), - vqtbl1q_u8(color.b, table_indices), - vqtbl1q_u8(color.a, table_indices), + vqtbl1q_u8(color.r, table_indices.val[i]), + vqtbl1q_u8(color.g, table_indices.val[i]), + vqtbl1q_u8(color.b, table_indices.val[i]), + vqtbl1q_u8(color.a, table_indices.val[i]), }; Store8x2RGBA(dst, x, y + i * 2, width, rgba); } |
