fixed 16x16

fixed 16x16

#if 0 // nef ML_OPEN_RUNNING
// W 7 x H 7 x C 2048 x K 2048
//#define MLO_GRP_SZ
#define MLO_GRP_SZ0 64
#define MLO_GRP_SZ1 1
#define MLO_GRP_SZ2 1
#define MLO_FILTER_SIZE0 1
#define MLO_FILTER_SIZE1 1
#define MLO_FILTER_PAD0 3
#define MLO_FILTER_PAD1 3
#define MLO_FILTER_STRIDE0 2
#define MLO_FILTER_STRIDE1 2
#define STRIDE_W 1
#define STRIDE_H 1
#define MLO_N_OUTPUTS 2048
#define MLO_N_INPUTS 1024
#define MLO_BATCH_SZ 16
//MLO_N_BATCH_LOOPS
#define MLO_IN_WIDTH 7
#define MLO_IN_HEIGHT 7
#define MLO_OUT_WIDTH 7
#define MLO_OUT_HEIGHT 7
#endif

#if 0 // nef ML_OPEN_RUNNING
// W 7 x H 7 x C 2048 x K 2048
//#define MLO_GRP_SZ
#define MLO_GRP_SZ0 256
#define MLO_GRP_SZ1 1
#define MLO_GRP_SZ2 1
#define MLO_FILTER_SIZE0 1
#define MLO_FILTER_SIZE1 1
#define MLO_FILTER_PAD0 0
#define MLO_FILTER_PAD1 0
#define MLO_FILTER_STRIDE0 2
#define MLO_FILTER_STRIDE1 2
#define STRIDE_W 1
#define STRIDE_H 1
#define MLO_N_OUTPUTS 2048
#define MLO_N_INPUTS 1024
#define MLO_BATCH_SZ 16
//MLO_N_BATCH_LOOPS
#define MLO_IN_WIDTH 14
#define MLO_IN_HEIGHT 14
#define MLO_OUT_WIDTH 7
#define MLO_OUT_HEIGHT 7
#endif

#if 0 // nef ML_OPEN_RUNNING
// W 14 x H 14 x C 1024 x K 512
//#define MLO_GRP_SZ
#define MLO_GRP_SZ0 256
#define MLO_GRP_SZ1 1
#define MLO_GRP_SZ2 1
#define MLO_FILTER_SIZE0 1
#define MLO_FILTER_SIZE1 1
#define MLO_FILTER_PAD0 0
#define MLO_FILTER_PAD1 0
#define MLO_FILTER_STRIDE0 2
#define MLO_FILTER_STRIDE1 2
#define STRIDE_W 1
#define STRIDE_H 1
#define MLO_N_OUTPUTS 512
#define MLO_N_INPUTS 1024
#define MLO_BATCH_SZ 16
//MLO_N_BATCH_LOOPS
#define MLO_IN_WIDTH 14
#define MLO_IN_HEIGHT 14
#define MLO_OUT_WIDTH 7
#define MLO_OUT_HEIGHT 7

#define MLO_LDS_REDUCTOIN 1
#define MLO_GLOBAL_ATOMIC 0
#define MLO_N_LDS_REDUCTION_ONCE 8
#define MLO_N_LDS_SIZE_PER_THREAD 8

#endif

#if 0 // nef ML_OPEN_RUNNING
// W 28 x H 28 x C 192 x K 64 X N 16
//#define MLO_GRP_SZ
#define MLO_GRP_SZ0 64
#define MLO_GRP_SZ1 1
#define MLO_GRP_SZ2 1
#define MLO_FILTER_SIZE0 1
#define MLO_FILTER_SIZE1 1
#define MLO_FILTER_PAD0 0
#define MLO_FILTER_PAD1 0
#define MLO_FILTER_STRIDE0 1
#define MLO_FILTER_STRIDE1 1
#define STRIDE_W 1
#define STRIDE_H 1
#define MLO_N_OUTPUTS 64
#define MLO_N_INPUTS 192
#define MLO_BATCH_SZ 16
#define MLO_IN_WIDTH 28
#define MLO_IN_HEIGHT 28
#define MLO_OUT_WIDTH 28
#define MLO_OUT_HEIGHT 28
#define MLO_LDS_REDUCTOIN 1
#define MLO_GLOBAL_ATOMIC 0

#define MLO_N_LOAD_DWORDS_PER_MAP_ONCE 64
#define MLO_N_LCL_IN_MAPS 8
#define MLO_N_LCL_OUT_MAPS 8

#define MLO_N_LCL_IN_MAPS_ONCE 8
#define MLO_N_LCL_OUT_MAPS_ONCE 8

#define MLO_READ_UNIT 2

//READ_UNIT == 1 for STRIDE and PAD mode

#define MLO_OUT_BATCH_STRIDE (MLO_OUT_WIDTH * MLO_OUT_HEIGHT * MLO_N_OUTPUTS)
#define MLO_OUT_CHANNEL_STRIDE (MLO_OUT_WIDTH * MLO_OUT_WIDTH)

#define MLO_IN_BATCH_STRIDE (MLO_IN_WIDTH * MLO_IN_HEIGHT * MLO_N_INPUTS)
#define MLO_IN_CHANNEL_STRIDE (MLO_IN_WIDTH * MLO_IN_HEIGHT)
#define MLO_WEI_BATCH_STRIDE (MLO_N_INPUTS * MLO_N_OUTPUTS)
#define MLO_WEI_CHANNEL_STRIDE (1 * 1 * MLO_N_INPUTS)
#define MLO_MAX_LOADS ((MLO_OUT_CHANNEL_STRIDE / MLO_READ_UNIT) * MLO_BATCH_SZ)

#define MLO_ACCUM_SZ (MLO_N_LCL_IN_MAPS * MLO_N_LCL_OUT_MAPS)
#define MLO_OUT_READ_SZ (N_LCL_OUT_MAPS * MLO_READ_UNIT)
#define MLO_IN_READ_SZ (MLO_N_LCL_IN_MAPS * MLO_READ_UNIT)

#define MLO_OUT_CHANNEL_READ_SZ (MLO_OUT_CHANNEL_STRIDE / MLO_READ_UNIT)

#define MLO_N_IN_TILE_BLOCK 4
#endif

#if 0
#define MLO_READ_UNIT 4

#define MLO_OUT_BATCH_STRIDE (MLO_OUT_WIDTH * MLO_OUT_HEIGHT * MLO_N_OUTPUTS)
#define MLO_OUT_CHANNEL_STRIDE (MLO_OUT_WIDTH * MLO_OUT_WIDTH)
#define MLO_OUT_STRIDE (1)
#define MLO_IN_BATCH_STRIDE (MLO_IN_WIDTH * MLO_IN_HEIGHT * MLO_N_INPUTS)
#define MLO_IN_CHANNEL_STRIDE (MLO_IN_WIDTH * MLO_IN_HEIGHT)
#define MLO_IN_STRIDE (2)
#define MLO_WEI_BATCH_STRIDE (MLO_N_INPUTS * MLO_N_OUTPUTS)
#define MLO_WEI_CHANNEL_STRIDE (1 * 1 * MLO_N_INPUTS)

//#define MLO_N_LCL_IN_MAPS       8
//#define MLO_N_LCL_OUT_MAPS      8
#define MLO_CACHELINE_DWORD_SZ 64

#endif

#if 0
#if(MLO_FILTER_PAD0 > 0 || MLO_FILTER_PAD1 > 0)
#define MLO_IN_PAD_MIN_X0 (MLO_FILTER_STRIDE0 - (MLO_FILTER_PAD0 % MLO_FILTER_STRIDE0))
#define MLO_IN_PAD_MIN_Y0 (MLO_FILTER_STRIDE1 - (MLO_FILTER_PAD1 % MLO_FILTER_STRIDE1))

#define MLO_IN_PAD_MIN_X (MLO_IN_PAD_MIN_X0 % MLO_FILTER_STRIDE0)
#define MLO_IN_PAD_MIN_Y (MLO_IN_PAD_MIN_Y0 % MLO_FILTER_STRIDE1)

#define MLO_OUT_PAD_MIN_X ((MLO_FILTER_PAD0 + MLO_FILTER_STRIDE0 - 1) / MLO_FILTER_STRIDE0)
#define MLO_OUT_PAD_MIN_Y ((MLO_FILTER_PAD1 + MLO_FILTER_STRIDE1 - 1) / MLO_FILTER_STRIDE1)

#define MLO_OUT_PAD_WIDTH \
    (((MLO_IN_WIDTH - MLO_IN_PAD_MIN_X + MLO_FILTER_STRIDE0 - 1) / MLO_FILTER_STRIDE0))
#define MLO_OUT_PAD_HEIGHT \
    (((MLO_IN_HEIGHT - MLO_IN_PAD_MIN_Y + MLO_FILTER_STRIDE0 - 1) / MLO_FILTER_STRIDE1))

#else
#define MLO_IN_PAD_MIN_X 0
#define MLO_IN_PAD_MIN_Y 0

#define MLO_OUT_PAD_MIN_X 0
#define MLO_OUT_PAD_MIN_Y 0

#define MLO_OUT_PAD_WIDTH MLO_OUT_WIDTH
#define MLO_OUT_PAD_HEIGHT MLO_OUT_HEIGHT
#endif
#endif

#if 0

#define MLO_N_BATCH_PER_WAVE ((MLO_BATCH_SZ + (MLO_GRP_SZ0 / 64) - 1) / (MLO_GRP_SZ0 / 64))
#define MLO_N_LOOPS_PER_MAP                                                          \
    ((MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT + MLO_N_LOAD_DWORDS_PER_MAP_ONCE - 1) / \
     (MLO_N_LOAD_DWORDS_PER_MAP_ONCE))
#define MLO_N_DWORDS_LAST_LOAD_PER_MAP \
    ((MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT) % (MLO_N_LOAD_DWORDS_PER_MAP_ONCE))

#define MLO_IN_NON_ALIGN (MLO_N_INPUTS & 0x7)
#define MLO_OUT_NON_ALIGN (MLO_N_OUTPUTS & 0x7)
#define MLO_IN_LAST_GROUP (MLO_N_INPUTS & (~0x7))
#define MLO_OUT_LAST_GROUP (MLO_N_OUTPUTS & (~0x7))
#define MLO_MAX_LOAD_DWORD ((MLO_OUT_CHANNEL_STRIDE / MLO_READ_UNIT) * MLO_BATCH_SZ)
#endif

// FLAT_LOAD_DWORDx4 is max
// MIN(MLO_OUT_PAD_WIDTH , 8)

#if 0

//64x64 only works MLO_OUT_PAD_WIDTH<=8
//following are trick to speed-up shader compiler for unused MIOpenCvBwdWrW_64x64
#if MLO_OUT_PAD_WIDTH > 8
#define MLO_N_LOAD_DWORD_ONCE_PER_THREAD 1
#else
#define MLO_N_DWORD_PER_LOOP (MLO_OUT_PAD_WIDTH)
#endif

#endif

#define _FLOAT float
#define _FLOAT2 float2
#define _FLOAT4 float4
#define _FLOAT8 float8

#ifndef FLT_MAX
#define FLT_MAX 3.402823466e+38F /* max value */
#endif

#define UNUSED __attribute__((__unused__))
#define DBG_OUT_OF_RNGE 0

__attribute__((always_inline)) uint iDiv(uint v, uint d)
{
    uint r = (uint)((float)v * (1.f / (float)d) + 0.00001f);
    return (r);
}

__attribute__((always_inline)) uint iMod(uint v, uint u, uint d)
{
    uint r = v - mul24(u, d);
    return (r);
}

// top_df        ==> out        in [Batch][output][out_H][out_W]
// bot           ==> gard_input in [Batch][inputs][IN_H][IN_W]
// weights_df    ==> weights    in [output][input][filter][filter]

__attribute__((reqd_work_group_size(MLO_GRP_SZ0, MLO_GRP_SZ1, MLO_GRP_SZ2))) __kernel void
MIOpenCvBwdWrW_8x8map(const __global _FLOAT* __restrict top_df,
                      const __global _FLOAT* __restrict bot,
                      __global _FLOAT* __restrict weights_df,
                      UNUSED _FLOAT padding_val)
{
    __local _FLOAT sdata[MLO_GRP_SZ0 * 8];
    // 8x8 MUL_ADD per thread
    // Every thread 1DWORDs per MAP * 8 Maps
    // every 64 threads Load continous 64 DWORDs per MAP * 8 Maps
    // LDS reduction of every 64 threads for 8x8 results
    // NO LDS data exchange

    // Global_group_Id0:  [TILE_BLOCK]
    // Global_Id0:  [C/8] * 64 or 256
    // Global_group_Id1:  [K/8]
    // Global_group_Id2:  [C/8/ (MLO_N_IN_TILE_BLOCK= 8 or 4) ]

    // uint group_id0 = get_group_id(0);
    // uint group_id1 = get_group_id(1);

    uint local_Id0 = get_local_id(0);

// traverse small batch size to have better performance
#if MLO_IN_BATCH_STRIDE < MLO_OUT_BATCH_STRIDE
    uint C_OFFSET = get_group_id(0) * MLO_N_LCL_IN_MAPS;
    uint K_OFFSET = get_group_id(1) * MLO_N_LCL_OUT_MAPS;

#else
    uint K_OFFSET          = get_group_id(0) * MLO_N_LCL_OUT_MAPS;
    uint C_OFFSET          = get_group_id(1) * MLO_N_LCL_IN_MAPS;

#endif

    uint glb_out_off0 = K_OFFSET * MLO_OUT_CHANNEL_STRIDE;
    uint glb_in_off0  = C_OFFSET * MLO_IN_CHANNEL_STRIDE;

    // NO preload
    __private _FLOAT load_buf_top[MLO_N_LCL_OUT_MAPS * MLO_READ_UNIT];
    __private _FLOAT load_buf_bot[MLO_N_LCL_IN_MAPS * MLO_READ_UNIT];

    __private _FLOAT accum[MLO_ACCUM_SZ];

    // CNHW will be continous address to utlize X4 load;
    // NCHW will be hard mode till now

    for(uint i = 0; i < MLO_ACCUM_SZ; i++)
    {
        accum[i] = 0;
    }

    for(uint i = 0; i < MLO_N_LCL_IN_MAPS; i++)
    {
        sdata[local_Id0 + i * MLO_GRP_SZ0] = 0;
    }

    for(uint faked_off = local_Id0; faked_off < MLO_MAX_LOADS; faked_off += MLO_GRP_SZ0)
    {
#if MLO_FILTER_STRIDE0 > 1 || MLO_FILTER_STRIDE1 > 1 || MLO_FILTER_STRIDE0 > 1 || \
    MLO_FILTER_STRIDE1 > 1
        // MLO_READ_UNIT==1

        uint batch_id   = iDiv(faked_off, (MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT));
        uint faked_off2 = iMod(faked_off, batch_id, (MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT));

        uint out_x_off = iDiv(faked_off2, MLO_OUT_PAD_WIDTH);
        uint out_y_off = iMod(faked_off2, out_x_off, MLO_OUT_PAD_WIDTH);

        uint out_image_off =
            (out_y_off + MLO_OUT_PAD_MIN_Y) * MLO_OUT_WIDTH + (out_x_off + MLO_OUT_PAD_MIN_X);

        uint in_x_off = out_x_off * MLO_FILTER_STRIDE0 + MLO_IN_PAD_MIN_X;
        uint in_y_off = out_y_off * MLO_FILTER_STRIDE1 + MLO_IN_PAD_MIN_Y;

        uint in_image_off = in_y_off * MLO_IN_WIDTH + in_x_off;

#else
        uint batch_id      = iDiv(faked_off, (MLO_OUT_CHANNEL_READ_SZ));           // batch
        uint image_off     = iMod(faked_off, batch_id, (MLO_OUT_CHANNEL_READ_SZ)); // pixel offset
        uint in_image_off  = image_off * MLO_READ_UNIT;
        uint out_image_off = image_off * MLO_READ_UNIT;
#endif
        uint glb_in_off = glb_in_off0 + batch_id * MLO_IN_BATCH_STRIDE + in_image_off;

        // Address offset mode is not used in shader compiler
        // Most VGPRs are used up by Address
        for(uint c = 0; c < MLO_N_LCL_IN_MAPS; ++c)
        {
            uint bot_off                = glb_in_off + c * MLO_IN_CHANNEL_STRIDE;
            const __global _FLOAT* bot1 = &bot[bot_off];

            for(uint i = 0; i < MLO_READ_UNIT; ++i)
            {
                load_buf_bot[c * MLO_READ_UNIT + i] = bot1[i];
            }
        }

        uint glb_out_off = glb_out_off0 + batch_id * MLO_OUT_BATCH_STRIDE + out_image_off;
        for(uint k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
        {
            uint top_off = glb_out_off + k * MLO_OUT_CHANNEL_STRIDE;

            const __global _FLOAT* top1 = &top_df[top_off];

            for(uint i = 0; i < MLO_READ_UNIT; ++i)
            {
                load_buf_top[k * MLO_READ_UNIT + i] = top1[i];
            }
        }

        // processing
        // outside loop-i save 1 VGPR than loop-i inside
        for(uint i = 0; i < MLO_READ_UNIT; ++i)
        {
            for(uint k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
            {
                for(uint c = 0; c < MLO_N_LCL_IN_MAPS; ++c)
                {
                    {
                        accum[k * MLO_N_LCL_IN_MAPS + c] += load_buf_bot[c * MLO_READ_UNIT + i] *
                                                            load_buf_top[k * MLO_READ_UNIT + i];
                    }
                }
            }
        }
    }

#define LAST_PIXELS (MLO_OUT_CHANNEL_STRIDE % MLO_READ_UNIT)

// PAD/STRIDE never goes to following since MLO_READ_UNIT == 1
#if LAST_PIXELS > 0
#define MLO_MAX_LOADS2 (MLO_BATCH_SZ * LAST_PIXELS)
#define MLO_LAST_PIXEL_OFFSET (MLO_OUT_CHANNEL_STRIDE - LAST_PIXELS)

    for(uint faked_off = local_Id0; faked_off < MLO_MAX_LOADS2; faked_off += MLO_GRP_SZ0)
    {
#if MLO_FILTER_STRIDE0 > 1 || MLO_FILTER_STRIDE1 > 1 || MLO_FILTER_STRIDE0 > 1 || \
    MLO_FILTER_STRIDE1 > 1
        uint batch_id   = iDiv(faked_off, (MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT));
        uint faked_off2 = iMod(faked_off, batch_id, (MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT));

        uint out_x_off = iDiv(faked_off2, MLO_OUT_PAD_WIDTH);
        uint out_y_off = iMod(faked_off2, out_x_off, MLO_OUT_PAD_WIDTH);

        uint out_image_off =
            (out_y_off + MLO_OUT_PAD_MIN_Y) * MLO_OUT_WIDTH + (out_x_off + MLO_OUT_PAD_MIN_X);

        uint in_x_off = out_x_off * MLO_FILTER_STRIDE0 + MLO_IN_PAD_MIN_X;
        uint in_y_off = out_y_off * MLO_FILTER_STRIDE1 + MLO_IN_PAD_MIN_Y;

        uint in_image_off = in_y_off * MLO_IN_WIDTH + in_x_off;

#else
        uint batch_id = iDiv(faked_off, (LAST_PIXELS)); // batch
        uint image_off =
            iMod(faked_off, batch_id, (LAST_PIXELS)) + MLO_LAST_PIXEL_OFFSET; // pixel offset
        uint in_image_off  = image_off * 1;
        uint out_image_off = image_off * 1;
#endif
        uint glb_in_off = glb_in_off0 + batch_id * MLO_IN_BATCH_STRIDE + in_image_off;

        // Address offset mode is not used in shader compiler
        // Most VGPRs are used up by Address
        for(uint c = 0; c < MLO_N_LCL_IN_MAPS; ++c)
        {
            uint bot_off                = glb_in_off + c * MLO_IN_CHANNEL_STRIDE;
            const __global _FLOAT* bot1 = &bot[bot_off];

            for(uint i = 0; i < 1; ++i)
            {
                load_buf_bot[c * MLO_READ_UNIT + i] = bot1[i];
            }
        }

        uint glb_out_off = glb_out_off0 + batch_id * MLO_OUT_BATCH_STRIDE + out_image_off;
        for(uint k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
        {
            uint top_off = glb_out_off + k * MLO_OUT_CHANNEL_STRIDE;

            const __global _FLOAT* top1 = &top_df[top_off];

            for(uint i = 0; i < 1; ++i)
            {
                load_buf_top[k * MLO_READ_UNIT + i] = top1[i];
            }
        }

        // processing
        // outside loop-i save 1 VGPR than loop-i inside
        for(uint i = 0; i < 1; ++i)
        {
            for(uint k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
            {
                for(uint c = 0; c < MLO_N_LCL_IN_MAPS; ++c)
                {
                    {
                        accum[k * MLO_N_LCL_IN_MAPS + c] += load_buf_bot[c * MLO_READ_UNIT + i] *
                                                            load_buf_top[k * MLO_READ_UNIT + i];
                    }
                }
            }
        }
    }
#endif

    __private _FLOAT accum_to_store = 0;

    for(uint K = 0; K < MLO_N_LCL_OUT_MAPS; K++)
    {
         barrier(CLK_LOCAL_MEM_FENCE);

        for(uint C = 0; C < MLO_N_LCL_IN_MAPS; C++)
        {
            sdata[local_Id0 + MLO_GRP_SZ0 * C] = accum[K * MLO_N_LCL_IN_MAPS + C];
        }

        barrier(CLK_LOCAL_MEM_FENCE);

        // Reduction every MLO_GRP_SZ0* MLO_GRP_SZ1 to trhead 0
        for(uint s = ((MLO_GRP_SZ0) >> 2); s > 0; s = (s >> 2))
        {
            if(local_Id0 < s)
            {
                for(uint C = 0; C < MLO_N_LCL_IN_MAPS; C++)
                {
                    sdata[local_Id0 + MLO_GRP_SZ0 * C] +=
                        sdata[MLO_GRP_SZ0 * C + local_Id0 + s] +
                        sdata[MLO_GRP_SZ0 * C + local_Id0 + 2 * s] +
                        sdata[MLO_GRP_SZ0 * C + local_Id0 + 3 * s];
                }
            }
            // NO need inside 1 wave: barrier(CLK_LOCAL_MEM_FENCE);
        }

        barrier(CLK_LOCAL_MEM_FENCE);

        // MLO_N_LCL_IN_MAPS store
        if((local_Id0 & ~0x7) == (K * MLO_N_LCL_IN_MAPS))
        {
            accum_to_store = sdata[0 + (local_Id0 & 0x7) * MLO_GRP_SZ0];
        }

        // only 1st wave need to barrier: it will remove all scratch registers

    }

    if(local_Id0 < (MLO_ACCUM_SZ))
    {

        // Store to Memory
        __global _FLOAT* __restrict weights_ptr =
            weights_df + (K_OFFSET + (local_Id0 / MLO_N_LCL_IN_MAPS)) * MLO_WEI_CHANNEL_STRIDE +
            C_OFFSET + (local_Id0 % MLO_N_LCL_IN_MAPS);

        {
            __global _FLOAT* weights_ptr2 = weights_ptr;
            *weights_ptr2                 = accum_to_store;
        }
    }
}

__attribute__((reqd_work_group_size(MLO_GRP_SZ0, MLO_GRP_SZ1, MLO_GRP_SZ2))) __kernel void
MIOpenCvBwdWrW_16x16map(const __global _FLOAT* __restrict top_df,
                        const __global _FLOAT* __restrict bot,
                        __global _FLOAT* __restrict weights_df,
                        UNUSED _FLOAT padding_val)
{
    __local _FLOAT sdata[MLO_GRP_SZ0 * 8];
    // 64 threds split into 4 grpoups: every 16 threads accumulate 8x8
    //
    // 256 threads split inot 4 groups : every 64 threads accumulate 8x8
    //

    // Global_group_Id0:  [TILE_BLOCK]
    // Global_Id0:  [C/16] * 64 or 256
    // Global_group_Id1:  [K/16]
    // Global_group_Id2:  [C/16/ (MLO_N_IN_TILE_BLOCK= 8 or 4) ]

    // uint group_id0 = get_group_id(0);
    // uint group_id1 = get_group_id(1);

    uint local_Id0 = get_local_id(0);

// traverse small batch size to have better performance
#if MLO_IN_BATCH_STRIDE < MLO_OUT_BATCH_STRIDE
    uint C_OFFSET = get_group_id(0) * MLO_N_LCL_IN_MAPS;
    uint K_OFFSET = get_group_id(1) * MLO_N_LCL_OUT_MAPS;

#else
    uint K_OFFSET          = get_group_id(0) * MLO_N_LCL_OUT_MAPS;
    uint C_OFFSET          = get_group_id(1) * MLO_N_LCL_IN_MAPS;

#endif

    // Split into 4 groups for C[0,1,0,1], K [0,0,1,1]
    uint k_offset2 = ((local_Id0 / (MLO_GRP_SZ0 / 4)) / 2) * MLO_N_LCL_OUT_MAPS_ONCE;
    uint c_offset2 = ((local_Id0 / (MLO_GRP_SZ0 / 4)) % 2) * MLO_N_LCL_IN_MAPS_ONCE;

    uint glb_out_off0 = (K_OFFSET + k_offset2) * MLO_OUT_CHANNEL_STRIDE;
    uint glb_in_off0  = (C_OFFSET + c_offset2) * MLO_IN_CHANNEL_STRIDE;

    // NO preload
    __private _FLOAT load_buf_top[MLO_N_LCL_OUT_MAPS_ONCE * MLO_READ_UNIT];
    __private _FLOAT load_buf_bot[MLO_N_LCL_IN_MAPS_ONCE * MLO_READ_UNIT];

    __private _FLOAT accum[MLO_ACCUM_SZ];

    // CNHW will be continous address to utlize X4 load;
    // NCHW will be hard mode till now

    for(uint i = 0; i < MLO_ACCUM_SZ; i++)
    {
        accum[i] = 0;
    }

    for(uint i = 0; i < MLO_N_LCL_OUT_MAPS_ONCE; i++)
    {
        sdata[local_Id0 + i * MLO_GRP_SZ0] = 0;
    }

    for(uint faked_off = (local_Id0 % (MLO_GRP_SZ0 / 4)); faked_off < MLO_MAX_LOADS;
        faked_off += (MLO_GRP_SZ0 / 4))
    {
#if MLO_FILTER_STRIDE0 > 1 || MLO_FILTER_STRIDE1 > 1 || MLO_FILTER_STRIDE0 > 1 || \
    MLO_FILTER_STRIDE1 > 1

#if MLO_READ_UNIT == 1
        uint batch_id   = iDiv(faked_off, (MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT));
        uint faked_off2 = iMod(faked_off, batch_id, (MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT));

        uint out_x_off = iDiv(faked_off2, MLO_OUT_PAD_WIDTH);
        uint out_y_off = iMod(faked_off2, out_x_off, MLO_OUT_PAD_WIDTH);

        uint out_image_off =
            (out_y_off + MLO_OUT_PAD_MIN_Y) * MLO_OUT_WIDTH + (out_x_off + MLO_OUT_PAD_MIN_X);

        uint in_x_off = out_x_off * MLO_FILTER_STRIDE0 + MLO_IN_PAD_MIN_X;
        uint in_y_off = out_y_off * MLO_FILTER_STRIDE1 + MLO_IN_PAD_MIN_Y;

        uint in_image_off = in_y_off * MLO_IN_WIDTH + in_x_off;
#endif
#if 0 // PER_ROW which will be enabled after SGPR offset is enabled.
        uint batch_id   = iDiv( faked_off,  (MLO_OUT_PAD_WIDTH ));
        uint faked_off2 = iMod( faked_off,  batch_id, (MLO_OUT_PAD_WIDTH ));

        uint out_x_off = 0;
        uint out_y_off = faked_off2;

        uint out_image_off = (out_y_off + MLO_OUT_PAD_MIN_Y) * MLO_OUT_WIDTH + (out_x_off +MLO_OUT_PAD_MIN_X);

        uint in_x_off = out_x_off * MLO_FILTER_STRIDE0 + MLO_IN_PAD_MIN_X;
        uint in_y_off = out_y_off * MLO_FILTER_STRIDE1 + MLO_IN_PAD_MIN_Y;

        uint in_image_off  = in_y_off * MLO_IN_WIDTH + in_x_off;

        //uint glb_in_off  = glb_in_off0  + batch_id * MLO_IN_BATCH_STRIDE   + in_image_off ;
        //uint glb_out_off = glb_out_off0 + batch_id * MLO_OUT_BATCH_STRIDE  + out_image_off;

#endif
#else
        uint batch_id      = iDiv(faked_off, (MLO_OUT_CHANNEL_READ_SZ));           // batch
        uint image_off     = iMod(faked_off, batch_id, (MLO_OUT_CHANNEL_READ_SZ)); // pixel offset
        uint in_image_off  = image_off * MLO_READ_UNIT;
        uint out_image_off = image_off * MLO_READ_UNIT;
#endif
        uint glb_in_off = glb_in_off0 + batch_id * MLO_IN_BATCH_STRIDE + in_image_off;

        // Address offset mode is not used in shader compiler
        // Most VGPRs are used up by Address
        for(uint c = 0; c < MLO_N_LCL_IN_MAPS_ONCE; ++c)
        {
            uint bot_off                = glb_in_off + c * MLO_IN_CHANNEL_STRIDE;
            const __global _FLOAT* bot1 = &bot[bot_off];

            for(uint i = 0; i < MLO_READ_UNIT; ++i)
            {
                load_buf_bot[c * MLO_READ_UNIT + i] = bot1[i * MLO_FILTER_STRIDE0];
            }
        }

        uint glb_out_off = glb_out_off0 + batch_id * MLO_OUT_BATCH_STRIDE + out_image_off;
        for(uint k = 0; k < MLO_N_LCL_OUT_MAPS_ONCE; ++k)
        {
            uint top_off = glb_out_off + k * MLO_OUT_CHANNEL_STRIDE;

            const __global _FLOAT* top1 = &top_df[top_off];

            for(uint i = 0; i < MLO_READ_UNIT; ++i)
            {
                load_buf_top[k * MLO_READ_UNIT + i] = top1[i];
            }
        }

        // processing
        // outside loop-i save 1 VGPR than loop-i inside
        for(uint i = 0; i < MLO_READ_UNIT; ++i)
        {
            for(uint k = 0; k < MLO_N_LCL_OUT_MAPS_ONCE; ++k)
            {
                for(uint c = 0; c < MLO_N_LCL_IN_MAPS_ONCE; ++c)
                {
                    // for(uint i = 0; i < MLO_READ_UNIT; ++i)
                    {
                        accum[k * MLO_N_LCL_IN_MAPS_ONCE + c] +=
                            load_buf_bot[c * MLO_READ_UNIT + i] *
                            load_buf_top[k * MLO_READ_UNIT + i];
                    }
                }
            }
        }
    }

#undef LAST_PIXELS
#undef MLO_MAX_LOADS2
#undef MLO_LAST_PIXEL_OFFSET

#define LAST_PIXELS (MLO_OUT_CHANNEL_STRIDE % MLO_READ_UNIT)

// PAD/STRIDE never goes to LAST_PIXELS
#if LAST_PIXELS > 0
#define MLO_MAX_LOADS2 (MLO_BATCH_SZ * LAST_PIXELS)
#define MLO_LAST_PIXEL_OFFSET (MLO_OUT_CHANNEL_STRIDE - LAST_PIXELS)

    for(uint faked_off = (local_Id0 % (MLO_GRP_SZ0 / 4)); faked_off < MLO_MAX_LOADS2;
        faked_off += (MLO_GRP_SZ0 / 4))
    {
#if MLO_FILTER_STRIDE0 > 1 || MLO_FILTER_STRIDE1 > 1 || MLO_FILTER_STRIDE0 > 1 || \
    MLO_FILTER_STRIDE1 > 1
        uint batch_id   = iDiv(faked_off, (MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT));
        uint faked_off2 = iMod(faked_off, batch_id, (MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT));

        uint out_x_off = iDiv(faked_off2, MLO_OUT_PAD_WIDTH);
        uint out_y_off = iMod(faked_off2, x_off, MLO_OUT_PAD_WIDTH) + MLO_OUT_PAD_MIN_X;

        uint out_image_off =
            (out_y_off + MLO_OUT_PAD_MIN_X) * MLO_OUT_WIDTH + out_x_off + MLO_OUT_PAD_MIN_X;

        uint in_x_off = out_x_off * MLO_FILTER_STRIDE0 + MLO_IN_PAD_MIN_X;
        uint in_y_off = out_y_off * MLO_FILTER_STRIDE1 + MLO_IN_PAD_MIN_Y;

        uint in_image_off = in_y_off * MLO_IN_WIDTH + in_x_off;

#else
        uint batch_id      = iDiv(faked_off, (LAST_PIXELS));           // batch
        uint image_off     = iMod(faked_off, batch_id, (LAST_PIXELS)); // pixel offset
        image_off += MLO_LAST_PIXEL_OFFSET;
#endif
        uint glb_in_off = glb_in_off0 + batch_id * MLO_IN_BATCH_STRIDE + image_off * 1;

        // Address offset mode is not used in shader compiler
        // Most VGPRs are used up by Address
        for(uint c = 0; c < MLO_N_LCL_IN_MAPS_ONCE; ++c)
        {
            uint bot_off                = glb_in_off + c * MLO_IN_CHANNEL_STRIDE;
            const __global _FLOAT* bot1 = &bot[bot_off];

            for(uint i = 0; i < 1; ++i)
            {
                load_buf_bot[c * MLO_READ_UNIT + i] = bot1[i];
            }
        }

        uint glb_out_off = glb_out_off0 + batch_id * MLO_OUT_BATCH_STRIDE + image_off * 1;
        for(uint k = 0; k < MLO_N_LCL_OUT_MAPS_ONCE; ++k)
        {
            uint top_off = glb_out_off + k * MLO_OUT_CHANNEL_STRIDE;

            const __global _FLOAT* top1 = &top_df[top_off];

            for(uint i = 0; i < 1; ++i)
            {
                load_buf_top[k * MLO_READ_UNIT + i] = top1[i];
            }
        }

        // processing
        // outside loop-i save 1 VGPR than loop-i inside
        for(uint i = 0; i < 1; ++i)
        {
            for(uint k = 0; k < MLO_N_LCL_OUT_MAPS_ONCE; ++k)
            {
                for(uint c = 0; c < MLO_N_LCL_IN_MAPS_ONCE; ++c)
                {
                    {
                        accum[k * MLO_N_LCL_IN_MAPS_ONCE + c] +=
                            load_buf_bot[c * MLO_READ_UNIT + i] *
                            load_buf_top[k * MLO_READ_UNIT + i];
                    }
                }
            }
        }
    }
#endif

    __private _FLOAT accum_to_store[4];
    accum_to_store[0] = 0;
    accum_to_store[1] = 0;
    accum_to_store[2] = 0;
    accum_to_store[3] = 0;

    // 1 loop reduction 8xC per thread, 32 result per workgroup

    for(uint K = 0; K < MLO_N_LCL_OUT_MAPS_ONCE; K++)
    {
            barrier(CLK_LOCAL_MEM_FENCE);

        for(uint C = 0; C < MLO_N_LCL_IN_MAPS_ONCE; C++)
        {
            sdata[local_Id0 + MLO_GRP_SZ0 * C] = accum[K * MLO_N_LCL_IN_MAPS_ONCE + C];
        }
        barrier(CLK_LOCAL_MEM_FENCE);

        // Reduction every MLO_GRP_SZ0* MLO_GRP_SZ1 to trhead 0 inside 1 wave
        for(uint s = (MLO_GRP_SZ0 >> 2); s >= 4; s = (s >> 2))
        {
            // Every time reduce to 1/4
            // Final: offset 0, 1, 2, 3 has the accumualte value
            if(local_Id0 < s)
            {
                for(uint C = 0; C < MLO_N_LCL_IN_MAPS_ONCE; C++)
                {
                    // reduce to final 4x
                    sdata[local_Id0 + MLO_GRP_SZ0 * C] =
                        sdata[local_Id0 * 4 + 0 + MLO_GRP_SZ0 * C] +
                        sdata[local_Id0 * 4 + 1 + MLO_GRP_SZ0 * C] +
                        sdata[local_Id0 * 4 + 2 + MLO_GRP_SZ0 * C] +
                        sdata[local_Id0 * 4 + 3 + MLO_GRP_SZ0 * C];
                }
            }
            // NO need inside 1 wave: barrier(CLK_LOCAL_MEM_FENCE);
        }

            barrier(CLK_LOCAL_MEM_FENCE);

        // MLO_N_LCL_IN_MAPS store 32 DWORD once
        if((local_Id0 & ~0x7) == (K * (MLO_N_LCL_IN_MAPS_ONCE)))
        {
            accum_to_store[0] = sdata[0 + (local_Id0 & 0x7) * MLO_GRP_SZ0];
            accum_to_store[1] = sdata[1 + (local_Id0 & 0x7) * MLO_GRP_SZ0];
            accum_to_store[2] = sdata[2 + (local_Id0 & 0x7) * MLO_GRP_SZ0];
            accum_to_store[3] = sdata[3 + (local_Id0 & 0x7) * MLO_GRP_SZ0];
        }

        // only 1st wave need to barrier: it will remove all scratch registers
    }

    if(local_Id0 < (MLO_ACCUM_SZ))
    {

        // Store to Memory
        __global _FLOAT* __restrict weights_ptr =
            weights_df +
            (K_OFFSET + (local_Id0 / MLO_N_LCL_IN_MAPS_ONCE)) * MLO_WEI_CHANNEL_STRIDE + C_OFFSET +
            (local_Id0 % MLO_N_LCL_IN_MAPS_ONCE);

        {
            __global _FLOAT* weights_ptr1 = weights_ptr;
            for(uint k = 0; k < 2; k++)
                for(uint c = 0; c < 2; c++)
                {
                    __global _FLOAT* weights_ptr2 =
                        weights_ptr1 + k * MLO_N_LCL_IN_MAPS_ONCE * MLO_WEI_CHANNEL_STRIDE +
                        c * MLO_N_LCL_IN_MAPS_ONCE;
                    *weights_ptr2 = accum_to_store[k * 2 + c];
                }
        }
    }
}

#if 0 // nef ML_OPEN_RUNNING
// W 7 x H 7 x C 2048 x K 2048
//#define MLO_GRP_SZ
#define MLO_GRP_SZ0 64
#define MLO_GRP_SZ1 1
#define MLO_GRP_SZ2 1
#define MLO_FILTER_SIZE0 1
#define MLO_FILTER_SIZE1 1
#define MLO_FILTER_PAD0 3
#define MLO_FILTER_PAD1 3
#define MLO_FILTER_STRIDE0 2
#define MLO_FILTER_STRIDE1 2
#define STRIDE_W 1
#define STRIDE_H 1
#define MLO_N_OUTPUTS 2048
#define MLO_N_INPUTS 1024
#define MLO_BATCH_SZ 16
//MLO_N_BATCH_LOOPS
#define MLO_IN_WIDTH 7
#define MLO_IN_HEIGHT 7
#define MLO_OUT_WIDTH 7
#define MLO_OUT_HEIGHT 7
#endif

#if 0 // nef ML_OPEN_RUNNING
// W 7 x H 7 x C 2048 x K 2048
//#define MLO_GRP_SZ
#define MLO_GRP_SZ0 256
#define MLO_GRP_SZ1 1
#define MLO_GRP_SZ2 1
#define MLO_FILTER_SIZE0 1
#define MLO_FILTER_SIZE1 1
#define MLO_FILTER_PAD0 0
#define MLO_FILTER_PAD1 0
#define MLO_FILTER_STRIDE0 2
#define MLO_FILTER_STRIDE1 2
#define STRIDE_W 1
#define STRIDE_H 1
#define MLO_N_OUTPUTS 2048
#define MLO_N_INPUTS 1024
#define MLO_BATCH_SZ 16
//MLO_N_BATCH_LOOPS
#define MLO_IN_WIDTH 14
#define MLO_IN_HEIGHT 14
#define MLO_OUT_WIDTH 7
#define MLO_OUT_HEIGHT 7
#endif

#if 0 // nef ML_OPEN_RUNNING
// W 14 x H 14 x C 1024 x K 512
//#define MLO_GRP_SZ
#define MLO_GRP_SZ0 256
#define MLO_GRP_SZ1 1
#define MLO_GRP_SZ2 1
#define MLO_FILTER_SIZE0 1
#define MLO_FILTER_SIZE1 1
#define MLO_FILTER_PAD0 0
#define MLO_FILTER_PAD1 0
#define MLO_FILTER_STRIDE0 2
#define MLO_FILTER_STRIDE1 2
#define STRIDE_W 1
#define STRIDE_H 1
#define MLO_N_OUTPUTS 512
#define MLO_N_INPUTS 1024
#define MLO_BATCH_SZ 16
//MLO_N_BATCH_LOOPS
#define MLO_IN_WIDTH 14
#define MLO_IN_HEIGHT 14
#define MLO_OUT_WIDTH 7
#define MLO_OUT_HEIGHT 7

#define MLO_LDS_REDUCTOIN 1
#define MLO_GLOBAL_ATOMIC 0
#define MLO_N_LDS_REDUCTION_ONCE 8
#define MLO_N_LDS_SIZE_PER_THREAD 8

#endif

#if 0 // nef ML_OPEN_RUNNING
// W 28 x H 28 x C 192 x K 64 X N 16
//#define MLO_GRP_SZ
#define MLO_GRP_SZ0 64
#define MLO_GRP_SZ1 1
#define MLO_GRP_SZ2 1
#define MLO_FILTER_SIZE0 1
#define MLO_FILTER_SIZE1 1
#define MLO_FILTER_PAD0 0
#define MLO_FILTER_PAD1 0
#define MLO_FILTER_STRIDE0 1
#define MLO_FILTER_STRIDE1 1
#define STRIDE_W 1
#define STRIDE_H 1
#define MLO_N_OUTPUTS 64
#define MLO_N_INPUTS 192
#define MLO_BATCH_SZ 16
#define MLO_IN_WIDTH 28
#define MLO_IN_HEIGHT 28
#define MLO_OUT_WIDTH 28
#define MLO_OUT_HEIGHT 28
#define MLO_LDS_REDUCTOIN 1
#define MLO_GLOBAL_ATOMIC 0

#define MLO_N_LOAD_DWORDS_PER_MAP_ONCE 64
#define MLO_N_LCL_IN_MAPS 8
#define MLO_N_LCL_OUT_MAPS 8

#define MLO_N_LCL_IN_MAPS_ONCE 8
#define MLO_N_LCL_OUT_MAPS_ONCE 8

#define MLO_READ_UNIT 2

//READ_UNIT == 1 for STRIDE and PAD mode

#define MLO_OUT_BATCH_STRIDE (MLO_OUT_WIDTH * MLO_OUT_HEIGHT * MLO_N_OUTPUTS)
#define MLO_OUT_CHANNEL_STRIDE (MLO_OUT_WIDTH * MLO_OUT_WIDTH)

#define MLO_IN_BATCH_STRIDE (MLO_IN_WIDTH * MLO_IN_HEIGHT * MLO_N_INPUTS)
#define MLO_IN_CHANNEL_STRIDE (MLO_IN_WIDTH * MLO_IN_HEIGHT)
#define MLO_WEI_BATCH_STRIDE (MLO_N_INPUTS * MLO_N_OUTPUTS)
#define MLO_WEI_CHANNEL_STRIDE (1 * 1 * MLO_N_INPUTS)
#define MLO_MAX_LOADS ((MLO_OUT_CHANNEL_STRIDE / MLO_READ_UNIT) * MLO_BATCH_SZ)

#define MLO_ACCUM_SZ (MLO_N_LCL_IN_MAPS * MLO_N_LCL_OUT_MAPS)
#define MLO_OUT_READ_SZ (N_LCL_OUT_MAPS * MLO_READ_UNIT)
#define MLO_IN_READ_SZ (MLO_N_LCL_IN_MAPS * MLO_READ_UNIT)

#define MLO_OUT_CHANNEL_READ_SZ (MLO_OUT_CHANNEL_STRIDE / MLO_READ_UNIT)

#define MLO_N_IN_TILE_BLOCK 4
#endif

#if 0
#define MLO_READ_UNIT 4

#define MLO_OUT_BATCH_STRIDE (MLO_OUT_WIDTH * MLO_OUT_HEIGHT * MLO_N_OUTPUTS)
#define MLO_OUT_CHANNEL_STRIDE (MLO_OUT_WIDTH * MLO_OUT_WIDTH)
#define MLO_OUT_STRIDE (1)
#define MLO_IN_BATCH_STRIDE (MLO_IN_WIDTH * MLO_IN_HEIGHT * MLO_N_INPUTS)
#define MLO_IN_CHANNEL_STRIDE (MLO_IN_WIDTH * MLO_IN_HEIGHT)
#define MLO_IN_STRIDE (2)
#define MLO_WEI_BATCH_STRIDE (MLO_N_INPUTS * MLO_N_OUTPUTS)
#define MLO_WEI_CHANNEL_STRIDE (1 * 1 * MLO_N_INPUTS)

//#define MLO_N_LCL_IN_MAPS       8
//#define MLO_N_LCL_OUT_MAPS      8
#define MLO_CACHELINE_DWORD_SZ 64

#endif

#if 0
#if(MLO_FILTER_PAD0 > 0 || MLO_FILTER_PAD1 > 0)
#define MLO_IN_PAD_MIN_X0 (MLO_FILTER_STRIDE0 - (MLO_FILTER_PAD0 % MLO_FILTER_STRIDE0))
#define MLO_IN_PAD_MIN_Y0 (MLO_FILTER_STRIDE1 - (MLO_FILTER_PAD1 % MLO_FILTER_STRIDE1))

#define MLO_IN_PAD_MIN_X (MLO_IN_PAD_MIN_X0 % MLO_FILTER_STRIDE0)
#define MLO_IN_PAD_MIN_Y (MLO_IN_PAD_MIN_Y0 % MLO_FILTER_STRIDE1)

#define MLO_OUT_PAD_MIN_X ((MLO_FILTER_PAD0 + MLO_FILTER_STRIDE0 - 1) / MLO_FILTER_STRIDE0)
#define MLO_OUT_PAD_MIN_Y ((MLO_FILTER_PAD1 + MLO_FILTER_STRIDE1 - 1) / MLO_FILTER_STRIDE1)

#define MLO_OUT_PAD_WIDTH \
    (((MLO_IN_WIDTH - MLO_IN_PAD_MIN_X + MLO_FILTER_STRIDE0 - 1) / MLO_FILTER_STRIDE0))
#define MLO_OUT_PAD_HEIGHT \
    (((MLO_IN_HEIGHT - MLO_IN_PAD_MIN_Y + MLO_FILTER_STRIDE0 - 1) / MLO_FILTER_STRIDE1))

#else
#define MLO_IN_PAD_MIN_X 0
#define MLO_IN_PAD_MIN_Y 0

#define MLO_OUT_PAD_MIN_X 0
#define MLO_OUT_PAD_MIN_Y 0

#define MLO_OUT_PAD_WIDTH MLO_OUT_WIDTH
#define MLO_OUT_PAD_HEIGHT MLO_OUT_HEIGHT
#endif
#endif

#if 0

#define MLO_N_BATCH_PER_WAVE ((MLO_BATCH_SZ + (MLO_GRP_SZ0 / 64) - 1) / (MLO_GRP_SZ0 / 64))
#define MLO_N_LOOPS_PER_MAP                                                          \
    ((MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT + MLO_N_LOAD_DWORDS_PER_MAP_ONCE - 1) / \
     (MLO_N_LOAD_DWORDS_PER_MAP_ONCE))
#define MLO_N_DWORDS_LAST_LOAD_PER_MAP \
    ((MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT) % (MLO_N_LOAD_DWORDS_PER_MAP_ONCE))

#define MLO_IN_NON_ALIGN (MLO_N_INPUTS & 0x7)
#define MLO_OUT_NON_ALIGN (MLO_N_OUTPUTS & 0x7)
#define MLO_IN_LAST_GROUP (MLO_N_INPUTS & (~0x7))
#define MLO_OUT_LAST_GROUP (MLO_N_OUTPUTS & (~0x7))
#define MLO_MAX_LOAD_DWORD ((MLO_OUT_CHANNEL_STRIDE / MLO_READ_UNIT) * MLO_BATCH_SZ)
#endif

// FLAT_LOAD_DWORDx4 is max
// MIN(MLO_OUT_PAD_WIDTH , 8)

#if 0

//64x64 only works MLO_OUT_PAD_WIDTH<=8
//following are trick to speed-up shader compiler for unused MIOpenCvBwdWrW_64x64
#if MLO_OUT_PAD_WIDTH > 8
#define MLO_N_LOAD_DWORD_ONCE_PER_THREAD 1
#else
#define MLO_N_DWORD_PER_LOOP (MLO_OUT_PAD_WIDTH)
#endif

#endif

#define _FLOAT float
#define _FLOAT2 float2
#define _FLOAT4 float4
#define _FLOAT8 float8

#ifndef FLT_MAX
#define FLT_MAX 3.402823466e+38F /* max value */
#endif

#define UNUSED __attribute__((__unused__))
#define DBG_OUT_OF_RNGE 0

__attribute__((always_inline)) uint iDiv(uint v, uint d)
{
    uint r = (uint)((float)v * (1.f / (float)d) + 0.00001f);
    return (r);
}

__attribute__((always_inline)) uint iMod(uint v, uint u, uint d)
{
    uint r = v - mul24(u, d);
    return (r);
}

// top_df        ==> out        in [Batch][output][out_H][out_W]
// bot           ==> gard_input in [Batch][inputs][IN_H][IN_W]
// weights_df    ==> weights    in [output][input][filter][filter]

__attribute__((reqd_work_group_size(MLO_GRP_SZ0, MLO_GRP_SZ1, MLO_GRP_SZ2))) __kernel void
MIOpenCvBwdWrW_8x8map(const __global _FLOAT* __restrict top_df,
                      const __global _FLOAT* __restrict bot,
                      __global _FLOAT* __restrict weights_df,
                      UNUSED _FLOAT padding_val)
{
    __local _FLOAT sdata[MLO_GRP_SZ0 * 8];
    // 8x8 MUL_ADD per thread
    // Every thread 1DWORDs per MAP * 8 Maps
    // every 64 threads Load continous 64 DWORDs per MAP * 8 Maps
    // LDS reduction of every 64 threads for 8x8 results
    // NO LDS data exchange

    // Global_group_Id0:  [TILE_BLOCK]
    // Global_Id0:  [C/8] * 64 or 256
    // Global_group_Id1:  [K/8]
    // Global_group_Id2:  [C/8/ (MLO_N_IN_TILE_BLOCK= 8 or 4) ]

    // uint group_id0 = get_group_id(0);
    // uint group_id1 = get_group_id(1);

    uint local_Id0 = get_local_id(0);

// traverse small batch size to have better performance
#if MLO_IN_BATCH_STRIDE < MLO_OUT_BATCH_STRIDE
    uint C_OFFSET = get_group_id(0) * MLO_N_LCL_IN_MAPS;
    uint K_OFFSET = get_group_id(1) * MLO_N_LCL_OUT_MAPS;

#else
    uint K_OFFSET          = get_group_id(0) * MLO_N_LCL_OUT_MAPS;
    uint C_OFFSET          = get_group_id(1) * MLO_N_LCL_IN_MAPS;

#endif

    uint glb_out_off0 = K_OFFSET * MLO_OUT_CHANNEL_STRIDE;
    uint glb_in_off0  = C_OFFSET * MLO_IN_CHANNEL_STRIDE;

    // NO preload
    __private _FLOAT load_buf_top[MLO_N_LCL_OUT_MAPS * MLO_READ_UNIT];
    __private _FLOAT load_buf_bot[MLO_N_LCL_IN_MAPS * MLO_READ_UNIT];

    __private _FLOAT accum[MLO_ACCUM_SZ];

    // CNHW will be continous address to utlize X4 load;
    // NCHW will be hard mode till now

    for(uint i = 0; i < MLO_ACCUM_SZ; i++)
    {
        accum[i] = 0;
    }

    for(uint i = 0; i < MLO_N_LCL_IN_MAPS; i++)
    {
        sdata[local_Id0 + i * MLO_GRP_SZ0] = 0;
    }

    for(uint faked_off = local_Id0; faked_off < MLO_MAX_LOADS; faked_off += MLO_GRP_SZ0)
    {
#if MLO_FILTER_STRIDE0 > 1 || MLO_FILTER_STRIDE1 > 1 || MLO_FILTER_STRIDE0 > 1 || \
    MLO_FILTER_STRIDE1 > 1
        // MLO_READ_UNIT==1

        uint batch_id   = iDiv(faked_off, (MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT));
        uint faked_off2 = iMod(faked_off, batch_id, (MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT));

        uint out_x_off = iDiv(faked_off2, MLO_OUT_PAD_WIDTH);
        uint out_y_off = iMod(faked_off2, out_x_off, MLO_OUT_PAD_WIDTH);

        uint out_image_off =
            (out_y_off + MLO_OUT_PAD_MIN_Y) * MLO_OUT_WIDTH + (out_x_off + MLO_OUT_PAD_MIN_X);

        uint in_x_off = out_x_off * MLO_FILTER_STRIDE0 + MLO_IN_PAD_MIN_X;
        uint in_y_off = out_y_off * MLO_FILTER_STRIDE1 + MLO_IN_PAD_MIN_Y;

        uint in_image_off = in_y_off * MLO_IN_WIDTH + in_x_off;

#else
        uint batch_id      = iDiv(faked_off, (MLO_OUT_CHANNEL_READ_SZ));           // batch
        uint image_off     = iMod(faked_off, batch_id, (MLO_OUT_CHANNEL_READ_SZ)); // pixel offset
        uint in_image_off  = image_off * MLO_READ_UNIT;
        uint out_image_off = image_off * MLO_READ_UNIT;
#endif
        uint glb_in_off = glb_in_off0 + batch_id * MLO_IN_BATCH_STRIDE + in_image_off;

        // Address offset mode is not used in shader compiler
        // Most VGPRs are used up by Address
        for(uint c = 0; c < MLO_N_LCL_IN_MAPS; ++c)
        {
            uint bot_off                = glb_in_off + c * MLO_IN_CHANNEL_STRIDE;
            const __global _FLOAT* bot1 = &bot[bot_off];

            for(uint i = 0; i < MLO_READ_UNIT; ++i)
            {
                load_buf_bot[c * MLO_READ_UNIT + i] = bot1[i];
            }
        }

        uint glb_out_off = glb_out_off0 + batch_id * MLO_OUT_BATCH_STRIDE + out_image_off;
        for(uint k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
        {
            uint top_off = glb_out_off + k * MLO_OUT_CHANNEL_STRIDE;

            const __global _FLOAT* top1 = &top_df[top_off];

            for(uint i = 0; i < MLO_READ_UNIT; ++i)
            {
                load_buf_top[k * MLO_READ_UNIT + i] = top1[i];
            }
        }

        // processing
        // outside loop-i save 1 VGPR than loop-i inside
        for(uint i = 0; i < MLO_READ_UNIT; ++i)
        {
            for(uint k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
            {
                for(uint c = 0; c < MLO_N_LCL_IN_MAPS; ++c)
                {
                    {
                        accum[k * MLO_N_LCL_IN_MAPS + c] += load_buf_bot[c * MLO_READ_UNIT + i] *
                                                            load_buf_top[k * MLO_READ_UNIT + i];
                    }
                }
            }
        }
    }

#define LAST_PIXELS (MLO_OUT_CHANNEL_STRIDE % MLO_READ_UNIT)

// PAD/STRIDE never goes to following since MLO_READ_UNIT == 1
#if LAST_PIXELS > 0
#define MLO_MAX_LOADS2 (MLO_BATCH_SZ * LAST_PIXELS)
#define MLO_LAST_PIXEL_OFFSET (MLO_OUT_CHANNEL_STRIDE - LAST_PIXELS)

    for(uint faked_off = local_Id0; faked_off < MLO_MAX_LOADS2; faked_off += MLO_GRP_SZ0)
    {
#if MLO_FILTER_STRIDE0 > 1 || MLO_FILTER_STRIDE1 > 1 || MLO_FILTER_STRIDE0 > 1 || \
    MLO_FILTER_STRIDE1 > 1
        uint batch_id   = iDiv(faked_off, (MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT));
        uint faked_off2 = iMod(faked_off, batch_id, (MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT));

        uint out_x_off = iDiv(faked_off2, MLO_OUT_PAD_WIDTH);
        uint out_y_off = iMod(faked_off2, out_x_off, MLO_OUT_PAD_WIDTH);

        uint out_image_off =
            (out_y_off + MLO_OUT_PAD_MIN_Y) * MLO_OUT_WIDTH + (out_x_off + MLO_OUT_PAD_MIN_X);

        uint in_x_off = out_x_off * MLO_FILTER_STRIDE0 + MLO_IN_PAD_MIN_X;
        uint in_y_off = out_y_off * MLO_FILTER_STRIDE1 + MLO_IN_PAD_MIN_Y;

        uint in_image_off = in_y_off * MLO_IN_WIDTH + in_x_off;

#else
        uint batch_id = iDiv(faked_off, (LAST_PIXELS)); // batch
        uint image_off =
            iMod(faked_off, batch_id, (LAST_PIXELS)) + MLO_LAST_PIXEL_OFFSET; // pixel offset
        uint in_image_off  = image_off * 1;
        uint out_image_off = image_off * 1;
#endif
        uint glb_in_off = glb_in_off0 + batch_id * MLO_IN_BATCH_STRIDE + in_image_off;

        // Address offset mode is not used in shader compiler
        // Most VGPRs are used up by Address
        for(uint c = 0; c < MLO_N_LCL_IN_MAPS; ++c)
        {
            uint bot_off                = glb_in_off + c * MLO_IN_CHANNEL_STRIDE;
            const __global _FLOAT* bot1 = &bot[bot_off];

            for(uint i = 0; i < 1; ++i)
            {
                load_buf_bot[c * MLO_READ_UNIT + i] = bot1[i];
            }
        }

        uint glb_out_off = glb_out_off0 + batch_id * MLO_OUT_BATCH_STRIDE + out_image_off;
        for(uint k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
        {
            uint top_off = glb_out_off + k * MLO_OUT_CHANNEL_STRIDE;

            const __global _FLOAT* top1 = &top_df[top_off];

            for(uint i = 0; i < 1; ++i)
            {
                load_buf_top[k * MLO_READ_UNIT + i] = top1[i];
            }
        }

        // processing
        // outside loop-i save 1 VGPR than loop-i inside
        for(uint i = 0; i < 1; ++i)
        {
            for(uint k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
            {
                for(uint c = 0; c < MLO_N_LCL_IN_MAPS; ++c)
                {
                    {
                        accum[k * MLO_N_LCL_IN_MAPS + c] += load_buf_bot[c * MLO_READ_UNIT + i] *
                                                            load_buf_top[k * MLO_READ_UNIT + i];
                    }
                }
            }
        }
    }
#endif

    __private _FLOAT accum_to_store = 0;

    for(uint K = 0; K < MLO_N_LCL_OUT_MAPS; K++)
    {
         barrier(CLK_LOCAL_MEM_FENCE);

        for(uint C = 0; C < MLO_N_LCL_IN_MAPS; C++)
        {
            sdata[local_Id0 + MLO_GRP_SZ0 * C] = accum[K * MLO_N_LCL_IN_MAPS + C];
        }

        barrier(CLK_LOCAL_MEM_FENCE);

        // Reduction every MLO_GRP_SZ0* MLO_GRP_SZ1 to trhead 0
        for(uint s = ((MLO_GRP_SZ0) >> 2); s > 0; s = (s >> 2))
        {
            if(local_Id0 < s)
            {
                for(uint C = 0; C < MLO_N_LCL_IN_MAPS; C++)
                {
                    sdata[local_Id0 + MLO_GRP_SZ0 * C] +=
                        sdata[MLO_GRP_SZ0 * C + local_Id0 + s] +
                        sdata[MLO_GRP_SZ0 * C + local_Id0 + 2 * s] +
                        sdata[MLO_GRP_SZ0 * C + local_Id0 + 3 * s];
                }
            }
            // NO need inside 1 wave: barrier(CLK_LOCAL_MEM_FENCE);
        }

        barrier(CLK_LOCAL_MEM_FENCE);

        // MLO_N_LCL_IN_MAPS store
        if((local_Id0 & ~0x7) == (K * MLO_N_LCL_IN_MAPS))
        {
            accum_to_store = sdata[0 + (local_Id0 & 0x7) * MLO_GRP_SZ0];
        }

        // only 1st wave need to barrier: it will remove all scratch registers

    }

    if(local_Id0 < (MLO_ACCUM_SZ))
    {

        // Store to Memory
        __global _FLOAT* __restrict weights_ptr =
            weights_df + (K_OFFSET + (local_Id0 / MLO_N_LCL_IN_MAPS)) * MLO_WEI_CHANNEL_STRIDE +
            C_OFFSET + (local_Id0 % MLO_N_LCL_IN_MAPS);

        {
            __global _FLOAT* weights_ptr2 = weights_ptr;
            *weights_ptr2                 = accum_to_store;
        }
    }
}

__attribute__((reqd_work_group_size(MLO_GRP_SZ0, MLO_GRP_SZ1, MLO_GRP_SZ2))) __kernel void
MIOpenCvBwdWrW_16x16map(const __global _FLOAT* __restrict top_df,
                        const __global _FLOAT* __restrict bot,
                        __global _FLOAT* __restrict weights_df,
                        UNUSED _FLOAT padding_val)
{
    __local _FLOAT sdata[MLO_GRP_SZ0 * 8];
    // 64 threds split into 4 grpoups: every 16 threads accumulate 8x8
    //
    // 256 threads split inot 4 groups : every 64 threads accumulate 8x8
    //

    // Global_group_Id0:  [TILE_BLOCK]
    // Global_Id0:  [C/16] * 64 or 256
    // Global_group_Id1:  [K/16]
    // Global_group_Id2:  [C/16/ (MLO_N_IN_TILE_BLOCK= 8 or 4) ]

    // uint group_id0 = get_group_id(0);
    // uint group_id1 = get_group_id(1);

    uint local_Id0 = get_local_id(0);

// traverse small batch size to have better performance
#if MLO_IN_BATCH_STRIDE < MLO_OUT_BATCH_STRIDE
    uint C_OFFSET = get_group_id(0) * MLO_N_LCL_IN_MAPS;
    uint K_OFFSET = get_group_id(1) * MLO_N_LCL_OUT_MAPS;

#else
    uint K_OFFSET          = get_group_id(0) * MLO_N_LCL_OUT_MAPS;
    uint C_OFFSET          = get_group_id(1) * MLO_N_LCL_IN_MAPS;

#endif

    // Split into 4 groups for C[0,1,0,1], K [0,0,1,1]
    uint k_offset2 = ((local_Id0 / (MLO_GRP_SZ0 / 4)) / 2) * MLO_N_LCL_OUT_MAPS_ONCE;
    uint c_offset2 = ((local_Id0 / (MLO_GRP_SZ0 / 4)) % 2) * MLO_N_LCL_IN_MAPS_ONCE;

    uint glb_out_off0 = (K_OFFSET + k_offset2) * MLO_OUT_CHANNEL_STRIDE;
    uint glb_in_off0  = (C_OFFSET + c_offset2) * MLO_IN_CHANNEL_STRIDE;

    // NO preload
    __private _FLOAT load_buf_top[MLO_N_LCL_OUT_MAPS_ONCE * MLO_READ_UNIT];
    __private _FLOAT load_buf_bot[MLO_N_LCL_IN_MAPS_ONCE * MLO_READ_UNIT];

    __private _FLOAT accum[MLO_ACCUM_SZ];

    // CNHW will be continous address to utlize X4 load;
    // NCHW will be hard mode till now

    for(uint i = 0; i < MLO_ACCUM_SZ; i++)
    {
        accum[i] = 0;
    }

    for(uint i = 0; i < MLO_N_LCL_OUT_MAPS_ONCE; i++)
    {
        sdata[local_Id0 + i * MLO_GRP_SZ0] = 0;
    }

    for(uint faked_off = (local_Id0 % (MLO_GRP_SZ0 / 4)); faked_off < MLO_MAX_LOADS;
        faked_off += (MLO_GRP_SZ0 / 4))
    {
#if MLO_FILTER_STRIDE0 > 1 || MLO_FILTER_STRIDE1 > 1 || MLO_FILTER_STRIDE0 > 1 || \
    MLO_FILTER_STRIDE1 > 1

#if MLO_READ_UNIT == 1
        uint batch_id   = iDiv(faked_off, (MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT));
        uint faked_off2 = iMod(faked_off, batch_id, (MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT));

        uint out_x_off = iDiv(faked_off2, MLO_OUT_PAD_WIDTH);
        uint out_y_off = iMod(faked_off2, out_x_off, MLO_OUT_PAD_WIDTH);

        uint out_image_off =
            (out_y_off + MLO_OUT_PAD_MIN_Y) * MLO_OUT_WIDTH + (out_x_off + MLO_OUT_PAD_MIN_X);

        uint in_x_off = out_x_off * MLO_FILTER_STRIDE0 + MLO_IN_PAD_MIN_X;
        uint in_y_off = out_y_off * MLO_FILTER_STRIDE1 + MLO_IN_PAD_MIN_Y;

        uint in_image_off = in_y_off * MLO_IN_WIDTH + in_x_off;
#endif
#if 0 // PER_ROW which will be enabled after SGPR offset is enabled.
        uint batch_id   = iDiv( faked_off,  (MLO_OUT_PAD_WIDTH ));
        uint faked_off2 = iMod( faked_off,  batch_id, (MLO_OUT_PAD_WIDTH ));

        uint out_x_off = 0;
        uint out_y_off = faked_off2;

        uint out_image_off = (out_y_off + MLO_OUT_PAD_MIN_Y) * MLO_OUT_WIDTH + (out_x_off +MLO_OUT_PAD_MIN_X);

        uint in_x_off = out_x_off * MLO_FILTER_STRIDE0 + MLO_IN_PAD_MIN_X;
        uint in_y_off = out_y_off * MLO_FILTER_STRIDE1 + MLO_IN_PAD_MIN_Y;

        uint in_image_off  = in_y_off * MLO_IN_WIDTH + in_x_off;

        //uint glb_in_off  = glb_in_off0  + batch_id * MLO_IN_BATCH_STRIDE   + in_image_off ;
        //uint glb_out_off = glb_out_off0 + batch_id * MLO_OUT_BATCH_STRIDE  + out_image_off;

#endif
#else
        uint batch_id      = iDiv(faked_off, (MLO_OUT_CHANNEL_READ_SZ));           // batch
        uint image_off     = iMod(faked_off, batch_id, (MLO_OUT_CHANNEL_READ_SZ)); // pixel offset
        uint in_image_off  = image_off * MLO_READ_UNIT;
        uint out_image_off = image_off * MLO_READ_UNIT;
#endif
        uint glb_in_off = glb_in_off0 + batch_id * MLO_IN_BATCH_STRIDE + in_image_off;

        // Address offset mode is not used in shader compiler
        // Most VGPRs are used up by Address
        for(uint c = 0; c < MLO_N_LCL_IN_MAPS_ONCE; ++c)
        {
            uint bot_off                = glb_in_off + c * MLO_IN_CHANNEL_STRIDE;
            const __global _FLOAT* bot1 = &bot[bot_off];

            for(uint i = 0; i < MLO_READ_UNIT; ++i)
            {
                load_buf_bot[c * MLO_READ_UNIT + i] = bot1[i * MLO_FILTER_STRIDE0];
            }
        }

        uint glb_out_off = glb_out_off0 + batch_id * MLO_OUT_BATCH_STRIDE + out_image_off;
        for(uint k = 0; k < MLO_N_LCL_OUT_MAPS_ONCE; ++k)
        {
            uint top_off = glb_out_off + k * MLO_OUT_CHANNEL_STRIDE;

            const __global _FLOAT* top1 = &top_df[top_off];

            for(uint i = 0; i < MLO_READ_UNIT; ++i)
            {
                load_buf_top[k * MLO_READ_UNIT + i] = top1[i];
            }
        }

        // processing
        // outside loop-i save 1 VGPR than loop-i inside
        for(uint i = 0; i < MLO_READ_UNIT; ++i)
        {
            for(uint k = 0; k < MLO_N_LCL_OUT_MAPS_ONCE; ++k)
            {
                for(uint c = 0; c < MLO_N_LCL_IN_MAPS_ONCE; ++c)
                {
                    // for(uint i = 0; i < MLO_READ_UNIT; ++i)
                    {
                        accum[k * MLO_N_LCL_IN_MAPS_ONCE + c] +=
                            load_buf_bot[c * MLO_READ_UNIT + i] *
                            load_buf_top[k * MLO_READ_UNIT + i];
                    }
                }
            }
        }
    }

#undef LAST_PIXELS
#undef MLO_MAX_LOADS2
#undef MLO_LAST_PIXEL_OFFSET

#define LAST_PIXELS (MLO_OUT_CHANNEL_STRIDE % MLO_READ_UNIT)

// PAD/STRIDE never goes to LAST_PIXELS
#if LAST_PIXELS > 0
#define MLO_MAX_LOADS2 (MLO_BATCH_SZ * LAST_PIXELS)
#define MLO_LAST_PIXEL_OFFSET (MLO_OUT_CHANNEL_STRIDE - LAST_PIXELS)

    for(uint faked_off = (local_Id0 % (MLO_GRP_SZ0 / 4)); faked_off < MLO_MAX_LOADS2;
        faked_off += (MLO_GRP_SZ0 / 4))
    {
#if MLO_FILTER_STRIDE0 > 1 || MLO_FILTER_STRIDE1 > 1 || MLO_FILTER_STRIDE0 > 1 || \
    MLO_FILTER_STRIDE1 > 1
        uint batch_id   = iDiv(faked_off, (MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT));
        uint faked_off2 = iMod(faked_off, batch_id, (MLO_OUT_PAD_WIDTH * MLO_OUT_PAD_HEIGHT));

        uint out_x_off = iDiv(faked_off2, MLO_OUT_PAD_WIDTH);
        uint out_y_off = iMod(faked_off2, x_off, MLO_OUT_PAD_WIDTH) + MLO_OUT_PAD_MIN_X;

        uint out_image_off =
            (out_y_off + MLO_OUT_PAD_MIN_X) * MLO_OUT_WIDTH + out_x_off + MLO_OUT_PAD_MIN_X;

        uint in_x_off = out_x_off * MLO_FILTER_STRIDE0 + MLO_IN_PAD_MIN_X;
        uint in_y_off = out_y_off * MLO_FILTER_STRIDE1 + MLO_IN_PAD_MIN_Y;

        uint in_image_off = in_y_off * MLO_IN_WIDTH + in_x_off;

#else
        uint batch_id      = iDiv(faked_off, (LAST_PIXELS));           // batch
        uint image_off     = iMod(faked_off, batch_id, (LAST_PIXELS)); // pixel offset
        image_off += MLO_LAST_PIXEL_OFFSET;
#endif
        uint glb_in_off = glb_in_off0 + batch_id * MLO_IN_BATCH_STRIDE + image_off * 1;

        // Address offset mode is not used in shader compiler
        // Most VGPRs are used up by Address
        for(uint c = 0; c < MLO_N_LCL_IN_MAPS_ONCE; ++c)
        {
            uint bot_off                = glb_in_off + c * MLO_IN_CHANNEL_STRIDE;
            const __global _FLOAT* bot1 = &bot[bot_off];

            for(uint i = 0; i < 1; ++i)
            {
                load_buf_bot[c * MLO_READ_UNIT + i] = bot1[i];
            }
        }

        uint glb_out_off = glb_out_off0 + batch_id * MLO_OUT_BATCH_STRIDE + image_off * 1;
        for(uint k = 0; k < MLO_N_LCL_OUT_MAPS_ONCE; ++k)
        {
            uint top_off = glb_out_off + k * MLO_OUT_CHANNEL_STRIDE;

            const __global _FLOAT* top1 = &top_df[top_off];

            for(uint i = 0; i < 1; ++i)
            {
                load_buf_top[k * MLO_READ_UNIT + i] = top1[i];
            }
        }

        // processing
        // outside loop-i save 1 VGPR than loop-i inside
        for(uint i = 0; i < 1; ++i)
        {
            for(uint k = 0; k < MLO_N_LCL_OUT_MAPS_ONCE; ++k)
            {
                for(uint c = 0; c < MLO_N_LCL_IN_MAPS_ONCE; ++c)
                {
                    {
                        accum[k * MLO_N_LCL_IN_MAPS_ONCE + c] +=
                            load_buf_bot[c * MLO_READ_UNIT + i] *
                            load_buf_top[k * MLO_READ_UNIT + i];
                    }
                }
            }
        }
    }
#endif

    __private _FLOAT accum_to_store[4];
    accum_to_store[0] = 0;
    accum_to_store[1] = 0;
    accum_to_store[2] = 0;
    accum_to_store[3] = 0;

    // 1 loop reduction 8xC per thread, 32 result per workgroup

    for(uint K = 0; K < MLO_N_LCL_OUT_MAPS_ONCE; K++)
    {
        for(uint C = 0; C < MLO_N_LCL_IN_MAPS_ONCE; C++)
        {
            sdata[local_Id0 + MLO_GRP_SZ0 * C] = accum[K * MLO_N_LCL_IN_MAPS_ONCE + C];
        }
        barrier(CLK_LOCAL_MEM_FENCE);

        // Reduction every MLO_GRP_SZ0* MLO_GRP_SZ1 to trhead 0 inside 1 wave
        for(uint s = (MLO_GRP_SZ0 >> 2); s >= 4; s = (s >> 2))
        {
            // Every time reduce to 1/4
            // Final: offset 0, 1, 2, 3 has the accumualte value
            if(local_Id0 < s)
            {
                for(uint C = 0; C < MLO_N_LCL_IN_MAPS_ONCE; C++)
                {
                    // reduce to final 4x
                    sdata[local_Id0 + MLO_GRP_SZ0 * C] =
                        sdata[local_Id0 * 4 + 0 + MLO_GRP_SZ0 * C] +
                        sdata[local_Id0 * 4 + 1 + MLO_GRP_SZ0 * C] +
                        sdata[local_Id0 * 4 + 2 + MLO_GRP_SZ0 * C] +
                        sdata[local_Id0 * 4 + 3 + MLO_GRP_SZ0 * C];
                }
            }
            // NO need inside 1 wave: barrier(CLK_LOCAL_MEM_FENCE);
        }

        // MLO_N_LCL_IN_MAPS store 32 DWORD once
        if((local_Id0 & ~0x7) == (K * (MLO_N_LCL_IN_MAPS_ONCE)))
        {
            accum_to_store[0] = sdata[0 + (local_Id0 & 0x7) * MLO_GRP_SZ0];
            accum_to_store[1] = sdata[1 + (local_Id0 & 0x7) * MLO_GRP_SZ0];
            accum_to_store[2] = sdata[2 + (local_Id0 & 0x7) * MLO_GRP_SZ0];
            accum_to_store[3] = sdata[3 + (local_Id0 & 0x7) * MLO_GRP_SZ0];
        }

        // only 1st wave need to barrier: it will remove all scratch registers
        if(local_Id0 < 64)
            barrier(CLK_LOCAL_MEM_FENCE);
    }

    if(local_Id0 < (MLO_ACCUM_SZ))
    {

        // Store to Memory
        __global _FLOAT* __restrict weights_ptr =
            weights_df +
            (K_OFFSET + (local_Id0 / MLO_N_LCL_IN_MAPS_ONCE)) * MLO_WEI_CHANNEL_STRIDE + C_OFFSET +
            (local_Id0 % MLO_N_LCL_IN_MAPS_ONCE);

        {
            __global _FLOAT* weights_ptr1 = weights_ptr;
            for(uint k = 0; k < 2; k++)
                for(uint c = 0; c < 2; c++)
                {
                    __global _FLOAT* weights_ptr2 =
                        weights_ptr1 + k * MLO_N_LCL_IN_MAPS_ONCE * MLO_WEI_CHANNEL_STRIDE +
                        c * MLO_N_LCL_IN_MAPS_ONCE;
                    *weights_ptr2 = accum_to_store[k * 2 + c];
                }
        }
    }
}


Some clean

Some clean

#include "kernel_header.hcl"
#include "kernel_matrix.hcl"
#include "kernel_cam.hcl"
#include "kernel_color.hcl"
#include "kernel_cone.hcl"
#include "kernel_cylinder.hcl"
#include "kernel_data.hcl"
#include "kernel_debug.hcl"
#include "kernel_plane.hcl"
#include "kernel_sphere.hcl"

static t_hit			ray_hit(const __local t_scene *scene, const float3 origin, const float3 ray)
{
	unsigned int			i = 0;
	int						max = get_max_obj(scene);
	t_hit					hit = hit_init();
	float					dist = 0;

	while (i < max)
	{
		if (i < scene->n_cones)
			if (((dist = inter_cone(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 1;
				hit.id = i;
			}
		if (i < scene->n_cylinders)
			if (((dist = inter_cylinder(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 2;
				hit.id = i;
			}
		if (i < scene->n_planes)
			if (((dist = inter_plan(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 4;
				hit.id = i;
			}
		if (i < scene->n_spheres)
			if (((dist = inter_sphere(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 5;
				hit.id = i;
			}
		i++;
	}
	return (hit);
}

float3			get_hit_normale(const __local t_scene *scene, float3 ray, t_hit hit)
{
	float3		res, save;

	if (hit.type == 1)
		res = get_cone_normale(scene, hit);
	else if (hit.type == 2)
		res = get_cylinder_normal(scene, hit);
	else if (hit.type == 4)
	{
		if (dot(PLANE[hit.id].normale, -ray) < 0)
			res = -PLANE[hit.id].normale;
		else
			res = PLANE[hit.id].normale;

		if (scene->flag & OPTION_WAVE)
		{
			/*					VAGUELETTE						*/
			save = res;
			save.y = res.y + 0.8 * sin((hit.pos.x + scene->u_time));
			return (fast_normalize(save));
		}
	}
	else if (hit.type == 5)
		res = hit.pos - SPHERE[hit.id].pos;
	save = res;
	if (scene->flag & OPTION_WAVE)
	{
		/*						VAGUELETTE							*/
		save.x = res.x + 0.8 * sin(res.y * 10 + scene->u_time);
		//save.z = save.z + 0.8 * sin(save.x * 10 + scene->u_time);
		//save.y = res.y + 0.8 * sin(res.x * 10 + scene->u_time);
	}

	return (fast_normalize(save));
}

unsigned int			phong2(const __local t_scene *scene, const t_hit hit, const float3 ray)
{
	int					i;
	i = 0;
	unsigned int		res_color;
	res_color = get_ambient(scene, get_obj_hue(scene, hit));
	float				tmp;
	tmp = 0;
	float3				reflect;
	reflect = 0;
	float3 __private	diffuse;
	diffuse = 0;
	float __private	brightness;
	brightness = 0;
	int __private hue;
	hue = 0;
	int __private hue_light;
	hue_light = 0;
	unsigned int __private col_r, col_g, col_b, obj_r, obj_g, obj_b, l_r, l_b, l_g;
	col_r = 0;
	col_g = 0;
	col_b = 0;
	obj_r = 0;
	obj_g = 0;
	obj_b = 0;
	l_r = 0;
	l_g = 0;
	l_b = 0;
	t_light_ray			light_ray;
	t_hit				light_hit;
	float __private pow_of_spec;
	pow_of_spec = 0;
	int __private light_color;
	light_color = 0;
	float3 __private speculos;
	speculos = 0;

	light_ray.dir = LIGHT[0].pos - hit.pos;
	light_ray.dist = fast_length(light_ray.dir);
	light_ray.dir = fast_normalize(light_ray.dir);
	light_hit = ray_hit(scene, hit.pos, light_ray.dir);
	light_hit.id = i;
	light_hit.type = 3;

		tmp = 0;
		light_ray.dir = LIGHT[i].pos - hit.pos;
		light_ray.dist = fast_length(light_ray.dir);
		light_ray.dir = fast_normalize(light_ray.dir);
		light_hit = ray_hit(scene, hit.pos, light_ray.dir);
		light_hit.id = i;
		light_hit.type = 3;
		if (light_hit.dist < light_ray.dist && light_hit.dist > 0)
			res_color = 0;
		else
		{
			tmp = dot(hit.normale, light_ray.dir);
			if (tmp > 0.f)
			{
				brightness = (float __private)LIGHT[light_hit.id].brightness;

				if (hit.type == 1)
					diffuse = (float3 __private)CONES[hit.id].diff;
				else if (hit.type == 2)
					diffuse = (float3 __private)CYLIND[hit.id].diff;
				else if (hit.type == 4)
					diffuse = (float3 __private)PLANE[hit.id].diff;
				else if (hit.type == 5)
					diffuse = (float3 __private)SPHERE[hit.id].diff;
				else
					diffuse = 0.f;

				if (hit.type == 1)
					hue = (int __private)CONES[hit.id].color;
				if (hit.type == 2)
					hue = (int __private)CYLIND[hit.id].color;
				if (hit.type == 3)
					hue = (int __private)LIGHT[hit.id].color;
				if (hit.type == 4)
					hue = (int __private)PLANE[hit.id].color;
				if (hit.type == 5)
					hue = (int __private)SPHERE[hit.id].color;

				if (hit.type == 1)
					hue_light = (int __private)CONES[light_hit.id].color;
				if (hit.type == 2)
					hue_light = (int __private)CYLIND[light_hit.id].color;
				if (hit.type == 3)
					hue_light = (int __private)LIGHT[light_hit.id].color;
				if (hit.type == 4)
					hue_light = (int __private)PLANE[light_hit.id].color;
				if (hit.type == 5)
					hue_light = (int __private)SPHERE[light_hit.id].color;
				col_r = (res_color & 0xFF0000) >> 16;
				col_g = (res_color & 0x00FF00) >> 8;
				col_b = (res_color & 0x0000FF);
				obj_r = (hue & 0xFF0000) >> 16;
				obj_g = (hue & 0x00FF00) >> 8;
				obj_b = (hue & 0x00000FF);
				l_r = (hue_light & 0xFF0000) >> 16;
				l_g = (hue_light & 0x00FF00) >> 8;
				l_b = (hue_light & 0x0000FF);

				col_r += ((l_r * brightness) + obj_r) * tmp * diffuse.x;
				col_g += ((l_g * brightness) + obj_g) * tmp * diffuse.y;
				col_b += ((l_b * brightness) + obj_b) * tmp * diffuse.z;
				(col_r > 255 ? col_r = 255 : 0);
				(col_g > 255 ? col_g = 255 : 0);
				(col_b > 255 ? col_b = 255 : 0);
				res_color = ((col_r << 16) + (col_g << 8) + col_b);
			}
			reflect = fast_normalize(((float)(2.0 * dot(hit.normale, light_ray.dir)) * hit.normale) - light_ray.dir);
			tmp = dot(reflect, -ray);
			if (tmp > 0.f)
			{
				speculos = 0.f;
				if (hit.type == 1)
					speculos = (float3 __private)CONES[hit.id].spec;
				else if (hit.type == 2)
					speculos = (float3 __private)CYLIND[hit.id].spec;
				else if (hit.type == 4)
					speculos = (float3 __private)PLANE[hit.id].spec;
				else if (hit.type == 5)
					speculos = (float3 __private)SPHERE[hit.id].spec;
				else
					speculos = 0.f;
				col_r = (res_color & 0x00FF0000) >> 16;
				col_g = (res_color & 0x0000FF00) >> 8;
				col_b = (res_color & 0x000000FF);

				pow_of_spec = native_powr(tmp, (LIGHT[light_hit.id].shrink));
				light_color = LIGHT[light_hit.id].color;
				col_r += (((light_color & 0xFF0000) >> 16) * pow_of_spec) * speculos.x;
				col_g += ((light_color & 0x00FF00) >> 8) * pow_of_spec * speculos.y;
				col_b += (light_color & 0x0000FF) * pow_of_spec * speculos.z;
				(col_r > 255 ? col_r = 255 : 0);
				(col_g > 255 ? col_g = 255 : 0);
				(col_b > 255 ? col_b = 255 : 0);
				res_color = ((col_r << 16) + (col_g << 8) + col_b);
			}
		}
	return (res_color);
}

unsigned int			phong3(const __local t_scene *scene, const t_hit hit, const float3 ray)
{
	int					i = -1;
	unsigned int		res_color = get_ambient(scene, get_obj_hue(scene, hit));
	float				tmp;
	float3				reflect = 0;

	t_light_ray			light_ray;
	t_hit				light_hit = hit_init();

	while (++i < scene->n_lights)
	{
		tmp = 0;
		light_ray.dir = LIGHT[i].pos - hit.pos;
		light_ray.dist = fast_length(light_ray.dir);
		light_ray.dir = fast_normalize(light_ray.dir);
		light_hit = ray_hit(scene, hit.pos, light_ray.dir);
		light_hit.id = i;
		light_hit.type = 3;
		if (light_hit.dist < light_ray.dist && light_hit.dist > 0)
		{
		}
		else
		{
			tmp = dot(hit.normale, light_ray.dir);
			if (tmp > 0)
				res_color = color_diffuse(scene, hit, light_hit, res_color, tmp);
			reflect = fast_normalize(((float)(2.0 * dot(hit.normale, light_ray.dir)) * hit.normale) - light_ray.dir);
			tmp = dot(reflect, -ray);
			if (tmp > 0)
				res_color = color_specular(scene, hit, light_hit, res_color, tmp);
		}
	}
	return (res_color);
}

unsigned int			phong4(const __local t_scene *scene, const t_hit hit, const float3 ray)
{
	int					i = -1;
	unsigned int		res_color = get_ambient(scene, get_obj_hue(scene, hit));
	float				tmp;
	float3				reflect = 0;

	t_light_ray			light_ray;
	t_hit				light_hit = hit_init();

	while (++i < scene->n_lights)
	{
		tmp = 0;
		light_ray.dir = LIGHT[i].pos - hit.pos;
		light_ray.dist = fast_length(light_ray.dir);
		light_ray.dir = fast_normalize(light_ray.dir);
		light_hit = ray_hit(scene, hit.pos, light_ray.dir);
		light_hit.id = i;
		light_hit.type = 3;
		if (light_hit.dist < light_ray.dist && light_hit.dist > 0)
		{
		}
		else
		{
			tmp = dot(hit.normale, light_ray.dir);
			if (tmp > 0)
				res_color = color_diffuse(scene, hit, light_hit, res_color, tmp);
			reflect = fast_normalize(((float)(2.0 * dot(hit.normale, light_ray.dir)) * hit.normale) - light_ray.dir);
			tmp = dot(reflect, -ray);
			if (tmp > 0)
				res_color = color_specular(scene, hit, light_hit, res_color, tmp);
		}
	}
	return (res_color);
}

static unsigned int		bounce(const __local t_scene *scene, const float3 ray, const t_hit old_hit, const int depth)
{
	int				i = depth;
	unsigned int	color = 0;
	float3			reflex = 0;
	t_hit			new_hit;
	while (i > 0)
	{
		reflex = fast_normalize(ray - (2 * (float)dot(old_hit.normale, ray) * old_hit.normale));
		new_hit.dist = MAX_DIST;
		new_hit = ray_hit(scene, old_hit.pos, reflex);
		if (new_hit.dist > 0 && new_hit.dist < MAX_DIST)
		{
			new_hit.pos = (new_hit.dist * reflex) + old_hit.pos;
			new_hit.normale = get_hit_normale(scene, ray, new_hit);
			new_hit.pos = new_hit.pos + ((new_hit.dist / 100) * new_hit.normale);
			color += 0.1 * phong2(scene, new_hit, reflex);
		}
		i--;
	}
	return (color);
}

static unsigned int	get_pixel_color(const __local t_scene *scene, float3 ray)
{
	t_hit			hit;
	int				depth = scene->depth;
	unsigned int	color  = 0;
	unsigned int	bounce_color = 0;

	hit.dist = MAX_DIST;
	hit = ray_hit(scene, (ACTIVECAM.pos), ray);
	if (hit.dist > 0 && hit.dist < MAX_DIST) // ajout d'une distance max pour virer acnee mais pas fiable a 100%
	{
		hit.pos = (hit.dist * ray) + (ACTIVECAM.pos);
		hit.normale = get_hit_normale(scene, ray, hit);
		hit.pos = hit.pos + ((hit.dist / 100) * hit.normale);
		color = phong2(scene, hit, ray);
		if (depth > 0)
			bounce_color = bounce(scene, ray, hit, depth);
		return (color + bounce_color);
//		return (blend_add(color, 0.8*bounce_color));
	}
	return (get_ambient(scene, BACKCOLOR));
}

__kernel void	ray_trace(	__global	char		*output,
							__global	t_hit		*target_obj,
							__global	t_scene		*scene_data,
							__global	t_cam		*cameras_data,
							__global	t_cone		*cones_data,
							__global	t_cylinder	*cylinders_data,
							__global	t_light		*lights_data,
							__global	t_plane		*planes_data,
							__global	t_sphere	*spheres_data,
							__local		t_scene		*scene,
							__local		t_cam		*cameras,
							__local		t_cone		*cones,
							__local		t_cylinder	*cylinders,
							__local		t_light		*lights,
							__local		t_plane		*planes,
							__local		t_sphere	*spheres,
							__private	float		u_time,
							__private	int			flag)
{
 	event_t	ev;
	ev = async_work_group_copy((__local char *)scene, (__global char *)scene_data, sizeof(t_scene), 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)cameras, (__global char *)cameras_data, sizeof(t_cam) * scene->n_cams, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)cones, (__global char *)cones_data, sizeof(t_cone) * scene->n_cones, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)cylinders, (__global char *)cylinders_data, sizeof(t_cylinder) * scene->n_cylinders, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)lights, (__global char *)lights_data, sizeof(t_light) * scene->n_lights, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)planes, (__global char *)planes_data, sizeof(t_plane) * scene->n_planes, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)spheres, (__global char *)spheres_data, sizeof(t_sphere) * scene->n_spheres, 0);
	wait_group_events(1, &ev);

	uint2	pix;
	pix.x = get_global_id(0);
	pix.y = get_global_id(1);

	scene->cameras = cameras;
	scene->cones = cones;
	scene->cylinders = cylinders;
	scene->lights = lights;
	scene->planes = planes;
	scene->spheres = spheres;
	scene->u_time = u_time;
	scene->flag = flag;

	int			id = pix.x + (scene->win_w * pix.y); // NE PAS VIRER ID CAR BESOIN DANS MACRO OUTPUTE

	float3	prim_ray = get_ray_cam(scene, pix);
	if (pix.x == scene->mou_x && pix.y == scene->mou_y)
		*target_obj = ray_hit(scene, ACTIVECAM.pos, prim_ray);
	OUTPUTE = get_pixel_color(scene, prim_ray);
}

#include "kernel_header.hcl"
#include "kernel_matrix.hcl"
#include "kernel_cam.hcl"
#include "kernel_color.hcl"
#include "kernel_cone.hcl"
#include "kernel_cylinder.hcl"
#include "kernel_data.hcl"
#include "kernel_debug.hcl"
#include "kernel_plane.hcl"
#include "kernel_sphere.hcl"

static t_hit			ray_hit(const __local t_scene *scene, const float3 origin, const float3 ray)
{
	unsigned int			i = 0;
	int						max = get_max_obj(scene);
	t_hit					hit = hit_init();
	float					dist = 0;

	while (i < max)
	{
		if (i < scene->n_cones)
			if (((dist = inter_cone(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 1;
				hit.id = i;
			}
		if (i < scene->n_cylinders)
			if (((dist = inter_cylinder(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 2;
				hit.id = i;
			}
		if (i < scene->n_planes)
			if (((dist = inter_plan(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 4;
				hit.id = i;
			}
		if (i < scene->n_spheres)
			if (((dist = inter_sphere(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 5;
				hit.id = i;
			}
		i++;
	}
	return (hit);
}

static t_hit			ray_hit2(const __local t_scene *scene, const float3 origin, const float3 ray)
{
	unsigned int			i = 0;
	int						max = get_max_obj(scene);
	t_hit					hit = hit_init();
	float					dist = 0;

	while (i < max)
	{
		if (i < scene->n_cones)
			if (((dist = inter_cone(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 1;
				hit.id = i;
			}
		if (i < scene->n_cylinders)
			if (((dist = inter_cylinder(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 2;
				hit.id = i;
			}
		if (i < scene->n_planes)
			if (((dist = inter_plan(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 4;
				hit.id = i;
			}
		if (i < scene->n_spheres)
			if (((dist = inter_sphere(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 5;
				hit.id = i;
			}
		i++;
	}
	return (hit);
}

float3			get_hit_normale(const __local t_scene *scene, float3 ray, t_hit hit)
{
	float3		res, save;

	if (hit.type == 1)
		res = get_cone_normale(scene, hit);
	else if (hit.type == 2)
		res = get_cylinder_normal(scene, hit);
	else if (hit.type == 4)
	{
		if (dot(PLANE[hit.id].normale, -ray) < 0)
			res = -PLANE[hit.id].normale;
		else
			res = PLANE[hit.id].normale;

		if (scene->flag & OPTION_WAVE)
		{
			/*					VAGUELETTE						*/
			save = res;
			save.y = res.y + 0.8 * sin((hit.pos.x + scene->u_time));
			return (fast_normalize(save));
		}
	}
	else if (hit.type == 5)
		res = hit.pos - SPHERE[hit.id].pos;
	save = res;
	if (scene->flag & OPTION_WAVE)
	{
		/*						VAGUELETTE							*/
		save.x = res.x + 0.8 * sin(res.y * 10 + scene->u_time);
		//save.z = save.z + 0.8 * sin(save.x * 10 + scene->u_time);
		//save.y = res.y + 0.8 * sin(res.x * 10 + scene->u_time);
	}

	return (fast_normalize(save));
}

unsigned int			color_diffuse2(const __local t_scene *scene, const t_hit hit, \
										const t_hit light_hit, const unsigned int color, const float coef)
{
	float3		__private	diffuse = get_obj_diffuse(scene, hit);
	float		__private	brightness = (float __private)LIGHT[light_hit.id].brightness;

	if (hit.type == 1)
		diffuse = (float3 __private)CONES[hit.id].diff;
	else if (hit.type == 2)
		diffuse = (float3 __private)CYLIND[hit.id].diff;
	else if (hit.type == 4)
		diffuse = (float3 __private)PLANE[hit.id].diff;
	else if (hit.type == 5)
		diffuse = (float3 __private)SPHERE[hit.id].diff;
	else
		diffuse = 0.f;

	int __private hue;

	if (hit.type == 1)
		hue = (int __private)CONES[hit.id].color;
	if (hit.type == 2)
		hue = (int __private)CYLIND[hit.id].color;
	if (hit.type == 3)
		hue = (int __private)LIGHT[hit.id].color;
	if (hit.type == 4)
		hue = (int __private)PLANE[hit.id].color;
	if (hit.type == 5)
		hue = (int __private)SPHERE[hit.id].color;
	int __private hue_light;

	if (hit.type == 1)
		hue_light = (int __private)CONES[light_hit.id].color;
	if (hit.type == 2)
		hue_light = (int __private)CYLIND[light_hit.id].color;
	if (hit.type == 3)
		hue_light = (int __private)LIGHT[light_hit.id].color;
	if (hit.type == 4)
		hue_light = (int __private)PLANE[light_hit.id].color;
	if (hit.type == 5)
		hue_light = (int __private)SPHERE[light_hit.id].color;
	unsigned int	col_r = (color & 0xFF0000) >> 16;
	unsigned int	col_g = (color & 0x00FF00) >> 8;
	unsigned int	col_b = (color & 0x0000FF);
	unsigned int	obj_r = (hue & 0xFF0000) >> 16;
	unsigned int	obj_g = (hue & 0x00FF00) >> 8;
	unsigned int	obj_b = (hue & 0x00000FF);
	unsigned int	l_r = (hue_light & 0xFF0000) >> 16;
	unsigned int	l_g = (hue_light & 0x00FF00) >> 8;
	unsigned int	l_b = (hue_light & 0x0000FF);

	col_r += ((l_r * brightness) + obj_r) * coef * diffuse.x;
	col_g += ((l_g * brightness) + obj_g) * coef * diffuse.y;
	col_b += ((l_b * brightness) + obj_b) * coef * diffuse.z;
	(col_r > 255 ? col_r = 255 : 0);
	(col_g > 255 ? col_g = 255 : 0);
	(col_b > 255 ? col_b = 255 : 0);
	return ((col_r << 16) + (col_g << 8) + col_b);
}

unsigned int			color_specular2(const __local t_scene *scene, const t_hit hit, \
										const t_hit light_hit, const unsigned int color, const float coef)
{
	float3 __private	speculos = 0.f;
	if (hit.type == 1)
		speculos = (float3 __private)CONES[hit.id].spec;
	else if (hit.type == 2)
		speculos = (float3 __private)CYLIND[hit.id].spec;
	else if (hit.type == 4)
		speculos = (float3 __private)PLANE[hit.id].spec;
	else if (hit.type == 5)
		speculos = (float3 __private)SPHERE[hit.id].spec;
	else
		speculos = 0.f;
	float			old_coef = coef;
	unsigned int	col_r = (color & 0x00FF0000) >> 16;
	unsigned int	col_g = (color & 0x0000FF00) >> 8;
	unsigned int	col_b = (color & 0x000000FF);

	float pow_of_spec;
	pow_of_spec = native_powr(old_coef, (int)(LIGHT[light_hit.id].shrink));
 	int light_color = LIGHT[light_hit.id].color;
	//printf("%.2f\n", pow_of_spec);
	//float a = pow_of_spec * 2;
	col_r += (((light_color & 0xFF0000) >> 16) * pow_of_spec) * speculos.x;
	//printf("%.2f\n", speculos.x);
	col_g += ((light_color & 0x00FF00) >> 8) * pow_of_spec * speculos.y;
	col_b += (light_color & 0x0000FF) * pow_of_spec * speculos.z;
	(col_r > 255 ? col_r = 255 : 0);
	(col_g > 255 ? col_g = 255 : 0);
	(col_b > 255 ? col_b = 255 : 0);
	return ((col_r << 16) + (col_g << 8) + col_b);
}

unsigned int			phong(const __local t_scene *scene, const t_hit hit, const float3 ray)
{
	int					i = -1;
//	unsigned int		res_color = get_ambient(scene, get_obj_hue(scene, hit));
float				tmp2;
	float				tmp;
	float3				reflect = 0;
	unsigned int __private obj_color;
	if (hit.type == 1)
		obj_color = (unsigned int __private)CONES[hit.id].color;
	if (hit.type == 2)
		obj_color = (unsigned int __private)CYLIND[hit.id].color;
	if (hit.type == 3)
		obj_color = (unsigned int __private)LIGHT[hit.id].color;
	if (hit.type == 4)
		obj_color = (unsigned int __private)PLANE[hit.id].color;
	if (hit.type == 5)
		obj_color = (unsigned int __private)SPHERE[hit.id].color;
	unsigned int r, g, b;

	r = (obj_color & 0xFF0000) >> 16;
	g = (obj_color & 0x00FF00) >> 8;
	b = (obj_color & 0x0000FF);
	r = (0.01 + r * scene->ambient.x > 255 ? 255 : 0.01 + r * scene->ambient.x);
	g = (0.01 + g * scene->ambient.y > 255 ? 255 : 0.01 + g * scene->ambient.y);
	b = (0.01 + b * scene->ambient.z > 255 ? 255 : 0.01 + b * scene->ambient.z);
	unsigned int res_color = ((r << 16) + (g << 8) + b);

//	unsigned int res_color = 0x0FF0FF;
	t_light_ray			light_ray;
	t_hit				light_hit;// = hit_init();
	light_hit.dist = 0.0;
	light_hit.type = 0;
	light_hit.id = 0;
	light_hit.pos = 0.0;
	light_hit.normale = 0.0;

	while (++i < scene->n_lights)
	{
		tmp = 0;
		tmp2 = 0;
		light_ray.dir = LIGHT[i].pos - hit.pos;
		light_ray.dist = fast_length(light_ray.dir);
		light_ray.dir = fast_normalize(light_ray.dir);
		light_hit = ray_hit(scene, hit.pos, light_ray.dir);
		light_hit.id = i;
		light_hit.type = 3;
		if (light_hit.dist < light_ray.dist  && light_hit.dist > 0)
		{
			;
		}
		else
		{
			tmp = dot(hit.normale, light_ray.dir);
			if (tmp > 0.005)
			{
				float3		__private	diffuse = get_obj_diffuse(scene, hit);
				float		__private	brightness = (float __private)LIGHT[light_hit.id].brightness;

				if (hit.type == 1)
					diffuse = (float3 __private)CONES[hit.id].diff;
				else if (hit.type == 2)
					diffuse = (float3 __private)CYLIND[hit.id].diff;
				else if (hit.type == 4)
					diffuse = (float3 __private)PLANE[hit.id].diff;
				else if (hit.type == 5)
					diffuse = (float3 __private)SPHERE[hit.id].diff;
				else
					diffuse = 0.f;

				int __private hue;

				if (hit.type == 1)
					hue = (int __private)CONES[hit.id].color;
				if (hit.type == 2)
					hue = (int __private)CYLIND[hit.id].color;
				if (hit.type == 3)
					hue = (int __private)LIGHT[hit.id].color;
				if (hit.type == 4)
					hue = (int __private)PLANE[hit.id].color;
				if (hit.type == 5)
					hue = (int __private)SPHERE[hit.id].color;
				int __private hue_light;

				if (hit.type == 1)
					hue_light = (int __private)CONES[light_hit.id].color;
				if (hit.type == 2)
					hue_light = (int __private)CYLIND[light_hit.id].color;
				if (hit.type == 3)
					hue_light = (int __private)LIGHT[light_hit.id].color;
				if (hit.type == 4)
					hue_light = (int __private)PLANE[light_hit.id].color;
				if (hit.type == 5)
					hue_light = (int __private)SPHERE[light_hit.id].color;
				unsigned int	col_r = (res_color & 0xFF0000) >> 16;
				unsigned int	col_g = (res_color & 0x00FF00) >> 8;
				unsigned int	col_b = (res_color & 0x0000FF);
				unsigned int	obj_r = (hue & 0xFF0000) >> 16;
				unsigned int	obj_g = (hue & 0x00FF00) >> 8;
				unsigned int	obj_b = (hue & 0x00000FF);
				unsigned int	l_r = (hue_light & 0xFF0000) >> 16;
				unsigned int	l_g = (hue_light & 0x00FF00) >> 8;
				unsigned int	l_b = (hue_light & 0x0000FF);

				col_r += ((l_r * brightness) + obj_r) * tmp * diffuse.x;
				col_g += ((l_g * brightness) + obj_g) * tmp * diffuse.y;
				col_b += ((l_b * brightness) + obj_b) * tmp * diffuse.z;
				(col_r > 255 ? col_r = 255 : 0);
				(col_g > 255 ? col_g = 255 : 0);
				(col_b > 255 ? col_b = 255 : 0);
				res_color = ((col_r << 16) + (col_g << 8) + col_b);
			}
			reflect = fast_normalize(((float)(2.0 * dot(hit.normale, light_ray.dir)) * hit.normale) - light_ray.dir);
			tmp2 = dot(reflect, -ray);
			if (tmp2 > 0.005)
			{
				//res_color = color_specular2(scene, hit, light_hit, res_color, tmp2);
			}
		}
	}
	return (res_color);
}

unsigned int			phong2(const __local t_scene *scene, const t_hit hit, const float3 ray)
{
	int					i;
	i = 0;
	unsigned int		res_color;
	res_color = get_ambient(scene, get_obj_hue(scene, hit));
	float				tmp;
	tmp = 0;
	float3				reflect;
	reflect = 0;
	float3 __private	diffuse;
	diffuse = 0;
	float __private	brightness;
	brightness = 0;
	int __private hue;
	hue = 0;
	int __private hue_light;
	hue_light = 0;
	unsigned int __private col_r, col_g, col_b, obj_r, obj_g, obj_b, l_r, l_b, l_g;
	col_r = 0;
	col_g = 0;
	col_b = 0;
	obj_r = 0;
	obj_g = 0;
	obj_b = 0;
	l_r = 0;
	l_g = 0;
	l_b = 0;
	t_light_ray			light_ray;
	t_hit				light_hit;
	float __private pow_of_spec;
	pow_of_spec = 0;
	int __private light_color;
	light_color = 0;
	float3 __private speculos;
	speculos = 0;

	light_ray.dir = LIGHT[0].pos - hit.pos;
	light_ray.dist = fast_length(light_ray.dir);
	light_ray.dir = fast_normalize(light_ray.dir);
	light_hit = ray_hit(scene, hit.pos, light_ray.dir);
	light_hit.id = i;
	light_hit.type = 3;

		tmp = 0;
		light_ray.dir = LIGHT[i].pos - hit.pos;
		light_ray.dist = fast_length(light_ray.dir);
		light_ray.dir = fast_normalize(light_ray.dir);
		light_hit = ray_hit(scene, hit.pos, light_ray.dir);
		light_hit.id = i;
		light_hit.type = 3;
		if (light_hit.dist < light_ray.dist && light_hit.dist > 0)
			res_color = 0;
		else
		{
			tmp = dot(hit.normale, light_ray.dir);
			if (tmp > 0.f)
			{
				brightness = (float __private)LIGHT[light_hit.id].brightness;

				if (hit.type == 1)
					diffuse = (float3 __private)CONES[hit.id].diff;
				else if (hit.type == 2)
					diffuse = (float3 __private)CYLIND[hit.id].diff;
				else if (hit.type == 4)
					diffuse = (float3 __private)PLANE[hit.id].diff;
				else if (hit.type == 5)
					diffuse = (float3 __private)SPHERE[hit.id].diff;
				else
					diffuse = 0.f;

				if (hit.type == 1)
					hue = (int __private)CONES[hit.id].color;
				if (hit.type == 2)
					hue = (int __private)CYLIND[hit.id].color;
				if (hit.type == 3)
					hue = (int __private)LIGHT[hit.id].color;
				if (hit.type == 4)
					hue = (int __private)PLANE[hit.id].color;
				if (hit.type == 5)
					hue = (int __private)SPHERE[hit.id].color;

				if (hit.type == 1)
					hue_light = (int __private)CONES[light_hit.id].color;
				if (hit.type == 2)
					hue_light = (int __private)CYLIND[light_hit.id].color;
				if (hit.type == 3)
					hue_light = (int __private)LIGHT[light_hit.id].color;
				if (hit.type == 4)
					hue_light = (int __private)PLANE[light_hit.id].color;
				if (hit.type == 5)
					hue_light = (int __private)SPHERE[light_hit.id].color;
				col_r = (res_color & 0xFF0000) >> 16;
				col_g = (res_color & 0x00FF00) >> 8;
				col_b = (res_color & 0x0000FF);
				obj_r = (hue & 0xFF0000) >> 16;
				obj_g = (hue & 0x00FF00) >> 8;
				obj_b = (hue & 0x00000FF);
				l_r = (hue_light & 0xFF0000) >> 16;
				l_g = (hue_light & 0x00FF00) >> 8;
				l_b = (hue_light & 0x0000FF);

				col_r += ((l_r * brightness) + obj_r) * tmp * diffuse.x;
				col_g += ((l_g * brightness) + obj_g) * tmp * diffuse.y;
				col_b += ((l_b * brightness) + obj_b) * tmp * diffuse.z;
				(col_r > 255 ? col_r = 255 : 0);
				(col_g > 255 ? col_g = 255 : 0);
				(col_b > 255 ? col_b = 255 : 0);
				res_color = ((col_r << 16) + (col_g << 8) + col_b);
			}
		//		res_color = color_diffuse(scene, hit, light_hit, res_color, tmp);
			reflect = fast_normalize(((float)(2.0 * dot(hit.normale, light_ray.dir)) * hit.normale) - light_ray.dir);
			tmp = dot(reflect, -ray);
			if (tmp > 0.f)
			{
				speculos = 0.f;
				if (hit.type == 1)
					speculos = (float3 __private)CONES[hit.id].spec;
				else if (hit.type == 2)
					speculos = (float3 __private)CYLIND[hit.id].spec;
				else if (hit.type == 4)
					speculos = (float3 __private)PLANE[hit.id].spec;
				else if (hit.type == 5)
					speculos = (float3 __private)SPHERE[hit.id].spec;
				else
					speculos = 0.f;
				col_r = (res_color & 0x00FF0000) >> 16;
				col_g = (res_color & 0x0000FF00) >> 8;
				col_b = (res_color & 0x000000FF);

				pow_of_spec = native_powr(tmp, (LIGHT[light_hit.id].shrink));
				light_color = LIGHT[light_hit.id].color;
				col_r += (((light_color & 0xFF0000) >> 16) * pow_of_spec) * speculos.x;
				col_g += ((light_color & 0x00FF00) >> 8) * pow_of_spec * speculos.y;
				col_b += (light_color & 0x0000FF) * pow_of_spec * speculos.z;
				(col_r > 255 ? col_r = 255 : 0);
				(col_g > 255 ? col_g = 255 : 0);
				(col_b > 255 ? col_b = 255 : 0);
				/*col_r += ((LIGHT[light_hit.id].color & 0x00FF0000) >> 16) * pow_of_spec * speculos.x;
				col_g += ((LIGHT[light_hit.id].color & 0x0000FF00) >> 8) * pow_of_spec * speculos.y;
				col_b += (LIGHT[light_hit.id].color & 0x000000FF) * pow_of_spec * speculos.z;
				(col_r > 255 ? col_r = 255 : 0);
				(col_g > 255 ? col_g = 255 : 0);
				(col_b > 255 ? col_b = 255 : 0);*/
				res_color = ((col_r << 16) + (col_g << 8) + col_b);
			}
		}
	return (res_color);
}

unsigned int			phong3(const __local t_scene *scene, const t_hit hit, const float3 ray)
{
	int					i = -1;
	unsigned int		res_color = get_ambient(scene, get_obj_hue(scene, hit));
	float				tmp;
	float3				reflect = 0;

	t_light_ray			light_ray;
	t_hit				light_hit = hit_init();

	while (++i < scene->n_lights)
	{
		tmp = 0;
		light_ray.dir = LIGHT[i].pos - hit.pos;
		light_ray.dist = fast_length(light_ray.dir);
		light_ray.dir = fast_normalize(light_ray.dir);
		light_hit = ray_hit(scene, hit.pos, light_ray.dir);
		light_hit.id = i;
		light_hit.type = 3;
		if (light_hit.dist < light_ray.dist && light_hit.dist > 0)
		{
		}
		else
		{
			tmp = dot(hit.normale, light_ray.dir);
			if (tmp > 0)
				res_color = color_diffuse(scene, hit, light_hit, res_color, tmp);
			reflect = fast_normalize(((float)(2.0 * dot(hit.normale, light_ray.dir)) * hit.normale) - light_ray.dir);
			tmp = dot(reflect, -ray);
			if (tmp > 0)
				res_color = color_specular(scene, hit, light_hit, res_color, tmp);
		}
	}
	return (res_color);
}

unsigned int			phong4(const __local t_scene *scene, const t_hit hit, const float3 ray)
{
	int					i = -1;
	unsigned int		res_color = get_ambient(scene, get_obj_hue(scene, hit));
	float				tmp;
	float3				reflect = 0;

	t_light_ray			light_ray;
	t_hit				light_hit = hit_init();

	while (++i < scene->n_lights)
	{
		tmp = 0;
		light_ray.dir = LIGHT[i].pos - hit.pos;
		light_ray.dist = fast_length(light_ray.dir);
		light_ray.dir = fast_normalize(light_ray.dir);
		light_hit = ray_hit(scene, hit.pos, light_ray.dir);
		light_hit.id = i;
		light_hit.type = 3;
		if (light_hit.dist < light_ray.dist && light_hit.dist > 0)
		{
		}
		else
		{
			tmp = dot(hit.normale, light_ray.dir);
			if (tmp > 0)
				res_color = color_diffuse(scene, hit, light_hit, res_color, tmp);
			reflect = fast_normalize(((float)(2.0 * dot(hit.normale, light_ray.dir)) * hit.normale) - light_ray.dir);
			tmp = dot(reflect, -ray);
			if (tmp > 0)
				res_color = color_specular(scene, hit, light_hit, res_color, tmp);
		}
	}
	return (res_color);
}

static unsigned int		bounce(const __local t_scene *scene, const float3 ray, const t_hit old_hit, const int depth)
{
	int				i = depth;
	unsigned int	color = 0;
	float3			reflex = 0;
	t_hit			new_hit;
	while (i > 0)
	{
		reflex = fast_normalize(ray - (2 * (float)dot(old_hit.normale, ray) * old_hit.normale));
		new_hit.dist = MAX_DIST;
		new_hit = ray_hit(scene, old_hit.pos, reflex);
		if (new_hit.dist > 0 && new_hit.dist < MAX_DIST)
		{
			new_hit.pos = (new_hit.dist * reflex) + old_hit.pos;
			new_hit.normale = get_hit_normale(scene, ray, new_hit);
			new_hit.pos = new_hit.pos + ((new_hit.dist / 100) * new_hit.normale);
			color += 0.1 * phong2(scene, new_hit, reflex);
		}
		i--;
	}
	return (color);
}

static unsigned int	get_pixel_color(const __local t_scene *scene, float3 ray)
{
	t_hit			hit;
	int				depth = scene->depth;
	unsigned int	color  = 0;
	unsigned int	bounce_color = 0;

	hit.dist = MAX_DIST;
	hit = ray_hit(scene, (ACTIVECAM.pos), ray);
	if (hit.dist > 0 && hit.dist < MAX_DIST) // ajout d'une distance max pour virer acnee mais pas fiable a 100%
	{
		hit.pos = (hit.dist * ray) + (ACTIVECAM.pos);
		hit.normale = get_hit_normale(scene, ray, hit);
		hit.pos = hit.pos + ((hit.dist / 100) * hit.normale);
		color = phong2(scene, hit, ray);
		if (depth > 0)
			bounce_color = bounce(scene, ray, hit, depth);
		return (color + bounce_color);
//		return (blend_add(color, 0.8*bounce_color));
	}
	return (get_ambient(scene, BACKCOLOR));
}

__kernel void	ray_trace(	__global	char		*output,
							__global	t_hit		*target_obj,
							__global	t_scene		*scene_data,
							__global	t_cam		*cameras_data,
							__global	t_cone		*cones_data,
							__global	t_cylinder	*cylinders_data,
							__global	t_light		*lights_data,
							__global	t_plane		*planes_data,
							__global	t_sphere	*spheres_data,
							__local		t_scene		*scene,
							__local		t_cam		*cameras,
							__local		t_cone		*cones,
							__local		t_cylinder	*cylinders,
							__local		t_light		*lights,
							__local		t_plane		*planes,
							__local		t_sphere	*spheres,
							__private	float		u_time,
							__private	int			flag)
{
 	event_t	ev;
	ev = async_work_group_copy((__local char *)scene, (__global char *)scene_data, sizeof(t_scene), 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)cameras, (__global char *)cameras_data, sizeof(t_cam) * scene->n_cams, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)cones, (__global char *)cones_data, sizeof(t_cone) * scene->n_cones, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)cylinders, (__global char *)cylinders_data, sizeof(t_cylinder) * scene->n_cylinders, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)lights, (__global char *)lights_data, sizeof(t_light) * scene->n_lights, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)planes, (__global char *)planes_data, sizeof(t_plane) * scene->n_planes, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)spheres, (__global char *)spheres_data, sizeof(t_sphere) * scene->n_spheres, 0);
	wait_group_events(1, &ev);

	uint2	pix;
	pix.x = get_global_id(0);
	pix.y = get_global_id(1);

	scene->cameras = cameras;
	if (!pix.x && !pix.y)
	{
		printf("%i\n", sizeof(__private int));
		printf("%.2f, %.2f, %.2f\n", scene->cameras->pos.x,
		 							scene->cameras->pos.y,
									 scene->cameras->pos.z);
	}
	scene->cones = cones;
	scene->cylinders = cylinders;
	scene->lights = lights;
	scene->planes = planes;
	scene->spheres = spheres;
	scene->u_time = u_time;
	scene->flag = flag;
	if (!pix.x && !pix.y)
	{
		printf("%i\n", sizeof(__private int));
		printf("%.2f, %.2f, %.2f\n", scene->cameras->pos.x,
									scene->cameras->pos.y,
									 scene->cameras->pos.z);
	}

	int			id = pix.x + (scene->win_w * pix.y); // NE PAS VIRER ID CAR BESOIN DANS MACRO OUTPUTE

	float3	prim_ray = get_ray_cam(scene, pix);
	if (pix.x == scene->mou_x && pix.y == scene->mou_y)
		*target_obj = ray_hit(scene, ACTIVECAM.pos, prim_ray);
	OUTPUTE = get_pixel_color(scene, prim_ray);
}


cl_demosaic: remove loop read to improve performance

 cl failed on optimaztion of simple loop, then changed to
 paste every read in kernel to reduce loop

/*
 * function: kernel_demosaic
 * input:    image2d_t as read only
 * output:   image2d_t as write only
 */

"__kernel void kernel_demosaic (__read_only image2d_t input, __write_only image2d_t output)    "
"{                                                                                "
"    int x = 2 * get_global_id (0);                                                            "
"    int y = 2 * get_global_id (1);                                                            "
//"    sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_NONE | CLK_FILTER_NEAREST;  "
"    sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;  "

"                                                                                "
"    int x0 = x - 1;                                                                           "
"    int y0 = y - 1;                                                                           "
"    float4 p[16];                                                                             "
"    p[0] = read_imagef (input, sampler, (int2)(x0, y0));                                      "
"    p[1] = read_imagef (input, sampler, (int2)(x0 + 1, y0));                                  "
"    p[2] = read_imagef (input, sampler, (int2)(x0 + 2, y0));                                  "
"    p[3] = read_imagef (input, sampler, (int2)(x0 + 3, y0));                                  "
"    p[4] = read_imagef (input, sampler, (int2)(x0, y0 + 1));                                  "
"    p[5] = read_imagef (input, sampler, (int2)(x0 + 1, y0 + 1));                              "
"    p[6] = read_imagef (input, sampler, (int2)(x0 + 2, y0 + 1));                              "
"    p[7] = read_imagef (input, sampler, (int2)(x0 + 3, y0 + 1));                              "
"    p[8] = read_imagef (input, sampler, (int2)(x0, y0 + 2));                                  "
"    p[9] = read_imagef (input, sampler, (int2)(x0 + 1, y0 + 2));                              "
"    p[10] = read_imagef (input, sampler, (int2)(x0 + 2, y0 + 2));                             "
"    p[11] = read_imagef (input, sampler, (int2)(x0 + 3, y0 + 2));                             "
"    p[12] = read_imagef (input, sampler, (int2)(x0, y0 + 3));                                 "
"    p[13] = read_imagef (input, sampler, (int2)(x0 + 1, y0 + 3));                             "
"    p[14] = read_imagef (input, sampler, (int2)(x0 + 2, y0 + 3));                             "
"    p[15] = read_imagef (input, sampler, (int2)(x0 + 3, y0 + 3));                             "

//"    for (int i = 0; i < 16; ++i) {                                                            "
//"        p[i] = read_imagef (input, sampler, (int2)(x0 + i % 4, y0 + i / 4));                  "
//"    }                                                                                "
"                                                                                "
"    float4 p00, p01, p10, p11;                                                                "
"    p00.x = (p[4].x + p[6].x) / 2.0;                                                          "
"    p00.y = (p[5].x * 4 + p[0].x + p[2].x + p[8].x + p[10].x) / 8.0;                          "
"    p00.z = (p[1].x + p[9].x) / 2.0;                                                          "
"    p01.x = p[6].x;                                                                           "
"    p01.y = (p[2].x + p[5].x + p[7].x+ p[10].x) / 4.0;                                        "
"    p01.z = (p[1].x + p[3].x + p[9].x+ p[11].x) / 4.0;                                        "
"    p10.x = (p[4].x + p[6].x + p[12].x+ p[14].x) / 4.0;                                       "
"    p10.y = (p[5].x + p[8].x + p[10].x+ p[13].x) / 4.0;                                       "
"    p10.z = p[9].x;                                                                           "
"    p11.x = (p[6].x + p[14].x) / 2.0;                                                         "
"    p11.y = (p[10].x * 4 + p[5].x + p[7].x + p[13].x + p[15].x) / 8.0;                        "
"    p11.z = (p[9].x + p[11].x) / 2.0;                                                         "
"                                                                                "
"    write_imagef (output, (int2)(x, y), p00);                                                 "
"    write_imagef (output, (int2)(x + 1, y), p01);                                             "
"    write_imagef (output, (int2)(x, y + 1), p10);                                             "
"    write_imagef (output, (int2)(x + 1, y + 1), p11);                                         "
"}                                                                                "

/*
 * function: kernel_demosaic
 * input:    image2d_t as read only
 * output:   image2d_t as write only
 */

"__kernel void kernel_demosaic (__read_only image2d_t input, __write_only image2d_t output)    "
"{                                                                                "
"    int x = 2 * get_global_id (0);                                                            "
"    int y = 2 * get_global_id (1);                                                            "
"    sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_NONE | CLK_FILTER_NEAREST;  "
//"    sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;  "

"                                                                                "
"    int x0 = x - 1;                                                                           "
"    int y0 = y - 1;                                                                           "
"    float4 p[16];                                                                             "
"    for (int i = 0; i < 16; ++i) {                                                            "
"        p[i] = read_imagef (input, sampler, (int2)(x0 + i % 4, y0 + i / 4));                  "
"    }                                                                                "
"                                                                                "
"    float4 p00, p01, p10, p11;                                                                "
"    p00.x = (p[4].x + p[6].x) / 2.0;                                                          "
"    p00.y = (p[5].x * 4 + p[0].x + p[2].x + p[8].x + p[10].x) / 8.0;                          "
"    p00.z = (p[1].x + p[9].x) / 2.0;                                                          "
"    p01.x = p[6].x;                                                                           "
"    p01.y = (p[2].x + p[5].x + p[7].x+ p[10].x) / 4.0;                                        "
"    p01.z = (p[1].x + p[3].x + p[9].x+ p[11].x) / 4.0;                                        "
"    p10.x = (p[4].x + p[6].x + p[12].x+ p[14].x) / 4.0;                                       "
"    p10.y = (p[5].x + p[8].x + p[10].x+ p[13].x) / 4.0;                                       "
"    p10.z = p[9].x;                                                                           "
"    p11.x = (p[6].x + p[14].x) / 2.0;                                                         "
"    p11.y = (p[10].x * 4 + p[5].x + p[7].x + p[13].x + p[15].x) / 8.0;                        "
"    p11.z = (p[9].x + p[11].x) / 2.0;                                                         "
"                                                                                "
"    write_imagef (output, (int2)(x, y), p00);                                                 "
"    write_imagef (output, (int2)(x + 1, y), p01);                                             "
"    write_imagef (output, (int2)(x, y + 1), p10);                                             "
"    write_imagef (output, (int2)(x + 1, y + 1), p11);                                         "
"}                                                                                "