// Copyright (c) 2018-2020 Intel Corporation
//
// 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 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.

#include "mfx_common.h"
#if defined(MFX_ENABLE_H265_VIDEO_ENCODE)
#include "mfx_h265_encode_hw_utils.h"
#include "mfx_h265_encode_hw_ddi.h"
#include "mfx_enc_common.h"
#include <assert.h>
#include <math.h>
#include <algorithm>

namespace MfxHwH265Encode
{

const mfxU32 TableA1[][6] =
{
//  Level   |MaxLumaPs |    MaxCPB    | MaxSlice  | MaxTileRows | MaxTileCols
//                                      Segments  |
//                                      PerPicture|
/*  1   */ {   36864,      350,    350,        16,            1,           1},
/*  2   */ {  122880,     1500,   1500,        16,            1,           1},
/*  2.1 */ {  245760,     3000,   3000,        20,            1,           1},
/*  3   */ {  552960,     6000,   6000,        30,            2,           2},
/*  3.1 */ {  983040,    10000,  10000,        40,            3,           3},
/*  4   */ { 2228224,    12000,  30000,        75,            5,           5},
/*  4.1 */ { 2228224,    20000,  50000,        75,            5,           5},
/*  5   */ { 8912896,    25000, 100000,       200,           11,          10},
/*  5.1 */ { 8912896,    40000, 160000,       200,           11,          10},
/*  5.2 */ { 8912896,    60000, 240000,       200,           11,          10},
/*  6   */ {35651584,    60000, 240000,       600,           22,          20},
/*  6.1 */ {35651584,   120000, 480000,       600,           22,          20},
/*  6.2 */ {35651584,   240000, 800000,       600,           22,          20},
};

const mfxU32 TableA2[][4] =
{
//  Level   | MaxLumaSr |      MaxBR   | MinCr
/*  1    */ {    552960,    128,    128,    2},
/*  2    */ {   3686400,   1500,   1500,    2},
/*  2.1  */ {   7372800,   3000,   3000,    2},
/*  3    */ {  16588800,   6000,   6000,    2},
/*  3.1  */ {  33177600,  10000,  10000,    2},
/*  4    */ {  66846720,  12000,  30000,    4},
/*  4.1  */ { 133693440,  20000,  50000,    4},
/*  5    */ { 267386880,  25000, 100000,    6},
/*  5.1  */ { 534773760,  40000, 160000,    8},
/*  5.2  */ {1069547520,  60000, 240000,    8},
/*  6    */ {1069547520,  60000, 240000,    8},
/*  6.1  */ {2139095040, 120000, 480000,    8},
/*  6.2  */ {4278190080, 240000, 800000,    6},
};

const mfxU16 MaxLidx = (sizeof(TableA1) / sizeof(TableA1[0]));

const mfxU16 LevelIdxToMfx[] =
{
    MFX_LEVEL_HEVC_1 ,
    MFX_LEVEL_HEVC_2 ,
    MFX_LEVEL_HEVC_21,
    MFX_LEVEL_HEVC_3 ,
    MFX_LEVEL_HEVC_31,
    MFX_LEVEL_HEVC_4 ,
    MFX_LEVEL_HEVC_41,
    MFX_LEVEL_HEVC_5 ,
    MFX_LEVEL_HEVC_51,
    MFX_LEVEL_HEVC_52,
    MFX_LEVEL_HEVC_6 ,
    MFX_LEVEL_HEVC_61,
    MFX_LEVEL_HEVC_62,
};

inline mfxU16 MaxTidx(mfxU16 l) { return (LevelIdxToMfx[std::min(l, MaxLidx)] >= MFX_LEVEL_HEVC_4); }

mfxU16 LevelIdx(mfxU16 mfx_level)
{
    switch (mfx_level & 0xFF)
    {
    case  MFX_LEVEL_HEVC_1  : return  0;
    case  MFX_LEVEL_HEVC_2  : return  1;
    case  MFX_LEVEL_HEVC_21 : return  2;
    case  MFX_LEVEL_HEVC_3  : return  3;
    case  MFX_LEVEL_HEVC_31 : return  4;
    case  MFX_LEVEL_HEVC_4  : return  5;
    case  MFX_LEVEL_HEVC_41 : return  6;
    case  MFX_LEVEL_HEVC_5  : return  7;
    case  MFX_LEVEL_HEVC_51 : return  8;
    case  MFX_LEVEL_HEVC_52 : return  9;
    case  MFX_LEVEL_HEVC_6  : return 10;
    case  MFX_LEVEL_HEVC_61 : return 11;
    case  MFX_LEVEL_HEVC_62 : return 12;
    default: break;
    }
    assert("unknown level");
    return 0;
}

mfxU16 TierIdx(mfxU16 mfx_level)
{
    return !!(mfx_level & MFX_TIER_HEVC_HIGH);
}

mfxU16 MfxLevel(mfxU16 l, mfxU16 t){ return LevelIdxToMfx[std::min(l, MaxLidx)] | (MFX_TIER_HEVC_HIGH * !!t); }

mfxU32 GetMaxDpbSize(mfxU32 PicSizeInSamplesY, mfxU32 MaxLumaPs, mfxU32 maxDpbPicBuf)
{
    if (PicSizeInSamplesY <= (MaxLumaPs >> 2))
        return std::min(4 * maxDpbPicBuf, 16U);
    else if (PicSizeInSamplesY <= (MaxLumaPs >> 1))
        return std::min( 2 * maxDpbPicBuf, 16U);
    else if (PicSizeInSamplesY <= ((3 * MaxLumaPs) >> 2))
        return std::min( (4 * maxDpbPicBuf ) / 3, 16U);

    return maxDpbPicBuf;
}

mfxStatus CheckProfile(mfxVideoParam& par, eMFXHWType platform)
{
    mfxStatus sts = MFX_ERR_NONE;

#if (MFX_VERSION >= 1027)
    mfxExtHEVCParam* pHevcPar = ExtBuffer::Get(par);
    mfxExtCodingOption3* pCO3 = ExtBuffer::Get(par);

    mfxU16 ChromaFormat = pCO3 ? pCO3->TargetChromaFormatPlus1 - 1 : par.mfx.FrameInfo.ChromaFormat;
    mfxU16 BitDepth = pCO3 ? pCO3->TargetBitDepthLuma : par.mfx.FrameInfo.BitDepthLuma;

    if (pCO3)
    {
        ChromaFormat = pCO3->TargetChromaFormatPlus1 - 1;
        BitDepth = pCO3->TargetBitDepthLuma;
    }

    switch (par.mfx.CodecProfile)
    {
    case 0:
        break;

    case MFX_PROFILE_HEVC_REXT:
        if (platform < MFX_HW_ICL)
            return MFX_ERR_INVALID_VIDEO_PARAM;

        if (pHevcPar)
        {
            mfxU64 REXTConstr = pHevcPar->GeneralConstraintFlags;

            if (   ((REXTConstr & MFX_HEVC_CONSTR_REXT_MAX_8BIT)  && BitDepth > 8)
                || ((REXTConstr & MFX_HEVC_CONSTR_REXT_MAX_10BIT) && BitDepth > 10)
                || ((REXTConstr & MFX_HEVC_CONSTR_REXT_MAX_12BIT) && BitDepth > 12)
                || ((REXTConstr & MFX_HEVC_CONSTR_REXT_MAX_420CHROMA) && ChromaFormat > MFX_CHROMAFORMAT_YUV420)
                || ((REXTConstr & MFX_HEVC_CONSTR_REXT_MAX_422CHROMA) && ChromaFormat > MFX_CHROMAFORMAT_YUV422))
                return MFX_ERR_INVALID_VIDEO_PARAM;
        }
        break;
    case MFX_PROFILE_HEVC_MAIN10:
        if (BitDepth != 10 && BitDepth !=0)
            return MFX_ERR_INVALID_VIDEO_PARAM;
        break;

    case MFX_PROFILE_HEVC_MAINSP:
        if (par.mfx.GopPicSize > 1)
        {
            par.mfx.GopPicSize = 1;
            sts = MFX_WRN_INCOMPATIBLE_VIDEO_PARAM;
        }
        break;

    case MFX_PROFILE_HEVC_MAIN:
        if (BitDepth != 8 && BitDepth !=0)
            return MFX_ERR_INVALID_VIDEO_PARAM;
        break;

    default:
        return MFX_ERR_INVALID_VIDEO_PARAM;
    }
#else
    (void)platform;

    switch (par.mfx.CodecProfile)
    {
    case 0:
        break;

    case MFX_PROFILE_HEVC_MAIN10:
        if (par.mfx.FrameInfo.BitDepthLuma != 10 && par.mfx.FrameInfo.BitDepthLuma !=0)
            return MFX_ERR_INVALID_VIDEO_PARAM;
        break;

    case MFX_PROFILE_HEVC_MAINSP:
        if (par.mfx.GopPicSize > 1)
        {
            par.mfx.GopPicSize = 1;
            sts = MFX_WRN_INCOMPATIBLE_VIDEO_PARAM;
        }
        break;

    case MFX_PROFILE_HEVC_MAIN:
        if (par.mfx.FrameInfo.BitDepthLuma != 8 && par.mfx.FrameInfo.BitDepthLuma !=0)
            return MFX_ERR_INVALID_VIDEO_PARAM;
        break;

    default:
        return MFX_ERR_INVALID_VIDEO_PARAM;
    }
#endif

    return sts;
}
mfxU16 getNumBPyrLayers(mfxU16 GopRefDist)
{
    if (GopRefDist < 3)
        return 1;
    mfxU16 refB = GopRefDist - 1;
    mfxU16 num_layers = 0;
    for (mfxU16 x = refB; x > 0;  x = (x - 1) / 2)
    {
        num_layers++;
    }
    return num_layers;
}
mfxU16 minRefForPyramid(mfxU16 GopRefDist, bool bField)
{
    assert(GopRefDist > 0);
    mfxU16 refB  = (GopRefDist - 1) / 2;

    for (mfxU16 x = refB; x > 2;)
    {
        x     = (x - 1) / 2;
        refB -= x;
    }

    return bField ? ((2 + refB)*2 +1) : (2 + refB);
}

mfxU32 GetMaxDpbSizeByLevel(MfxVideoParam const & par)
{
    assert(par.mfx.CodecLevel != 0);

    mfxU32 MaxLumaPs         = TableA1[LevelIdx(par.mfx.CodecLevel)][0];
    mfxU32 PicSizeInSamplesY = (mfxU32)par.m_ext.HEVCParam.PicWidthInLumaSamples * par.m_ext.HEVCParam.PicHeightInLumaSamples;

    return GetMaxDpbSize(PicSizeInSamplesY, MaxLumaPs, 6);
}

mfxU32 GetMaxKbpsByLevel(MfxVideoParam const & par)
{
    assert(par.mfx.CodecLevel != 0);

    return TableA2[LevelIdx(par.mfx.CodecLevel)][1+TierIdx(par.mfx.CodecLevel)] * 1100 / 1000;
}

mfxF64 GetMaxFrByLevel(MfxVideoParam const & par)
{
    assert(par.mfx.CodecLevel != 0);

    mfxU32 MaxLumaSr   = TableA2[LevelIdx(par.mfx.CodecLevel)][0];
    mfxU32 PicSizeInSamplesY = (mfxU32)par.m_ext.HEVCParam.PicWidthInLumaSamples * par.m_ext.HEVCParam.PicHeightInLumaSamples;

    return (mfxF64)MaxLumaSr / PicSizeInSamplesY;
}

mfxU32 GetMaxCpbInKBByLevel(MfxVideoParam const & par)
{
    assert(par.mfx.CodecLevel != 0);

    return TableA1[LevelIdx(par.mfx.CodecLevel)][1+TierIdx(par.mfx.CodecLevel)] * 1100 / 8000;
}

mfxStatus CorrectLevel(MfxVideoParam& par, bool bCheckOnly)
{
    if ( par.mfx.FrameInfo.FrameRateExtD == 0 || par.mfx.FrameInfo.FrameRateExtN == 0 ||par.mfx.GopRefDist == 0)
        return MFX_ERR_NONE;

    mfxStatus sts = MFX_ERR_NONE;
    //mfxU32 CpbBrVclFactor    = 1000;
    mfxU32 CpbBrNalFactor    = 1100;
    mfxU32 PicSizeInSamplesY = (mfxU32)par.m_ext.HEVCParam.PicWidthInLumaSamples * par.m_ext.HEVCParam.PicHeightInLumaSamples;
    mfxU32 LumaSr            = Ceil(mfxF64(PicSizeInSamplesY) * par.mfx.FrameInfo.FrameRateExtN / par.mfx.FrameInfo.FrameRateExtD);
    mfxU16 NewLevel          = par.mfx.CodecLevel;

    mfxU16 lidx = LevelIdx(par.mfx.CodecLevel);
    mfxU16 tidx = TierIdx(par.mfx.CodecLevel);

    if (tidx > MaxTidx(lidx))
        tidx = 0;

    while (lidx < MaxLidx)
    {
        mfxU32 MaxLumaPs   = TableA1[lidx][0];
        mfxU32 MaxCPB      = TableA1[lidx][1+tidx];
        mfxU32 MaxSSPP     = TableA1[lidx][3];
        mfxU32 MaxTileRows = TableA1[lidx][4];
        mfxU32 MaxTileCols = TableA1[lidx][5];
        mfxU32 MaxLumaSr   = TableA2[lidx][0];
        mfxU32 MaxBR       = TableA2[lidx][1+tidx];
        //mfxU32 MinCR       = TableA2[lidx][3];
        mfxU32 MaxDpbSize  = GetMaxDpbSize(PicSizeInSamplesY, MaxLumaPs, 6);

        if (   PicSizeInSamplesY > MaxLumaPs
            || par.m_ext.HEVCParam.PicWidthInLumaSamples > sqrt((mfxF64)MaxLumaPs * 8)
            || par.m_ext.HEVCParam.PicHeightInLumaSamples > sqrt((mfxF64)MaxLumaPs * 8)
            || (mfxU32)par.mfx.NumRefFrame + 1 > MaxDpbSize
            || par.m_ext.HEVCTiles.NumTileColumns > MaxTileCols
            || par.m_ext.HEVCTiles.NumTileRows > MaxTileRows
            || (mfxU32)par.mfx.NumSlice > MaxSSPP
            || (par.isBPyramid() && MaxDpbSize < minRefForPyramid(par.mfx.GopRefDist,par.isField())))
        {
            lidx ++;
            continue;
        }

        //if (par.mfx.RateControlMethod != MFX_RATECONTROL_CQP)
        {
            if (   par.BufferSizeInKB * 8000 > CpbBrNalFactor * MaxCPB
                || LumaSr > MaxLumaSr
                || par.MaxKbps * 1000 > CpbBrNalFactor * MaxBR
                || par.TargetKbps * 1000 > CpbBrNalFactor * MaxBR)
            {
                if (tidx >= MaxTidx(lidx))
                {
                    lidx ++;
                    tidx = 0;
                }
                else
                    tidx ++;

                continue;
            }
        }

        break;
    }

    NewLevel = MfxLevel(lidx, tidx);

    if (par.mfx.CodecLevel != NewLevel)
    {
        par.mfx.CodecLevel = bCheckOnly ? 0 : NewLevel;
        sts = MFX_WRN_INCOMPATIBLE_VIDEO_PARAM;
    }
    return sts;
}

mfxU16 AddTileSlices(
    MfxVideoParam& par,
    mfxU32 SliceStructure,
    mfxU32 nCol,
    mfxU32 nRow,
    mfxU32 nSlice)
{
    mfxU32 nLCU = nCol * nRow;
    mfxU32 nLcuPerSlice = nLCU / nSlice;
    mfxU32 nSlicePrev = (mfxU32)par.m_slice.size();

    if (par.m_ext.CO2.NumMbPerSlice != 0)
        nLcuPerSlice = par.m_ext.CO2.NumMbPerSlice;

    if (SliceStructure == 0)
    {
        nSlice = 1;
        nLcuPerSlice = nLCU;
    }
    else if (SliceStructure == 2 && par.m_ext.CO2.NumMbPerSlice != 0)
    {
        //in case of NumMbPerSlice != 0 need to check alignment
        //if it's value is not aligned, warning will be generated in CheckVideoParam() after MakeSlices() call
        nLcuPerSlice = CeilDiv(par.m_ext.CO2.NumMbPerSlice, nCol) * nCol;
    }
    else if (SliceStructure < 4)
    {
        nSlice = std::min(nSlice, nRow);
        mfxU32 nRowsPerSlice = CeilDiv(nRow, nSlice);

        if (SliceStructure == 1)
        {
            mfxU32 r0 = nRowsPerSlice;
            mfxU32 r1 = nRowsPerSlice;

            while (((r0 & (r0 - 1)) || (nRow % r0)) && r0 < nRow)
                r0 ++;

            while (((r1 & (r1 - 1)) || (nRow % r1)) && r1)
                r1 --;

            nRowsPerSlice = (r1 > 0 && (nRowsPerSlice - r1 < r0 - nRowsPerSlice)) ? r1 : r0;

            nSlice = CeilDiv(nRow, nRowsPerSlice);
        }
        else
        {
            while (nRowsPerSlice * (nSlice - 1) >= nRow)
            {
                nSlice ++;
                nRowsPerSlice = CeilDiv(nRow, nSlice);
            }
        }

        nLcuPerSlice = nRowsPerSlice * nCol;
    }

    par.m_slice.resize(nSlicePrev + nSlice);
    if (SliceStructure == 2) {
        mfxU32 i = nSlicePrev;
        for (; i < par.m_slice.size(); i++)
        {
            par.m_slice[i].NumLCU = nLcuPerSlice;
            par.m_slice[i].SegmentAddress = (i > 0) ? (par.m_slice[i - 1].SegmentAddress + par.m_slice[i - 1].NumLCU) : 0;
        }
        if (par.m_slice.size() > 1)
        {
            par.m_slice[i - 1].NumLCU = par.m_slice[nSlicePrev].SegmentAddress + nLCU - par.m_slice[i - 1].SegmentAddress;
        }
    }
    else {
        mfxU32 f = (nSlice < 2 || (nLCU % nSlice == 0) || par.m_ext.CO2.NumMbPerSlice) ? 0 : (nLCU % nSlice);
        bool   s = false;

        if (f == 0)
        {
            (void)f;
        }
        else if (f > nSlice / 2)
        {
            s = false;
            nLcuPerSlice += 1;
            f = (nSlice / (nSlice - f));

            while ((nLcuPerSlice * nSlice - (nLCU / f) > nLCU) && f)
                f--;
        }
        else
        {
            s = true;
            f = (nSlice % f == 0) ? nSlice / f : nSlice / f + 1;
        }

        mfxU32 i = nSlicePrev;
        for (; i < par.m_slice.size(); i++)
        {
            par.m_slice[i].NumLCU = (i == nSlicePrev + nSlice - 1) ? nLCU : nLcuPerSlice + s * (f && i % f == 0) - !s * (f && i % f == 0);
            par.m_slice[i].SegmentAddress = (i > 0) ? (par.m_slice[i - 1].SegmentAddress + par.m_slice[i - 1].NumLCU) : 0;

            if (i != 0)
                par.m_slice[i - 1].NumLCU = par.m_slice[i].SegmentAddress - par.m_slice[i - 1].SegmentAddress;
        }
        if ((i > 0) && ((i - 1) < par.m_slice.size()))
        {
            par.m_slice[i - 1].NumLCU = par.m_slice[nSlicePrev].SegmentAddress + nLCU - par.m_slice[i - 1].SegmentAddress;
        }
    }
    return (mfxU16)nSlice;
}

struct tmpTileInfo
{
    mfxU32 id;
    mfxU32 nCol;
    mfxU32 nRow;
    mfxU32 nLCU;
    mfxU32 nSlice;
};

inline mfxF64 nSliceCoeff(tmpTileInfo const & tile)
{
    assert(tile.nSlice > 0);
    return (mfxF64(tile.nLCU) / tile.nSlice);
}

mfxU16 MakeSlices(MfxVideoParam& par, mfxU32 SliceStructure)
{
    mfxU32 nCol   = CeilDiv(par.m_ext.HEVCParam.PicWidthInLumaSamples, par.LCUSize);
    mfxU32 nRow   = CeilDiv(par.m_ext.HEVCParam.PicHeightInLumaSamples, par.LCUSize);
    mfxU32 nTCol  = std::max<mfxU32>(par.m_ext.HEVCTiles.NumTileColumns, 1);
    mfxU32 nTRow  = std::max<mfxU32>(par.m_ext.HEVCTiles.NumTileRows,    1);
    mfxU32 nTile  = nTCol * nTRow;
    mfxU32 nLCU   = nCol * nRow;
    mfxU32 nSlice = std::min<mfxU32>({nLCU, MAX_SLICES, std::max<mfxU32>(par.mfx.NumSlice, 1)});

#if (MFX_VERSION >= 1027)
    /*
        A tile must wholly contain all the slices within it. Slices cannot cross tile boundaries.
        If a slice contains more than one tile, it must contain all the tiles in the frame, i.e. there can only be one slice in the frame.
        Normally, each tile will contain a single slice. So  2x2 and 4 slices is normal.
        Fewer slices than tiles is illegal (e.g. 3 slice, 2x2 tiles)
        More slices than tiles is legal as long as the slices do not cross tile boundaries; the app needs to use the slice segment address and number of LCUs in the slice to define the slices within the tiles.
    */
    nSlice = std::max(nSlice, nTile);

    if (par.m_platform >= MFX_HW_ICL && IsOn(par.mfx.LowPower)) {
        if (nTile == 1)
            SliceStructure = 2;
    }
#endif // MFX_VERSION >= 1027

    par.m_slice.resize(0);
    if (par.m_ext.CO2.NumMbPerSlice != 0)
        nSlice = CeilDiv(nLCU / nTile, par.m_ext.CO2.NumMbPerSlice) * nTile;

    if (SliceStructure == 0)
        nSlice = 1;

    if (nSlice > 1)
        nSlice = std::max(nSlice, nTile);

    if (nTile == 1) //TileScan = RasterScan, no SegmentAddress conversion required
        return (mfxU16)AddTileSlices(par, SliceStructure, nCol, nRow, nSlice);

    if (nTile > 1 && nSlice > nTile && IsOn(par.mfx.LowPower))
    {
        // DDI:0.9972: VDEnc supports multiple static slices in case of single tile frame only.
        //             But for multi tiles frame, no single tile can contain multiple static slices.
        // Actually it's no matter for driver whether dynamic or static slices are used
        // It returns an error in the both cases.
        nSlice = nTile;
    }

    std::vector<mfxU32> colWidth(nTCol, 0);
    std::vector<mfxU32> rowHeight(nTRow, 0);
    std::vector<mfxU32> colBd(nTCol+1, 0);
    std::vector<mfxU32> rowBd(nTRow+1, 0);
    std::vector<mfxU32> TsToRs(nLCU);

    //assume uniform spacing
    for (mfxU32 i = 0; i < nTCol; i ++)
        colWidth[i] = ((i + 1) * nCol) / nTCol - (i * nCol) / nTCol;

    for (mfxU32 j = 0; j < nTRow; j ++)
        rowHeight[j] = ((j + 1) * nRow) / nTRow - (j * nRow) / nTRow;

    for (mfxU32 i = 0; i < nTCol; i ++)
        colBd[i + 1] = colBd[i] + colWidth[i];

    for (mfxU32 j = 0; j < nTRow; j ++)
        rowBd[j + 1] = rowBd[j] + rowHeight[j];

    for (mfxU32 rso = 0; rso < nLCU; rso ++)
    {
        mfxU32 tbX = rso % nCol;
        mfxU32 tbY = rso / nCol;
        mfxU32 tileX = 0;
        mfxU32 tileY = 0;
        mfxU32 tso   = 0;

        for (mfxU32 i = 0; i < nTCol; i ++)
            if (tbX >= colBd[i])
                tileX = i;

        for (mfxU32 j = 0; j < nTRow; j ++)
            if (tbY >= rowBd[j])
                tileY = j;

        for (mfxU32 i = 0; i < tileX; i ++)
            tso += rowHeight[tileY] * colWidth[i];

        for (mfxU32 j = 0; j < tileY; j ++)
            tso += nCol * rowHeight[j];

        tso += (tbY - rowBd[tileY]) * colWidth[tileX] + tbX - colBd[tileX];

        assert(tso < nLCU);

        TsToRs[tso] = rso;
    }

    if (nSlice == 1)
    {
        AddTileSlices(par, SliceStructure, nCol, nRow, nSlice);
    }
    else
    {
        std::vector<tmpTileInfo> tile(nTile);
        mfxU32 id = 0;
        mfxU32 nLcuPerSlice = CeilDiv(nLCU, nSlice);
        mfxU32 nSliceRest   = nSlice;

        for (mfxU32 j = 0; j < nTRow; j ++)
        {
            for (mfxU32 i = 0; i < nTCol; i ++)
            {
                tile[id].id = id;
                tile[id].nCol = colWidth[i];
                tile[id].nRow = rowHeight[j];
                tile[id].nLCU = tile[id].nCol * tile[id].nRow;
                tile[id].nSlice = std::max(1U, tile[id].nLCU / nLcuPerSlice);
                nSliceRest -= tile[id].nSlice;
                id ++;
            }
        }

        if (nSliceRest)
        {
            while (nSliceRest)
            {
                MFX_SORT_COMMON(tile, tile.size(), nSliceCoeff(tile[_i]) < nSliceCoeff(tile[_j]));

                assert(tile[0].nLCU > tile[0].nSlice);

                tile[0].nSlice ++;
                nSliceRest --;
            }

            MFX_SORT_STRUCT(tile, tile.size(), id, >);
        }

        for (mfxU32 i = 0; i < tile.size(); i ++)
            AddTileSlices(par, SliceStructure, tile[i].nCol, tile[i].nRow, tile[i].nSlice);
    }

    for (mfxU32 i = 0; i < par.m_slice.size(); i ++)
    {
        assert(par.m_slice[i].SegmentAddress < nLCU);

        par.m_slice[i].SegmentAddress = TsToRs[par.m_slice[i].SegmentAddress];
    }

    assert(par.m_slice.size() <= MAX_SLICES);

    return (mfxU16)par.m_slice.size();
}

bool CheckTriStateOption(mfxU16 & opt)
{
    if (opt != MFX_CODINGOPTION_UNKNOWN &&
        opt != MFX_CODINGOPTION_ON &&
        opt != MFX_CODINGOPTION_OFF)
    {
        opt = MFX_CODINGOPTION_UNKNOWN;
        return true;
    }

    return false;
}

template <class T, class U0>
bool CheckMin(T & opt, U0 min)
{
    if (opt < min)
    {
        opt = T(min);
        return true;
    }

    return false;
}

template <class T, class U0>
bool CheckMax(T & opt, U0 max)
{
    if (opt > max)
    {
        opt = T(max);
        return true;
    }

    return false;
}

template <class T, class U0, class U1, class U2>
bool CheckRangeDflt(T & opt, U0 min, U1 max, U2 deflt)
{
    if ((opt < T(min) || opt > T(max)) && opt != T(deflt))
    {
        opt = T(deflt);
        return true;
    }

    return false;
}

template <class T, class U0>
bool CheckOption(T & opt, U0 deflt)
{
    if (opt == T(deflt))
        return false;

    opt = T(deflt);
    return true;
}

template <class T, class U0, class U1>
bool CheckOption(T & opt, U0 deflt, U1 s0)
{
    if (opt == T(deflt)) return false;
    if (CheckOption(opt, (T)s0))
    {
        opt = T(deflt);
        return true;
    }
    return false;
}
template <class T, class U0, class U1, class U2>
bool CheckOption(T & opt, U0 deflt, U1 s0, U2 s1)
{
    if (opt == T(deflt)) return false;
    if (CheckOption(opt, (T)s0, (T)s1))
    {
        opt = T(deflt);
        return true;
    }
    return false;
}
template <class T, class U0, class U1, class U2, class U3>
bool CheckOption(T & opt, U0 deflt, U1 s0, U2 s1, U3 s2)
{
    if (opt == T(deflt)) return false;
    if (CheckOption(opt, (T)s0, (T)s1, (T)s2))
    {
        opt = T(deflt);
        return true;
    }
    return false;
}
template <class T, class U0, class U1, class U2, class U3, class U4>
bool CheckOption(T & opt, U0 deflt, U1 s0, U2 s1, U3 s2, U4 s3)
{
    if (opt == T(deflt)) return false;
    if (CheckOption(opt, (T)s0, (T)s1, (T)s2, (T)s3))
    {
        opt = T(deflt);
        return true;
    }
    return false;
}
template <class T, class U0, class U1, class U2, class U3, class U4, class U5>
bool CheckOption(T & opt, U0 deflt, U1 s0, U2 s1, U3 s2, U4 s3, U5 s4)
{
    if (opt == T(deflt)) return false;
    if (CheckOption(opt, (T)s0, (T)s1, (T)s2, (T)s3, (T)s4))
    {
        opt = T(deflt);
        return true;
    }
    return false;
}
template <class T, class U0, class U1, class U2, class U3, class U4, class U5, class U6>
bool CheckOption(T & opt, U0 deflt, U1 s0, U2 s1, U3 s2, U4 s3, U5 s4, U6 s5)
{
    if (opt == T(deflt)) return false;
    if (CheckOption(opt, (T)s0, (T)s1, (T)s2, (T)s3, (T)s4, (T)s5))
    {
        opt = T(deflt);
        return true;
    }
    return false;
}
template <class T, class U0, class U1, class U2, class U3, class U4, class U5, class U6, class U7>
bool CheckOption(T & opt, U0 deflt, U1 s0, U2 s1, U3 s2, U4 s3, U5 s4, U6 s5, U7 s6)
{
    if (opt == T(deflt)) return false;
    if (CheckOption(opt, (T)s0, (T)s1, (T)s2, (T)s3, (T)s4, (T)s5, (T)s6))
    {
        opt = T(deflt);
        return true;
    }
    return false;
}
template <class T, class U0, class U1, class U2, class U3, class U4, class U5, class U6, class U7, class U8>
bool CheckOption(T & opt, U0 deflt, U1 s0, U2 s1, U3 s2, U4 s3, U5 s4, U6 s5, U7 s6, U8 s7)
{
    if (opt == T(deflt)) return false;
    if (CheckOption(opt, (T)s0, (T)s1, (T)s2, (T)s3, (T)s4, (T)s5, (T)s6, (T)s7))
    {
        opt = T(deflt);
        return true;
    }
    return false;
}
template <class T, class U0, class U1, class U2, class U3, class U4, class U5, class U6, class U7, class U8, class U9>
bool CheckOption(T & opt, U0 deflt, U1 s0, U2 s1, U3 s2, U4 s3, U5 s4, U6 s5, U7 s6, U8 s7, U9 s8)
{
    if (opt == T(deflt)) return false;
    if (CheckOption(opt, (T)s0, (T)s1, (T)s2, (T)s3, (T)s4, (T)s5, (T)s6, (T)s7, (T)s8))
    {
        opt = T(deflt);
        return true;
    }
    return false;
}
template <class T, class U0, class U1, class U2, class U3, class U4, class U5, class U6, class U7, class U8, class U9, class U10>
bool CheckOption(T & opt, U0 deflt, U1 s0, U2 s1, U3 s2, U4 s3, U5 s4, U6 s5, U7 s6, U8 s7, U9 s8, U10 s9)
{
    if (opt == T(deflt)) return false;
    if (CheckOption(opt, (T)s0, (T)s1, (T)s2, (T)s3, (T)s4, (T)s5, (T)s6, (T)s7, (T)s8, (T)s9))
    {
        opt = T(deflt);
        return true;
    }
    return false;
}
template <class T, class U0, class U1, class U2, class U3, class U4, class U5, class U6, class U7, class U8, class U9, class U10, class U11>
bool CheckOption(T & opt, U0 deflt, U1 s0, U2 s1, U3 s2, U4 s3, U5 s4, U6 s5, U7 s6, U8 s7, U9 s8, U10 s9, U11 s10)
{
    if (opt == T(deflt)) return false;
    if (CheckOption(opt, (T)s0, (T)s1, (T)s2, (T)s3, (T)s4, (T)s5, (T)s6, (T)s7, (T)s8, (T)s9, (T)s10))
    {
        opt = T(deflt);
        return true;
    }
    return false;
}

bool CheckTU(mfxU8 support, mfxU16& tu)
{
    assert(tu < 8);

    mfxI16 abs_diff = 0;
    bool   sign = 0;
    mfxI16 newtu = tu;
    bool   changed = false;

    do
    {
        newtu = tu + (1 - 2 * sign) * abs_diff;
        abs_diff += !sign;
        sign = !sign;
    } while (!(support & (1 << (newtu - 1))) && newtu > 0);

    changed = (tu != newtu);
    tu      = newtu;

    return changed;
}

mfxU32 GetDefaultLCUSize(MfxVideoParam const & par,
                         ENCODE_CAPS_HEVC const & hwCaps)
{
    mfxU32 LCUSize = 32;

#if (MFX_VERSION >= 1025)

    if (par.m_platform >= MFX_HW_CNL) {
        if (IsOn(par.mfx.LowPower))
            LCUSize = 64;//HW limitation only 64x64 LCU is supported
        else
            LCUSize = (1 << (CeilLog2(hwCaps.LCUSizeSupported + 1) + 3)); // set max supported
    }

#else
    (void)par;
    (void)hwCaps;
#endif // (MFX_VERSION >= 1025)

    assert((LCUSize >> 4) & hwCaps.LCUSizeSupported);

    return LCUSize;
}
#if (MFX_VERSION >= 1025)

//bool CheckLCUSize(mfxU32 LCUSizeSupported, mfxU32& LCUSize)
//{
//    /*
//    -    LCUSizeSupported - Supported LCU sizes, bit fields
//    0b001 : 16x16
//    0b010 : 32x32
//    0b100 : 64x64
//    */
//    assert(LCUSizeSupported > 0);
//
//    if (LCUSize == 0) { // zero is allowed
//        return true;
//    }
//
//    if ((LCUSize == 16) || (LCUSize == 32) || (LCUSize == 64)) {    // explicitly assigned lcusize
//        if ((LCUSize >> 4) & LCUSizeSupported)
//            return true;
//    }
//
//    LCUSize = 0;
//    return false;
//}


bool CheckLCUSize(mfxU32 LCUSizeSupported, mfxU16& LCUSize)
{
    /*
    -    LCUSizeSupported - Supported LCU sizes, bit fields
    0b001 : 16x16
    0b010 : 32x32
    0b100 : 64x64
    */
    assert(LCUSizeSupported > 0);

    if (LCUSize == 0) { // zero is allowed
        return true;
    }

    if ((LCUSize == 16) || (LCUSize == 32) || (LCUSize == 64)) {    // explicitly assigned lcusize
        if ((LCUSize >> 4) & LCUSizeSupported)
            return true;
    }

    LCUSize = 0;
    return false;
}

enum FType { // Frame type for GetMaxNumRef( , type)
    P = 0,
    BL0 = 1,
    BL1 = 2
};

mfxU16 GetMaxNumRef(MfxVideoParam &par, FType type)
{
    if (par.mfx.TargetUsage < 1 || par.mfx.TargetUsage > 7)
        return 0;

    if (IsOff(par.mfx.LowPower))
    {   // VME
        mfxU16 maxNumRefsL0[7] = { 4, 4, 3, 3, 3, 1, 1 };
        mfxU16 maxNumRefsL1[7] = { 2, 2, 1, 1, 1, 1, 1 };
        return (type != BL1) ? maxNumRefsL0[par.mfx.TargetUsage - 1] : maxNumRefsL1[par.mfx.TargetUsage - 1];
    }

    // VDENC
    if (type == P) { // LowDelay B (P)
        mfxU16 maxNumRefsL0L1[7] = { 3, 3, 2, 2, 2, 1, 1 };
        return maxNumRefsL0L1[par.mfx.TargetUsage - 1];
    }
    else { // RA B (neither ICL nor CNL here)
        mfxU16 maxNumRefsL0[7] = { 2, 2, 1, 1, 1, 1, 1 };
        mfxU16 maxNumRefsL1[7] = { 1, 1, 1, 1, 1, 1, 1 };
        return  (type == BL0) ? maxNumRefsL0[par.mfx.TargetUsage - 1] : maxNumRefsL1[par.mfx.TargetUsage - 1];
    }
}

#endif // (MFX_VERSION >= 1025)

#if defined (MFX_ENABLE_HEVCE_ROI) || defined (MFX_ENABLE_HEVCE_DIRTY_RECT)
mfxStatus CheckAndFixRect(RectData *rect, MfxVideoParam const & par, ENCODE_CAPS_HEVC const & caps) {
    mfxU32 changed = 0;

    mfxU32 blockSize = 1 << (caps.BlockSize + 3);
    changed += CheckRange(rect->Left,   0u, mfxU32(par.mfx.FrameInfo.Width));
    changed += CheckRange(rect->Right,  0u, mfxU32(par.mfx.FrameInfo.Width));
    changed += CheckRange(rect->Top,    0u, mfxU32(par.mfx.FrameInfo.Height));
    changed += CheckRange(rect->Bottom, 0u, mfxU32(par.mfx.FrameInfo.Height));
    // align to BlockSize size
    changed += AlignDown(rect->Left, blockSize);
    changed += AlignDown(rect->Top,  blockSize);
    changed += AlignUp(rect->Right,  blockSize);
    changed += AlignUp(rect->Bottom, blockSize);

    if (changed)
        return MFX_WRN_INCOMPATIBLE_VIDEO_PARAM;

    return MFX_ERR_NONE;
}
#endif

#ifdef MFX_ENABLE_HEVCE_ROI
mfxStatus CheckAndFixRoi(MfxVideoParam  const & par, ENCODE_CAPS_HEVC const & caps, mfxExtEncoderROI *ROI, bool &bROIViaMBQP)
{
    MFX_CHECK(ROI, MFX_ERR_INVALID_VIDEO_PARAM);
    mfxStatus sts = MFX_ERR_NONE, rsts = MFX_ERR_NONE;
    mfxU32 changed = 0, invalid = 0;

    bROIViaMBQP = false;

    if (!(par.mfx.RateControlMethod == MFX_RATECONTROL_CBR ||
        par.mfx.RateControlMethod == MFX_RATECONTROL_VBR ||
        par.mfx.RateControlMethod == MFX_RATECONTROL_CQP ||
        par.mfx.RateControlMethod == MFX_RATECONTROL_QVBR))
    {
        invalid++;
    }
    if (caps.MaxNumOfROI == 0)
    {
        if (par.isSWBRC())
            bROIViaMBQP = true;
        else
            invalid++;
    }

    if (par.mfx.RateControlMethod == MFX_RATECONTROL_CQP) {
        invalid += (caps.ROIDeltaQPSupport == 0);
    }
    else if (par.isSWBRC())
    {
        if (caps.ROIDeltaQPSupport == 0)
            bROIViaMBQP = true;

    }
    else
    {
#if MFX_VERSION > 1021
        if (!caps.ROIDeltaQPSupport && !caps.ROIBRCPriorityLevelSupport)
        {
            invalid++;
        }
        if (ROI->ROIMode == MFX_ROI_MODE_QP_DELTA && !caps.ROIDeltaQPSupport)
        {
            invalid++;
        }
#endif // MFX_VERSION > 1021
    }

    mfxU16 maxNumOfRoi = (caps.MaxNumOfROI <= MAX_NUM_ROI  && (!bROIViaMBQP)) ? caps.MaxNumOfROI : MAX_NUM_ROI;

    changed += CheckMax(ROI->NumROI, maxNumOfRoi);

    for (mfxU16 i = 0; i < ROI->NumROI; i++)
    {
        // check that rectangle dimensions don't conflict with each other and don't exceed frame size
        RectData *rect = (RectData *)&(ROI->ROI[i]);

        rsts = CheckAndFixRect(rect, par, caps);

        // Elimination of invalid rects
        // Driver requirement
        if (ROI->ROI[i].Left >= ROI->ROI[i].Right || ROI->ROI[i].Top >= ROI->ROI[i].Bottom) {
            ROI->NumROI--;
            for (mfxU16 j = i; j < ROI->NumROI; j++)
                ROI->ROI[j] = ROI->ROI[j + 1];
            changed++;
            i--;
            continue;
        }

        if (par.mfx.RateControlMethod == MFX_RATECONTROL_CQP) {
            invalid += CheckRange(ROI->ROI[i].DeltaQP, -51, 51);
        } else if (par.mfx.RateControlMethod) {
#if MFX_VERSION > 1021
            if (ROI->ROIMode == MFX_ROI_MODE_QP_DELTA)
                invalid += CheckRange(ROI->ROI[i].DeltaQP, -51, 51);
            else if (ROI->ROIMode == MFX_ROI_MODE_PRIORITY)
                invalid += CheckRange(ROI->ROI[i].Priority, -3, 3);
#else
        invalid += CheckRange(ROI->ROI[i].Priority, -3, 3);
#endif // MFX_VERSION > 1021
        }

#if MFX_VERSION > 1021
        if (ROI->ROIMode == MFX_ROI_MODE_PRIORITY && !caps.ROIBRCPriorityLevelSupport) {
            // Priority mode is not supported. Convert it to delta QP mode
            ROI->ROI[i].DeltaQP = (mfxI16)(-1 * par.m_ext.ROI.ROI[i].Priority);
        }
#endif
    }

#if MFX_VERSION > 1021
    if (ROI->NumROI && ROI->ROIMode == MFX_ROI_MODE_PRIORITY && !caps.ROIBRCPriorityLevelSupport)
    {
        // Priority mode is not supported. Convert it to delta QP mode
        ROI->ROIMode = MFX_ROI_MODE_QP_DELTA;
        changed++;
    }
#endif

    if (changed)
        sts = MFX_WRN_INCOMPATIBLE_VIDEO_PARAM;
    if (invalid)
        sts = MFX_ERR_INVALID_VIDEO_PARAM;

    return GetWorstSts(sts, rsts);
}

#endif // MFX_ENABLE_HEVCE_ROI

#ifdef MFX_ENABLE_HEVCE_DIRTY_RECT
mfxStatus CheckAndFixDirtyRect(ENCODE_CAPS_HEVC const & caps, MfxVideoParam const & par, mfxExtDirtyRect *DirtyRect)
{
    mfxStatus sts = MFX_ERR_NONE, rsts = MFX_ERR_NONE;
    mfxU32 changed = 0, invalid = 0;

    if (caps.DirtyRectSupport == 0)
    {
        invalid++;
        DirtyRect->NumRect = 0;
    }

    changed += CheckMax(DirtyRect->NumRect, MAX_NUM_DIRTY_RECT);

    for (mfxU16 i = 0; i < DirtyRect->NumRect; i++)
    {
        // check that rectangle dimensions don't conflict with each other and don't exceed frame size
        RectData *rect = (RectData *)&(DirtyRect->Rect[i]);
        // Dirty rectangle (0, 0, 0, 0) is a valid dirty rectangle and means that frame is not changed.
        if (rect->Left==0 && rect->Right==0 && rect->Top==0 && rect->Bottom==0) continue;

        rsts = CheckAndFixRect(rect, par, caps);

        // Elimination of invalid rects
        // Driver requirement
        if (rect->Left >= rect->Right || rect->Top >= rect->Bottom) {
            DirtyRect->NumRect--;
            for (mfxU16 j = i; j < DirtyRect->NumRect; j++)
                DirtyRect->Rect[j] = DirtyRect->Rect[j + 1];
            i--;
            changed++;
            continue;
        }
    }
    if (changed)
        sts = MFX_WRN_INCOMPATIBLE_VIDEO_PARAM;
    if (invalid)
        sts = MFX_ERR_INVALID_VIDEO_PARAM;

    return GetWorstSts(sts, rsts);
}

#endif // MFX_ENABLE_HEVCE_DIRTY_RECT

const mfxU16 AVBR_ACCURACY_MIN = 1;
const mfxU16 AVBR_ACCURACY_MAX = 65535;

const mfxU16 AVBR_CONVERGENCE_MIN = 1;
const mfxU16 AVBR_CONVERGENCE_MAX = 65535;

template <class T>
void InheritOption(T optInit, T & optReset)
{
    if (optReset == 0)
        optReset = optInit;
}

void InheritDefaultValues(
    MfxVideoParam const & parInit,
    MfxVideoParam &       parReset)
{
    InheritOption(parInit.AsyncDepth,             parReset.AsyncDepth);
    InheritOption(parInit.mfx.BRCParamMultiplier, parReset.mfx.BRCParamMultiplier);
    InheritOption(parInit.mfx.CodecId,            parReset.mfx.CodecId);
    InheritOption(parInit.mfx.CodecProfile,       parReset.mfx.CodecProfile);
    InheritOption(parInit.mfx.CodecLevel,         parReset.mfx.CodecLevel);
    InheritOption(parInit.mfx.NumThread,          parReset.mfx.NumThread);
    InheritOption(parInit.mfx.TargetUsage,        parReset.mfx.TargetUsage);
    InheritOption(parInit.mfx.GopPicSize,         parReset.mfx.GopPicSize);
    InheritOption(parInit.mfx.GopRefDist,         parReset.mfx.GopRefDist);
    InheritOption(parInit.mfx.GopOptFlag,         parReset.mfx.GopOptFlag);
    InheritOption(parInit.mfx.IdrInterval,        parReset.mfx.IdrInterval);
    InheritOption(parInit.mfx.RateControlMethod,  parReset.mfx.RateControlMethod);
    InheritOption(parInit.mfx.BufferSizeInKB,     parReset.mfx.BufferSizeInKB);
    InheritOption(parInit.mfx.NumSlice,           parReset.mfx.NumSlice);
    InheritOption(parInit.mfx.NumRefFrame,        parReset.mfx.NumRefFrame);
    InheritOption(parInit.mfx.LowPower,           parReset.mfx.LowPower);

    if (parInit.mfx.RateControlMethod == MFX_RATECONTROL_CBR && parReset.mfx.RateControlMethod == MFX_RATECONTROL_CBR)
    {

        InheritOption(parInit.mfx.InitialDelayInKB, parReset.mfx.InitialDelayInKB);
        InheritOption(parInit.mfx.TargetKbps,       parReset.mfx.TargetKbps);
    }

    if (parInit.mfx.RateControlMethod == MFX_RATECONTROL_VBR && parReset.mfx.RateControlMethod == MFX_RATECONTROL_VBR)
    {
        InheritOption(parInit.mfx.InitialDelayInKB, parReset.mfx.InitialDelayInKB);
        InheritOption(parInit.mfx.TargetKbps,       parReset.mfx.TargetKbps);
        InheritOption(parInit.mfx.MaxKbps,          parReset.mfx.MaxKbps);
    }

    if (parInit.mfx.RateControlMethod == MFX_RATECONTROL_CQP && parReset.mfx.RateControlMethod == MFX_RATECONTROL_CQP)
    {
        InheritOption(parInit.mfx.QPI, parReset.mfx.QPI);
        InheritOption(parInit.mfx.QPP, parReset.mfx.QPP);
        InheritOption(parInit.mfx.QPB, parReset.mfx.QPB);
    }

    if (parInit.mfx.RateControlMethod == MFX_RATECONTROL_AVBR && parReset.mfx.RateControlMethod == MFX_RATECONTROL_AVBR)
    {
        InheritOption(parInit.mfx.Accuracy,    parReset.mfx.Accuracy);
        InheritOption(parInit.mfx.Convergence, parReset.mfx.Convergence);
    }

    if (parInit.mfx.RateControlMethod == MFX_RATECONTROL_ICQ && parReset.mfx.RateControlMethod == MFX_RATECONTROL_LA_ICQ)
    {
        InheritOption(parInit.mfx.ICQQuality, parReset.mfx.ICQQuality);
    }

    if (parInit.mfx.RateControlMethod == MFX_RATECONTROL_VCM && parReset.mfx.RateControlMethod == MFX_RATECONTROL_VCM)
    {
        InheritOption(parInit.mfx.InitialDelayInKB, parReset.mfx.InitialDelayInKB);
        InheritOption(parInit.mfx.TargetKbps,       parReset.mfx.TargetKbps);
        InheritOption(parInit.mfx.MaxKbps,          parReset.mfx.MaxKbps);
    }


    InheritOption(parInit.mfx.FrameInfo.FourCC,         parReset.mfx.FrameInfo.FourCC);
    InheritOption(parInit.mfx.FrameInfo.Width,          parReset.mfx.FrameInfo.Width);
    InheritOption(parInit.mfx.FrameInfo.Height,         parReset.mfx.FrameInfo.Height);
    InheritOption(parInit.mfx.FrameInfo.CropX,          parReset.mfx.FrameInfo.CropX);
    InheritOption(parInit.mfx.FrameInfo.CropY,          parReset.mfx.FrameInfo.CropY);
    InheritOption(parInit.mfx.FrameInfo.CropW,          parReset.mfx.FrameInfo.CropW);
    InheritOption(parInit.mfx.FrameInfo.CropH,          parReset.mfx.FrameInfo.CropH);
    InheritOption(parInit.mfx.FrameInfo.FrameRateExtN,  parReset.mfx.FrameInfo.FrameRateExtN);
    InheritOption(parInit.mfx.FrameInfo.FrameRateExtD,  parReset.mfx.FrameInfo.FrameRateExtD);
    InheritOption(parInit.mfx.FrameInfo.AspectRatioW,   parReset.mfx.FrameInfo.AspectRatioW);
    InheritOption(parInit.mfx.FrameInfo.AspectRatioH,   parReset.mfx.FrameInfo.AspectRatioH);

    mfxExtHEVCParam const * extHEVCInit  = &parInit.m_ext.HEVCParam;
    mfxExtHEVCParam *       extHEVCReset = &parReset.m_ext.HEVCParam;

    InheritOption(extHEVCInit->GeneralConstraintFlags,  extHEVCReset->GeneralConstraintFlags);
    //InheritOption(extHEVCInit->PicHeightInLumaSamples,  extHEVCReset->PicHeightInLumaSamples);
    //InheritOption(extHEVCInit->PicWidthInLumaSamples,   extHEVCReset->PicWidthInLumaSamples);
#if (MFX_VERSION >= 1026)
    InheritOption(extHEVCInit->SampleAdaptiveOffset, extHEVCReset->SampleAdaptiveOffset);
    InheritOption(extHEVCInit->LCUSize, extHEVCReset->LCUSize);
#endif

    mfxExtHEVCTiles const * extHEVCTilInit  = &parInit.m_ext.HEVCTiles;
    mfxExtHEVCTiles *       extHEVCTilReset = &parReset.m_ext.HEVCTiles;

    InheritOption(extHEVCTilInit->NumTileColumns,  extHEVCTilReset->NumTileColumns);
    InheritOption(extHEVCTilInit->NumTileRows,     extHEVCTilReset->NumTileRows);

    mfxExtCodingOption const * extOptInit  = &parInit.m_ext.CO;
    mfxExtCodingOption *       extOptReset = &parReset.m_ext.CO;

    InheritOption(extOptInit->VuiNalHrdParameters,extOptReset->VuiNalHrdParameters);
    InheritOption(extOptInit->NalHrdConformance,extOptReset->NalHrdConformance);
    InheritOption(extOptInit->PicTimingSEI,extOptReset->PicTimingSEI);


    mfxExtCodingOption2 const * extOpt2Init  = &parInit.m_ext.CO2;
    mfxExtCodingOption2 *       extOpt2Reset = &parReset.m_ext.CO2;

    InheritOption(extOpt2Init->IntRefType,      extOpt2Reset->IntRefType);
    InheritOption(extOpt2Init->IntRefCycleSize, extOpt2Reset->IntRefCycleSize);
    InheritOption(extOpt2Init->IntRefQPDelta,   extOpt2Reset->IntRefQPDelta);
    InheritOption(extOpt2Init->MBBRC,      extOpt2Reset->MBBRC);
    InheritOption(extOpt2Init->ExtBRC,      extOpt2Reset->ExtBRC);

    InheritOption(extOpt2Init->BRefType,   extOpt2Reset->BRefType);
    InheritOption(extOpt2Init->NumMbPerSlice,   extOpt2Reset->NumMbPerSlice);
    InheritOption(extOpt2Init->MaxFrameSize,   extOpt2Reset->MaxFrameSize);
    InheritOption(extOpt2Init->MinQPI, extOpt2Reset->MinQPI);
    InheritOption(extOpt2Init->MaxQPI, extOpt2Reset->MaxQPI);
    InheritOption(extOpt2Init->MinQPP, extOpt2Reset->MinQPP);
    InheritOption(extOpt2Init->MaxQPP, extOpt2Reset->MaxQPP);
    InheritOption(extOpt2Init->MinQPB, extOpt2Reset->MinQPB);
    InheritOption(extOpt2Init->MaxQPB, extOpt2Reset->MaxQPB);

    //DisableDeblockingIdc=0 is valid value, reset 1 -> 0 possible
    //InheritOption(extOpt2Init->DisableDeblockingIdc,  extOpt2Reset->DisableDeblockingIdc);

    mfxExtCodingOption3 const * extOpt3Init  = &parInit.m_ext.CO3;
    mfxExtCodingOption3 *       extOpt3Reset = &parReset.m_ext.CO3;

    InheritOption(extOpt3Init->IntRefCycleDist, extOpt3Reset->IntRefCycleDist);
    InheritOption(extOpt3Init->PRefType, extOpt3Reset->PRefType);
    InheritOption(extOpt3Init->GPB, extOpt3Reset->GPB);

#if (MFX_VERSION >= 1026)
    InheritOption(extOpt3Init->TransformSkip, extOpt3Reset->TransformSkip);
#endif
#if (MFX_VERSION >= 1027)
    InheritOption(extOpt3Init->TargetChromaFormatPlus1, extOpt3Reset->TargetChromaFormatPlus1);
    InheritOption(extOpt3Init->TargetBitDepthLuma,      extOpt3Reset->TargetBitDepthLuma);
    InheritOption(extOpt3Init->TargetBitDepthChroma,    extOpt3Reset->TargetBitDepthChroma);
#endif
    InheritOption(extOpt3Init->WinBRCMaxAvgKbps, extOpt3Reset->WinBRCMaxAvgKbps);
    InheritOption(extOpt3Init->WinBRCSize, extOpt3Reset->WinBRCSize);
    InheritOption(extOpt3Init->EnableMBQP, extOpt3Reset->EnableMBQP);

    if (parInit.mfx.RateControlMethod == MFX_RATECONTROL_QVBR && parReset.mfx.RateControlMethod == MFX_RATECONTROL_QVBR)
    {
        InheritOption(parInit.mfx.InitialDelayInKB, parReset.mfx.InitialDelayInKB);
        InheritOption(parInit.mfx.TargetKbps,       parReset.mfx.TargetKbps);
        InheritOption(parInit.mfx.MaxKbps,          parReset.mfx.MaxKbps);
        InheritOption(extOpt3Init->QVBRQuality,     extOpt3Reset->QVBRQuality);
    }

    for (int i = 0; i < 8; i++)
    {
#if (MFX_VERSION >= 1025)
        if (parInit.mfx.TargetUsage != parReset.mfx.TargetUsage)
        {  // NumActiveRefs depends on TU
            extOpt3Reset->NumRefActiveP[i]   = std::min<mfxU16>(extOpt3Init->NumRefActiveP[i],   GetMaxNumRef(parReset, P));
            extOpt3Reset->NumRefActiveBL0[i] = std::min<mfxU16>(extOpt3Init->NumRefActiveBL0[i], GetMaxNumRef(parReset, BL0));
            extOpt3Reset->NumRefActiveBL1[i] = std::min<mfxU16>(extOpt3Init->NumRefActiveBL1[i], GetMaxNumRef(parReset, BL1));
        }  else
#endif
        {
            InheritOption(extOpt3Init->NumRefActiveP[i],   extOpt3Reset->NumRefActiveP[i]);
            InheritOption(extOpt3Init->NumRefActiveBL0[i], extOpt3Reset->NumRefActiveBL0[i]);
            InheritOption(extOpt3Init->NumRefActiveBL1[i], extOpt3Reset->NumRefActiveBL1[i]);
        }
    }

    mfxExtVideoSignalInfo const*  extOptVSIInit  = &parInit.m_ext.VSI;
    mfxExtVideoSignalInfo*  extOptVSIReset = &parReset.m_ext.VSI;

    InheritOption(extOptVSIInit->VideoFormat,extOptVSIReset->VideoFormat );
    InheritOption(extOptVSIInit->ColourPrimaries,extOptVSIReset->ColourPrimaries );
    InheritOption(extOptVSIInit->TransferCharacteristics,extOptVSIReset->TransferCharacteristics );
    InheritOption(extOptVSIInit->MatrixCoefficients,extOptVSIReset->MatrixCoefficients );
    InheritOption(extOptVSIInit->VideoFullRange,extOptVSIReset->VideoFullRange );
    InheritOption(extOptVSIInit->ColourDescriptionPresent,extOptVSIReset->ColourDescriptionPresent );

    mfxExtCodingOptionDDI const* extOptDDIInit = &parInit.m_ext.DDI;
    mfxExtCodingOptionDDI      * extOptDDIReset = &parReset.m_ext.DDI;
#if (MFX_VERSION >= 1025)
    if (parInit.mfx.TargetUsage != parReset.mfx.TargetUsage)
    {   // NumActiveRefs depends on TU
        extOptDDIReset->NumActiveRefP   = std::min<mfxU16>(extOptDDIInit->NumActiveRefP,   GetMaxNumRef(parReset, P));
        extOptDDIReset->NumActiveRefBL0 = std::min<mfxU16>(extOptDDIInit->NumActiveRefBL0, GetMaxNumRef(parReset, BL0));
        extOptDDIReset->NumActiveRefBL1 = std::min<mfxU16>(extOptDDIInit->NumActiveRefBL1, GetMaxNumRef(parReset, BL1));
    }  else
#endif
    {
        InheritOption(extOptDDIInit->NumActiveRefP, extOptDDIReset->NumActiveRefP);
        InheritOption(extOptDDIInit->NumActiveRefBL0, extOptDDIReset->NumActiveRefBL0);
        InheritOption(extOptDDIInit->NumActiveRefBL1, extOptDDIReset->NumActiveRefBL1);
    }

    // not inherited:
    // InheritOption(parInit.mfx.FrameInfo.PicStruct,      parReset.mfx.FrameInfo.PicStruct);
    // InheritOption(parInit.IOPattern,                    parReset.IOPattern);
    // InheritOption(parInit.mfx.FrameInfo.ChromaFormat,   parReset.mfx.FrameInfo.ChromaFormat);

    mfxExtBRC const *   extBRCInit = &parInit.m_ext.extBRC;
    mfxExtBRC*   extBRCReset = &parReset.m_ext.extBRC;

    if (!extBRCReset->pthis &&
        !extBRCReset->Init &&
        !extBRCReset->Reset &&
        !extBRCReset->Close &&
        !extBRCReset->GetFrameCtrl &&
        !extBRCReset->Update)
    {
        *extBRCReset= *extBRCInit;
    }


}

inline bool isInVideoMem(MfxVideoParam const & par)
{
    return (par.IOPattern == MFX_IOPATTERN_IN_VIDEO_MEMORY)
        || ((par.IOPattern == MFX_IOPATTERN_IN_OPAQUE_MEMORY) && (par.m_ext.Opaque.In.Type == MFX_IOPATTERN_IN_VIDEO_MEMORY));
}

#if (MFX_VERSION >= 1025)
inline bool isSAOSupported(MfxVideoParam const & par)
{
    /* On Gen10 VDEnc, this flag should be set to 0 when SliceSizeControl flag is on.
    On Gen10/CNL both VDEnc and VME, this flag should be set to 0 for 10-bit encoding.
    On CNL+, this flag should be set to 0 for max LCU size is 16x16 */
    if (   par.m_platform < MFX_HW_CNL
        || (par.mfx.TargetUsage == MFX_TARGETUSAGE_BEST_SPEED && par.m_platform == MFX_HW_CNL)
        || par.m_ext.CO3.WeightedPred == MFX_WEIGHTED_PRED_EXPLICIT
        || par.m_ext.CO3.WeightedBiPred == MFX_WEIGHTED_PRED_EXPLICIT
        || par.LCUSize == 16
        || (   par.m_platform == MFX_HW_CNL
            && (
#if (MFX_VERSION >= 1027)
                   (par.m_ext.CO3.TargetBitDepthLuma == 10)
#else
                   (   par.mfx.CodecProfile == MFX_PROFILE_HEVC_MAIN10
                    || par.mfx.FrameInfo.BitDepthLuma == 10
                    || par.mfx.FrameInfo.FourCC == MFX_FOURCC_P010)
#endif //(MFX_VERSION >= 1027)
                || (IsOn(par.mfx.LowPower) && (par.m_ext.CO2.MaxSliceSize != 0))
               )
           )
       )
    {
        return false;
    }
    return true;
}

#endif //(MFX_VERSION >= 1025)

mfxU16 GetMaxChroma(MfxVideoParam const & par)
{
    mfxU16 c = MFX_CHROMAFORMAT_YUV444;

    if (   par.mfx.CodecProfile == MFX_PROFILE_HEVC_MAIN
        || par.mfx.CodecProfile == MFX_PROFILE_HEVC_MAINSP
        || par.mfx.CodecProfile == MFX_PROFILE_HEVC_MAIN10
        )
    {
        return MFX_CHROMAFORMAT_YUV420;
    }

    if (par.mfx.CodecProfile == MFX_PROFILE_HEVC_REXT)
    {
        mfxU64 REXTConstr = par.m_ext.HEVCParam.GeneralConstraintFlags;

        if (REXTConstr & MFX_HEVC_CONSTR_REXT_MAX_420CHROMA)
            c = std::min<mfxU16>(c, MFX_CHROMAFORMAT_YUV420);
        else if (REXTConstr & MFX_HEVC_CONSTR_REXT_MAX_422CHROMA)
            c = std::min<mfxU16>(c, MFX_CHROMAFORMAT_YUV422);
    }


    switch (par.mfx.FrameInfo.FourCC)
    {
    case MFX_FOURCC_NV12:
    case MFX_FOURCC_P010:
#if (MFX_VERSION >= 1031)
    case MFX_FOURCC_P016:
#endif
        c = std::min<mfxU16>(c, MFX_CHROMAFORMAT_YUV420);
        break;

    case MFX_FOURCC_YUY2:
#if (MFX_VERSION >= 1027)
    case MFX_FOURCC_Y210:
#endif
    case MFX_FOURCC_P210:
#if (MFX_VERSION >= 1031)
    case MFX_FOURCC_Y216:
#endif
        c = std::min<mfxU16>(c, MFX_CHROMAFORMAT_YUV422);
        break;
    case MFX_FOURCC_A2RGB10:
    case MFX_FOURCC_RGB4:
    case MFX_FOURCC_AYUV:
#if (MFX_VERSION >= 1027)
    case MFX_FOURCC_Y410:
#endif
#if (MFX_VERSION >= 1031)
    case MFX_FOURCC_Y416:
#endif
    default:
        break;
    }

    return c;
}

mfxU16 GetMaxBitDepth(mfxU32 FourCC)
{
    switch (FourCC)
    {
    case MFX_FOURCC_RGB4:
    case MFX_FOURCC_NV12:
    case MFX_FOURCC_YUY2:
    case MFX_FOURCC_AYUV:
        return 8;
    case MFX_FOURCC_A2RGB10:
    case MFX_FOURCC_P010:
    case MFX_FOURCC_P210:
#if (MFX_VERSION >= 1027)
    case MFX_FOURCC_Y210:
    case MFX_FOURCC_Y410:
#endif
        return 10;
#if (MFX_VERSION >= 1031)
    case MFX_FOURCC_P016:
    case MFX_FOURCC_Y216:
    case MFX_FOURCC_Y416:
        return 12;
#endif
    default:
        break;
    }

    return 8;
}

mfxU16 GetMaxBitDepth(MfxVideoParam const & par, mfxU32 MaxEncodedBitDepth = 3)
{
    const mfxU16 undefined = 999;
    mfxU16 d = undefined;

    d = std::min<mfxU16>(d, 8 + (1 << MaxEncodedBitDepth));

    if (   par.mfx.CodecProfile == MFX_PROFILE_HEVC_MAIN
        || par.mfx.CodecProfile == MFX_PROFILE_HEVC_MAINSP
        )
    {
        return 8;
    }

    if (par.mfx.FrameInfo.BitDepthLuma)
        d = std::min(d, par.mfx.FrameInfo.BitDepthLuma);

    if ( par.mfx.CodecProfile == MFX_PROFILE_HEVC_MAIN10 )
        d = std::min<mfxU16>(d, 10);

    if (par.mfx.CodecProfile == MFX_PROFILE_HEVC_REXT
        )
    {
        mfxU64 REXTConstr = par.m_ext.HEVCParam.GeneralConstraintFlags;

        if (REXTConstr & MFX_HEVC_CONSTR_REXT_MAX_8BIT)
            d = std::min<mfxU16>(d, 8);
        else if (REXTConstr & MFX_HEVC_CONSTR_REXT_MAX_10BIT)
            d = std::min<mfxU16>(d, 10);
        else if (REXTConstr & MFX_HEVC_CONSTR_REXT_MAX_12BIT)
            d = std::min<mfxU16>(d, 12);
    }

    d = std::min<mfxU16>(d, GetMaxBitDepth(par.mfx.FrameInfo.FourCC));

    return (d == undefined ? 8 : d);
}

mfxU32 GetRawBytes(mfxU16 w, mfxU16 h, mfxU16 ChromaFormat, mfxU16 BitDepth)
{
    mfxU32 s = w * h;

    if (ChromaFormat == MFX_CHROMAFORMAT_YUV420)
        s = s * 3 / 2;
    else if (ChromaFormat == MFX_CHROMAFORMAT_YUV422)
        s *= 2;
    else if (ChromaFormat == MFX_CHROMAFORMAT_YUV444)
        s *= 3;

    assert(BitDepth >= 8);
    if (BitDepth != 8)
        s = (s * BitDepth + 7) / 8;

    return s;
}

mfxStatus CheckInputParam(mfxVideoParam *inPar, mfxVideoParam *outPar)
{
    mfxStatus sts = MFX_ERR_NONE;
    mfxU8 invalid = 0;

    if (inPar->mfx.CodecId != MFX_CODEC_HEVC)
        invalid++;

    if (CheckOption(inPar->mfx.FrameInfo.ChromaFormat, (mfxU16)MFX_CHROMAFORMAT_YUV420,
                                                       (mfxU16)MFX_CHROMAFORMAT_YUV422,
                                                       (mfxU16)MFX_CHROMAFORMAT_YUV444))
        invalid++;

    if (invalid)
        return (outPar) ? MFX_ERR_UNSUPPORTED : MFX_ERR_INVALID_VIDEO_PARAM;

    if (outPar) // Query
    {
        // matching ExtBuffers
        sts = ExtBuffer::CheckBuffers(*inPar, *outPar);
        if (sts == MFX_ERR_INVALID_VIDEO_PARAM)
            sts = MFX_ERR_UNSUPPORTED;
    }
    else
    {
        sts = ExtBuffer::CheckBuffers(*inPar);
    }

    return sts;
}

mfxStatus CheckVideoParam(MfxVideoParam& par, MFX_ENCODE_CAPS_HEVC const & caps, bool bInit = false)
{
    mfxStatus sts = MFX_ERR_NONE;
    mfxU32 invalid = 0, changed = 0;

    mfxF64 maxFR   = 300.;
    mfxU32 maxBR   = 0xFFFFFFFF;
    mfxU32 maxBuf  = 0xFFFFFFFF;
    mfxU16 maxDPB  = 16;
    mfxU16 minQP   = (IsOn(par.mfx.LowPower) && !par.isSWBRC()) ? 10 : 1;   // 10 is min QP for VDENC
    mfxU16 maxQP   = 51;
    mfxU16 surfAlignW = HW_SURF_ALIGN_W;
    mfxU16 surfAlignH = HW_SURF_ALIGN_H;
    mfxU16 MaxTileColumns = MAX_NUM_TILE_COLUMNS;
    mfxU16 MaxTileRows    = MAX_NUM_TILE_ROWS;

    mfxExtCodingOption2& CO2 = par.m_ext.CO2;
    mfxExtCodingOption3& CO3 = par.m_ext.CO3;
#ifdef MFX_ENABLE_HEVCE_ROI
    mfxExtEncoderROI* ROI = &par.m_ext.ROI;
#endif // MFX_ENABLE_HEVCE_ROI
#ifdef MFX_ENABLE_HEVCE_DIRTY_RECT
    mfxExtDirtyRect* DirtyRect = &par.m_ext.DirtyRect;
#endif // MFX_ENABLE_HEVCE_DIRTY_RECT

    changed += CheckTriStateOption(par.mfx.LowPower);

#if (MFX_VERSION >= 1026)
    if (par.m_ext.HEVCParam.LCUSize != 0)
    {
        if (CheckLCUSize(caps.ddi_caps.LCUSizeSupported, par.m_ext.HEVCParam.LCUSize))
        {
            par.LCUSize = par.m_ext.HEVCParam.LCUSize;
        }
        else
            invalid++;
    }
#endif // MFX_VERSION >= 1026
    if (!par.LCUSize)
        par.LCUSize = GetDefaultLCUSize(par, caps.ddi_caps); //  that a local copy of actual value;

#if (MFX_VERSION >= 1027)
    mfxU16 maxBitDepth = GetMaxBitDepth(par, caps.ddi_caps.MaxEncodedBitDepth);
    mfxU16 maxChroma = GetMaxChroma(par);

    invalid += CheckOption(par.mfx.FrameInfo.FourCC
        , (mfxU32)MFX_FOURCC_NV12
        , (mfxU32)MFX_FOURCC_RGB4
        , (mfxU32)MFX_FOURCC_Y210
        , (mfxU32)MFX_FOURCC_Y410
        , (mfxU32)MFX_FOURCC_AYUV
        , (mfxU32)MFX_FOURCC_P210
        , (mfxU32)MFX_FOURCC_YUY2
        , (mfxU32)MFX_FOURCC_P010
        , (mfxU32)MFX_FOURCC_A2RGB10
#if (MFX_VERSION >= 1031)
        , (mfxU32)MFX_FOURCC_P016
        , (mfxU32)MFX_FOURCC_Y216
        , (mfxU32)MFX_FOURCC_Y416
#endif
    );

    if (IsOn(par.mfx.LowPower)) {   // 422 target is not supported on VDENC
        if (CO3.TargetChromaFormatPlus1 == (1 + MFX_CHROMAFORMAT_YUV422))
        invalid++;
    } else {   // 444 target is not POR on VME
        if (CO3.TargetChromaFormatPlus1 == (1 + MFX_CHROMAFORMAT_YUV444))
        invalid++;
    }

    // par.mfx.FrameInfo.ChromaFormat should be checked before Construct to set TargetChromaFormat correctly in case CO3 is absent
    switch(par.mfx.FrameInfo.FourCC)
    {
    case MFX_FOURCC_AYUV:
    case MFX_FOURCC_RGB4:
        invalid += CheckOption(par.mfx.FrameInfo.ChromaFormat, (mfxU16)MFX_CHROMAFORMAT_YUV444);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthLuma, 8, 0);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthChroma, 8, 0);
        break;
    case MFX_FOURCC_A2RGB10:
    case MFX_FOURCC_Y410:
        invalid += CheckOption(par.mfx.FrameInfo.ChromaFormat, (mfxU16)MFX_CHROMAFORMAT_YUV444);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthLuma, 10, 0);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthChroma, 10, 0);
        break;
    case MFX_FOURCC_P210:
    case MFX_FOURCC_Y210:
        invalid += CheckOption(par.mfx.FrameInfo.ChromaFormat, (mfxU16)MFX_CHROMAFORMAT_YUV422);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthLuma, 10, 0);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthChroma, 10, 0);
        break;
    case MFX_FOURCC_YUY2:
        invalid += CheckOption(par.mfx.FrameInfo.ChromaFormat, (mfxU16)MFX_CHROMAFORMAT_YUV422);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthLuma, 8, 0);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthChroma, 8, 0);
        break;
    case MFX_FOURCC_P010:
        invalid += CheckOption(par.mfx.FrameInfo.ChromaFormat, (mfxU16)MFX_CHROMAFORMAT_YUV420);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthLuma, 10, 0);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthChroma, 10, 0);
        break;
    case MFX_FOURCC_NV12:
        invalid += CheckOption(par.mfx.FrameInfo.ChromaFormat, (mfxU16)MFX_CHROMAFORMAT_YUV420);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthLuma, 8, 0);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthChroma, 8, 0);
        break;
#if (MFX_VERSION >= 1031)
    case MFX_FOURCC_P016:
        invalid += CheckOption(par.mfx.FrameInfo.ChromaFormat, (mfxU16)MFX_CHROMAFORMAT_YUV420);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthLuma, 12, 0);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthChroma, 12, 0);
        break;
    case MFX_FOURCC_Y216:
        invalid += CheckOption(par.mfx.FrameInfo.ChromaFormat, (mfxU16)MFX_CHROMAFORMAT_YUV422);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthLuma, 12, 0);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthChroma, 12, 0);
        break;
    case MFX_FOURCC_Y416:
        invalid += CheckOption(par.mfx.FrameInfo.ChromaFormat, (mfxU16)MFX_CHROMAFORMAT_YUV444);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthLuma, 12, 0);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthChroma, 12, 0);
        break;
#endif
    default:
        par.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12;
        invalid++;
        //no break
    }

    changed += CheckMax(par.mfx.FrameInfo.BitDepthChroma, par.mfx.FrameInfo.BitDepthLuma);

    switch (maxBitDepth)
    {
#if (MFX_VERSION >= 1031)
    case 12:
        invalid += CheckOption(CO3.TargetBitDepthLuma, 12/*, 10, 8*/, 0);
        invalid += CheckOption(CO3.TargetBitDepthChroma, 12/*, 10, 8*/, 0);
        break;
#endif
    case 10:
        invalid += CheckOption(CO3.TargetBitDepthLuma, 10, 8, 0);
        invalid += CheckOption(CO3.TargetBitDepthChroma, 10, 8, 0);
        break;
    case 8:
    default:
        invalid += CheckOption(CO3.TargetBitDepthLuma, 8, 0);
        invalid += CheckOption(CO3.TargetBitDepthChroma, 8, 0);
        break;
    }

    switch (CO3.TargetBitDepthLuma)
    {
#if (MFX_VERSION >= 1031)
    case 12:
        invalid += CheckOption(CO3.TargetBitDepthChroma, 12/*, 10, 8*/, 0);
        invalid += CheckOption(par.mfx.CodecProfile
            , (mfxU16)MFX_PROFILE_HEVC_REXT
            , 0
            );
        break;
#endif
    case 10:
        invalid += CheckOption(CO3.TargetBitDepthChroma, 10, 8, 0);
        invalid += CheckOption(par.mfx.CodecProfile
            , (mfxU16)MFX_PROFILE_HEVC_MAIN10
            , (mfxU16)MFX_PROFILE_HEVC_REXT
            , 0
            );
        break;
    case 8:
    default:
        invalid += CheckOption(CO3.TargetBitDepthChroma, 8, 0);
        invalid += CheckOption(par.mfx.CodecProfile
            , (mfxU16)MFX_PROFILE_HEVC_MAIN
            , (mfxU16)MFX_PROFILE_HEVC_MAINSP
            , (mfxU16)MFX_PROFILE_HEVC_MAIN10
            , (mfxU16)MFX_PROFILE_HEVC_REXT
            , (mfxU16)0
            );
        break;
    }

    if (CO3.TargetBitDepthLuma > 8)
    {
        maxQP += 6 * (CO3.TargetBitDepthLuma - 8);
        // negative QP for CQP VME only;
        // remove "IsOn(par.mfx.LowPower)" when caps.NegativeQPSupport is correctly set in driver
        if ((caps.ddi_caps.NegativeQPSupport == 0) || IsOn(par.mfx.LowPower) || (par.mfx.RateControlMethod != MFX_RATECONTROL_CQP))
            minQP += 6 * (CO3.TargetBitDepthLuma - 8);
    }

    switch (maxChroma)
    {
    case MFX_CHROMAFORMAT_YUV444:
        invalid += CheckOption(CO3.TargetChromaFormatPlus1
            , mfxU16(1 + MFX_CHROMAFORMAT_YUV420)
            , mfxU16(1 + MFX_CHROMAFORMAT_YUV422)
            , mfxU16(1 + MFX_CHROMAFORMAT_YUV444));
        break;
    case MFX_CHROMAFORMAT_YUV422:
        invalid += CheckOption(CO3.TargetChromaFormatPlus1
            , mfxU16(1 + MFX_CHROMAFORMAT_YUV420)
            , mfxU16(1 + MFX_CHROMAFORMAT_YUV422));
        break;
    case MFX_CHROMAFORMAT_YUV420:
    default:
        invalid += CheckOption(CO3.TargetChromaFormatPlus1
            , mfxU16(1 + MFX_CHROMAFORMAT_YUV420));
        break;
    }

    if (CO3.TargetChromaFormatPlus1 == (1 + MFX_CHROMAFORMAT_YUV444) && !caps.ddi_caps.YUV444ReconSupport)
    {
        CO3.TargetChromaFormatPlus1 = 1 + MFX_CHROMAFORMAT_YUV420;
        invalid++;
    }

    if (CO3.TargetChromaFormatPlus1 == (1 + MFX_CHROMAFORMAT_YUV422) && !caps.ddi_caps.YUV422ReconSupport)
    {
        CO3.TargetChromaFormatPlus1 = 1 + MFX_CHROMAFORMAT_YUV420;
        invalid++;
    }

    // In surf >= target format
    if (CO3.TargetChromaFormatPlus1 == (1 + MFX_CHROMAFORMAT_YUV444) && CO3.TargetBitDepthLuma == 10)
    {   //In: 444x10 only
        invalid += CheckOption(par.mfx.FrameInfo.FourCC
            , (mfxU32)MFX_FOURCC_Y410
            , (mfxU32)MFX_FOURCC_A2RGB10);
    }
    else if (CO3.TargetChromaFormatPlus1 == (1 + MFX_CHROMAFORMAT_YUV444) && CO3.TargetBitDepthLuma == 8)
    {   //In: 444x8, 444x10
        invalid += CheckOption(par.mfx.FrameInfo.FourCC
            , (mfxU32)MFX_FOURCC_AYUV
            , (mfxU32)MFX_FOURCC_Y410
            , (mfxU32)MFX_FOURCC_RGB4
            , (mfxU32)MFX_FOURCC_A2RGB10);
    }
    else if (CO3.TargetChromaFormatPlus1 == (1 + MFX_CHROMAFORMAT_YUV422) && CO3.TargetBitDepthLuma == 10)
    {   //In: 422x10, 444x10
        invalid += CheckOption(par.mfx.FrameInfo.FourCC
            , (mfxU32)MFX_FOURCC_P210
            , (mfxU32)MFX_FOURCC_Y210
            , (mfxU32)MFX_FOURCC_Y410
            , (mfxU32)MFX_FOURCC_A2RGB10);
    }
    else if (CO3.TargetChromaFormatPlus1 == (1 + MFX_CHROMAFORMAT_YUV422) && CO3.TargetBitDepthLuma == 8)
    {   //In: 422x8, 422x10, 444x8, 444x10
        invalid += CheckOption(par.mfx.FrameInfo.FourCC
            , (mfxU32)MFX_FOURCC_YUY2, (mfxU32)MFX_FOURCC_Y210, (mfxU32)MFX_FOURCC_P210
            , (mfxU32)MFX_FOURCC_AYUV, (mfxU32)MFX_FOURCC_Y410
            , (mfxU32)MFX_FOURCC_RGB4, (mfxU32)MFX_FOURCC_A2RGB10);
    }
    else if (CO3.TargetChromaFormatPlus1 == (1 + MFX_CHROMAFORMAT_YUV420) && CO3.TargetBitDepthLuma == 10)
    {   //In: 420x10, 422x10, 444x10
        invalid += CheckOption(par.mfx.FrameInfo.FourCC
            , (mfxU32)MFX_FOURCC_P010
            , (mfxU32)MFX_FOURCC_P210
            , (mfxU32)MFX_FOURCC_Y210
            , (mfxU32)MFX_FOURCC_Y410
            , (mfxU32)MFX_FOURCC_A2RGB10);
    }
    else if (CO3.TargetChromaFormatPlus1 == (1 + MFX_CHROMAFORMAT_YUV420) && CO3.TargetBitDepthLuma == 8)
    {   //In: 420x8, 420x10, 422x8, 422x10, 444x8, 444x10
        invalid += CheckOption(par.mfx.FrameInfo.FourCC
            , (mfxU32)MFX_FOURCC_NV12, (mfxU32)MFX_FOURCC_P010, (mfxU32)MFX_FOURCC_RGB4
            , (mfxU32)MFX_FOURCC_YUY2, (mfxU32)MFX_FOURCC_P210, (mfxU32)MFX_FOURCC_Y210
            , (mfxU32)MFX_FOURCC_AYUV, (mfxU32)MFX_FOURCC_Y410, (mfxU32)MFX_FOURCC_A2RGB10);
    }
#if (MFX_VERSION >= 1031)
    else if (CO3.TargetChromaFormatPlus1 == (1 + MFX_CHROMAFORMAT_YUV444) && CO3.TargetBitDepthLuma == 12)
    {   //In: 444x12
        invalid += CheckOption(par.mfx.FrameInfo.FourCC
            , (mfxU32)MFX_FOURCC_Y416);
    }
    else if (CO3.TargetChromaFormatPlus1 == (1 + MFX_CHROMAFORMAT_YUV422) && CO3.TargetBitDepthLuma == 12)
    {   //In: 422x12, 444x12
        invalid += CheckOption(par.mfx.FrameInfo.FourCC
            , (mfxU32)MFX_FOURCC_Y216
            , (mfxU32)MFX_FOURCC_Y416);
    }
    else if (CO3.TargetChromaFormatPlus1 == (1 + MFX_CHROMAFORMAT_YUV420) && CO3.TargetBitDepthLuma == 12)
    {   //In: 420x12, 422x12, 444x12
        invalid += CheckOption(par.mfx.FrameInfo.FourCC
            , (mfxU32)MFX_FOURCC_P016
            , (mfxU32)MFX_FOURCC_Y216
            , (mfxU32)MFX_FOURCC_Y416);
    }
#endif
    else
    {
        assert(!"undefined target format");
        invalid++;
    }
#else
    if (caps.ddi_caps.BitDepth8Only == 0) // 10-bit supported
    {
        // For 10 bit encode we need adjust min/max QP
        mfxU16 BitDepthLuma = par.mfx.FrameInfo.BitDepthLuma;

        if (BitDepthLuma == 0 )
            BitDepthLuma = (par.mfx.CodecProfile == MFX_PROFILE_HEVC_MAIN10 || par.mfx.FrameInfo.FourCC == MFX_FOURCC_P010) ? 10 : 8;

        maxQP += 6 * (BitDepthLuma - 8);
        minQP += 6 * (BitDepthLuma - 8);
    }
#endif //#if (MFX_VERSION >= 1027)

    if (CO3.LowDelayBRC == MFX_CODINGOPTION_ON) {
        if ((par.m_platform < MFX_HW_ICL) ||
            (par.mfx.RateControlMethod != MFX_RATECONTROL_VBR &&
            par.mfx.RateControlMethod != MFX_RATECONTROL_QVBR &&
            par.mfx.RateControlMethod != MFX_RATECONTROL_VCM)) {
            CO3.LowDelayBRC = MFX_CODINGOPTION_OFF;
            changed++;
        }
        else {
            if (CO3.WinBRCMaxAvgKbps || CO3.WinBRCSize) {
                CO3.WinBRCMaxAvgKbps = 0;
                CO3.WinBRCSize = 0;
                changed++;
            }

            if (par.mfx.GopRefDist != 0 && par.mfx.GopRefDist != 1) {
                par.mfx.GopRefDist = 1;
                changed++;
            }
        }
    }

    changed +=  par.CheckExtBufferParam();

    if (par.mfx.CodecLevel !=0 && par.mfx.CodecLevel != MFX_LEVEL_HEVC_1 && LevelIdx(par.mfx.CodecLevel) == 0)
        invalid += CheckOption(par.mfx.CodecLevel, 0); // invalid level

    if (par.mfx.CodecLevel)
    {
        maxFR  = GetMaxFrByLevel(par);
        maxBR  = GetMaxKbpsByLevel(par);
        maxBuf = GetMaxCpbInKBByLevel(par);
        maxDPB = (mfxU16)GetMaxDpbSizeByLevel(par);
    }

    if ((!par.mfx.FrameInfo.Width) ||
        (!par.mfx.FrameInfo.Height))
    {
        return MFX_ERR_INVALID_VIDEO_PARAM;
    }

    if (bInit)
    {
        invalid += CheckMin(par.mfx.FrameInfo.Width,  mfx::align2_value(par.mfx.FrameInfo.Width, surfAlignW));
        invalid += CheckMin(par.mfx.FrameInfo.Height, mfx::align2_value(par.mfx.FrameInfo.Height, surfAlignH));
    }
    else
    {
        changed += CheckMin(par.mfx.FrameInfo.Width,  mfx::align2_value(par.mfx.FrameInfo.Width,  surfAlignW));
        changed += CheckMin(par.mfx.FrameInfo.Height, mfx::align2_value(par.mfx.FrameInfo.Height, surfAlignH));
    }

    invalid += CheckMax(par.mfx.FrameInfo.Width, caps.ddi_caps.MaxPicWidth);
    invalid += CheckMax(par.mfx.FrameInfo.Height, caps.ddi_caps.MaxPicHeight);

    invalid += CheckMax(par.m_ext.HEVCParam.PicWidthInLumaSamples, par.mfx.FrameInfo.Width);
    invalid += CheckMax(par.m_ext.HEVCParam.PicHeightInLumaSamples, par.mfx.FrameInfo.Height);

    changed += CheckMin(par.m_ext.HEVCParam.PicWidthInLumaSamples,  mfx::align2_value(par.m_ext.HEVCParam.PicWidthInLumaSamples,  par.CodedPicAlignment));
    changed += CheckMin(par.m_ext.HEVCParam.PicHeightInLumaSamples, mfx::align2_value(par.m_ext.HEVCParam.PicHeightInLumaSamples, par.CodedPicAlignment));

#if (MFX_VERSION < 1027)
    if (par.mfx.CodecProfile == MFX_PROFILE_HEVC_MAIN || par.mfx.CodecProfile == MFX_PROFILE_HEVC_MAINSP)
    {
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthLuma, 8, 0);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthChroma, 8, 0);
    }

    if (par.mfx.CodecProfile == MFX_PROFILE_HEVC_MAIN10)
    {
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthLuma, 10, 0);
        invalid += CheckOption(par.mfx.FrameInfo.BitDepthChroma, 10, 0);
        par.mfx.FrameInfo.Shift = 1;
    }
#endif

    if (!caps.ddi_caps.TileSupport)
    {
        MaxTileColumns = 1;
        MaxTileRows    = 1;
    }
    else
    {
        mfxU32 minTileWidth = MIN_TILE_SIZE_WIDTH;
        mfxU32 minTileHeight = MIN_TILE_SIZE_HEIGHT;

        // min 2x2 lcu is supported on VDEnc
        // TODO: replace indirect NumScalablePipesMinus1 by platform
        if (caps.ddi_caps.NumScalablePipesMinus1 > 0 && IsOn(par.mfx.LowPower))
            minTileHeight = 128;

        mfxU16 nCol = (mfxU16)(par.m_ext.HEVCParam.PicWidthInLumaSamples / minTileWidth);
        mfxU16 nRow = (mfxU16)(par.m_ext.HEVCParam.PicHeightInLumaSamples / minTileHeight);
        CheckMin(nCol, 1);
        CheckMin(nRow, 1);

        changed += CheckMax(par.m_ext.HEVCTiles.NumTileColumns, nCol);
        changed += CheckMax(par.m_ext.HEVCTiles.NumTileRows, nRow);

        if ((par.m_platform == MFX_HW_ICL || par.m_platform == MFX_HW_ICL_LP) && IsOn(par.mfx.LowPower) && par.m_ext.HEVCTiles.NumTileColumns > 1 && par.m_ext.HEVCTiles.NumTileRows > 1) {
            // for ICL VDEnc only 1xN or Nx1 configurations are allowed for single pipe
            // we ignore "Rows" condition
            changed += CheckMax(par.m_ext.HEVCTiles.NumTileRows, 1);
        }
    }

    invalid += CheckMax(par.m_ext.HEVCTiles.NumTileColumns, MaxTileColumns);
    invalid += CheckMax(par.m_ext.HEVCTiles.NumTileRows, MaxTileRows);

    changed += CheckMax(par.mfx.TargetUsage, (mfxU32)MFX_TARGETUSAGE_BEST_SPEED);

    if (par.mfx.TargetUsage && caps.ddi_caps.TUSupport)
        changed += CheckTU(caps.ddi_caps.TUSupport, par.mfx.TargetUsage);

    changed += CheckMax(par.mfx.GopRefDist, caps.ddi_caps.SliceIPOnly ? 1 : (par.mfx.GopPicSize ? std::max(1, par.mfx.GopPicSize - 1) : 0xFFFF));

    invalid += CheckOption(par.Protected
        , 0);


    changed += CheckOption(par.IOPattern
        , (mfxU32)MFX_IOPATTERN_IN_VIDEO_MEMORY
        , (mfxU32)MFX_IOPATTERN_IN_SYSTEM_MEMORY
        , (mfxU32)MFX_IOPATTERN_IN_OPAQUE_MEMORY
        , 0);

    if (par.mfx.RateControlMethod == (mfxU32)MFX_RATECONTROL_AVBR)
    {
        par.mfx.RateControlMethod = (mfxU32)MFX_RATECONTROL_VBR;
        par.mfx.Accuracy = 0;
        par.mfx.Convergence = 0;
        changed ++;
    }


    invalid += CheckOption(par.mfx.RateControlMethod
        , 0
        , caps.CBRSupport ? MFX_RATECONTROL_CBR : 0
        , caps.VBRSupport ? MFX_RATECONTROL_VBR : 0
        , (mfxU32)MFX_RATECONTROL_AVBR
        , caps.CQPSupport ? MFX_RATECONTROL_CQP : 0
        , (mfxU32)MFX_RATECONTROL_LA_EXT
        , caps.ICQSupport ? MFX_RATECONTROL_ICQ : 0
        , caps.ddi_caps.VCMBitRateControl ? MFX_RATECONTROL_VCM : 0
        , caps.ddi_caps.QVBRBRCSupport ? MFX_RATECONTROL_QVBR : 0
        );

    if (par.mfx.RateControlMethod == MFX_RATECONTROL_ICQ)
        invalid += CheckMax(par.mfx.ICQQuality, 51);

    if (par.mfx.RateControlMethod == MFX_RATECONTROL_QVBR)
        invalid += CheckMax(CO3.QVBRQuality, 51);

    if (par.mfx.RateControlMethod == MFX_RATECONTROL_AVBR)
    {
        if (par.mfx.Accuracy)
            changed += CheckRange(par.mfx.Accuracy, AVBR_ACCURACY_MIN, AVBR_ACCURACY_MAX);
        if (par.mfx.Convergence)
            changed += CheckRange(par.mfx.Convergence, AVBR_CONVERGENCE_MIN, AVBR_CONVERGENCE_MAX);
    }


    changed += CheckTriStateOption(par.m_ext.CO2.MBBRC);

    if (caps.ddi_caps.MBBRCSupport == 0 || par.mfx.RateControlMethod == MFX_RATECONTROL_CQP ||par.isSWBRC())
        changed += CheckOption(par.m_ext.CO2.MBBRC, (mfxU32)MFX_CODINGOPTION_OFF, 0);
    else
        changed += CheckOption(par.m_ext.CO2.MBBRC, (mfxU32)MFX_CODINGOPTION_ON, (mfxU32)MFX_CODINGOPTION_OFF, 0);


    if (IsOn(par.m_ext.CO2.ExtBRC) && par.mfx.RateControlMethod != 0 && par.mfx.RateControlMethod != MFX_RATECONTROL_CBR && par.mfx.RateControlMethod != MFX_RATECONTROL_VBR)
    {
        par.m_ext.CO2.ExtBRC = MFX_CODINGOPTION_OFF;
        changed ++;
    }

    if ((!IsOn(par.m_ext.CO2.ExtBRC)) &&
        (par.m_ext.extBRC.pthis || par.m_ext.extBRC.Init || par.m_ext.extBRC.Close || par.m_ext.extBRC.GetFrameCtrl || par.m_ext.extBRC.Update || par.m_ext.extBRC.Reset))
    {
        par.m_ext.extBRC.pthis = 0;
        par.m_ext.extBRC.Init = 0;
        par.m_ext.extBRC.Close = 0;
        par.m_ext.extBRC.GetFrameCtrl = 0;
        par.m_ext.extBRC.Update = 0;
        par.m_ext.extBRC.Reset = 0;
        changed++;
    }
    if ((par.m_ext.extBRC.pthis || par.m_ext.extBRC.Init || par.m_ext.extBRC.Close || par.m_ext.extBRC.GetFrameCtrl || par.m_ext.extBRC.Update || par.m_ext.extBRC.Reset) &&
        (!par.m_ext.extBRC.pthis || !par.m_ext.extBRC.Init || !par.m_ext.extBRC.Close || !par.m_ext.extBRC.GetFrameCtrl || !par.m_ext.extBRC.Update || !par.m_ext.extBRC.Reset))
    {
        par.m_ext.extBRC.pthis = 0;
        par.m_ext.extBRC.Init = 0;
        par.m_ext.extBRC.Close = 0;
        par.m_ext.extBRC.GetFrameCtrl = 0;
        par.m_ext.extBRC.Update = 0;
        par.m_ext.extBRC.Reset = 0;
        par.m_ext.CO2.ExtBRC = 0;
        bInit ? invalid ++ : changed++;
    }

#ifdef MFX_ENABLE_HEVCE_INTERLACE

    if (par.mfx.RateControlMethod == MFX_RATECONTROL_CQP ||
        (IsOn(par.m_ext.CO2.ExtBRC) && (par.mfx.RateControlMethod == MFX_RATECONTROL_CBR || par.mfx.RateControlMethod == MFX_RATECONTROL_VBR)))
    {
        changed += CheckOption(par.mfx.FrameInfo.PicStruct, (mfxU16)MFX_PICSTRUCT_PROGRESSIVE, MFX_PICSTRUCT_FIELD_TOP, MFX_PICSTRUCT_FIELD_BOTTOM, MFX_PICSTRUCT_FIELD_SINGLE, MFX_PICSTRUCT_FIELD_TFF, MFX_PICSTRUCT_FIELD_BFF,0);
    }
    else
#endif
    {
        changed += CheckOption(par.mfx.FrameInfo.PicStruct, (mfxU16)MFX_PICSTRUCT_PROGRESSIVE, 0);
    }
    if (par.m_ext.HEVCParam.PicWidthInLumaSamples > 0)
    {
        changed += CheckRange(par.mfx.FrameInfo.CropX, 0, par.m_ext.HEVCParam.PicWidthInLumaSamples);
        changed += CheckRange(par.mfx.FrameInfo.CropW, 0, par.m_ext.HEVCParam.PicWidthInLumaSamples - par.mfx.FrameInfo.CropX);
    }

    if (par.m_ext.HEVCParam.PicHeightInLumaSamples > 0)
    {
        changed += CheckRange(par.mfx.FrameInfo.CropY, 0, par.m_ext.HEVCParam.PicHeightInLumaSamples);
        changed += CheckRange(par.mfx.FrameInfo.CropH, 0, par.m_ext.HEVCParam.PicHeightInLumaSamples - par.mfx.FrameInfo.CropY);
    }

#if (MFX_VERSION < 1027)
    {
        invalid += CheckOption(par.mfx.FrameInfo.ChromaFormat, (mfxU16)MFX_CHROMAFORMAT_YUV420, 0);
        if (par.mfx.CodecProfile == MFX_PROFILE_HEVC_MAIN || par.mfx.CodecProfile == MFX_PROFILE_HEVC_MAINSP)
            invalid += CheckOption(par.mfx.FrameInfo.FourCC, (mfxU32)MFX_FOURCC_NV12, 0);
        else if (par.mfx.CodecProfile == MFX_PROFILE_HEVC_MAIN10)
            invalid += CheckOption(par.mfx.FrameInfo.FourCC, (mfxU32)MFX_FOURCC_P010, 0);
        else if (par.mfx.FrameInfo.FourCC && par.mfx.FrameInfo.FourCC != (mfxU32)MFX_FOURCC_NV12 && par.mfx.FrameInfo.FourCC != (mfxU32)MFX_FOURCC_P010)
        {
            invalid ++;
            par.mfx.FrameInfo.FourCC = 0;
        }
    }
#endif

    if ((par.mfx.FrameInfo.FourCC == MFX_FOURCC_P010) && isInVideoMem(par))
        changed += CheckMin(par.mfx.FrameInfo.Shift, 1);

#if (MFX_VERSION >= 1027)
    if ((par.mfx.FrameInfo.FourCC == MFX_FOURCC_Y210) && isInVideoMem(par))
            changed += CheckMin(par.mfx.FrameInfo.Shift, 1);
#endif
#if (MFX_VERSION >= 1031)
    if ((par.mfx.FrameInfo.FourCC == MFX_FOURCC_P016) && isInVideoMem(par))
            changed += CheckMin(par.mfx.FrameInfo.Shift, 1);
#endif

    if (par.mfx.FrameInfo.FrameRateExtN && par.mfx.FrameInfo.FrameRateExtD) // FR <= 300
    {
        if (par.mfx.FrameInfo.FrameRateExtN > (mfxU32)300 * par.mfx.FrameInfo.FrameRateExtD)
        {
            par.mfx.FrameInfo.FrameRateExtN = par.mfx.FrameInfo.FrameRateExtD = 0;
            invalid++;
        }
    }

    if ((par.mfx.FrameInfo.FrameRateExtN == 0) !=
        (par.mfx.FrameInfo.FrameRateExtD == 0))
    {
        par.mfx.FrameInfo.FrameRateExtN = 0;
        par.mfx.FrameInfo.FrameRateExtD = 0;
        invalid ++;
    }

    changed += CheckMax(par.mfx.NumRefFrame, maxDPB - 1);

    if (par.mfx.NumRefFrame)
    {
        // If NumActiveRef parameters are set already, DPB size should not be less than NumActiveRef+1
        maxDPB = std::max({ par.mfx.NumRefFrame + 1, par.m_ext.DDI.NumActiveRefP + 1,
            par.m_ext.DDI.NumActiveRefBL0 + par.m_ext.DDI.NumActiveRefBL1 + 1 });
    }

    if (   (par.mfx.RateControlMethod == MFX_RATECONTROL_VBR
         || par.mfx.RateControlMethod == MFX_RATECONTROL_QVBR
         || par.mfx.RateControlMethod == MFX_RATECONTROL_VCM)
        && par.MaxKbps != 0
        && par.TargetKbps != 0
        && par.MaxKbps < par.TargetKbps)
    {
        par.MaxKbps = par.TargetKbps;
        changed ++;
    }
    if ((par.mfx.RateControlMethod == MFX_RATECONTROL_CBR ||
        par.mfx.RateControlMethod == MFX_RATECONTROL_LA_EXT)
        && par.MaxKbps != par.TargetKbps
        && par.MaxKbps!= 0
        && par.TargetKbps!= 0)
    {
        par.MaxKbps = par.TargetKbps;
        changed ++;
    }

    if (par.mfx.RateControlMethod == MFX_RATECONTROL_CQP)
    {
        if (par.mfx.QPI)
            changed += CheckRange(par.mfx.QPI, minQP, maxQP);
        if (par.mfx.QPP)
            changed += CheckRange(par.mfx.QPP, minQP, maxQP);
        if (par.mfx.QPB)
            changed += CheckRange(par.mfx.QPB, minQP, maxQP);
    }

    if (par.BufferSizeInKB != 0)
    {
#if (MFX_VERSION >= 1027)
        mfxU32 rawBytes = GetRawBytes(
              par.m_ext.HEVCParam.PicWidthInLumaSamples
            , par.m_ext.HEVCParam.PicHeightInLumaSamples
            , CO3.TargetChromaFormatPlus1 - 1
            , CO3.TargetBitDepthLuma ? CO3.TargetBitDepthLuma : maxBitDepth) / 1000;
#else
        mfxU32 rawBytes = par.m_ext.HEVCParam.PicWidthInLumaSamples * par.m_ext.HEVCParam.PicHeightInLumaSamples * 3 / 2 / 1000;
#endif

        if (  (par.mfx.RateControlMethod == MFX_RATECONTROL_CQP
            || par.mfx.RateControlMethod == MFX_RATECONTROL_ICQ)
            && par.BufferSizeInKB < rawBytes)
        {
            par.BufferSizeInKB = rawBytes;
            changed ++;
        }
        else if (   par.mfx.RateControlMethod != MFX_RATECONTROL_CQP
                 && par.mfx.RateControlMethod != MFX_RATECONTROL_ICQ)
        {
            mfxF64 fr = par.mfx.FrameInfo.FrameRateExtD !=0 && par.mfx.FrameInfo.FrameRateExtN!=0 ? (mfxF64)par.mfx.FrameInfo.FrameRateExtN / (mfxF64)par.mfx.FrameInfo.FrameRateExtD : 30.;
            mfxU32 avgFS = Ceil((mfxF64)par.TargetKbps / fr / 8);

            changed += CheckRange(par.BufferSizeInKB, avgFS * 2 + 1, maxBuf);
        }
    }
    changed += CheckTriStateOption(par.m_ext.CO.VuiNalHrdParameters);
    changed += CheckTriStateOption(par.m_ext.CO.NalHrdConformance);

    if (par.mfx.RateControlMethod != MFX_RATECONTROL_CBR &&
        par.mfx.RateControlMethod != MFX_RATECONTROL_VBR &&
        par.mfx.RateControlMethod != MFX_RATECONTROL_VCM &&
        par.mfx.RateControlMethod != MFX_RATECONTROL_QVBR)
    {
       changed += CheckOption(par.m_ext.CO.VuiNalHrdParameters, (mfxU32)MFX_CODINGOPTION_OFF, 0);
       changed += CheckOption(par.m_ext.CO.NalHrdConformance,  (mfxU32)MFX_CODINGOPTION_OFF, 0);
       par.HRDConformance = false;
    }
    if (IsOff(par.m_ext.CO.NalHrdConformance))
    {
       changed += CheckOption(par.m_ext.CO.VuiNalHrdParameters, (mfxU32)MFX_CODINGOPTION_OFF, 0);
       par.HRDConformance = false;
    }

    changed += CheckTriStateOption(par.m_ext.CO.PicTimingSEI);

    if (par.m_ext.CO2.NumMbPerSlice != 0)
    {
        mfxU32 nLCU  = CeilDiv(par.m_ext.HEVCParam.PicHeightInLumaSamples, par.LCUSize) * CeilDiv(par.m_ext.HEVCParam.PicWidthInLumaSamples, par.LCUSize);
        mfxU32 nTile = std::max<mfxU32>(par.m_ext.HEVCTiles.NumTileColumns, 1) * std::max<mfxU32>(par.m_ext.HEVCTiles.NumTileRows, 1);
        mfxU32 maxSlicesPerTile = MAX_SLICES / nTile;
        mfxU32 maxSlicesTotal = maxSlicesPerTile * nTile;
        mfxU32 maxNumMbPerSlice = CeilDiv(nLCU, nTile);
        mfxU32 minNumMbPerSlice = CeilDiv(nLCU, maxSlicesTotal);
        changed += CheckRange(par.m_ext.CO2.NumMbPerSlice, minNumMbPerSlice, maxNumMbPerSlice);
    }

    changed += CheckOption(par.mfx.NumSlice, MakeSlices(par, caps.ddi_caps.SliceStructure), 0);

    if (par.m_ext.CO2.NumMbPerSlice != 0)
    {
        for (std::vector<MfxHwH265Encode::MfxVideoParam::SliceInfo>::iterator it = par.m_slice.begin(); it != par.m_slice.end(); ++it)
        {
            if ((it != par.m_slice.end() - 1) && (par.m_slice.size() > 1))
            {
                changed += CheckOption(par.m_ext.CO2.NumMbPerSlice, it->NumLCU); //check all except last slice
            }
            else
            {
                changed += CheckMin(par.m_ext.CO2.NumMbPerSlice, it->NumLCU); //last slice is less or equal previous slices
            }
        }
    }

    if (par.isTL())
        changed += CheckOption(par.mfx.GopRefDist, 1, 0);

    if (   par.m_ext.CO2.BRefType == MFX_B_REF_PYRAMID
           && par.mfx.GopRefDist > 0
           && ( par.mfx.GopRefDist < 2
            || minRefForPyramid(par.mfx.GopRefDist, par.isField()) > 16
            || (par.mfx.NumRefFrame && minRefForPyramid(par.mfx.GopRefDist, par.isField()) > par.mfx.NumRefFrame)))
    {
        if (par.mfx.EncodedOrder
         && par.mfx.NumRefFrame > 2 
         && minRefForPyramid(par.mfx.GopRefDist, par.isField()) > par.mfx.NumRefFrame)
        {
            par.bNonStandardReord = true;  // let's allow this mode in encoding order (may be special B pyr is used)
        }
        else
        {
            // disable B pyramid
            par.m_ext.CO2.BRefType = MFX_B_REF_OFF;
            changed++;
        }
    }
    if (par.mfx.GopRefDist > 1 && (par.mfx.NumRefFrame && par.mfx.NumRefFrame < (par.isField() ? 4 :2)) )
    {
        if (par.mfx.EncodedOrder && par.isField() && par.mfx.NumRefFrame > 1 && par.mfx.NumRefFrame < 4)
        {
            par.bNonStandardReord = true;  // let's allow this mode in encoding order 
        }
        else
        {
            par.mfx.NumRefFrame = par.isField() ? 4 : 2;
            changed++;
        }
    }

    if (par.m_ext.CO3.PRefType == MFX_P_REF_PYRAMID &&  par.mfx.GopRefDist > 1)
    {
        par.m_ext.CO3.PRefType = MFX_P_REF_DEFAULT;
        changed ++;
    }

    {
        mfxU16 prev = 0;

        invalid += CheckOption(par.m_ext.AVCTL.Layer[0].Scale, 0, 1);
        invalid += CheckOption(par.m_ext.AVCTL.Layer[7].Scale, 0);

        for (mfxU16 i = 1; i < 7; i++)
        {
            if (!par.m_ext.AVCTL.Layer[i].Scale)
                continue;

            if (par.m_ext.AVCTL.Layer[i].Scale <= par.m_ext.AVCTL.Layer[prev].Scale
                || par.m_ext.AVCTL.Layer[i].Scale %  par.m_ext.AVCTL.Layer[prev].Scale)
            {
                par.m_ext.AVCTL.Layer[i].Scale = 0;
                invalid++;
                break;
            }
            prev = i;
        }
    }

    changed += CheckRangeDflt(par.m_ext.CO2.IntRefQPDelta, -51, 51, 0);
    invalid += CheckMax(par.m_ext.CO2.IntRefType, 2);

    if (caps.ddi_caps.RollingIntraRefresh == 0)
    {
        invalid += CheckOption(par.m_ext.CO2.IntRefType, 0);
        invalid += CheckOption(par.m_ext.CO2.IntRefCycleSize, 0);
        invalid += CheckOption(par.m_ext.CO3.IntRefCycleDist, 0);
    }

    if (par.m_ext.CO2.IntRefCycleSize != 0 &&
        par.mfx.GopPicSize != 0 &&
        par.m_ext.CO2.IntRefCycleSize >= par.mfx.GopPicSize)
    {
        // refresh cycle length shouldn't be greater or equal to GOP size
        par.m_ext.CO2.IntRefType = 0;
        par.m_ext.CO2.IntRefCycleSize = 0;
        changed +=1;
    }
    if (par.m_ext.CO2.MaxFrameSize && ((!par.isSWBRC() && par.mfx.RateControlMethod != MFX_RATECONTROL_VBR) ||
        par.mfx.RateControlMethod == MFX_RATECONTROL_LA_EXT))
    {
        par.m_ext.CO2.MaxFrameSize = 0;
        changed+=1;
    }

    if (par.m_ext.CO2.MaxFrameSize != 0 &&
        par.mfx.FrameInfo.FrameRateExtN != 0 && par.mfx.FrameInfo.FrameRateExtD != 0)
    {
        mfxF64 frameRate = mfxF64(par.mfx.FrameInfo.FrameRateExtN) / par.mfx.FrameInfo.FrameRateExtD;
        mfxU32 avgFrameSizeInBytes = mfxU32(par.TargetKbps * 1000 / frameRate / 8);
        if (par.m_ext.CO2.MaxFrameSize < avgFrameSizeInBytes)
        {
            changed+=1;
            par.m_ext.CO2.MaxFrameSize = avgFrameSizeInBytes;
        }
    }

    if (par.m_ext.CO3.IntRefCycleDist != 0 &&
        par.mfx.GopPicSize != 0 &&
        par.m_ext.CO3.IntRefCycleDist >= par.mfx.GopPicSize)
    {
        // refresh period length shouldn't be greater or equal to GOP size
        par.m_ext.CO2.IntRefType = 0;
        par.m_ext.CO3.IntRefCycleDist = 0;
        changed +=1;
    }

    if (par.m_ext.CO3.IntRefCycleDist != 0 &&
        par.m_ext.CO2.IntRefCycleSize != 0 &&
        par.m_ext.CO2.IntRefCycleSize > par.m_ext.CO3.IntRefCycleDist)
    {
        // refresh period shouldn't be greater than refresh cycle size
        par.m_ext.CO3.IntRefCycleDist = 0;
        changed +=1;
    }
    changed += CheckOption(par.m_ext.CO2.SkipFrame, (mfxU16) 0 , (mfxU16) MFX_SKIPFRAME_INSERT_DUMMY, (mfxU16) MFX_SKIPFRAME_INSERT_NOTHING );

    if (CheckRangeDflt(par.m_ext.VSI.VideoFormat,             0,   8, 5)) changed +=1;
    if (CheckRangeDflt(par.m_ext.VSI.ColourPrimaries,         0, 255, 2)) changed +=1;
    if (CheckRangeDflt(par.m_ext.VSI.TransferCharacteristics, 0, 255, 2)) changed +=1;
    if (CheckRangeDflt(par.m_ext.VSI.MatrixCoefficients,      0, 255, 2)) changed +=1;
    if (CheckOption(par.m_ext.VSI.VideoFullRange, 0, 1))                  changed +=1;
    if (CheckOption(par.m_ext.VSI.ColourDescriptionPresent, 0, 1))        changed +=1;

    if (caps.PSliceSupport)
        changed += CheckOption(CO3.GPB, (mfxU16)MFX_CODINGOPTION_UNKNOWN, (mfxU16)MFX_CODINGOPTION_ON, (mfxU16)MFX_CODINGOPTION_OFF);
    else
        changed += CheckOption(CO3.GPB, (mfxU16)MFX_CODINGOPTION_UNKNOWN, (mfxU16)MFX_CODINGOPTION_ON);

    changed += CheckTriStateOption(par.m_ext.CO.AUDelimiter);
    changed += CheckTriStateOption(CO2.RepeatPPS);
    changed += CheckTriStateOption(CO3.EnableQPOffset);

    if (   IsOn(CO3.EnableQPOffset)
        && (par.mfx.RateControlMethod != MFX_RATECONTROL_CQP
        || (par.mfx.GopRefDist > 1 && CO2.BRefType == MFX_B_REF_OFF)
        || (par.mfx.GopRefDist == 1 && CO3.PRefType == MFX_P_REF_SIMPLE)))
    {
        CO3.EnableQPOffset = MFX_CODINGOPTION_OFF;
        changed ++;
    }

    if (caps.ddi_caps.SliceByteSizeCtrl == 0)
        invalid += CheckOption(CO2.MaxSliceSize, 0);


    if (IsOn(CO3.EnableQPOffset))
    {
        mfxI16 QPX = (par.mfx.GopRefDist == 1) ? par.mfx.QPP : par.mfx.QPB;

        if (QPX)
        {
            for (mfxI16 i = 0; i < 8; i++)
                changed += CheckRange(CO3.QPOffset[i], minQP - QPX, maxQP - QPX);
        }
    }

    //check Active Reference

    {
        mfxU16 maxP  = std::min<mfxU16>(caps.ddi_caps.MaxNum_Reference0, maxDPB - 1);
        mfxU16 maxB0 = maxP;
        mfxU16 maxB1 = std::min<mfxU16>(caps.ddi_caps.MaxNum_Reference1, maxDPB - 1);

#if (MFX_VERSION >= 1025)
        if (par.m_platform >= MFX_HW_CNL)
        {
            maxP  = std::min<mfxU16>(maxP,  GetMaxNumRef(par, P));
            maxB0 = std::min<mfxU16>(maxB0, GetMaxNumRef(par, BL0));
            maxB1 = std::min<mfxU16>(maxB1, GetMaxNumRef(par, BL1));
        }
#endif

        changed += CheckMax(par.m_ext.DDI.NumActiveRefP,   maxP);
        changed += CheckMax(par.m_ext.DDI.NumActiveRefBL0, maxB0);
        changed += CheckMax(par.m_ext.DDI.NumActiveRefBL1, maxB1);

        maxP  = par.m_ext.DDI.NumActiveRefP   ? par.m_ext.DDI.NumActiveRefP   : maxP;
        maxB0 = par.m_ext.DDI.NumActiveRefBL0 ? par.m_ext.DDI.NumActiveRefBL0 : maxB0;
        maxB1 = par.m_ext.DDI.NumActiveRefBL1 ? par.m_ext.DDI.NumActiveRefBL1 : maxB1;

        for (mfxU16 i = 0; i < 8; i++)
        {
            changed += CheckMax(CO3.NumRefActiveP[i],   maxP);
            changed += CheckMax(CO3.NumRefActiveBL0[i], maxB0);
            changed += CheckMax(CO3.NumRefActiveBL1[i], maxB1);
        }
    }

#if (MFX_VERSION >= 1026)
    changed += CheckTriStateOption(CO3.TransformSkip);

    if (IsOn(CO3.TransformSkip) && par.m_platform < MFX_HW_CNL)
    {
        CO3.TransformSkip = 0;
        changed++;
    }
#endif

#ifdef MFX_ENABLE_HEVCE_ROI
    if (ROI->NumROI) {   // !!! if ENCODE_BLOCKQPDATA is provided NumROI is assumed to be 0
        sts = CheckAndFixRoi(par, caps.ddi_caps, ROI, par.bROIViaMBQP);
        if (sts < MFX_ERR_NONE) {
            invalid++;
        }
        else if (sts > MFX_ERR_NONE) {
            changed++;
        }
    }
#endif // MFX_ENABLE_HEVCE_ROI

#ifdef MFX_ENABLE_HEVCE_DIRTY_RECT
    if (DirtyRect->NumRect) {
        sts = CheckAndFixDirtyRect(caps.ddi_caps, par, DirtyRect);
        if (sts < MFX_ERR_NONE) {
            invalid++;
        }
        else if (sts > MFX_ERR_NONE) {
            changed++;
        }
    }
#endif // MFX_ENABLE_HEVCE_DIRTY_RECT

    if (CO3.EnableMBQP !=0 || par.bROIViaMBQP)
    {
        if (par.mfx.RateControlMethod != MFX_RATECONTROL_CQP && !par.isSWBRC())
            changed += CheckOption(CO3.EnableMBQP, (mfxU16)MFX_CODINGOPTION_UNKNOWN, (mfxU16)MFX_CODINGOPTION_ON);
        else if (caps.ddi_caps.MbQpDataSupport == 0)
        {
#ifdef MFX_ENABLE_HEVCE_ROI
            if (par.bROIViaMBQP)
            {
                ROI->NumROI = 0;
                invalid++;
                par.bROIViaMBQP = false;
            }
#endif
            changed += CheckOption(CO3.EnableMBQP, (mfxU16)MFX_CODINGOPTION_UNKNOWN, (mfxU16)MFX_CODINGOPTION_OFF);
        }


    }
    //not supported in driver
    changed += CheckOption(CO2.EnableMAD, (mfxU16)MFX_CODINGOPTION_UNKNOWN, (mfxU16)MFX_CODINGOPTION_OFF);

    if (CO3.WinBRCSize > 0 || CO3.WinBRCMaxAvgKbps > 0)
    {
        if ((par.mfx.RateControlMethod != MFX_RATECONTROL_VBR && par.mfx.RateControlMethod != MFX_RATECONTROL_QVBR))
        {
            //Sliding window is for VBR or QVBR
            CO3.WinBRCMaxAvgKbps = 0;
            CO3.WinBRCSize = 0;
            changed++;
        }
        else if (!IsOn(CO2.ExtBRC))
        {
            // check WinBRCSize
            if (CO3.WinBRCSize > 0)
            {
                if (par.mfx.FrameInfo.FrameRateExtN != 0 && par.mfx.FrameInfo.FrameRateExtD != 0)
                {
                    mfxU16 iframerate = (mfxU16)ceil((mfxF64)par.mfx.FrameInfo.FrameRateExtN / par.mfx.FrameInfo.FrameRateExtD);
                    changed += CheckOption(CO3.WinBRCSize, iframerate);
                }
                else
                {
                    //if FrameRateExtN and FrameRateExtD are not set - need to calculate them
                    CalculateMFXFramerate((mfxF64)CO3.WinBRCSize, &par.mfx.FrameInfo.FrameRateExtN, &par.mfx.FrameInfo.FrameRateExtD);
                }
            }

            // check WinBRCMaxAvgKbps
            if (CO3.WinBRCMaxAvgKbps)
            {
                if (par.MaxKbps)
                {
                    changed += CheckOption(CO3.WinBRCMaxAvgKbps, par.MaxKbps);
                }
                else
                {
                    if (par.TargetKbps && CO3.WinBRCMaxAvgKbps < par.TargetKbps) // WinBRCMaxAvgKbps must not be less than TargetKbps
                    {
                        CO3.WinBRCMaxAvgKbps = 0;
                        CO3.WinBRCSize = 0;
                        invalid++;
                    }
                    else
                    {
                        changed += CheckOption(par.MaxKbps, par.TargetKbps);
                    }
                }
            }
            else
            {
                changed += CheckOption(CO3.WinBRCMaxAvgKbps, par.MaxKbps);
            }
        }
        else if (par.TargetKbps && CO3.WinBRCMaxAvgKbps && CO3.WinBRCMaxAvgKbps < par.TargetKbps) // ExtBRC is on
        {
            CO3.WinBRCMaxAvgKbps = 0;
            CO3.WinBRCSize = 0;
            invalid++;
        }
    }
    if (par.mfx.RateControlMethod != MFX_RATECONTROL_CQP && par.isSWBRC())
    {
        changed += CheckRangeDflt(CO2.MinQPI, minQP, maxQP, 0);
        changed += CheckRangeDflt(CO2.MaxQPI, CO2.MinQPI, maxQP, 0);
        changed += CheckRangeDflt(CO2.MinQPP, minQP, maxQP, 0);
        changed += CheckRangeDflt(CO2.MaxQPP, CO2.MinQPP, maxQP, 0);
        changed += CheckRangeDflt(CO2.MinQPB, minQP, maxQP, 0);
        changed += CheckRangeDflt(CO2.MaxQPB, CO2.MinQPB, maxQP, 0);
    }
    else
    {
        changed += CheckRangeDflt(CO2.MinQPI, 0, 0, 0);
        changed += CheckRangeDflt(CO2.MaxQPI, 0, 0, 0);
        changed += CheckRangeDflt(CO2.MinQPP, 0, 0, 0);
        changed += CheckRangeDflt(CO2.MaxQPP, 0, 0, 0);
        changed += CheckRangeDflt(CO2.MinQPB, 0, 0, 0);
        changed += CheckRangeDflt(CO2.MaxQPB, 0, 0, 0);
    }

#if (MFX_VERSION >= 1026)
    if (isSAOSupported(par))
    {
        changed += CheckOption(par.m_ext.HEVCParam.SampleAdaptiveOffset
            , (mfxU16)MFX_SAO_UNKNOWN
            , (mfxU16)MFX_SAO_DISABLE
            , (mfxU16)MFX_SAO_ENABLE_LUMA
            , (mfxU16)MFX_SAO_ENABLE_CHROMA
            , (mfxU16)(MFX_SAO_ENABLE_LUMA | MFX_SAO_ENABLE_CHROMA)
        );
        //On Gen10+ VDEnc SAO for only Luma/Chroma in CQP mode isn't supported by driver until real customer usage
        if (par.m_platform == MFX_HW_CNL &&
            par.mfx.RateControlMethod == MFX_RATECONTROL_CQP &&
            par.mfx.LowPower == MFX_CODINGOPTION_ON &&
            (par.m_ext.HEVCParam.SampleAdaptiveOffset == (mfxU16)MFX_SAO_ENABLE_LUMA || par.m_ext.HEVCParam.SampleAdaptiveOffset == (mfxU16)MFX_SAO_ENABLE_CHROMA))
        {
            par.m_ext.HEVCParam.SampleAdaptiveOffset = (mfxU16)MFX_SAO_DISABLE;
            invalid++;
        }
    }
    else
    {
        changed += CheckOption(par.m_ext.HEVCParam.SampleAdaptiveOffset
            , (mfxU16)MFX_SAO_UNKNOWN
            , (mfxU16)MFX_SAO_DISABLE
        );
    }
#endif //(MFX_VERSION >= 1026)

#if defined(MFX_ENABLE_HEVCE_WEIGHTED_PREDICTION)
    changed += CheckOption(CO3.WeightedPred
        , (mfxU16)MFX_WEIGHTED_PRED_UNKNOWN
        , (mfxU16)MFX_WEIGHTED_PRED_DEFAULT
        , caps.ddi_caps.NoWeightedPred ? 0 : MFX_WEIGHTED_PRED_EXPLICIT);

    changed += CheckOption(CO3.WeightedBiPred
        , (mfxU16)MFX_WEIGHTED_PRED_UNKNOWN
        , (mfxU16)MFX_WEIGHTED_PRED_DEFAULT
        , caps.ddi_caps.NoWeightedPred ? 0 : MFX_WEIGHTED_PRED_EXPLICIT);

#if defined(MFX_ENABLE_HEVCE_FADE_DETECTION)
    if (caps.ddi_caps.NoWeightedPred || par.m_platform < MFX_HW_ICL)
    {
        changed += CheckOption(CO3.FadeDetection
            , (mfxU16)MFX_CODINGOPTION_UNKNOWN
            , (mfxU16)MFX_CODINGOPTION_OFF);
    }
    else
    {
        changed += CheckTriStateOption(CO3.FadeDetection);
    }
#endif //defined(MFX_ENABLE_HEVCE_FADE_DETECTION)
#endif //defined(MFX_ENABLE_HEVCE_WEIGHTED_PREDICTION)

#if MFX_VERSION >= MFX_VERSION_NEXT
    changed += CheckRangeDflt(CO3.DeblockingAlphaTcOffset, -12, 12, 0);
    changed += CheckRangeDflt(CO3.DeblockingBetaOffset, -12, 12, 0);
#endif

#if (MFX_VERSION >= 1027)
    if (par.m_platform < MFX_HW_ICL)
        changed += CheckOption(par.m_ext.HEVCParam.GeneralConstraintFlags, 0);
#endif // MFX_VERSION >= 1027

#if (MFX_VERSION >= 1025)
    if (par.mfx.EncodedOrder)
        changed += CheckOption(CO3.EnableNalUnitType, MFX_CODINGOPTION_ON, MFX_CODINGOPTION_OFF, MFX_CODINGOPTION_UNKNOWN);
    else
        changed += CheckOption(CO3.EnableNalUnitType, MFX_CODINGOPTION_OFF, MFX_CODINGOPTION_UNKNOWN);
#endif

    sts = CheckProfile(par, par.m_platform);

    if (sts >= MFX_ERR_NONE && par.mfx.CodecLevel > 0)  // QueryIOSurf, Init or Reset
    {
        if (sts == MFX_WRN_INCOMPATIBLE_VIDEO_PARAM) changed +=1;
        sts = CorrectLevel(par, false);
    }

    changed += CheckTriStateOption(par.m_ext.DDI.QpAdjust);

    if (sts == MFX_ERR_NONE && changed)
        sts = MFX_WRN_INCOMPATIBLE_VIDEO_PARAM;

    if (sts >= MFX_ERR_NONE && invalid)
        sts = MFX_ERR_INVALID_VIDEO_PARAM;  // for Query will be replaced by MFX_ERR_UNSUPPORTED

    return sts;
}

/*
    Setting up default values for video parameters, based on hwCaps.

    Default value for LowPower is setting up in function SetLowpowerDefault
*/
void SetDefaults(
    MfxVideoParam &              par,
    MFX_ENCODE_CAPS_HEVC const & hwCaps)
{
    mfxU64 rawBits = (mfxU64)par.m_ext.HEVCParam.PicWidthInLumaSamples * par.m_ext.HEVCParam.PicHeightInLumaSamples * 3 / 2 * 8;
    mfxF64 maxFR   = 300.;
    mfxU32 maxBR   = 0xFFFFFFFF;
    mfxU32 maxBuf  = 0xFFFFFFFF;
    mfxU16 maxDPB  = 16;
    mfxU16 maxQP   = 51;
    mfxU16 minQP = (IsOn(par.mfx.LowPower) && !par.isSWBRC()) ? 10 : 1;   // 10 is min QP for VDENC

    mfxExtCodingOption2& CO2 = par.m_ext.CO2;
    mfxExtCodingOption3& CO3 = par.m_ext.CO3;

    // CheckVideoParam is always called before SetDefaults
    // par.LCUSize must be set to correct correct user provided value or default one
    ////    if (!par.LCUSize)
    ////        par.LCUSize = GetDefaultLCUSize(par, hwCaps);
    // just report this value back to user parameters
#if MFX_VERSION >= 1026
    par.m_ext.HEVCParam.LCUSize = (mfxU16)par.LCUSize; // typecast is safe since value must be valid 8,16,32,64
#endif

    if (par.mfx.CodecLevel)
    {
        maxFR  = GetMaxFrByLevel(par);
        maxBR  = GetMaxKbpsByLevel(par);
        maxBuf = GetMaxCpbInKBByLevel(par);
        maxDPB = (mfxU16)GetMaxDpbSizeByLevel(par);
    }

#if (MFX_VERSION >= 1027)
    if (!par.mfx.FrameInfo.FourCC)
        par.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12;

    if (!par.mfx.FrameInfo.BitDepthLuma)
        par.mfx.FrameInfo.BitDepthLuma = GetMaxBitDepth(par.mfx.FrameInfo.FourCC);

    if (!par.mfx.FrameInfo.BitDepthChroma)
        par.mfx.FrameInfo.BitDepthChroma = par.mfx.FrameInfo.BitDepthLuma;

    if (!CO3.TargetChromaFormatPlus1)
        CO3.TargetChromaFormatPlus1 = GetMaxChroma(par);

    if (!CO3.TargetBitDepthLuma)
        CO3.TargetBitDepthLuma = GetMaxBitDepth(par, hwCaps.ddi_caps.MaxEncodedBitDepth);

    rawBits = (mfxU64)GetRawBytes(
          par.m_ext.HEVCParam.PicWidthInLumaSamples
        , par.m_ext.HEVCParam.PicHeightInLumaSamples
        , CO3.TargetChromaFormatPlus1 - 1
        , CO3.TargetBitDepthLuma) * 8;

    if (CO3.TargetBitDepthLuma > 8)
    {
        maxQP += 6 * (CO3.TargetBitDepthLuma - 8);

        if (IsOn(par.mfx.LowPower) || par.m_platform < MFX_HW_ICL)
            minQP += 6 * (CO3.TargetBitDepthLuma - 8);
    }

    if (!par.mfx.CodecProfile)
    {
        if (   CO3.TargetChromaFormatPlus1 - 1 != MFX_CHROMAFORMAT_YUV420
            || CO3.TargetBitDepthLuma > 10)
        {
            par.mfx.CodecProfile = MFX_PROFILE_HEVC_REXT;
        }
        else if (CO3.TargetBitDepthLuma == 10)
        {
            par.mfx.CodecProfile = MFX_PROFILE_HEVC_MAIN10;
        }
        else
        {
            par.mfx.CodecProfile = MFX_PROFILE_HEVC_MAIN;
        }
    }
#else
    if (!par.mfx.CodecProfile)
    {
        par.mfx.CodecProfile = mfxU16((par.mfx.FrameInfo.BitDepthLuma > 8 || par.mfx.FrameInfo.FourCC == MFX_FOURCC_P010) ? MFX_PROFILE_HEVC_MAIN10 : MFX_PROFILE_HEVC_MAIN);
    }
#endif

    if (!par.AsyncDepth)
        par.AsyncDepth = 5;

    if (!par.mfx.TargetUsage)
        par.mfx.TargetUsage = 4;

    if (hwCaps.ddi_caps.TUSupport)
        CheckTU(hwCaps.ddi_caps.TUSupport, par.mfx.TargetUsage);

    if (!par.m_ext.HEVCTiles.NumTileColumns)
        par.m_ext.HEVCTiles.NumTileColumns = 1;

    if (!par.m_ext.HEVCTiles.NumTileRows)
        par.m_ext.HEVCTiles.NumTileRows = 1;

    if (!par.mfx.NumSlice || !par.m_slice.size())
    {
        MakeSlices(par, hwCaps.ddi_caps.SliceStructure);
        par.mfx.NumSlice = (mfxU16)par.m_slice.size();
    }

#if (MFX_VERSION < 1027)
    if (!par.mfx.FrameInfo.FourCC)
        par.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12;
#endif

    if (!par.mfx.FrameInfo.CropW)
        par.mfx.FrameInfo.CropW = par.m_ext.HEVCParam.PicWidthInLumaSamples - par.mfx.FrameInfo.CropX;

    if (!par.mfx.FrameInfo.CropH)
        par.mfx.FrameInfo.CropH = par.m_ext.HEVCParam.PicHeightInLumaSamples - par.mfx.FrameInfo.CropY;

    if (!par.mfx.FrameInfo.FrameRateExtN || !par.mfx.FrameInfo.FrameRateExtD)
    {
        par.mfx.FrameInfo.FrameRateExtD = 1001;
        par.mfx.FrameInfo.FrameRateExtN = mfxU32(std::min(30000./par.mfx.FrameInfo.FrameRateExtD, maxFR) * par.mfx.FrameInfo.FrameRateExtD);
    }

    if (!par.mfx.FrameInfo.AspectRatioW)
        par.mfx.FrameInfo.AspectRatioW = 1;

    if (!par.mfx.FrameInfo.AspectRatioH)
        par.mfx.FrameInfo.AspectRatioH = 1;

    //if (!par.mfx.FrameInfo.PicStruct)
    //    par.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;

#if (MFX_VERSION < 1027)
    if (!par.mfx.FrameInfo.ChromaFormat)
        par.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420;

    if (!par.mfx.FrameInfo.BitDepthLuma)
        par.mfx.FrameInfo.BitDepthLuma = (par.mfx.FrameInfo.FourCC == MFX_FOURCC_P010) ? 10 : 8;

    if (!par.mfx.FrameInfo.BitDepthChroma)
        par.mfx.FrameInfo.BitDepthChroma = par.mfx.FrameInfo.BitDepthLuma;

    if (par.mfx.FrameInfo.BitDepthLuma > 8)
    {
        rawBits = rawBits / 8 * par.mfx.FrameInfo.BitDepthLuma;
        maxQP += 6 * (par.mfx.FrameInfo.BitDepthLuma - 8);

        if (IsOn(par.mfx.LowPower) || (par.m_platform >= MFX_HW_KBL
#if (MFX_VERSION >= 1025)
            && par.m_platform < MFX_HW_CNL
#endif
            ))
            minQP += 6 * (par.mfx.FrameInfo.BitDepthLuma - 8);
    }
#endif

    if (!par.mfx.RateControlMethod)
        par.mfx.RateControlMethod = MFX_RATECONTROL_CQP;

    if (par.mfx.RateControlMethod == MFX_RATECONTROL_CQP)
    {
        if (!par.mfx.QPI)
            par.mfx.QPI = mfxU16(std::max<mfxI32>(par.mfx.QPP - 2, minQP) * !!par.mfx.QPP);
        if (!par.mfx.QPI)
            par.mfx.QPI = mfxU16(std::max<mfxI32>(par.mfx.QPB - 4, minQP) * !!par.mfx.QPB);
        if (!par.mfx.QPI)
            par.mfx.QPI = std::max<mfxU16>(minQP, (maxQP + 1) / 2);
        if (!par.mfx.QPP)
            par.mfx.QPP = std::min<mfxU16>(par.mfx.QPI + 2, maxQP);
        if (!par.mfx.QPB)
            par.mfx.QPB = std::min<mfxU16>(par.mfx.QPP + 2, maxQP);

        if (!par.BufferSizeInKB)
            par.BufferSizeInKB = std::min(maxBuf, mfxU32(rawBits / 8000));

    }
    else if (   par.mfx.RateControlMethod == MFX_RATECONTROL_ICQ)
    {
        if (!par.BufferSizeInKB)
            par.BufferSizeInKB = std::min(maxBuf, mfxU32(rawBits / 8000));
    }
    else if (   par.mfx.RateControlMethod == MFX_RATECONTROL_CBR
             || par.mfx.RateControlMethod == MFX_RATECONTROL_VBR
             || par.mfx.RateControlMethod == MFX_RATECONTROL_QVBR
             || par.mfx.RateControlMethod == MFX_RATECONTROL_VCM
             || par.mfx.RateControlMethod == MFX_RATECONTROL_LA_EXT)
    {
        if (!par.TargetKbps)
        {
            if (par.mfx.FrameInfo.FrameRateExtD) // KW
                par.TargetKbps = std::min(maxBR, mfxU32(rawBits * par.mfx.FrameInfo.FrameRateExtN / par.mfx.FrameInfo.FrameRateExtD / 150000));
            else
            {
                assert(!"FrameRateExtD = 0");
                par.TargetKbps = std::min(maxBR, mfxU32(rawBits * std::min(maxFR, 30000. / 1001) / 150000));
            }
        }
        if (!par.MaxKbps)
            par.MaxKbps = par.TargetKbps;
        if (!par.BufferSizeInKB)
        {
            par.BufferSizeInKB = std::min(maxBuf, par.MaxKbps / 4); //2 sec: the same as H264
            par.BufferSizeInKB = std::max(par.BufferSizeInKB, par.InitialDelayInKB);
        }
        if (!par.InitialDelayInKB)
            par.InitialDelayInKB = par.BufferSizeInKB / 2;
    }
    else if(par.mfx.RateControlMethod == MFX_RATECONTROL_AVBR)
    {
        if (!par.mfx.Accuracy)
            par.mfx.Accuracy =  AVBR_ACCURACY_MAX;
        if (!par.mfx.Convergence)
            par.mfx.Convergence =  AVBR_CONVERGENCE_MAX;
    }

    if (par.mfx.RateControlMethod == MFX_RATECONTROL_ICQ && !par.mfx.ICQQuality)
        par.mfx.ICQQuality = 26;

    if (par.mfx.RateControlMethod == MFX_RATECONTROL_QVBR && !CO3.QVBRQuality)
        CO3.QVBRQuality = 26;

    /*if (!par.mfx.GopOptFlag)
        par.mfx.GopOptFlag = MFX_GOP_CLOSED;*/

    if (!par.mfx.GopPicSize)
        par.mfx.GopPicSize = (par.mfx.CodecProfile == MFX_PROFILE_HEVC_MAINSP ? 1 : 0xFFFF);

    if (!par.mfx.GopRefDist)
    {
        if (par.isTL() || hwCaps.ddi_caps.SliceIPOnly || par.mfx.GopPicSize < 3 || par.mfx.NumRefFrame == 1)
            par.mfx.GopRefDist = 1;
        else
            par.mfx.GopRefDist = std::min<mfxU16>(par.mfx.GopPicSize - 1, (par.mfx.RateControlMethod == MFX_RATECONTROL_CQP || par.isSWBRC()) ? 8 : 4);
    }

    if (par.m_ext.CO2.BRefType == MFX_B_REF_UNKNOWN)
    {
        if (par.mfx.GopRefDist > 3 && ((minRefForPyramid(par.mfx.GopRefDist, par.isField()) <= par.mfx.NumRefFrame) || par.mfx.NumRefFrame == 0))
            par.m_ext.CO2.BRefType = MFX_B_REF_PYRAMID;
        else
            par.m_ext.CO2.BRefType = MFX_B_REF_OFF;
    }

    if (CO3.LowDelayBRC == MFX_CODINGOPTION_UNKNOWN)
        CO3.LowDelayBRC = MFX_CODINGOPTION_OFF;

    if (par.m_ext.CO2.MBBRC == MFX_CODINGOPTION_UNKNOWN &&
        (par.mfx.RateControlMethod == MFX_RATECONTROL_CQP || par.isSWBRC() || IsOn(par.mfx.LowPower)))
        par.m_ext.CO2.MBBRC = MFX_CODINGOPTION_OFF; // disable MBBRC for those cases. For other cases, MBBRC can be on or off at the driver's discretion.

    if (CO3.LowDelayBRC == MFX_CODINGOPTION_ON && !CO2.MaxFrameSize && par.mfx.FrameInfo.FrameRateExtN && par.mfx.FrameInfo.FrameRateExtD) {

        mfxF64 frameRate = mfxF64(par.mfx.FrameInfo.FrameRateExtN) / par.mfx.FrameInfo.FrameRateExtD;
        mfxU32 avgFrameSizeInBytes = mfxU32(par.TargetKbps * 1000 / frameRate / 8);

        if (par.MaxKbps)
            avgFrameSizeInBytes = mfxU32(par.MaxKbps * 1000 / frameRate / 8);

        if (!CO3.MaxFrameSizeP)
            CO3.MaxFrameSizeP = avgFrameSizeInBytes;
        if (!CO3.MaxFrameSizeI)
            CO3.MaxFrameSizeI = 2 * CO3.MaxFrameSizeP;

        CO2.MaxFrameSize = CO3.MaxFrameSizeI;
    }

    {
        // calculate ActiveReference

        mfxU16 RefActiveP   = par.m_ext.DDI.NumActiveRefP;
        mfxU16 RefActiveBL0 = par.m_ext.DDI.NumActiveRefBL0;
        mfxU16 RefActiveBL1 = par.m_ext.DDI.NumActiveRefBL1;

        if (!RefActiveP)
            RefActiveP   = *std::max_element(std::begin(CO3.NumRefActiveP), std::end(CO3.NumRefActiveP));

        if (!RefActiveBL0)
            RefActiveBL0 = *std::max_element(std::begin(CO3.NumRefActiveBL0), std::end(CO3.NumRefActiveBL0));

        if (!RefActiveBL1)
            RefActiveBL1 = *std::max_element(std::begin(CO3.NumRefActiveBL1), std::end(CO3.NumRefActiveBL1));

        bool bDefinedActiveRef = (RefActiveP || RefActiveBL0 || RefActiveBL1);

        if (!RefActiveP)
            RefActiveP = (par.mfx.TargetUsage == 7) ? 1 :
                par.mfx.NumRefFrame ? std::min<mfxU16>(hwCaps.ddi_caps.MaxNum_Reference0, par.mfx.NumRefFrame) : hwCaps.ddi_caps.MaxNum_Reference0;

        if (!RefActiveBL0)
            RefActiveBL0 = RefActiveP;

        if (!RefActiveBL1)
            RefActiveBL1 = (par.mfx.TargetUsage == 7) ? 1 :
                par.mfx.NumRefFrame ? std::min<mfxU16>(hwCaps.ddi_caps.MaxNum_Reference1, par.mfx.NumRefFrame) : hwCaps.ddi_caps.MaxNum_Reference1;

#if (MFX_VERSION >= 1025)
        if (par.m_platform >= MFX_HW_CNL)
        {
            RefActiveP   = std::min(RefActiveP,   GetMaxNumRef(par, P));
            RefActiveBL0 = std::min(RefActiveBL0, GetMaxNumRef(par, BL0));
            RefActiveBL1 = std::min(RefActiveBL1, GetMaxNumRef(par, BL1));
        }
#endif

        //set ActiveReference

        if (!par.m_ext.DDI.NumActiveRefP)
            par.m_ext.DDI.NumActiveRefP = RefActiveP;

        if (!par.m_ext.DDI.NumActiveRefBL0)
            par.m_ext.DDI.NumActiveRefBL0 = RefActiveBL0;

        if (!par.m_ext.DDI.NumActiveRefBL1)
            par.m_ext.DDI.NumActiveRefBL1 = RefActiveBL1;

        for (mfxU16 i = 0; i < 8; i++)
            if (!CO3.NumRefActiveP[i])
                CO3.NumRefActiveP[i] = ((i == 0) ? par.m_ext.DDI.NumActiveRefP: CO3.NumRefActiveP[i-1]);

        for (mfxU16 i = 0; i < 8; i++)
            if (CO3.NumRefActiveBL0[i] == 0)  CO3.NumRefActiveBL0[i] = par.m_ext.DDI.NumActiveRefBL0;

        for (mfxU16 i = 0; i < 8; i++)
            if (CO3.NumRefActiveBL1[i] == 0)  CO3.NumRefActiveBL1[i] = par.m_ext.DDI.NumActiveRefBL1;

        // set DPB size
        if (!par.mfx.NumRefFrame)
        {
            mfxU16 k = par.isField() ? 2 : 1;
            if (par.isBPyramid())
            {
                par.mfx.NumRefFrame = mfxU16(minRefForPyramid(par.mfx.GopRefDist, par.isField()));
                if (bDefinedActiveRef)
                {
                    par.mfx.NumRefFrame = std::max<mfxU16>(par.mfx.NumRefFrame, CO3.NumRefActiveP[0]*k);
                    for (mfxU16 i = 0; i < getNumBPyrLayers(par.mfx.GopRefDist); i++)
                    {
                        par.mfx.NumRefFrame = std::max<mfxU16>(par.mfx.NumRefFrame, (CO3.NumRefActiveBL0[i] + i + 1)*k);
                        par.mfx.NumRefFrame = std::max<mfxU16>(par.mfx.NumRefFrame, (CO3.NumRefActiveBL1[i] + i + 1)*k);
                    }
                }

            }
            else if (par.isLowDelay())
            {
                par.mfx.NumRefFrame = std::max<mfxU16>((mfxU16)par.PPyrInterval*k, RefActiveP);
            }
            else
            {
                par.mfx.NumRefFrame = (std::max(RefActiveP, RefActiveBL0) + (par.mfx.GopRefDist > 1) * RefActiveBL0)*k;
            }
            par.mfx.NumRefFrame = mfx::clamp<mfxU16>(par.mfx.NumRefFrame, par.NumTL() - 1, maxDPB - 1);
            par.PPyrInterval = std::min<mfxU32>(par.PPyrInterval, par.mfx.NumRefFrame);
        }
    }
    if (par.m_ext.CO2.ExtBRC == MFX_CODINGOPTION_UNKNOWN)
        par.m_ext.CO2.ExtBRC = MFX_CODINGOPTION_OFF;

    if (par.m_ext.CO3.PRefType == MFX_P_REF_DEFAULT)
    {
        if (par.mfx.GopRefDist == 1 && (par.mfx.RateControlMethod == MFX_RATECONTROL_CQP || par.isSWBRC()))
            par.m_ext.CO3.PRefType = MFX_P_REF_PYRAMID;
        else if (par.mfx.GopRefDist == 1)
            par.m_ext.CO3.PRefType = MFX_P_REF_SIMPLE;
    }

    /*if (   DEFAULT_LTR_INTERVAL > 0
        && !par.LTRInterval
        && par.NumRefLX[0] > 1
        && par.mfx.GopPicSize > (DEFAULT_LTR_INTERVAL * 2)
        && (par.mfx.RateControlMethod == MFX_RATECONTROL_CBR || par.mfx.RateControlMethod == MFX_RATECONTROL_VBR))
        par.LTRInterval = DEFAULT_LTR_INTERVAL;*/

    if (!par.m_ext.AVCTL.Layer[0].Scale)
        par.m_ext.AVCTL.Layer[0].Scale = 1;

    if (!par.mfx.CodecLevel)
        CorrectLevel(par, false);

    if (par.m_ext.CO2.IntRefType && par.m_ext.CO2.IntRefCycleSize == 0)
    {
        // set intra refresh cycle to 1 sec by default
        par.m_ext.CO2.IntRefCycleSize =
            (mfxU16)((par.mfx.FrameInfo.FrameRateExtN + par.mfx.FrameInfo.FrameRateExtD - 1) / par.mfx.FrameInfo.FrameRateExtD);
    }
    if (!par.m_ext.CO.NalHrdConformance)
        par.m_ext.CO.NalHrdConformance =(mfxU16) (par.HRDConformance ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF);

    if (!par.m_ext.CO.VuiNalHrdParameters)
        par.m_ext.CO.VuiNalHrdParameters = (mfxU16) (par.HRDConformance ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF);

     if (!par.m_ext.CO.PicTimingSEI)
        par.m_ext.CO.PicTimingSEI = (mfxU16)(par.HRDConformance || par.isField() ? MFX_CODINGOPTION_ON: MFX_CODINGOPTION_OFF);

    if (!par.m_ext.CO.AUDelimiter)
        par.m_ext.CO.AUDelimiter = MFX_CODINGOPTION_OFF;

    if (!par.m_ext.CO2.RepeatPPS)
        par.m_ext.CO2.RepeatPPS = MFX_CODINGOPTION_OFF;
    if (!CO3.GPB)
        CO3.GPB = MFX_CODINGOPTION_ON;
    if (!CO3.EnableQPOffset)
    {
        if (   par.mfx.RateControlMethod == MFX_RATECONTROL_CQP
            && (CO2.BRefType == MFX_B_REF_PYRAMID || CO3.PRefType == MFX_P_REF_PYRAMID))
        {
            CO3.EnableQPOffset = MFX_CODINGOPTION_ON;
            mfxI16 QPX = (par.mfx.GopRefDist == 1) ? par.mfx.QPP : par.mfx.QPB;

            for (mfxI16 i = 0; i < 8; i++)
                CO3.QPOffset[i] = mfx::clamp<mfxI16>(i + (par.mfx.GopRefDist > 1), (mfxI16)minQP - QPX, (mfxI16)maxQP - QPX);
        }
        else
            CO3.EnableQPOffset = MFX_CODINGOPTION_OFF;
    }

    if (IsOff(CO3.EnableQPOffset))
        Zero(CO3.QPOffset);

    if (CO2.MinQPI || CO2.MinQPP || CO2.MinQPB ||
        CO2.MaxQPI || CO2.MaxQPP || CO2.MaxQPB)
    {
        if (!CO2.MinQPI)
            CO2.MinQPI = (mfxU8)minQP;
        if (!CO2.MinQPP)
            CO2.MinQPP = (mfxU8)minQP;
        if (!CO2.MinQPB)
            CO2.MinQPB = (mfxU8)minQP;

        if (!CO2.MaxQPI)
            CO2.MaxQPI = (mfxU8)maxQP;
        if (!CO2.MaxQPP)
            CO2.MaxQPP = (mfxU8)maxQP;
        if (!CO2.MaxQPB)
            CO2.MaxQPB = (mfxU8)maxQP;
    }


#if (MFX_VERSION >= 1026)
    if (!CO3.TransformSkip)
        CO3.TransformSkip = (mfxU16)((par.m_platform < MFX_HW_ICL) ? MFX_CODINGOPTION_OFF : MFX_CODINGOPTION_ON);

    if (!par.m_ext.HEVCParam.SampleAdaptiveOffset)
        par.m_ext.HEVCParam.SampleAdaptiveOffset = isSAOSupported(par) ? (MFX_SAO_ENABLE_LUMA | MFX_SAO_ENABLE_CHROMA) : MFX_SAO_DISABLE;

    if (!par.m_ext.DDI.QpAdjust)
        par.m_ext.DDI.QpAdjust = MFX_CODINGOPTION_OFF;
#endif // MFX_VERSION >= 1026

    if (CO3.EnableMBQP == 0)
    {
        if (par.isSWBRC() || par.mfx.RateControlMethod == MFX_RATECONTROL_CQP)
            CO3.EnableMBQP = MFX_CODINGOPTION_OFF;
        else
            CO3.EnableMBQP = MFX_CODINGOPTION_ON;
    }
    if (CO3.WinBRCSize > 0 || CO3.WinBRCMaxAvgKbps > 0)
    {
        if (!CO3.WinBRCSize)
            CO3.WinBRCSize = mfxU16((par.mfx.FrameInfo.FrameRateExtN + par.mfx.FrameInfo.FrameRateExtD - 1)/par.mfx.FrameInfo.FrameRateExtD);
        if (!CO3.WinBRCMaxAvgKbps)
            CO3.WinBRCMaxAvgKbps = (mfxU16)(par.MaxKbps/par.mfx.BRCParamMultiplier);
    }

#if (MFX_VERSION >= 1027)
    if ( (par.mfx.CodecProfile == MFX_PROFILE_HEVC_REXT && !par.m_ext.HEVCParam.GeneralConstraintFlags)
        )
    {
        mfxU64& constr = par.m_ext.HEVCParam.GeneralConstraintFlags;

        if (CO3.TargetChromaFormatPlus1 - 1 <= MFX_CHROMAFORMAT_YUV422)
            constr |= MFX_HEVC_CONSTR_REXT_MAX_422CHROMA;
        if (CO3.TargetChromaFormatPlus1 - 1 <= MFX_CHROMAFORMAT_YUV420)
            constr |= MFX_HEVC_CONSTR_REXT_MAX_420CHROMA;

        if (CO3.TargetBitDepthLuma <= 12)
            constr |= MFX_HEVC_CONSTR_REXT_MAX_12BIT;
        if (CO3.TargetBitDepthLuma <= 10)
            constr |= MFX_HEVC_CONSTR_REXT_MAX_10BIT;
        if (   CO3.TargetBitDepthLuma <= 8
            //there is no Main 4:2:2 in current standard spec.(2016/12), only Main 4:2:2 10
            && (CO3.TargetChromaFormatPlus1 - 1 != MFX_CHROMAFORMAT_YUV422))
            constr |= MFX_HEVC_CONSTR_REXT_MAX_8BIT;

        constr |= MFX_HEVC_CONSTR_REXT_LOWER_BIT_RATE;
    }
#endif

#if defined(MFX_ENABLE_HEVCE_WEIGHTED_PREDICTION)

#if defined(MFX_ENABLE_HEVCE_FADE_DETECTION)
    if (!CO3.FadeDetection)
        CO3.FadeDetection = MFX_CODINGOPTION_OFF;
#endif //defined(MFX_ENABLE_HEVCE_FADE_DETECTION)

    if (!CO3.WeightedPred)
    {
        CO3.WeightedPred =
#if defined(MFX_ENABLE_HEVCE_FADE_DETECTION)
            IsOn(CO3.FadeDetection) ? (mfxU16)MFX_WEIGHTED_PRED_EXPLICIT :
#endif //defined(MFX_ENABLE_HEVCE_FADE_DETECTION)
                (mfxU16)MFX_WEIGHTED_PRED_DEFAULT;
    }

    if (!CO3.WeightedBiPred)
    {
        CO3.WeightedBiPred =
#if defined(MFX_ENABLE_HEVCE_FADE_DETECTION)
            IsOn(CO3.FadeDetection) ? (mfxU16)MFX_WEIGHTED_PRED_EXPLICIT :
#endif //defined(MFX_ENABLE_HEVCE_FADE_DETECTION)
                (mfxU16)MFX_WEIGHTED_PRED_DEFAULT;
    }
#endif //defined(MFX_ENABLE_HEVCE_WEIGHTED_PREDICTION)

#if (MFX_VERSION >= 1025)
    if (par.mfx.EncodedOrder)
        CO3.EnableNalUnitType = MFX_CODINGOPTION_ON;
    else
        CO3.EnableNalUnitType = MFX_CODINGOPTION_OFF;
#endif


}

} //namespace MfxHwH265Encode
#endif
