在傳統 2D 遊戲開發中,位圖資源無論以何種色深儲存,載入後通常會統一轉換為目標顯示模式所需的色深。這類轉換多發生於初始化階段,效能壓力不大。然而,隨著遊戲技術演進,未來的 2D 引擎將支援 Voxel 物件與即時光線處理,32 級亮度已無法滿足需求,內部運算勢必採用真彩色(24/32-bit)。但在某些輸出場合(如特定顯示裝置或相容性考量),仍需輸出 15/16-bit 高彩色,因此必須尋找高速的即時轉換方案。
最直觀的 C 語言實作如下:
red = (truecolor >> 8) & 0xF800;
green = (truecolor >> 5) & 0x07E0;
blue = (truecolor >> 3) & 0x001F;
hicolor = red | green | blue;
此做法因位移與遮罩操作頻繁,效能低落。改用 x86 組合語言可大幅優化:
lodsd ; EAX = RRRRRRRR GGGGGGGG BBBBBBBB
shr eax, 3 ; 000RRRRR RRRGGGGG GGGBBBBB
shl al, 2 ; 000RRRRR RRRGGGGG GBBBBBxx
shl ax, 3 ; 000RRRRR GGGGGGBBB BBxxxxx
dec esi
shr eax, 5 ; 00000000 RRRRRGGG GGGBBBBB
stosw
雖然指令精簡,但大量使用部分暫存器(AL, AX)會造成管線停滯,效率大打折扣。優化策略包括:單迴圈處理兩像素(交錯使用 EAX/EBX),或將後段改為查表法。
MMX 的 SIMD 特性讓我們能並行處理多組資料。關鍵在於將 RGB 三通道分開儲存(例如:位圖每一列分為 R、G、B 三段),即可一次載入 4 個像素的同色通道資料,並行運算後合併寫入。範例程式碼:
; MM7 = 0xF800F800F800F800 (紅通道遮罩)
; MM6 = 0xFC00FC00FC00FC00 (綠通道遮罩)
punpcklbw mm0, [red+edx] ; 展開紅通道
punpcklbw mm1, [green+edx] ; 展開綠通道
punpcklbw mm2, [blue+edx] ; 展開藍通道
pand mm0, mm7 ; 保留紅高 5 位
pand mm1, mm6 ; 保留綠高 6 位
psrlw mm2, 11 ; 藍通道右移 11 位
psrlw mm1, 5 ; 綠通道右移 5 位
por mm0, mm2
por mm0, mm1 ; 合併為 RRRRRGGG GGGBBBBB
movq [dis+edx*2], mm0 ; 寫入 4 個 16-bit 像素
add edx, 4
此法單迴圈處理 4 像素,配合快取友善的資料佈局,效能遠超純標量程式碼。本文旨在拋磚引玉,期待更多高手提出更優解。