/****************************************************************************
* Copyright (C) 2014-2015 Intel Corporation.   All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
* 
* @file tilingtraits.h
* 
* @brief Tiling traits.
* 
******************************************************************************/
#pragma once

#include "core/state.h"
#include "common/simdintrin.h"

template<SWR_TILE_MODE mode, int>
struct TilingTraits
{
    static const SWR_TILE_MODE TileMode{ mode };
    static UINT GetCu() { SWR_ASSERT(0); return 0; }
    static UINT GetCv() { SWR_ASSERT(0); return 0; }
    static UINT GetCr() { SWR_ASSERT(0); return 0; }
    static UINT GetTileIDShift() { SWR_ASSERT(0); return 0; }

    /// @todo correct pdep shifts for all rastertile dims.  Unused for now
    static UINT GetPdepX() { SWR_ASSERT(0); return 0x37; }
    static UINT GetPdepY() { SWR_ASSERT(0); return 0xC8; }
};

template<int X> struct TilingTraits <SWR_TILE_NONE, X>
{
    static const SWR_TILE_MODE TileMode{ SWR_TILE_NONE };
    static UINT GetCu() { return 0; }
    static UINT GetCv() { return 0; }
    static UINT GetCr() { return 0; }
    static UINT GetTileIDShift() { return 0; }
    static UINT GetPdepX() { return 0x00; }
    static UINT GetPdepY() { return 0x00; }
};

template<> struct TilingTraits <SWR_TILE_SWRZ, 8>
{
    static const SWR_TILE_MODE TileMode{ SWR_TILE_SWRZ };
    static UINT GetCu() { return KNOB_TILE_X_DIM_SHIFT; }
    static UINT GetCv() { return KNOB_TILE_Y_DIM_SHIFT; }
    static UINT GetCr() { return 0; }
    static UINT GetTileIDShift() { return KNOB_TILE_X_DIM_SHIFT + KNOB_TILE_Y_DIM_SHIFT; }

    /// @todo correct pdep shifts for all rastertile dims.  Unused for now
    static UINT GetPdepX() { SWR_ASSERT(0); return 0x00; }
    static UINT GetPdepY() { SWR_ASSERT(0); return 0x00; }
};

template<> struct TilingTraits <SWR_TILE_SWRZ, 32>
{
    static const SWR_TILE_MODE TileMode{ SWR_TILE_SWRZ };
    static UINT GetCu() { return KNOB_TILE_X_DIM_SHIFT + 2; }
    static UINT GetCv() { return KNOB_TILE_Y_DIM_SHIFT; }
    static UINT GetCr() { return 0; }
    static UINT GetTileIDShift() { return KNOB_TILE_X_DIM_SHIFT + KNOB_TILE_Y_DIM_SHIFT + 2; }

    static UINT GetPdepX() { return 0x37; }
    static UINT GetPdepY() { return 0xC8; }
};

template<> struct TilingTraits <SWR_TILE_SWRZ, 128>
{
    static const SWR_TILE_MODE TileMode{ SWR_TILE_SWRZ };
    static UINT GetCu() { return KNOB_TILE_X_DIM_SHIFT + 4; }
    static UINT GetCv() { return KNOB_TILE_Y_DIM_SHIFT; }
    static UINT GetCr() { return 0; }
    static UINT GetTileIDShift() { return KNOB_TILE_X_DIM_SHIFT + KNOB_TILE_Y_DIM_SHIFT + 4; }

    /// @todo correct pdep shifts for all rastertile dims.  Unused for now
    static UINT GetPdepX() { SWR_ASSERT(0); return 0x37; }
    static UINT GetPdepY() { SWR_ASSERT(0); return 0xC8; }
};

// y-major tiling layout unaffected by element size
template<int X> struct TilingTraits <SWR_TILE_MODE_YMAJOR, X>
{
    static const SWR_TILE_MODE TileMode{ SWR_TILE_MODE_YMAJOR };
    static UINT GetCu() { return 7; }
    static UINT GetCv() { return 5; }
    static UINT GetCr() { return 0; }
    static UINT GetTileIDShift() { return 12; }

    static UINT GetPdepX() { return 0xe0f; }
    static UINT GetPdepY() { return 0x1f0; }
};

// x-major tiling layout unaffected by element size
template<int X> struct TilingTraits <SWR_TILE_MODE_XMAJOR, X>
{
    static const SWR_TILE_MODE TileMode{ SWR_TILE_MODE_XMAJOR };
    static UINT GetCu() { return 9; }
    static UINT GetCv() { return 3; }
    static UINT GetCr() { return 0; }
    static UINT GetTileIDShift() { return 12; }

    static UINT GetPdepX() { return 0x1ff; }
    static UINT GetPdepY() { return 0xe00; }
};

template<int X> struct TilingTraits <SWR_TILE_MODE_WMAJOR, X>
{
    static const SWR_TILE_MODE TileMode{ SWR_TILE_MODE_WMAJOR };
    static UINT GetCu() { return 6; }
    static UINT GetCv() { return 6; }
    static UINT GetCr() { return 0; }
    static UINT GetTileIDShift() { return 12; }

    static UINT GetPdepX() { return 0xe15; }
    static UINT GetPdepY() { return 0x1ea; }
};

//////////////////////////////////////////////////////////////////////////
/// @brief Computes the tileID for 2D tiled surfaces
/// @param pitch - surface pitch in bytes
/// @param tileX - x offset in tiles
/// @param tileY - y offset in tiles
template<typename TTraits>
INLINE UINT ComputeTileOffset2D(UINT pitch, UINT tileX, UINT tileY)
{
    UINT tileID = tileY * (pitch >> TTraits::GetCu()) + tileX;
    return tileID << TTraits::GetTileIDShift();
}

//////////////////////////////////////////////////////////////////////////
/// @brief Computes the tileID for 3D tiled surfaces
/// @param qpitch - surface qpitch in rows
/// @param pitch - surface pitch in bytes
/// @param tileX - x offset in tiles
/// @param tileY - y offset in tiles
/// @param tileZ - y offset in tiles
template<typename TTraits>
INLINE UINT ComputeTileOffset3D(UINT qpitch, UINT pitch, UINT tileX, UINT tileY, UINT tileZ)
{
    UINT tileID = (tileZ * (qpitch >> TTraits::GetCv()) + tileY) * (pitch >> TTraits::GetCu()) + tileX;
    return tileID << TTraits::GetTileIDShift();
}

//////////////////////////////////////////////////////////////////////////
/// @brief Computes the byte offset for 2D tiled surfaces
/// @param pitch - surface pitch in bytes
/// @param x - x offset in bytes
/// @param y - y offset in rows
template<typename TTraits>
INLINE UINT ComputeOffset2D(UINT pitch, UINT x, UINT y)
{
    UINT tileID = ComputeTileOffset2D<TTraits>(pitch, x >> TTraits::GetCu(), y >> TTraits::GetCv());
    UINT xSwizzle = pdep_u32(x, TTraits::GetPdepX());
    UINT ySwizzle = pdep_u32(y, TTraits::GetPdepY());
    return (tileID | xSwizzle | ySwizzle);
}

#if KNOB_ARCH <= KNOB_ARCH_AVX
//////////////////////////////////////////////////////////////////////////
/// @brief Computes the byte offset for 2D tiled surfaces. Specialization
///        for tile-y surfaces that uses bit twiddling instead of pdep emulation.
/// @param pitch - surface pitch in bytes
/// @param x - x offset in bytes
/// @param y - y offset in rows
template<>
INLINE UINT ComputeOffset2D<TilingTraits<SWR_TILE_MODE_YMAJOR, 32> >(UINT pitch, UINT x, UINT y)
{
    typedef TilingTraits<SWR_TILE_MODE_YMAJOR, 32> TTraits;

    UINT tileID = ComputeTileOffset2D<TTraits>(pitch, x >> TTraits::GetCu(), y >> TTraits::GetCv());
    UINT xSwizzle = ((x << 5) & 0xe00) | (x & 0xf);
    UINT ySwizzle = (y << 4) & 0x1f0;
    return (tileID | xSwizzle | ySwizzle);
}
#endif

//////////////////////////////////////////////////////////////////////////
/// @brief Computes the byte offset for 3D tiled surfaces
/// @param qpitch - depth pitch in rows
/// @param pitch - surface pitch in bytes
/// @param x - x offset in bytes
/// @param y - y offset in rows
/// @param z - y offset in slices
template<typename TTraits>
INLINE UINT ComputeOffset3D(UINT qpitch, UINT pitch, UINT x, UINT y, UINT z)
{
    UINT tileID = ComputeTileOffset3D<TTraits>(qpitch, pitch, x >> TTraits::GetCu(), y >> TTraits::GetCv(), z >> TTraits::GetCr());
    UINT xSwizzle = pdep_u32(x, TTraits::GetPdepX());
    UINT ySwizzle = pdep_u32(y, TTraits::GetPdepY());
    return (tileID | xSwizzle | ySwizzle);
}
