/*****************************************************************************

        MatrixProc_avx2.cpp
        Author: Laurent de Soras, 2015

To be compiled with /arch:AVX in order to avoid SSE/AVX state switch
slowdown.

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*Tab=3***********************************************************************/



/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include "fmtcl/MatrixProc.h"
#include "fstb/def.h"

#include <immintrin.h>

#include <algorithm>

#include <cassert>



namespace fmtcl
{



/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



void	MatrixProc::setup_fnc_avx (bool int_proc_flag, SplFmt src_fmt, int src_bits, SplFmt dst_fmt, int dst_bits, bool single_plane_flag)
{
	fstb::unused (src_fmt, src_bits, dst_fmt, dst_bits);

	if (! int_proc_flag)
	{
		if (single_plane_flag)
		{
			_proc_ptr = &ThisType::process_1_flt_avx;
		}
		else
		{
			_proc_ptr = &ThisType::process_3_flt_avx;
		}
	}
}



void	MatrixProc::process_3_flt_avx (Frame <> dst, FrameRO <> src, int w, int h) const noexcept
{
	assert (dst.is_valid (_nbr_planes, h));
	assert (src.is_valid (_nbr_planes, h));
	assert (w > 0);
	assert (h > 0);

	static_assert (_nbr_planes == 3, "Code is hardcoded for 3 planes");

	const __m256   c00 = _mm256_set1_ps (_coef_flt_arr [ 0]);
	const __m256   c01 = _mm256_set1_ps (_coef_flt_arr [ 1]);
	const __m256   c02 = _mm256_set1_ps (_coef_flt_arr [ 2]);
	const __m256   c03 = _mm256_set1_ps (_coef_flt_arr [ 3]);
	const __m256   c04 = _mm256_set1_ps (_coef_flt_arr [ 4]);
	const __m256   c05 = _mm256_set1_ps (_coef_flt_arr [ 5]);
	const __m256   c06 = _mm256_set1_ps (_coef_flt_arr [ 6]);
	const __m256   c07 = _mm256_set1_ps (_coef_flt_arr [ 7]);
	const __m256   c08 = _mm256_set1_ps (_coef_flt_arr [ 8]);
	const __m256   c09 = _mm256_set1_ps (_coef_flt_arr [ 9]);
	const __m256   c10 = _mm256_set1_ps (_coef_flt_arr [10]);
	const __m256   c11 = _mm256_set1_ps (_coef_flt_arr [11]);

	for (int y = 0; y < h; ++y)
	{
		const FrameRO <float>   s { src };
		const Frame <float>     d { dst };

		for (int x = 0; x < w; x += 8)
		{
			const __m256   s0 = _mm256_load_ps (s [0]._ptr + x);
			const __m256   s1 = _mm256_load_ps (s [1]._ptr + x);
			const __m256   s2 = _mm256_load_ps (s [2]._ptr + x);

			const __m256   d0 = _mm256_add_ps (_mm256_add_ps (_mm256_add_ps (
				_mm256_mul_ps (s0, c00),
				_mm256_mul_ps (s1, c01)),
				_mm256_mul_ps (s2, c02)),
				                   c03);
			const __m256   d1 = _mm256_add_ps (_mm256_add_ps (_mm256_add_ps (
				_mm256_mul_ps (s0, c04),
				_mm256_mul_ps (s1, c05)),
				_mm256_mul_ps (s2, c06)),
				                   c07);
			const __m256   d2 = _mm256_add_ps (_mm256_add_ps (_mm256_add_ps (
				_mm256_mul_ps (s0, c08),
				_mm256_mul_ps (s1, c09)),
				_mm256_mul_ps (s2, c10)),
				                   c11);

			_mm256_store_ps (d [0]._ptr + x, d0);
			_mm256_store_ps (d [1]._ptr + x, d1);
			_mm256_store_ps (d [2]._ptr + x, d2);
		}

		src.step_line ();
		dst.step_line ();
	}

	_mm256_zeroupper ();	// Back to SSE state
}



void	MatrixProc::process_1_flt_avx (Frame <> dst, FrameRO <> src, int w, int h) const noexcept
{
	assert (dst.is_valid (          1, h));
	assert (src.is_valid (_nbr_planes, h));
	assert (w > 0);
	assert (h > 0);

	static_assert (_nbr_planes == 3, "Code is hardcoded for 3 planes");

	const __m256   c00 = _mm256_set1_ps (_coef_flt_arr [ 0]);
	const __m256   c01 = _mm256_set1_ps (_coef_flt_arr [ 1]);
	const __m256   c02 = _mm256_set1_ps (_coef_flt_arr [ 2]);
	const __m256   c03 = _mm256_set1_ps (_coef_flt_arr [ 3]);

	for (int y = 0; y < h; ++y)
	{
		const FrameRO <float>   s { src };
		const Plane <float>     d { dst [0] };

		for (int x = 0; x < w; x += 4)
		{
			const __m256   s0 = _mm256_load_ps (s [0]._ptr + x);
			const __m256   s1 = _mm256_load_ps (s [1]._ptr + x);
			const __m256   s2 = _mm256_load_ps (s [2]._ptr + x);

			const __m256   d0 = _mm256_add_ps (_mm256_add_ps (_mm256_add_ps (
				_mm256_mul_ps (s0, c00),
				_mm256_mul_ps (s1, c01)),
				_mm256_mul_ps (s2, c02)),
				                c03);

			_mm256_store_ps (d._ptr + x, d0);
		}

		src.step_line ();
		dst [0].step_line ();
	}

	_mm256_zeroupper ();	// Back to SSE state
}



/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



}	// namespace fmtcl



/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
