我正在转换的游戏在 8 位调色板纹理上运行,几乎每一帧我都必须将该纹理的一部分更新为 OpenGL 纹理以进行渲染。它看起来像这样:
unsigned short RGB565PaletteLookupTable[256]; // Lookup table
unsigned char* Src; // Source data
unsigned short* Dst; // Destination buffer
int SrcPitch; // Source data row length
int OriginX, OriginY, Width, Height; // Subrectangle to copy
assert( Width % 4 == 0 );
int SrcOffset = SrcPitch-Width;
Src += OriginY*SrcPitch+OriginX;
int x, y;
for( y = OriginY; y < OriginY+Height; ++y, Src += SrcOffset )
{
for( x = OriginX; x < OriginX+Width; x += 4 )
{
*Dst++ = RGB565PaletteLookupTable[*Src++];
*Dst++ = RGB565PaletteLookupTable[*Src++];
*Dst++ = RGB565PaletteLookupTable[*Src++];
*Dst++ = RGB565PaletteLookupTable[*Src++];
}
}
这段代码在游戏过程中占用了 17% 的主线程时间,所以我正在寻找加速它的方法。数据直接转到 glTexSubImage2D(),所以我无法更改目标缓冲区中的任何内容。它来自游戏中的代码,该代码是古老的且没有记录的,并且没有人知道它是如何工作的,所以我也不能乱搞。查找表也是由这个古老的代码提供的,并且可以在游戏中更改。
是否可以使用 Accelerate 框架/汇编指令/任何其他方式加速此代码?我阅读了将 RGB888 直接转换为 RGB565 的示例,但这些不需要使用查找表。我应该在哪里学习如何以最佳方式加速它?
更新:我发现 OriginX 也是 4 对齐的,并且能够以这种方式改进代码:
unsigned long RGB565PaletteLookupTable[256]; // Lookup table
unsigned char* Src; // Source data
unsigned long* Dst; // Destination buffer
int SrcPitch; // Source data row length
int OriginX, OriginY, Width, Height; // Subrectangle to copy
assert( Width % 4 == 0 );
int SrcOffset = SrcPitch-Width;
Src += OriginY*SrcPitch+OriginX;
SrcOffset >>= 2;
int x, y;
unsigned long* LSrc = (unsigned long*)Src;
for( y = OriginY; y < OriginY+Height; ++y, LSrc += SrcOffset )
{
for( x = OriginX; x < OriginX+Width; x += 4 )
{
unsigned long Indexes = *LSrc++;
unsigned long Result = RGB565PaletteLookupTable[ Indexes & 0xFF ];
Indexes >>= 8;
Result |= ( RGB565PaletteLookupTable[ Indexes & 0xFF ] << 16 );
*Dst++ = Result;
Indexes >>= 8;
Result = RGB565PaletteLookupTable[ Indexes & 0xFF ];
Indexes >>= 8;
Result |= ( RGB565PaletteLookupTable[ Indexes & 0xFF ] << 16 );
*Dst++ = Result;
}
}
据我所知,这段代码没有使用任何未对齐的内存访问。它稍微提高了性能,也就是说,它现在占用了主线程时间的 15.5%。不过,我希望能加快速度。
理论上,每个查找表操作都独立于先前的和后续的(除了它们每个都从同一个查找表读取的事实),所以我期待会有一些 SIMD 指令,或者可能是汇编指令这将允许并行查找许多像素。就像是
_mm_movemask_ps( _mm_cmpneq_ps( _mm_loadu_ps( cmp1 ), _mm_loadu_ps( cmp2 ) ) ) )
在 Mac 上,它与 memcmp( cmp1, cmp2, 16 ) 做同样的事情,只快 8 倍。
我现在继续寻找。
更新:我确定似乎没有办法使用 NEON 指令集加速表查找。该表需要 512 字节大,没有办法将其完全放入 ARM 寄存器中,VTBX NEON 指令一次最多可以处理 32 个字节,并且它还假设查找结果的大小必须等于索引。作为http://forums.arm.com/index.php?/topic/15521-8bit-look-up-table-by-neon-code/中描述的类似问题的解决方案,有些东西可能会解决,但它赢了不适合我的。因此,确保所有操作数的对齐正确似乎是解决此问题的最佳答案。