Silence uninitialised variable warnings in SSE blitters
[openttd/fttd.git] / src / blitter / 32bpp_sse2.cpp
blobbe3871eefad9a41c8c96b249f68a0648176a593a
1 /* $Id$ */
3 /*
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/>.
8 */
10 /** @file 32bpp_sse2.cpp Implementation of the SSE2 32 bpp blitter. */
12 #ifdef WITH_SSE
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;
22 /**
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;
56 switch (mode) {
57 default: {
58 switch (read_mode) {
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;
68 /* FALLTHROUGH */
71 case RM_WITH_SKIP: {
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);
75 ALPHA_BLEND_2();
76 _mm_storel_epi64((__m128i*) dst, srcABCD);
77 src += 2;
78 dst += 2;
80 if (bt_last == BT_ODD) {
81 __m128i srcABCD = _mm_cvtsi32_si128(src->data);
82 __m128i dstABCD = _mm_cvtsi32_si128(dst->data);
83 ALPHA_BLEND_2();
84 dst->data = _mm_cvtsi128_si32(srcABCD);
86 break;
89 default: NOT_REACHED();
91 break;
93 case BM_COLOUR_REMAP: {
94 switch (read_mode) {
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;
105 /* FALLTHROUGH */
108 case RM_WITH_SKIP: {
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. */
112 __m128i srcABCD;
113 if (src_mv->m) {
114 const uint r = remap[src_mv->m];
115 if (r != 0) {
116 Colour remapped_colour = AdjustBrightness(this->LookupColourInPalette(r), src_mv->v);
117 if (src->a == 255) {
118 *dst = remapped_colour;
119 } else {
120 remapped_colour.a = src->a;
121 srcABCD = _mm_cvtsi32_si128(remapped_colour.data);
122 goto bmcr_alpha_blend_single;
125 } else {
126 srcABCD = _mm_cvtsi32_si128(src->data);
127 if (src->a < 255) {
128 bmcr_alpha_blend_single:
129 __m128i dstABCD = _mm_cvtsi32_si128(dst->data);
130 ALPHA_BLEND_2();
132 dst->data = _mm_cvtsi128_si32(srcABCD);
134 src_mv++;
135 dst++;
136 src++;
138 break;
141 default: NOT_REACHED();
143 src_mv_line += si->sprite_width;
144 break;
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);
163 src += 2;
164 dst += 2;
166 if (bp->width & 1) {
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);
180 break;
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)
199 switch (mode) {
200 case BM_NORMAL: {
201 const BlockType bt_last = (BlockType) (bp->width & 1);
202 if (bp->skip_left != 0 || bp->width <= MARGIN_NORMAL_THRESHOLD) {
203 switch (bt_last) {
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();
208 } else {
209 switch (bt_last) {
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();
215 break;
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;
220 } else {
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. */
243 SpriteData sd;
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));
265 /* Copy colours. */
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--) {
274 if (src->a != 0) {
275 dst_rgba->a = src->a;
276 dst_mv->m = src->m;
277 if (src->m != 0) {
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;
287 } else {
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;
293 } else {
294 dst_rgba->data = 0;
295 *(uint16*) dst_mv = 0;
297 dst_rgba++;
298 dst_mv++;
299 src++;
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++;
307 else break;
308 dst_rgba++;
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;
317 nb_pix_transp = 0;
318 for (uint x = src_sprite->width; x != 0; x--) {
319 if (dst_rgba->a == 0) nb_pix_transp++;
320 else break;
321 dst_rgba--;
323 (*nb_right).data = nb_pix_transp; // no "& ~1" here, must be done when we know bp->width
327 return dst_sprite;
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;
345 c16 *= brightness;
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;
355 __m128i ret;
356 #ifdef _SQ64
357 ret = _mm_cvtsi64_si128(c16);
358 #else
359 INSR64(c16, ret, 0);
360 #endif
361 if (ob != 0) {
362 /* Reduce overbright strength. */
363 ob /= 2;
364 __m128i ob128;
365 #ifdef _SQ64
366 ob128 = _mm_cvtsi64_si128(ob | ob << 16 | ob << 32);
367 #else
368 INSR64(ob | ob << 16 | ob << 32, ob128, 0);
369 #endif
370 __m128i white = OVERBRIGHT_VALUE_MASK;
371 __m128i c128 = ret;
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 */