4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
10 /** @file 32bpp_sse2.cpp Implementation of the SSE2 32 bpp blitter. */
14 #include "../stdafx.h"
15 #include "../zoom_func.h"
16 #include "../settings_type.h"
17 #include "32bpp_sse2.hpp"
19 /** Instantiation of the SSE2 32bpp blitter factory. */
20 static FBlitter_32bppSSE2 iFBlitter_32bppSSE2
;
23 * Draws a sprite to a (screen) buffer. It is templated to allow faster operation.
25 * @tparam mode blitter mode
26 * @param bp further blitting parameters
27 * @param zoom zoom level at which we are drawing
29 IGNORE_UNINITIALIZED_WARNING_START
30 template <BlitterMode mode
, Blitter_32bppSSE2::ReadMode read_mode
, Blitter_32bppSSE2::BlockType bt_last
>
31 inline void Blitter_32bppSSE2::Draw(const Blitter::BlitterParams
*bp
, ZoomLevel zoom
)
33 Colour
*dst_line
= (Colour
*) bp
->dst
+ bp
->top
* bp
->pitch
+ bp
->left
;
34 int effective_width
= bp
->width
;
36 /* Find where to start reading in the source sprite */
37 const SpriteData
* const sd
= (const SpriteData
*) bp
->sprite
;
38 const SpriteInfo
* const si
= &sd
->infos
[zoom
];
39 const MapValue
*src_mv_line
= (const MapValue
*) &sd
->data
[si
->mv_offset
] + bp
->skip_top
* si
->sprite_width
;
40 const Colour
*src_rgba_line
= (const Colour
*) ((const byte
*) &sd
->data
[si
->sprite_offset
] + bp
->skip_top
* si
->sprite_line_size
);
42 if (read_mode
!= RM_WITH_MARGIN
) {
43 src_rgba_line
+= bp
->skip_left
;
44 src_mv_line
+= bp
->skip_left
;
47 /* Load these variables into register before loop. */
48 const __m128i clear_hi
= CLEAR_HIGH_BYTE_MASK
;
49 const __m128i tr_nom_base
= TRANSPARENT_NOM_BASE
;
51 for (int y
= bp
->height
; y
!= 0; y
--) {
52 Colour
*dst
= dst_line
;
53 const Colour
*src
= src_rgba_line
+ META_LENGTH
;
54 const MapValue
*src_mv
= src_mv_line
;
59 case RM_WITH_MARGIN
: {
60 src
+= src_rgba_line
[0].data
;
61 dst
+= src_rgba_line
[0].data
;
62 const int width_diff
= si
->sprite_width
- bp
->width
;
63 effective_width
= bp
->width
- (int) src_rgba_line
[0].data
;
64 const int delta_diff
= (int) src_rgba_line
[1].data
- width_diff
;
65 const int new_width
= effective_width
- (delta_diff
& ~1);
66 effective_width
= delta_diff
> 0 ? new_width
: effective_width
;
67 if (effective_width
<= 0) break;
72 for (uint x
= (uint
) effective_width
/ 2; x
> 0; x
--) {
73 __m128i srcABCD
= _mm_loadl_epi64((const __m128i
*) src
);
74 __m128i dstABCD
= _mm_loadl_epi64((__m128i
*) dst
);
76 _mm_storel_epi64((__m128i
*) dst
, srcABCD
);
80 if (bt_last
== BT_ODD
) {
81 __m128i srcABCD
= _mm_cvtsi32_si128(src
->data
);
82 __m128i dstABCD
= _mm_cvtsi32_si128(dst
->data
);
84 dst
->data
= _mm_cvtsi128_si32(srcABCD
);
89 default: NOT_REACHED();
93 case BM_COLOUR_REMAP
: {
95 case RM_WITH_MARGIN
: {
96 src
+= src_rgba_line
[0].data
;
97 src_mv
+= src_rgba_line
[0].data
;
98 dst
+= src_rgba_line
[0].data
;
99 const int width_diff
= si
->sprite_width
- bp
->width
;
100 effective_width
= bp
->width
- (int) src_rgba_line
[0].data
;
101 const int delta_diff
= (int) src_rgba_line
[1].data
- width_diff
;
102 const int new_width
= effective_width
- delta_diff
;
103 effective_width
= delta_diff
> 0 ? new_width
: effective_width
;
104 if (effective_width
<= 0) break;
109 const byte
*remap
= bp
->remap
;
110 for (uint x
= (uint
) effective_width
; x
!= 0; x
--) {
111 /* In case the m-channel is zero, do not remap this pixel in any way. */
114 const uint r
= remap
[src_mv
->m
];
116 Colour remapped_colour
= AdjustBrightness(this->LookupColourInPalette(r
), src_mv
->v
);
118 *dst
= remapped_colour
;
120 remapped_colour
.a
= src
->a
;
121 srcABCD
= _mm_cvtsi32_si128(remapped_colour
.data
);
122 goto bmcr_alpha_blend_single
;
126 srcABCD
= _mm_cvtsi32_si128(src
->data
);
128 bmcr_alpha_blend_single
:
129 __m128i dstABCD
= _mm_cvtsi32_si128(dst
->data
);
132 dst
->data
= _mm_cvtsi128_si32(srcABCD
);
141 default: NOT_REACHED();
143 src_mv_line
+= si
->sprite_width
;
146 case BM_TRANSPARENT
: {
147 /* Make the current colour a bit more black, so it looks like this image is transparent.
148 * rgb = rgb * ((256/4) * 4 - (alpha/4)) / ((256/4) * 4)
150 for (uint x
= (uint
) bp
->width
/ 2; x
> 0; x
--) {
151 __m128i srcABCD
= _mm_loadl_epi64((const __m128i
*) src
);
152 __m128i dstABCD
= _mm_loadl_epi64((__m128i
*) dst
);
153 __m128i srcAB
= _mm_unpacklo_epi8(srcABCD
, _mm_setzero_si128());
154 __m128i dstAB
= _mm_unpacklo_epi8(dstABCD
, _mm_setzero_si128());
155 __m128i alphaAB
= _mm_shufflelo_epi16(srcAB
, 0x3F);
156 alphaAB
= _mm_shufflehi_epi16(alphaAB
, 0x3F);
157 alphaAB
= _mm_srli_epi16(alphaAB
, 2); // Reduce to 64 levels of shades so the max value fits in 16 bits.
158 __m128i nom
= _mm_sub_epi16(tr_nom_base
, alphaAB
);
159 dstAB
= _mm_mullo_epi16(dstAB
, nom
);
160 dstAB
= _mm_srli_epi16(dstAB
, 8);
161 dstAB
= _mm_packus_epi16(dstAB
, dstAB
);
162 _mm_storel_epi64((__m128i
*) dst
, dstAB
);
167 __m128i srcABCD
= _mm_cvtsi32_si128(src
->data
);
168 __m128i dstABCD
= _mm_cvtsi32_si128(dst
->data
);
169 __m128i srcAB
= _mm_unpacklo_epi8(srcABCD
, _mm_setzero_si128());
170 __m128i dstAB
= _mm_unpacklo_epi8(dstABCD
, _mm_setzero_si128());
171 __m128i alphaAB
= _mm_shufflelo_epi16(srcAB
, 0x3F);
172 alphaAB
= _mm_shufflehi_epi16(alphaAB
, 0x3F);
173 alphaAB
= _mm_srli_epi16(alphaAB
, 2);
174 __m128i nom
= _mm_sub_epi16(tr_nom_base
, alphaAB
);
175 dstAB
= _mm_mullo_epi16(dstAB
, nom
);
176 dstAB
= _mm_srli_epi16(dstAB
, 8);
177 dstAB
= _mm_packus_epi16(dstAB
, dstAB
);
178 dst
->data
= _mm_cvtsi128_si32(dstAB
);
184 src_rgba_line
= (const Colour
*) ((const byte
*) src_rgba_line
+ si
->sprite_line_size
);
185 dst_line
+= bp
->pitch
;
188 IGNORE_UNINITIALIZED_WARNING_STOP
191 * Draws a sprite to a (screen) buffer. Calls adequate templated function.
193 * @param bp further blitting parameters
194 * @param mode blitter mode
195 * @param zoom zoom level at which we are drawing
197 void Blitter_32bppSSE2::Draw(Blitter::BlitterParams
*bp
, BlitterMode mode
, ZoomLevel zoom
)
201 const BlockType bt_last
= (BlockType
) (bp
->width
& 1);
202 if (bp
->skip_left
!= 0 || bp
->width
<= MARGIN_NORMAL_THRESHOLD
) {
204 case BT_EVEN
: Draw
<BM_NORMAL
, RM_WITH_SKIP
, BT_EVEN
>(bp
, zoom
); return;
205 case BT_ODD
: Draw
<BM_NORMAL
, RM_WITH_SKIP
, BT_ODD
>(bp
, zoom
); return;
206 default: NOT_REACHED();
210 case BT_EVEN
: Draw
<BM_NORMAL
, RM_WITH_MARGIN
, BT_EVEN
>(bp
, zoom
); return;
211 case BT_ODD
: Draw
<BM_NORMAL
, RM_WITH_MARGIN
, BT_ODD
>(bp
, zoom
); return;
212 default: NOT_REACHED();
217 case BM_COLOUR_REMAP
:
218 if (bp
->skip_left
!= 0 || bp
->width
<= MARGIN_REMAP_THRESHOLD
) {
219 Draw
<BM_COLOUR_REMAP
, RM_WITH_SKIP
, BT_NONE
>(bp
, zoom
); return;
221 Draw
<BM_COLOUR_REMAP
, RM_WITH_MARGIN
, BT_NONE
>(bp
, zoom
); return;
223 case BM_TRANSPARENT
: Draw
<BM_TRANSPARENT
, RM_NONE
, BT_NONE
>(bp
, zoom
); return;
224 default: NOT_REACHED();
228 Sprite
*Blitter_32bppSSE_Base::Encode(const SpriteLoader::Sprite
*sprite
, AllocatorProc
*allocator
)
230 /* First uint32 of a line = ~1 & the number of transparent pixels from the left.
231 * Second uint32 of a line = the number of transparent pixels from the right.
232 * Then all RGBA then all MV.
234 ZoomLevel zoom_min
= ZOOM_LVL_NORMAL
;
235 ZoomLevel zoom_max
= ZOOM_LVL_NORMAL
;
236 if (sprite
->type
!= ST_FONT
) {
237 zoom_min
= _settings_client
.gui
.zoom_min
;
238 zoom_max
= _settings_client
.gui
.zoom_max
;
239 if (zoom_max
== zoom_min
) zoom_max
= ZOOM_LVL_MAX
;
242 /* Calculate sizes and allocate. */
244 uint all_sprites_size
= 0;
245 for (ZoomLevel z
= zoom_min
; z
<= zoom_max
; z
++) {
246 const SpriteLoader::Sprite
*src_sprite
= &sprite
[z
];
247 sd
.infos
[z
].sprite_width
= src_sprite
->width
;
248 sd
.infos
[z
].sprite_offset
= all_sprites_size
;
249 sd
.infos
[z
].sprite_line_size
= sizeof(Colour
) * src_sprite
->width
+ sizeof(uint32
) * META_LENGTH
;
251 const uint rgba_size
= sd
.infos
[z
].sprite_line_size
* src_sprite
->height
;
252 sd
.infos
[z
].mv_offset
= all_sprites_size
+ rgba_size
;
254 const uint mv_size
= sizeof(MapValue
) * src_sprite
->width
* src_sprite
->height
;
255 all_sprites_size
+= rgba_size
+ mv_size
;
258 Sprite
*dst_sprite
= (Sprite
*) allocator(sizeof(Sprite
) + sizeof(SpriteData
) + all_sprites_size
);
259 dst_sprite
->height
= sprite
->height
;
260 dst_sprite
->width
= sprite
->width
;
261 dst_sprite
->x_offs
= sprite
->x_offs
;
262 dst_sprite
->y_offs
= sprite
->y_offs
;
263 memcpy(dst_sprite
->data
, &sd
, sizeof(SpriteData
));
266 for (ZoomLevel z
= zoom_min
; z
<= zoom_max
; z
++) {
267 const SpriteLoader::Sprite
*src_sprite
= &sprite
[z
];
268 const SpriteLoader::CommonPixel
*src
= (const SpriteLoader::CommonPixel
*) src_sprite
->data
;
269 Colour
*dst_rgba_line
= (Colour
*) &dst_sprite
->data
[sizeof(SpriteData
) + sd
.infos
[z
].sprite_offset
];
270 MapValue
*dst_mv
= (MapValue
*) &dst_sprite
->data
[sizeof(SpriteData
) + sd
.infos
[z
].mv_offset
];
271 for (uint y
= src_sprite
->height
; y
!= 0; y
--) {
272 Colour
*dst_rgba
= dst_rgba_line
+ META_LENGTH
;
273 for (uint x
= src_sprite
->width
; x
!= 0; x
--) {
275 dst_rgba
->a
= src
->a
;
278 /* Get brightest value (or default brightness if it's a black pixel). */
279 const uint8 rgb_max
= max(src
->r
, max(src
->g
, src
->b
));
280 dst_mv
->v
= (rgb_max
== 0) ? Blitter_32bppBase::DEFAULT_BRIGHTNESS
: rgb_max
;
282 /* Pre-convert the mapping channel to a RGB value. */
283 const Colour colour
= AdjustBrightness(Blitter_32bppBase::LookupColourInPalette(src
->m
), dst_mv
->v
);
284 dst_rgba
->r
= colour
.r
;
285 dst_rgba
->g
= colour
.g
;
286 dst_rgba
->b
= colour
.b
;
288 dst_rgba
->r
= src
->r
;
289 dst_rgba
->g
= src
->g
;
290 dst_rgba
->b
= src
->b
;
291 dst_mv
->v
= Blitter_32bppBase::DEFAULT_BRIGHTNESS
;
295 *(uint16
*) dst_mv
= 0;
302 /* Count the number of transparent pixels from the left. */
303 dst_rgba
= dst_rgba_line
+ META_LENGTH
;
304 uint32 nb_pix_transp
= 0;
305 for (uint x
= src_sprite
->width
; x
!= 0; x
--) {
306 if (dst_rgba
->a
== 0) nb_pix_transp
++;
310 (*dst_rgba_line
).data
= nb_pix_transp
& ~1; // "& ~1" to preserve the last block type
312 Colour
*nb_right
= dst_rgba_line
+ 1;
313 dst_rgba_line
= (Colour
*) ((byte
*) dst_rgba_line
+ sd
.infos
[z
].sprite_line_size
);
315 /* Count the number of transparent pixels from the right. */
316 dst_rgba
= dst_rgba_line
- 1;
318 for (uint x
= src_sprite
->width
; x
!= 0; x
--) {
319 if (dst_rgba
->a
== 0) nb_pix_transp
++;
323 (*nb_right
).data
= nb_pix_transp
; // no "& ~1" here, must be done when we know bp->width
330 /** ReallyAdjustBrightness() is not called that often.
331 * Inlining this function implies a far jump, which has a huge latency.
333 inline Colour
Blitter_32bppSSE2::AdjustBrightness(Colour colour
, uint8 brightness
)
335 /* Shortcut for normal brightness. */
336 if (brightness
== DEFAULT_BRIGHTNESS
) return colour
;
338 return Blitter_32bppSSE2::ReallyAdjustBrightness(colour
, brightness
);
341 IGNORE_UNINITIALIZED_WARNING_START
342 /* static */ Colour
Blitter_32bppSSE2::ReallyAdjustBrightness(Colour colour
, uint8 brightness
)
344 uint64 c16
= colour
.b
| (uint64
) colour
.g
<< 16 | (uint64
) colour
.r
<< 32;
346 uint64 c16_ob
= c16
; // Helps out of order execution.
347 c16
/= DEFAULT_BRIGHTNESS
;
348 c16
&= 0x01FF01FF01FFULL
;
350 /* Sum overbright (maximum for each rgb is 508, 9 bits, -255 is changed in -256 so we just have to take the 8 lower bits into account). */
351 c16_ob
= (((c16_ob
>> (8 + 7)) & 0x0100010001ULL
) * 0xFF) & c16
;
352 uint64 ob
= (uint16
) c16_ob
+ (uint16
) (c16_ob
>> 16) + (uint16
) (c16_ob
>> 32);
354 const uint32 alpha32
= colour
.data
& 0xFF000000;
357 ret
= _mm_cvtsi64_si128(c16
);
362 /* Reduce overbright strength. */
366 ob128
= _mm_cvtsi64_si128(ob
| ob
<< 16 | ob
<< 32);
368 INSR64(ob
| ob
<< 16 | ob
<< 32, ob128
, 0);
370 __m128i white
= OVERBRIGHT_VALUE_MASK
;
372 ret
= _mm_subs_epu16(white
, c128
); /* PSUBUSW, (255 - rgb) */
373 ret
= _mm_mullo_epi16(ret
, ob128
); /* PMULLW, ob*(255 - rgb) */
374 ret
= _mm_srli_epi16(ret
, 8); /* PSRLW, ob*(255 - rgb)/256 */
375 ret
= _mm_add_epi16(ret
, c128
); /* PADDW, ob*(255 - rgb)/256 + rgb */
378 ret
= _mm_packus_epi16(ret
, ret
); /* PACKUSWB, saturate and pack. */
379 return alpha32
| _mm_cvtsi128_si32(ret
);
381 IGNORE_UNINITIALIZED_WARNING_STOP
383 #endif /* WITH_SSE */