Browse Source

Improve normal map VRAM Compression with RGTC

BlueCube3310 1 year ago
parent
commit
5837e1fe3f

+ 1 - 0
modules/etcpak/SCsub

@@ -13,6 +13,7 @@ thirdparty_dir = "#thirdparty/etcpak/"
 thirdparty_sources = [
     "Dither.cpp",
     "ProcessDxtc.cpp",
+    "ProcessRgtc.cpp",
     "ProcessRGB.cpp",
     "Tables.cpp",
 ]

+ 11 - 2
modules/etcpak/image_compress_etcpak.cpp

@@ -35,6 +35,7 @@
 
 #include <ProcessDxtc.hpp>
 #include <ProcessRGB.hpp>
+#include <ProcessRgtc.hpp>
 
 EtcpakType _determine_etc_type(Image::UsedChannels p_channels) {
 	switch (p_channels) {
@@ -62,9 +63,9 @@ EtcpakType _determine_dxt_type(Image::UsedChannels p_channels) {
 		case Image::USED_CHANNELS_LA:
 			return EtcpakType::ETCPAK_TYPE_DXT5;
 		case Image::USED_CHANNELS_R:
-			return EtcpakType::ETCPAK_TYPE_DXT5;
+			return EtcpakType::ETCPAK_TYPE_RGTC_R;
 		case Image::USED_CHANNELS_RG:
-			return EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG;
+			return EtcpakType::ETCPAK_TYPE_RGTC_RG;
 		case Image::USED_CHANNELS_RGB:
 			return EtcpakType::ETCPAK_TYPE_DXT1;
 		case Image::USED_CHANNELS_RGBA:
@@ -127,6 +128,10 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img) {
 		r_img->convert_rg_to_ra_rgba8();
 	} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5) {
 		target_format = Image::FORMAT_DXT5;
+	} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_RGTC_R) {
+		target_format = Image::FORMAT_RGTC_R;
+	} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_RGTC_RG) {
+		target_format = Image::FORMAT_RGTC_RG;
 	} else {
 		ERR_FAIL_MSG("Invalid or unsupported etcpak compression format, not ETC or DXT.");
 	}
@@ -229,6 +234,10 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img) {
 			CompressDxt1Dither(src_mip_read, dest_mip_write, blocks, mip_w);
 		} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5 || p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG) {
 			CompressDxt5(src_mip_read, dest_mip_write, blocks, mip_w);
+		} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_RGTC_RG) {
+			CompressRgtcRG(src_mip_read, dest_mip_write, blocks, mip_w);
+		} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_RGTC_R) {
+			CompressRgtcR(src_mip_read, dest_mip_write, blocks, mip_w);
 		} else {
 			ERR_FAIL_MSG("etcpak: Invalid or unsupported compression format.");
 		}

+ 2 - 0
modules/etcpak/image_compress_etcpak.h

@@ -41,6 +41,8 @@ enum class EtcpakType {
 	ETCPAK_TYPE_DXT1,
 	ETCPAK_TYPE_DXT5,
 	ETCPAK_TYPE_DXT5_RA_AS_RG,
+	ETCPAK_TYPE_RGTC_R,
+	ETCPAK_TYPE_RGTC_RG,
 };
 
 void _compress_etc1(Image *r_img);

+ 106 - 0
thirdparty/etcpak/ProcessRgtc.cpp

@@ -0,0 +1,106 @@
+// -- GODOT start --
+
+#include "ForceInline.hpp"
+#include "ProcessRgtc.hpp"
+
+#include <assert.h>
+#include <string.h>
+
+static const uint8_t AlphaIndexTable[8] = { 1, 7, 6, 5, 4, 3, 2, 0 };
+
+static etcpak_force_inline uint64_t ProcessAlpha( const uint8_t* src )
+{
+    uint8_t solid8 = *src;
+    uint16_t solid16 = uint16_t( solid8 ) | ( uint16_t( solid8 ) << 8 );
+    uint32_t solid32 = uint32_t( solid16 ) | ( uint32_t( solid16 ) << 16 );
+    uint64_t solid64 = uint64_t( solid32 ) | ( uint64_t( solid32 ) << 32 );
+    if( memcmp( src, &solid64, 8 ) == 0 && memcmp( src+8, &solid64, 8 ) == 0 )
+    {
+        return solid8;
+    }
+
+    uint8_t min = src[0];
+    uint8_t max = min;
+    for( int i=1; i<16; i++ )
+    {
+        const auto v = src[i];
+        if( v > max ) max = v;
+        else if( v < min ) min = v;
+    }
+
+    uint32_t range = ( 8 << 13 ) / ( 1 + max - min );
+    uint64_t data = 0;
+    for( int i=0; i<16; i++ )
+    {
+        uint8_t a = src[i] - min;
+        uint64_t idx = AlphaIndexTable[( a * range ) >> 13];
+        data |= idx << (i*3);
+    }
+
+    return max | ( min << 8 ) | ( data << 16 );
+}
+
+void CompressRgtcR(const uint32_t *src, uint64_t *dst, uint32_t blocks, size_t width) 
+{
+	int i = 0;
+	auto ptr = dst;
+	do 
+	{
+		uint32_t rgba[4 * 4];
+		uint8_t r[4 * 4];
+
+		auto tmp = (char *)rgba;
+		memcpy(tmp, src + width * 0, 4 * 4);
+		memcpy(tmp + 4 * 4, src + width * 1, 4 * 4);
+		memcpy(tmp + 8 * 4, src + width * 2, 4 * 4);
+		memcpy(tmp + 12 * 4, src + width * 3, 4 * 4);
+		src += 4;
+		if (++i == width / 4) 
+		{
+			src += width * 3;
+			i = 0;
+		}
+
+		for (int i = 0; i < 16; i++) 
+		{
+			r[i] = rgba[i] & 0x000000FF;
+		}
+		*ptr++ = ProcessAlpha(r);
+	} 
+	while (--blocks);
+}
+
+void CompressRgtcRG(const uint32_t *src, uint64_t *dst, uint32_t blocks, size_t width) 
+{
+	int i = 0;
+	auto ptr = dst;
+	do 
+	{
+		uint32_t rgba[4 * 4];
+		uint8_t r[4 * 4];
+		uint8_t g[4 * 4];
+
+		auto tmp = (char *)rgba;
+		memcpy(tmp, src + width * 0, 4 * 4);
+		memcpy(tmp + 4 * 4, src + width * 1, 4 * 4);
+		memcpy(tmp + 8 * 4, src + width * 2, 4 * 4);
+		memcpy(tmp + 12 * 4, src + width * 3, 4 * 4);
+		src += 4;
+		if (++i == width / 4) 
+		{
+			src += width * 3;
+			i = 0;
+		}
+
+		for (int i = 0; i < 16; i++) 
+		{
+			r[i] = rgba[i] & 0x000000FF;
+			g[i] = (rgba[i] & 0x0000FF00) >> 8;
+		}
+		*ptr++ = ProcessAlpha(r);
+		*ptr++ = ProcessAlpha(g);
+	} 
+	while (--blocks);
+}
+
+// -- GODOT end --

+ 14 - 0
thirdparty/etcpak/ProcessRgtc.hpp

@@ -0,0 +1,14 @@
+// -- GODOT start --
+
+#ifndef __PROCESSRGTC_HPP__
+#define __PROCESSRGTC_HPP__
+
+#include <stddef.h>
+#include <stdint.h>
+
+void CompressRgtcR(const uint32_t *src, uint64_t *dst, uint32_t blocks, size_t width);
+void CompressRgtcRG(const uint32_t *src, uint64_t *dst, uint32_t blocks, size_t width);
+
+#endif
+
+// -- GODOT end --