From d067fa2b7d962a2d9b461ff56f3024454b98a59e Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sat, 14 May 2022 15:52:23 -0400 Subject: [PATCH] aurora: Fix I8 handling; add CMPR->RGBA conversion --- aurora/lib/gfx/gx.cpp | 3 - aurora/lib/gfx/texture.cpp | 1 - aurora/lib/gfx/texture_convert.cpp | 98 +++++++++++++++++++++++++++++- 3 files changed, 95 insertions(+), 7 deletions(-) diff --git a/aurora/lib/gfx/gx.cpp b/aurora/lib/gfx/gx.cpp index 2ea676372..8ed95d84b 100644 --- a/aurora/lib/gfx/gx.cpp +++ b/aurora/lib/gfx/gx.cpp @@ -583,9 +583,6 @@ void GXLoadTlut(const GXTlutObj* obj, GXTlut idx) noexcept { namespace aurora::gfx { static logvisor::Module Log("aurora::gfx::gx"); -// TODO remove this hack for build_shader -extern std::mutex g_pipelineMutex; - namespace gx { using gpu::g_device; using gpu::g_graphicsConfig; diff --git a/aurora/lib/gfx/texture.cpp b/aurora/lib/gfx/texture.cpp index d4a98f3e6..b42410667 100644 --- a/aurora/lib/gfx/texture.cpp +++ b/aurora/lib/gfx/texture.cpp @@ -138,7 +138,6 @@ TextureHandle new_render_texture(uint32_t width, uint32_t height, GX::TextureFor return std::make_shared(std::move(texture), std::move(textureView), size, wgpuFormat, 1, fmt, true); } -// TODO accept mip/layer parameters void write_texture(const TextureRef& ref, ArrayRef data) noexcept { ByteBuffer buffer; if (ref.gxFormat != InvalidTextureFormat) { diff --git a/aurora/lib/gfx/texture_convert.cpp b/aurora/lib/gfx/texture_convert.cpp index ad9d4fbbc..7eb03efe6 100644 --- a/aurora/lib/gfx/texture_convert.cpp +++ b/aurora/lib/gfx/texture_convert.cpp @@ -36,6 +36,14 @@ constexpr u8 Convert6To8(u8 v) { return static_cast((u32{v} << 2) | (u32{v} >> 4)); } +constexpr u8 S3TCBlend(u8 a_, u8 b_) { + u32 a = a_; + u32 b = b_; + return static_cast((((a << 1) + a) + ((b << 2) + b)) >> 3); +} + +constexpr u8 HalfBlend(u8 a, u8 b) { return static_cast((static_cast(a) + static_cast(b)) >> 1); } + static size_t ComputeMippedTexelCount(u32 w, u32 h, u32 mips) { size_t ret = w * h; for (u32 i = mips; i > 1; --i) { @@ -491,6 +499,90 @@ ByteBuffer BuildDXT1FromGCN(uint32_t width, uint32_t height, uint32_t mips, Arra return buf; } +ByteBuffer BuildRGBA8FromCMPR(u32 width, u32 height, u32 mips, ArrayRef data) { + const size_t texelCount = ComputeMippedTexelCount(width, height, mips); + const size_t blockCount = ComputeMippedBlockCountDXT1(width, height, mips); + ByteBuffer buf{sizeof(RGBA8) * texelCount}; + + u32 h = height; + u32 w = width; + u8* dst = buf.data(); + const u8* src = data.data(); + for (u32 mip = 0; mip < mips; ++mip) { + for (u32 yy = 0; yy < h; yy += 8) { + for (u32 xx = 0; xx < w; xx += 8) { + for (u32 yb = 0; yb < 8; yb += 4) { + for (u32 xb = 0; xb < 8; xb += 4) { + // CMPR difference: Big-endian color1/2 + const u16 color1 = bswap16(*reinterpret_cast(src)); + const u16 color2 = bswap16(*reinterpret_cast(src + 2)); + src += 4; + + // Fill in first two colors in color table. + std::array color_table{}; + + color_table[0] = Convert5To8(static_cast((color1 >> 11) & 0x1F)); + color_table[1] = Convert6To8(static_cast((color1 >> 5) & 0x3F)); + color_table[2] = Convert5To8(static_cast(color1 & 0x1F)); + color_table[3] = 0xFF; + + color_table[4] = Convert5To8(static_cast((color2 >> 11) & 0x1F)); + color_table[5] = Convert6To8(static_cast((color2 >> 5) & 0x3F)); + color_table[6] = Convert5To8(static_cast(color2 & 0x1F)); + color_table[7] = 0xFF; + if (color1 > color2) { + // Predict gradients. + color_table[8] = S3TCBlend(color_table[4], color_table[0]); + color_table[9] = S3TCBlend(color_table[5], color_table[1]); + color_table[10] = S3TCBlend(color_table[6], color_table[2]); + color_table[11] = 0xFF; + + color_table[12] = S3TCBlend(color_table[0], color_table[4]); + color_table[13] = S3TCBlend(color_table[1], color_table[5]); + color_table[14] = S3TCBlend(color_table[2], color_table[6]); + color_table[15] = 0xFF; + } else { + color_table[8] = HalfBlend(color_table[0], color_table[4]); + color_table[9] = HalfBlend(color_table[1], color_table[5]); + color_table[10] = HalfBlend(color_table[2], color_table[6]); + color_table[11] = 0xFF; + + // CMPR difference: GX fills with an alpha 0 midway point here. + color_table[12] = color_table[8]; + color_table[13] = color_table[9]; + color_table[14] = color_table[10]; + color_table[15] = 0; + } + + for (u32 y = 0; y < 4; ++y) { + u8 bits = src[y]; + for (u32 x = 0; x < 4; ++x) { + if (xx + xb + x >= w || yy + yb + y >= h) { + continue; + } + u8* dstOffs = dst + ((yy + yb + y) * w + (xx + xb + x)) * 4; + const u8* colorTableOffs = &color_table[static_cast((bits >> 6) & 3) * 4]; + memcpy(dstOffs, colorTableOffs, 4); + bits <<= 2; + } + } + src += 4; + } + } + } + } + dst += w * h * 4; + if (w > 1) { + w /= 2; + } + if (h > 1) { + h /= 2; + } + } + + return buf; +} + ByteBuffer convert_texture(GX::TextureFormat format, uint32_t width, uint32_t height, uint32_t mips, ArrayRef data) { switch (format) { @@ -500,7 +592,8 @@ ByteBuffer convert_texture(GX::TextureFormat format, uint32_t width, uint32_t he case GX::TF_I4: return BuildI4FromGCN(width, height, mips, data); case GX::TF_I8: - return BuildI8FromGCN(width, height, mips, data); + // No conversion + return {}; case GX::TF_IA4: return BuildIA4FromGCN(width, height, mips, data); case GX::TF_IA8: @@ -526,8 +619,7 @@ ByteBuffer convert_texture(GX::TextureFormat format, uint32_t width, uint32_t he if (gpu::g_device.HasFeature(wgpu::FeatureName::TextureCompressionBC)) { return BuildDXT1FromGCN(width, height, mips, data); } else { - Log.report(logvisor::Fatal, FMT_STRING("convert_texture: TODO implement CMPR to RGBA")); - unreachable(); + return BuildRGBA8FromCMPR(width, height, mips, data); } } }