Skip to content

Commit

Permalink
Implement ETC2_R and ETC2_RG compression to etcpak
Browse files Browse the repository at this point in the history
  • Loading branch information
BlueCube3310 committed Dec 19, 2023
1 parent bf8dd73 commit 50d33aa
Show file tree
Hide file tree
Showing 7 changed files with 548 additions and 42 deletions.
69 changes: 51 additions & 18 deletions modules/etcpak/image_compress_etcpak.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ EtcpakType _determine_etc_type(Image::UsedChannels p_channels) {
case Image::USED_CHANNELS_LA:
return EtcpakType::ETCPAK_TYPE_ETC2_ALPHA;
case Image::USED_CHANNELS_R:
return EtcpakType::ETCPAK_TYPE_ETC2;
return EtcpakType::ETCPAK_TYPE_ETC2_R;
case Image::USED_CHANNELS_RG:
return EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG;
return EtcpakType::ETCPAK_TYPE_ETC2_RG;
case Image::USED_CHANNELS_RGB:
return EtcpakType::ETCPAK_TYPE_ETC2;
case Image::USED_CHANNELS_RGBA:
Expand Down Expand Up @@ -114,6 +114,12 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img) {
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2) {
target_format = Image::FORMAT_ETC2_RGB8;
r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_R) {
target_format = Image::FORMAT_ETC2_R11;
r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RG) {
target_format = Image::FORMAT_ETC2_RG11;
r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
target_format = Image::FORMAT_ETC2_RA_AS_RG;
r_img->convert_rg_to_ra_rgba8();
Expand Down Expand Up @@ -224,22 +230,49 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img) {
// Override the src_mip_read pointer to our temporary Vector.
src_mip_read = padded_src.ptr();
}
if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1) {
CompressEtc1RgbDither(src_mip_read, dest_mip_write, blocks, mip_w);
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2) {
CompressEtc2Rgb(src_mip_read, dest_mip_write, blocks, mip_w, true);
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA || p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
CompressEtc2Rgba(src_mip_read, dest_mip_write, blocks, mip_w, true);
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT1) {
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.");

switch (p_compresstype) {
case EtcpakType::ETCPAK_TYPE_ETC1:
CompressEtc1RgbDither(src_mip_read, dest_mip_write, blocks, mip_w);
break;

case EtcpakType::ETCPAK_TYPE_ETC2:
CompressEtc2Rgb(src_mip_read, dest_mip_write, blocks, mip_w, true);
break;

case EtcpakType::ETCPAK_TYPE_ETC2_ALPHA:
case EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG:
CompressEtc2Rgba(src_mip_read, dest_mip_write, blocks, mip_w, true);
break;

case EtcpakType::ETCPAK_TYPE_ETC2_R:
CompressEtc2R8(src_mip_read, dest_mip_write, blocks, mip_w);
break;

case EtcpakType::ETCPAK_TYPE_ETC2_RG:
CompressEtc2RG8(src_mip_read, dest_mip_write, blocks, mip_w);
break;

case EtcpakType::ETCPAK_TYPE_DXT1:
CompressDxt1Dither(src_mip_read, dest_mip_write, blocks, mip_w);
break;

case EtcpakType::ETCPAK_TYPE_DXT5:
case EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG:
CompressDxt5(src_mip_read, dest_mip_write, blocks, mip_w);
break;

case EtcpakType::ETCPAK_TYPE_RGTC_R:
CompressRgtcR(src_mip_read, dest_mip_write, blocks, mip_w);
break;

case EtcpakType::ETCPAK_TYPE_RGTC_RG:
CompressRgtcRG(src_mip_read, dest_mip_write, blocks, mip_w);
break;

default:
ERR_FAIL_MSG("etcpak: Invalid or unsupported compression format.");
break;
}
}

Expand Down
2 changes: 2 additions & 0 deletions modules/etcpak/image_compress_etcpak.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ enum class EtcpakType {
ETCPAK_TYPE_ETC2,
ETCPAK_TYPE_ETC2_ALPHA,
ETCPAK_TYPE_ETC2_RA_AS_RG,
ETCPAK_TYPE_ETC2_R,
ETCPAK_TYPE_ETC2_RG,
ETCPAK_TYPE_DXT1,
ETCPAK_TYPE_DXT5,
ETCPAK_TYPE_DXT5_RA_AS_RG,
Expand Down
3 changes: 3 additions & 0 deletions thirdparty/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,9 @@ Files extracted from upstream source:
```
- `AUTHORS.txt` and `LICENSE.txt`

Two files (`ProcessRGB.{cpp,hpp}`) have been modified to provide ETC2_R and ETC2_RG compression,
the changes are based on the existing code.

Two files (`ProcessRgtc.{cpp,hpp}`) have been added to provide RGTC compression implementation,
based on library's `ProcessDxtc.{cpp,hpp}`.

Expand Down
142 changes: 142 additions & 0 deletions thirdparty/etcpak/ProcessRGB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4181,3 +4181,145 @@ void CompressEtc2Rgba( const uint32_t* src, uint64_t* dst, uint32_t blocks, size
}
while( --blocks );
}

// -- GODOT start --
void CompressEtc2R8( const uint32_t* src, uint64_t* dst, uint32_t blocks, size_t width )
{
int w = 0;
uint8_t r[4*4];
do
{
#ifdef __SSE4_1__
__m128 px0 = _mm_castsi128_ps( _mm_loadu_si128( (__m128i*)( src + width * 0 ) ) );
__m128 px1 = _mm_castsi128_ps( _mm_loadu_si128( (__m128i*)( src + width * 1 ) ) );
__m128 px2 = _mm_castsi128_ps( _mm_loadu_si128( (__m128i*)( src + width * 2 ) ) );
__m128 px3 = _mm_castsi128_ps( _mm_loadu_si128( (__m128i*)( src + width * 3 ) ) );

_MM_TRANSPOSE4_PS( px0, px1, px2, px3 );

__m128i c0 = _mm_castps_si128( px0 );
__m128i c1 = _mm_castps_si128( px1 );
__m128i c2 = _mm_castps_si128( px2 );
__m128i c3 = _mm_castps_si128( px3 );

__m128i mask = _mm_setr_epi32( 0x0e0a0602, -1, -1, -1 );

__m128i a0 = _mm_shuffle_epi8( c0, mask );
__m128i a1 = _mm_shuffle_epi8( c1, _mm_shuffle_epi32( mask, _MM_SHUFFLE( 3, 3, 0, 3 ) ) );
__m128i a2 = _mm_shuffle_epi8( c2, _mm_shuffle_epi32( mask, _MM_SHUFFLE( 3, 0, 3, 3 ) ) );
__m128i a3 = _mm_shuffle_epi8( c3, _mm_shuffle_epi32( mask, _MM_SHUFFLE( 0, 3, 3, 3 ) ) );

__m128i s0 = _mm_or_si128( a0, a1 );
__m128i s1 = _mm_or_si128( a2, a3 );
__m128i s2 = _mm_or_si128( s0, s1 );

_mm_store_si128( (__m128i*)r, s2 );

src += 4;
#else
auto ptr8 = r;
for( int x=0; x<4; x++ )
{
auto v = *src;
*ptr8++ = (v & 0xff0000) >> 16;
src += width;
v = *src;
*ptr8++ = (v & 0xff0000) >> 16;
src += width;
v = *src;
*ptr8++ = (v & 0xff0000) >> 16;
src += width;
v = *src;
*ptr8++ = (v & 0xff0000) >> 16;
src -= width * 3 - 1;
}
#endif
if( ++w == width/4 )
{
src += width * 3;
w = 0;
}
*dst++ = ProcessAlpha_ETC2( r );
}
while( --blocks );
}

void CompressEtc2RG8( const uint32_t* src, uint64_t* dst, uint32_t blocks, size_t width )
{
int w = 0;
uint8_t rg[4*4*2];
do
{
#ifdef __SSE4_1__
__m128 px0 = _mm_castsi128_ps( _mm_loadu_si128( (__m128i*)( src + width * 0 ) ) );
__m128 px1 = _mm_castsi128_ps( _mm_loadu_si128( (__m128i*)( src + width * 1 ) ) );
__m128 px2 = _mm_castsi128_ps( _mm_loadu_si128( (__m128i*)( src + width * 2 ) ) );
__m128 px3 = _mm_castsi128_ps( _mm_loadu_si128( (__m128i*)( src + width * 3 ) ) );

_MM_TRANSPOSE4_PS( px0, px1, px2, px3 );

__m128i c0 = _mm_castps_si128( px0 );
__m128i c1 = _mm_castps_si128( px1 );
__m128i c2 = _mm_castps_si128( px2 );
__m128i c3 = _mm_castps_si128( px3 );

__m128i mask = _mm_setr_epi32( 0x0e0a0602, -1, -1, -1 );

__m128i r0 = _mm_shuffle_epi8( c0, mask );
__m128i r1 = _mm_shuffle_epi8( c1, _mm_shuffle_epi32( mask, _MM_SHUFFLE( 3, 3, 0, 3 ) ) );
__m128i r2 = _mm_shuffle_epi8( c2, _mm_shuffle_epi32( mask, _MM_SHUFFLE( 3, 0, 3, 3 ) ) );
__m128i r3 = _mm_shuffle_epi8( c3, _mm_shuffle_epi32( mask, _MM_SHUFFLE( 0, 3, 3, 3 ) ) );

__m128i s0 = _mm_or_si128( r0, r1 );
__m128i s1 = _mm_or_si128( r2, r3 );
__m128i s2 = _mm_or_si128( s0, s1 );

_mm_store_si128( (__m128i*)rg, s2 );

mask = _mm_setr_epi32( 0x0d090501, -1, -1, -1 );

r0 = _mm_shuffle_epi8( c0, mask );
r1 = _mm_shuffle_epi8( c1, _mm_shuffle_epi32( mask, _MM_SHUFFLE( 3, 3, 0, 3 ) ) );
r2 = _mm_shuffle_epi8( c2, _mm_shuffle_epi32( mask, _MM_SHUFFLE( 3, 0, 3, 3 ) ) );
r3 = _mm_shuffle_epi8( c3, _mm_shuffle_epi32( mask, _MM_SHUFFLE( 0, 3, 3, 3 ) ) );

s0 = _mm_or_si128( r0, r1 );
s1 = _mm_or_si128( r2, r3 );
s2 = _mm_or_si128( s0, s1 );

_mm_store_si128( (__m128i*)&rg[16], s2 );
src += 4;
#else
auto ptrr = rg;
auto ptrg = ptrr + 16;
for( int x=0; x<4; x++ )
{
auto v = *src;
*ptrr++ = (v & 0xff0000) >> 16;
*ptrg++ = (v & 0xff00) >> 8;
src += width;
v = *src;
*ptrr++ = (v & 0xff0000) >> 16;
*ptrg++ = (v & 0xff00) >> 8;
src += width;
v = *src;
*ptrr++ = (v & 0xff0000) >> 16;
*ptrg++ = (v & 0xff00) >> 8;
src += width;
v = *src;
*ptrr++ = (v & 0xff0000) >> 16;
*ptrg++ = (v & 0xff00) >> 8;
src -= width * 3 - 1;
}
#endif
if( ++w == width/4 )
{
src += width * 3;
w = 0;
}
*dst++ = ProcessAlpha_ETC2( rg );
*dst++ = ProcessAlpha_ETC2( &rg[16] );
}
while( --blocks );
}
// -- GODOT end --
5 changes: 4 additions & 1 deletion thirdparty/etcpak/ProcessRGB.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@ void CompressEtc1Rgb( const uint32_t* src, uint64_t* dst, uint32_t blocks, size_
void CompressEtc1RgbDither( const uint32_t* src, uint64_t* dst, uint32_t blocks, size_t width );
void CompressEtc2Rgb( const uint32_t* src, uint64_t* dst, uint32_t blocks, size_t width, bool useHeuristics );
void CompressEtc2Rgba( const uint32_t* src, uint64_t* dst, uint32_t blocks, size_t width, bool useHeuristics );

// -- GODOT start --
void CompressEtc2R8( const uint32_t* src, uint64_t* dst, uint32_t blocks, size_t width );
void CompressEtc2RG8( const uint32_t* src, uint64_t* dst, uint32_t blocks, size_t width );
// -- GODOT end --
#endif
Loading

0 comments on commit 50d33aa

Please sign in to comment.