/*
* Copyright (c) 2017-2019, 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.
*/
//!
//! \file     codechal_encode_hevc_brc_g12.cpp
//! \brief    HEVC dual-pipe encoder brc kernels for GEN12.
//!

#include "codechal_encode_hevc_mbenc_g12.h"
#include "codechal_encode_hevc_brc_g12.h"
#include "codechal_encode_hevc_g12.h"
#include "mhw_vdbox_hcp_g12_X.h"
#include "Gen12_HEVC_B_LCU32.h"
#include "Gen12_HEVC_B_LCU64.h"
#include "cm_wrapper.h"

#include "Gen12_HEVC_BRC_INIT.h"
#include "Gen12_HEVC_BRC_RESET.h"
#include "Gen12_HEVC_BRC_UPDATE.h"
#include "Gen12_HEVC_BRC_LCUQP.h"

#include "codechal_debug.h"

#if USE_PROPRIETARY_CODE
#include "cm_device_rt.h"
#endif

#if MOS_MEDIASOLO_SUPPORTED
#include "mos_os_solo.h"
#endif // (_DEBUG || _RELEASE_INTERNAL)

MOS_STATUS CodecHalHevcBrcG12::AllocateBrcResources()
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS CodecHalHevcBrcG12::FreeBrcResources()
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;
    // Destroy the container for BRC buffers
    if (m_histBufferBrc)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroySurface(m_histBufferBrc));
        m_histBufferBrc = nullptr;
    }
    if (m_PAKStatsBufferBrc)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroySurface(m_PAKStatsBufferBrc));
        m_PAKStatsBufferBrc = nullptr;
    }
    if (m_PICStateInBufferBrc)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroySurface(m_PICStateInBufferBrc));
        m_PICStateInBufferBrc = nullptr;
    }
    if (m_PICStateOutBufferBrc)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroySurface(m_PICStateOutBufferBrc));
        m_PICStateOutBufferBrc = nullptr;
    }
    if (m_CombinedEncBufferBrc)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroySurface(m_CombinedEncBufferBrc));
        m_CombinedEncBufferBrc = nullptr;
    }
    if (m_PixelMBStatsBufferBrc)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroySurface(m_PixelMBStatsBufferBrc));
        m_PixelMBStatsBufferBrc = nullptr;
    }
    if (m_ConstDataBufferBRC)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroySurface(m_ConstDataBufferBRC));
        m_ConstDataBufferBRC = nullptr;
    }
    if (m_BrcMbQp)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroySurface(m_BrcMbQp));
        m_BrcMbQp = nullptr;
    }
    if (m_BrcROISurf)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroySurface(m_BrcROISurf));
        m_BrcROISurf = nullptr;
    }

    if (m_cmKrnBrcInit)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroyKernel(m_cmKrnBrcInit));
        m_cmKrnBrcInit = nullptr;
    }
    if (m_cmProgramBrcInit)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroyProgram(m_cmProgramBrcInit));
        m_cmProgramBrcInit = nullptr;
    }
    if (m_cmKrnBrcReset)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroyKernel(m_cmKrnBrcReset));
        m_cmKrnBrcReset = nullptr;
    }
    if (m_cmProgramBrcReset)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroyProgram(m_cmProgramBrcReset));
        m_cmProgramBrcReset = nullptr;
    }
    if (m_cmKrnBrcUpdate)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroyKernel(m_cmKrnBrcUpdate));
        m_cmKrnBrcUpdate = nullptr;
    }
    if (m_cmProgramBrcUpdate)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroyProgram(m_cmProgramBrcUpdate));
        m_cmProgramBrcUpdate = nullptr;
    }
    if (m_cmKrnBrcLCUQP)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroyKernel(m_cmKrnBrcLCUQP));
        m_cmKrnBrcLCUQP = nullptr;
    }
    if (m_cmProgramBrcLCUQP)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroyProgram(m_cmProgramBrcLCUQP));
        m_cmProgramBrcLCUQP = nullptr;
    }
    if (m_threadSpaceBrcInitReset)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroyThreadSpace(m_threadSpaceBrcInitReset));
        m_threadSpaceBrcInitReset = nullptr;
    }
    if (m_threadSpaceBrcUpdate)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroyThreadSpace(m_threadSpaceBrcUpdate));
        m_threadSpaceBrcUpdate = nullptr;
    }
    if (m_threadSpaceBrcLCUQP)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroyThreadSpace(m_threadSpaceBrcLCUQP));
        m_threadSpaceBrcLCUQP = nullptr;
    }
    return eStatus;
}

MOS_STATUS CodecHalHevcBrcG12::InitBrcKernelState()
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->LoadProgram((void *)HEVC_BRC_INIT_GENX,
        HEVC_BRC_INIT_GENX_SIZE,
        m_cmProgramBrcInit,
        "-nojitter"));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->CreateKernel(m_cmProgramBrcInit,
        "HEVC_brc_init",
        m_cmKrnBrcInit));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->LoadProgram((void *)HEVC_BRC_RESET_GENX,
        HEVC_BRC_RESET_GENX_SIZE,
        m_cmProgramBrcReset,
        "-nojitter"));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->CreateKernel(m_cmProgramBrcReset,
        "HEVC_brc_reset",
        m_cmKrnBrcReset));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->LoadProgram((void *)HEVC_BRC_UPDATE_GENX,
        HEVC_BRC_UPDATE_GENX_SIZE,
        m_cmProgramBrcUpdate,
        "-nojitter"));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->CreateKernel(m_cmProgramBrcUpdate,
        "HEVC_brc_update",
        m_cmKrnBrcUpdate));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->LoadProgram((void *)HEVC_BRC_LCUQP_GENX,
        HEVC_BRC_LCUQP_GENX_SIZE,
        m_cmProgramBrcLCUQP,
        "-nojitter"));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->CreateKernel(m_cmProgramBrcLCUQP,
        "HEVC_brc_lcuqp",
        m_cmKrnBrcLCUQP));

    return eStatus;
}
MOS_STATUS CodecHalHevcBrcG12::SetupKernelArgsBrcInit()
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    //Setup surfaces

    int idx = 0;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_cmKrnBrc->SetKernelArg(idx++, sizeof(encoderBrc->curbe), &encoderBrc->curbe));

    SurfaceIndex *pIndex0 = nullptr;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_histBufferBrc->GetIndex(pIndex0));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_cmKrnBrc->SetKernelArg(idx++, sizeof(SurfaceIndex), pIndex0));

    //Setup Distortion 2D surface
    CmSurface2D *brcDistortion = (encoderBrc->m_pictureCodingType == I_TYPE) ? encoderBrc->m_brcBuffers.brcIntraDistortionSurface
                                                                             : encoderBrc->m_brcBuffers.meBrcDistortionSurface;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(brcDistortion->GetIndex(pIndex0));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_cmKrnBrc->SetKernelArg(idx++, sizeof(SurfaceIndex), pIndex0));

    return eStatus;
}

MOS_STATUS CodecHalHevcBrcG12::SetupThreadSpace(CmKernel *cmKernel, CmThreadSpace *& threadSpace)
{
    MOS_STATUS  eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_CHK_STATUS_RETURN(cmKernel->SetThreadCount(1));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->CreateThreadSpace(1, 1, threadSpace));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(cmKernel->AssociateThreadSpace(threadSpace));
    return eStatus;
}

MOS_STATUS CodecHalHevcBrcG12::SetupBrcLcuqpThreadSpace(CmKernel *cmKernel, CmThreadSpace *& threadSpace)
{
    MOS_STATUS  eStatus = MOS_STATUS_SUCCESS;
    uint32_t xThread = (encoderBrc->m_downscaledWidthInMb4x * SCALE_FACTOR_4x + 15) >> 4;
    uint32_t yThread = (encoderBrc->m_downscaledHeightInMb4x * SCALE_FACTOR_4x + 7) >> 3;

    CODECHAL_ENCODE_CHK_STATUS_RETURN(cmKernel->SetThreadCount(xThread * yThread));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->CreateThreadSpace(xThread, yThread, threadSpace));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(cmKernel->AssociateThreadSpace(threadSpace));

    return eStatus;
}

MOS_STATUS CodecHalHevcBrcG12::InitCurbeDataBrcInit()
{
    MOS_STATUS  eStatus = MOS_STATUS_SUCCESS;
    return eStatus;
}

MOS_STATUS CodecHalHevcBrcG12::SetupSurfacesBrcInit()
{
    MOS_STATUS  eStatus = MOS_STATUS_SUCCESS;

    if (!m_histBufferBrc)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->CreateBuffer(
            &encoderBrc->m_brcBuffers.resBrcHistoryBuffer, //m_brcHistoryBufferSize,
            m_histBufferBrc));
    }

    return eStatus;
}

MOS_STATUS CodecHalHevcBrcG12::EncodeBrcInitResetKernel()
{
    MOS_STATUS  eStatus = MOS_STATUS_SUCCESS;
    // Setup curbe for BrcInitReset kernel
    if (encoderBrc->m_brcInit)
    {
        m_cmKrnBrc = m_cmKrnBrcInit;
    }
    else
    {
        m_cmKrnBrc = m_cmKrnBrcReset;
    }

    /* Only one CM kernel is used to switch between brcInit and brcReset, same rule for threadSpace.
       Destroy old one and create a new threadSpace when only brcReset is triggered. */
    if (encoderBrc->m_brcReset && m_threadSpaceBrcInitReset)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroyThreadSpace(m_threadSpaceBrcInitReset));
        m_threadSpaceBrcInitReset = nullptr;
    }

    if (!m_threadSpaceBrcInitReset)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(SetupThreadSpace(m_cmKrnBrc, m_threadSpaceBrcInitReset));
    }

    CODECHAL_ENCODE_CHK_STATUS_RETURN(BrcInitResetCurbe());

    CODECHAL_ENCODE_CHK_STATUS_RETURN(SetupSurfacesBrcInit());
    CODECHAL_ENCODE_CHK_STATUS_RETURN(SetupKernelArgsBrcInit());

    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmTask->AddKernel(m_cmKrnBrc));

    if (!encoderBrc->m_singleTaskPhaseSupported || encoderBrc->m_lastTaskInPhase)
    {
        CmEvent * event = CM_NO_EVENT;
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmQueue->EnqueueFast(encoderBrc->m_cmTask, event));

        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmTask->Reset());

        encoderBrc->m_lastTaskInPhase = false;
    }
    else
    {
        encoderBrc->m_cmTask->AddSync();
    }

    encoderBrc->m_brcInit = encoderBrc->m_brcReset = false;
    // End FW
    return eStatus;
}

MOS_STATUS CodecHalHevcBrcG12::BrcInitResetCurbe()
{
    MOS_STATUS  eStatus = MOS_STATUS_SUCCESS;

    // Initialize the CURBE data
    encoderBrc->curbe = encoderBrc->m_brcInitResetCurbeInit;

    uint32_t   profileLevelMaxFrame = encoderBrc->GetProfileLevelMaxFrameSize();

    if (encoderBrc->m_hevcSeqParams->RateControlMethod == RATECONTROL_CBR ||
        encoderBrc->m_hevcSeqParams->RateControlMethod == RATECONTROL_VBR ||
        encoderBrc->m_hevcSeqParams->RateControlMethod == RATECONTROL_AVBR)
    {
        if (encoderBrc->m_hevcSeqParams->InitVBVBufferFullnessInBit == 0)
        {
            CODECHAL_ENCODE_ASSERTMESSAGE("Initial VBV Buffer Fullness is zero\n");
            return MOS_STATUS_INVALID_PARAMETER;
        }

        if (encoderBrc->m_hevcSeqParams->VBVBufferSizeInBit == 0)
        {
            CODECHAL_ENCODE_ASSERTMESSAGE("VBV buffer size in bits is zero\n");
            return MOS_STATUS_INVALID_PARAMETER;
        }
    }

    encoderBrc->curbe.DW0_ProfileLevelMaxFrame = profileLevelMaxFrame;
    encoderBrc->curbe.DW1_InitBufFull = encoderBrc->m_hevcSeqParams->InitVBVBufferFullnessInBit;
    encoderBrc->curbe.DW2_BufSize = encoderBrc->m_hevcSeqParams->VBVBufferSizeInBit;
    encoderBrc->curbe.DW3_TargetBitRate = encoderBrc->m_hevcSeqParams->TargetBitRate * CODECHAL_ENCODE_BRC_KBPS;    //DDI in Kbits
    encoderBrc->curbe.DW4_MaximumBitRate = encoderBrc->m_hevcSeqParams->MaxBitRate * CODECHAL_ENCODE_BRC_KBPS;
    encoderBrc->curbe.DW5_MinimumBitRate = 0;
    encoderBrc->curbe.DW6_FrameRateM = encoderBrc->m_hevcSeqParams->FrameRate.Numerator;
    encoderBrc->curbe.DW7_FrameRateD = encoderBrc->m_hevcSeqParams->FrameRate.Denominator;
    encoderBrc->curbe.DW8_BRCFlag = encoderBrc->BRCINIT_IGNORE_PICTURE_HEADER_SIZE;  // always ignore the picture header size set in BRC Update curbe;

    if (encoderBrc->m_hevcPicParams->NumROI)
    {
        encoderBrc->curbe.DW8_BRCFlag |= encoderBrc->BRCINIT_DISABLE_MBBRC; // BRC ROI need disable MBBRC logic in LcuBrc Kernel
    }
    else
    {
        encoderBrc->curbe.DW8_BRCFlag |= (encoderBrc->m_lcuBrcEnabled) ? 0 : encoderBrc->BRCINIT_DISABLE_MBBRC;
    }

    encoderBrc->curbe.DW8_BRCFlag |= (encoderBrc->m_brcEnabled && encoderBrc->m_numPipe > 1) ? encoderBrc->BRCINIT_USEHUCBRC : 0;
    encoderBrc->curbe.DW8_BRCFlag |= (encoderBrc->m_enableFramePanicMode) ? encoderBrc->BRCINIT_PANIC_MODE_ISENABLED : 0;

    // For non-ICQ, ACQP Buffer always set to 1
    encoderBrc->curbe.DW25_ACQPBuffer = 1;

    encoderBrc->curbe.DW25_SlidingWindowSize = encoderBrc->m_slidingWindowSize;

    if (encoderBrc->m_hevcSeqParams->RateControlMethod == RATECONTROL_CBR)
    {
        encoderBrc->curbe.DW4_MaximumBitRate = encoderBrc->curbe.DW3_TargetBitRate;
        encoderBrc->curbe.DW8_BRCFlag |= encoderBrc->BRCINIT_ISCBR;
    }
    else if (encoderBrc->m_hevcSeqParams->RateControlMethod == RATECONTROL_VBR)
    {
        if (encoderBrc->curbe.DW4_MaximumBitRate < encoderBrc->curbe.DW3_TargetBitRate)
        {
            encoderBrc->curbe.DW4_MaximumBitRate = 2 * encoderBrc->curbe.DW3_TargetBitRate;
        }
        encoderBrc->curbe.DW8_BRCFlag |= encoderBrc->BRCINIT_ISVBR;
    }
    else if (encoderBrc->m_hevcSeqParams->RateControlMethod == RATECONTROL_AVBR)
    {
        encoderBrc->curbe.DW8_BRCFlag |= encoderBrc->BRCINIT_ISAVBR;
        // For AVBR, max bitrate = target bitrate,
        encoderBrc->curbe.DW3_TargetBitRate = encoderBrc->m_hevcSeqParams->TargetBitRate * CODECHAL_ENCODE_BRC_KBPS;    //DDI in Kbits
        encoderBrc->curbe.DW4_MaximumBitRate = encoderBrc->m_hevcSeqParams->TargetBitRate * CODECHAL_ENCODE_BRC_KBPS;
    }
    else if (encoderBrc->m_hevcSeqParams->RateControlMethod == RATECONTROL_ICQ)
    {
        encoderBrc->curbe.DW8_BRCFlag |= encoderBrc->BRCINIT_ISICQ;
        encoderBrc->curbe.DW25_ACQPBuffer = encoderBrc->m_hevcSeqParams->ICQQualityFactor;
    }
    else if (encoderBrc->m_hevcSeqParams->RateControlMethod == RATECONTROL_VCM)
    {
        encoderBrc->curbe.DW4_MaximumBitRate = encoderBrc->curbe.DW3_TargetBitRate;
        encoderBrc->curbe.DW8_BRCFlag |= encoderBrc->BRCINIT_ISVCM;
    }
    else if (encoderBrc->m_hevcSeqParams->RateControlMethod == RATECONTROL_CQP)
    {
        encoderBrc->curbe.DW8_BRCFlag = encoderBrc->BRCINIT_ISCQP;
    }
    else if (encoderBrc->m_hevcSeqParams->RateControlMethod == RATECONTROL_QVBR)
    {
        if (encoderBrc->curbe.DW4_MaximumBitRate < encoderBrc->curbe.DW3_TargetBitRate)
        {
            encoderBrc->curbe.DW4_MaximumBitRate = encoderBrc->curbe.DW3_TargetBitRate; // Use max bit rate for HRD compliance
        }
        encoderBrc->curbe.DW8_BRCFlag = encoderBrc->curbe.DW8_BRCFlag | encoderBrc->BRCINIT_ISQVBR | encoderBrc->BRCINIT_ISVBR; // We need to make sure that VBR is used for QP determination.
        // use ICQQualityFactor to determine the larger Qp for each MB
        encoderBrc->curbe.DW25_ACQPBuffer = encoderBrc->m_hevcSeqParams->ICQQualityFactor;
    }

    encoderBrc->curbe.DW9_FrameWidth = encoderBrc->m_oriFrameWidth;
    encoderBrc->curbe.DW10_FrameHeight = encoderBrc->m_oriFrameHeight;
    encoderBrc->curbe.DW10_AVBRAccuracy = encoderBrc->m_usAvbrAccuracy;
    encoderBrc->curbe.DW11_AVBRConvergence = encoderBrc->m_usAvbrConvergence;
    encoderBrc->curbe.DW12_NumberSlice = encoderBrc->m_numSlices;

    /**********************************************************************
    In case of non-HB/BPyramid Structure
    BRC_Param_A = GopP
    BRC_Param_B = GopB
    In case of HB/BPyramid GOP Structure
    BRC_Param_A, BRC_Param_B, BRC_Param_C, BRC_Param_D are
    BRC Parameters set as follows as per CModel equation
    ***********************************************************************/
    // BPyramid GOP
    const auto GopPicSize = encoderBrc->m_hevcSeqParams->GopPicSize;
    const auto GopRefDist = encoderBrc->m_hevcSeqParams->GopRefDist;

    encoderBrc->m_HierchGopBRCEnabled = false;
    if(encoderBrc->m_hevcSeqParams->HierarchicalFlag && GopRefDist > 1 && GopRefDist <= 8 )
    {      
        uint32_t numB[9]      = {0, 0, 1, 1, 1, 1, 1, 1, 1};
        uint32_t numB1[9]     = {0, 0, 0, 1, 2, 2, 2, 2, 2};
        uint32_t numB2[9]     = {0, 0, 0, 0, 0, 1, 2, 3, 4};

        uint32_t numOfPyramid = (GopPicSize - 1) / GopRefDist;
        uint32_t remOfPyramid = (GopPicSize - 1) % GopRefDist;

        encoderBrc->curbe.DW8_BRCGopP   = numOfPyramid * numB[GopRefDist] + numB[remOfPyramid + 1];
        encoderBrc->curbe.DW9_BRCGopB   = numOfPyramid * numB[GopRefDist] + numB[remOfPyramid];
        encoderBrc->curbe.DW13_BRCGopB1 = numOfPyramid * numB1[GopRefDist] + numB1[remOfPyramid];
        encoderBrc->curbe.DW14_BRCGopB2 = numOfPyramid * numB2[GopRefDist] + numB2[remOfPyramid];

        encoderBrc->m_HierchGopBRCEnabled = true;
     
        // B1 Level GOP
        if (GopRefDist <= 4 || encoderBrc->curbe.DW14_BRCGopB2 == 0)
        {
            encoderBrc->curbe.DW14_MaxBRCLevel = 3;
        }
        // B2 Level GOP
        else
        {
            encoderBrc->curbe.DW14_MaxBRCLevel = 4;
        }       
    }
    // For Regular GOP - No BPyramid
    else
    {
        encoderBrc->curbe.DW14_MaxBRCLevel = 1;
        encoderBrc->curbe.DW8_BRCGopP      = (GopRefDist) ? MOS_ROUNDUP_DIVIDE(GopPicSize - 1, GopRefDist) : 0;
        encoderBrc->curbe.DW9_BRCGopB      = GopPicSize - 1 - encoderBrc->curbe.DW8_BRCGopP;
    }

    // Set dynamic thresholds
    double inputBitsPerFrame = (double)((double)encoderBrc->curbe.DW4_MaximumBitRate * (double)encoderBrc->curbe.DW7_FrameRateD);
    inputBitsPerFrame = (double)(inputBitsPerFrame / encoderBrc->curbe.DW6_FrameRateM);

    if (encoderBrc->curbe.DW2_BufSize < (uint32_t)inputBitsPerFrame * 4)
    {
        encoderBrc->curbe.DW2_BufSize = (uint32_t)inputBitsPerFrame * 4;
    }

    if (encoderBrc->curbe.DW1_InitBufFull == 0)
    {
        encoderBrc->curbe.DW1_InitBufFull = 7 * encoderBrc->curbe.DW2_BufSize / 8;
    }
    if (encoderBrc->curbe.DW1_InitBufFull < (uint32_t)(inputBitsPerFrame * 2))
    {
        encoderBrc->curbe.DW1_InitBufFull = (uint32_t)(inputBitsPerFrame * 2);
    }
    if (encoderBrc->curbe.DW1_InitBufFull > encoderBrc->curbe.DW2_BufSize)
    {
        encoderBrc->curbe.DW1_InitBufFull = encoderBrc->curbe.DW2_BufSize;
    }

    if (encoderBrc->m_hevcSeqParams->RateControlMethod == RATECONTROL_AVBR)
    {
        // For AVBR, Buffer size =  2*Bitrate, InitVBV = 0.75 * BufferSize
        encoderBrc->curbe.DW2_BufSize = 2 * encoderBrc->m_hevcSeqParams->TargetBitRate * CODECHAL_ENCODE_BRC_KBPS;
        encoderBrc->curbe.DW1_InitBufFull = (uint32_t)(0.75 * encoderBrc->curbe.DW2_BufSize);
    }

    if (encoderBrc->m_hevcSeqParams->FrameSizeTolerance == EFRAMESIZETOL_EXTREMELY_LOW)
    {
        encoderBrc->curbe.DW15_LongTermInterval = 0; // no LTR for low delay brc
    }
    else
    {
        encoderBrc->curbe.DW15_LongTermInterval = (encoderBrc->m_enableBrcLTR && encoderBrc->m_ltrInterval) ?
                                                   encoderBrc->m_ltrInterval : encoderBrc->m_enableBrcLTR ? HEVC_BRC_LONG_TERM_REFRENCE_FLAG : 0;
    }

    double bpsRatio = ( (double) inputBitsPerFrame / (( (double) encoderBrc->curbe.DW2_BufSize) / 30));
    bpsRatio = (bpsRatio < 0.1) ? 0.1 : (bpsRatio > 3.5) ? 3.5 : bpsRatio;

    encoderBrc->curbe.DW19_DeviationThreshold0_PBframe = (uint32_t)(-50 * pow(0.90, bpsRatio));
    encoderBrc->curbe.DW19_DeviationThreshold1_PBframe = (uint32_t)(-50 * pow(0.66, bpsRatio));
    encoderBrc->curbe.DW19_DeviationThreshold2_PBframe = (uint32_t)(-50 * pow(0.46, bpsRatio));
    encoderBrc->curbe.DW19_DeviationThreshold3_PBframe = (uint32_t)(-50 * pow(0.3, bpsRatio));

    encoderBrc->curbe.DW20_DeviationThreshold4_PBframe = (uint32_t)(50 * pow(0.3, bpsRatio));
    encoderBrc->curbe.DW20_DeviationThreshold5_PBframe = (uint32_t)(50 * pow(0.46, bpsRatio));
    encoderBrc->curbe.DW20_DeviationThreshold6_PBframe = (uint32_t)(50 * pow(0.7, bpsRatio));
    encoderBrc->curbe.DW20_DeviationThreshold7_PBframe = (uint32_t)(50 * pow(0.9, bpsRatio));

    encoderBrc->curbe.DW21_DeviationThreshold0_VBRcontrol = (uint32_t)(-50 * pow(0.9, bpsRatio));
    encoderBrc->curbe.DW21_DeviationThreshold1_VBRcontrol = (uint32_t)(-50 * pow(0.7, bpsRatio));
    encoderBrc->curbe.DW21_DeviationThreshold2_VBRcontrol = (uint32_t)(-50 * pow(0.5, bpsRatio));
    encoderBrc->curbe.DW21_DeviationThreshold3_VBRcontrol = (uint32_t)(-50 * pow(0.3, bpsRatio));

    encoderBrc->curbe.DW22_DeviationThreshold4_VBRcontrol = (uint32_t)(100 * pow(0.4, bpsRatio));
    encoderBrc->curbe.DW22_DeviationThreshold5_VBRcontrol = (uint32_t)(100 * pow(0.5, bpsRatio));
    encoderBrc->curbe.DW22_DeviationThreshold6_VBRcontrol = (uint32_t)(100 * pow(0.75, bpsRatio));
    encoderBrc->curbe.DW22_DeviationThreshold7_VBRcontrol = (uint32_t)(100 * pow(0.9, bpsRatio));

    encoderBrc->curbe.DW23_DeviationThreshold0_Iframe = (uint32_t)(-50 * pow(0.8, bpsRatio));
    encoderBrc->curbe.DW23_DeviationThreshold1_Iframe = (uint32_t)(-50 * pow(0.6, bpsRatio));
    encoderBrc->curbe.DW23_DeviationThreshold2_Iframe = (uint32_t)(-50 * pow(0.34, bpsRatio));
    encoderBrc->curbe.DW23_DeviationThreshold3_Iframe = (uint32_t)(-50 * pow(0.2, bpsRatio));

    encoderBrc->curbe.DW24_DeviationThreshold4_Iframe = (uint32_t)(50 * pow(0.2, bpsRatio));
    encoderBrc->curbe.DW24_DeviationThreshold5_Iframe = (uint32_t)(50 * pow(0.4, bpsRatio));
    encoderBrc->curbe.DW24_DeviationThreshold6_Iframe = (uint32_t)(50 * pow(0.66, bpsRatio));
    encoderBrc->curbe.DW24_DeviationThreshold7_Iframe = (uint32_t)(50 * pow(0.9, bpsRatio));

    if (encoderBrc->m_hevcSeqParams->HierarchicalFlag && !encoderBrc->m_hevcSeqParams->LowDelayMode &&
        (encoderBrc->m_hevcSeqParams->GopRefDist == 4 || encoderBrc->m_hevcSeqParams->GopRefDist == 8))
    {
        encoderBrc->curbe.DW26_RandomAccess = true;
    }
    else
    {
        encoderBrc->curbe.DW26_RandomAccess = false;
    }

    if (encoderBrc->m_brcInit)
    {
        encoderBrc->m_dBrcInitCurrentTargetBufFullInBits = encoderBrc->curbe.DW1_InitBufFull;
    }

    encoderBrc->m_brcInitResetBufSizeInBits = encoderBrc->curbe.DW2_BufSize;
    encoderBrc->m_dBrcInitResetInputBitsPerFrame = inputBitsPerFrame;

    return eStatus;
}

MOS_STATUS CodecHalHevcBrcG12::BrcUpdateCurbe()
{
    MOS_STATUS  eStatus = MOS_STATUS_SUCCESS;

    // Initialize the CURBE data
    encoderBrc->curbeBrcUpdate = encoderBrc->m_brcUpdateCurbeInit;

    encoderBrc->curbeBrcUpdate.DW5_TargetSize_Flag = 0;

    if (encoderBrc->m_dBrcInitCurrentTargetBufFullInBits > (double)encoderBrc->m_brcInitResetBufSizeInBits)
    {
        encoderBrc->m_dBrcInitCurrentTargetBufFullInBits -= (double)encoderBrc->m_brcInitResetBufSizeInBits;
        encoderBrc->curbeBrcUpdate.DW5_TargetSize_Flag = 1;
    }

    if (encoderBrc->m_numSkipFrames)
    {
        // pass num/size of skipped frames to update BRC
        encoderBrc->curbeBrcUpdate.DW6_NumSkippedFrames = encoderBrc->m_numSkipFrames;
        encoderBrc->curbeBrcUpdate.DW15_SizeOfSkippedFrames = encoderBrc->m_sizeSkipFrames;

        // account for skipped frame in calculating CurrentTargetBufFullInBits
        encoderBrc->m_dBrcInitCurrentTargetBufFullInBits += encoderBrc->m_dBrcInitResetInputBitsPerFrame * encoderBrc->m_numSkipFrames;
    }

    encoderBrc->curbeBrcUpdate.DW0_TargetSize = (uint32_t)(encoderBrc->m_dBrcInitCurrentTargetBufFullInBits);
    encoderBrc->curbeBrcUpdate.DW1_FrameNumber = encoderBrc->m_storeData - 1; // Check if we can remove this (set to 0)

    uint32_t picHdrSize = encoderBrc->GetPicHdrSize();
    encoderBrc->curbeBrcUpdate.DW2_PictureHeaderSize = picHdrSize;

    encoderBrc->curbeBrcUpdate.DW5_CurrFrameBrcLevel = encoderBrc->m_currFrameBrcLevel;

    encoderBrc->curbeBrcUpdate.DW5_MaxNumPAKs = m_brcNumPakPasses;

    if (encoderBrc->m_hevcSeqParams->RateControlMethod == RATECONTROL_CQP)
    {
        encoderBrc->curbeBrcUpdate.DW6_CqpValue = encoderBrc->m_hevcPicParams->QpY + encoderBrc->m_hevcSliceParams->slice_qp_delta;
    }

    encoderBrc->curbeBrcUpdate.DW6_SlidingWindowEnable = (encoderBrc->m_hevcSeqParams->FrameSizeTolerance == EFRAMESIZETOL_LOW);

    if (encoderBrc->m_hevcPicParams->NumROI)
    {
        encoderBrc->curbeBrcUpdate.DW6_ROIEnable = encoderBrc->m_brcEnabled ? false : true;
        encoderBrc->curbeBrcUpdate.DW6_BRCROIEnable = encoderBrc->m_brcEnabled ? true : false;
        encoderBrc->curbeBrcUpdate.DW6_RoiRatio = encoderBrc->CalculateROIRatio();
    }

    if (encoderBrc->m_minMaxQpControlEnabled)
    {
        if (encoderBrc->m_hevcPicParams->CodingType == I_TYPE)
        {
            encoderBrc->curbeBrcUpdate.DW7_FrameMaxQP = encoderBrc->m_maxQpForI;
            encoderBrc->curbeBrcUpdate.DW7_FrameMinQP = encoderBrc->m_minQpForI;
        }
        else if (encoderBrc->m_hevcPicParams->CodingType == P_TYPE)
        {
            encoderBrc->curbeBrcUpdate.DW7_FrameMaxQP = encoderBrc->m_maxQpForP;
            encoderBrc->curbeBrcUpdate.DW7_FrameMinQP = encoderBrc->m_minQpForP;
        }
        else if (encoderBrc->m_hevcPicParams->CodingType == B_TYPE)
        {
            encoderBrc->curbeBrcUpdate.DW7_FrameMaxQP = encoderBrc->m_maxQpForB;
            encoderBrc->curbeBrcUpdate.DW7_FrameMinQP = encoderBrc->m_minQpForB;
        }
    }

    //for low delay brc
    encoderBrc->curbeBrcUpdate.DW6_LowDelayEnable      = (encoderBrc->m_hevcSeqParams->FrameSizeTolerance == EFRAMESIZETOL_EXTREMELY_LOW);
    encoderBrc->curbeBrcUpdate.DW16_UserMaxFrameSize   = encoderBrc->GetProfileLevelMaxFrameSize();

    encoderBrc->curbeBrcUpdate.DW14_ParallelMode = encoderBrc->m_hevcSeqParams->ParallelBRC;

    if (encoderBrc->m_hevcSeqParams->RateControlMethod == RATECONTROL_AVBR)
    {
        encoderBrc->curbeBrcUpdate.DW3_StartGAdjFrame0 = (uint32_t)((10 * encoderBrc->m_usAvbrConvergence) / (double)150);
        encoderBrc->curbeBrcUpdate.DW3_StartGAdjFrame1 = (uint32_t)((50 * encoderBrc->m_usAvbrConvergence) / (double)150);
        encoderBrc->curbeBrcUpdate.DW4_StartGAdjFrame2 = (uint32_t)((100 * encoderBrc->m_usAvbrConvergence) / (double)150);
        encoderBrc->curbeBrcUpdate.DW4_StartGAdjFrame3 = (uint32_t)((150 * encoderBrc->m_usAvbrConvergence) / (double)150);

        encoderBrc->curbeBrcUpdate.DW11_gRateRatioThreshold0 =
            (uint32_t)((100 - (encoderBrc->m_usAvbrAccuracy / (double)30)*(100 - 40)));
        encoderBrc->curbeBrcUpdate.DW11_gRateRatioThreshold1 =
            (uint32_t)((100 - (encoderBrc->m_usAvbrAccuracy / (double)30)*(100 - 75)));
        encoderBrc->curbeBrcUpdate.DW12_gRateRatioThreshold2 = (uint32_t)((100 - (encoderBrc->m_usAvbrAccuracy / (double)30)*(100 - 97)));
        encoderBrc->curbeBrcUpdate.DW12_gRateRatioThreshold3 = (uint32_t)((100 + (encoderBrc->m_usAvbrAccuracy / (double)30)*(103 - 100)));
        encoderBrc->curbeBrcUpdate.DW12_gRateRatioThreshold4 = (uint32_t)((100 + (encoderBrc->m_usAvbrAccuracy / (double)30)*(125 - 100)));
        encoderBrc->curbeBrcUpdate.DW12_gRateRatioThreshold5 = (uint32_t)((100 + (encoderBrc->m_usAvbrAccuracy / (double)30)*(160 - 100)));
    }

    if (encoderBrc->m_hevcSeqParams->FrameSizeTolerance == EFRAMESIZETOL_EXTREMELY_LOW)
    {
        encoderBrc->curbeBrcUpdate.DW17_LongTerm_Current = 0; // no LTR for low delay brc
    }
    else
    {
        encoderBrc->m_isFrameLTR = (CodecHal_PictureIsLongTermRef(encoderBrc->m_currReconstructedPic));
        encoderBrc->curbeBrcUpdate.DW17_LongTerm_Current = (encoderBrc->m_enableBrcLTR && encoderBrc->m_isFrameLTR) ? 1 : 0;
    }

    return eStatus;
}
MOS_STATUS CodecHalHevcBrcG12::EncodeBrcFrameUpdateKernel()
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
    CODECHAL_ENCODE_FUNCTION_ENTER;

    PerfTagSetting perfTag;
    perfTag.Value = 0;
    perfTag.Mode = (uint16_t)encoderBrc->m_mode & CODECHAL_ENCODE_MODE_BIT_MASK;
    perfTag.CallType = CODECHAL_ENCODE_PERFTAG_CALL_BRC_UPDATE;
    perfTag.PictureCodingType = encoderBrc->m_pictureCodingType;
    encoderBrc->GetOsInterface()->pfnSetPerfTag(encoderBrc->GetOsInterface(), perfTag.Value);
    encoderBrc->GetOsInterface()->pfnIncPerfBufferID(encoderBrc->GetOsInterface());

    if (!m_threadSpaceBrcUpdate)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(SetupThreadSpace(m_cmKrnBrcUpdate, m_threadSpaceBrcUpdate));
    }

    CODECHAL_ENCODE_CHK_STATUS_RETURN(BrcUpdateCurbe());
    CODECHAL_ENCODE_CHK_STATUS_RETURN(SetupSurfacesBrcUpdate());
    CODECHAL_ENCODE_CHK_STATUS_RETURN(SetupKernelArgsBrcUpdate());
    CODECHAL_DEBUG_TOOL(
        CODECHAL_ENCODE_CHK_STATUS_RETURN(DumpBrcInputBuffers()));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmTask->AddKernel(m_cmKrnBrcUpdate));

    if (!encoderBrc->m_singleTaskPhaseSupported || encoderBrc->m_lastTaskInPhase)
    {
        CmEvent * event = CM_NO_EVENT;
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmQueue->EnqueueFast(encoderBrc->m_cmTask, event));

        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmTask->Reset());

        encoderBrc->m_lastTaskInPhase = false;
    }
    else
    {
        encoderBrc->m_cmTask->AddSync();
    }
    return eStatus;
}

MOS_STATUS CodecHalHevcBrcG12::SetupSurfacesBrcUpdate()
{
    MOS_STATUS  eStatus = MOS_STATUS_SUCCESS;

    // Fill HCP_IMG_STATE so that BRC kernel can use it to generate the write buffer for PAK
    PMOS_RESOURCE brcHcpStateReadBuffer = &encoderBrc->m_brcBuffers.resBrcImageStatesReadBuffer[encoderBrc->m_currRecycledBufIdx];
    MHW_VDBOX_HEVC_PIC_STATE_G12 mhwHevcPicState;
    mhwHevcPicState.pHevcEncSeqParams = encoderBrc->m_hevcSeqParams;
    mhwHevcPicState.pHevcEncPicParams = encoderBrc->m_hevcPicParams;
    mhwHevcPicState.brcNumPakPasses = m_brcNumPakPasses;
    mhwHevcPicState.rhodomainRCEnable = encoderBrc->m_brcEnabled && (encoderBrc->m_numPipe > 1);
    mhwHevcPicState.bSAOEnable = encoderBrc->m_hevcSeqParams->SAO_enabled_flag ? (encoderBrc->m_hevcSliceParams->slice_sao_luma_flag || encoderBrc->m_hevcSliceParams->slice_sao_chroma_flag) : 0;
    mhwHevcPicState.bTransformSkipEnable = encoderBrc->m_hevcPicParams->transform_skip_enabled_flag;
    mhwHevcPicState.bHevcRdoqEnabled      = encoderBrc->m_hevcRdoqEnabled;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_hcpInterface->AddHcpHevcPicBrcBuffer(brcHcpStateReadBuffer, &mhwHevcPicState));

    PMOS_SURFACE brcConstantData = &encoderBrc->m_brcBuffers.sBrcConstantDataBuffer[encoderBrc->m_currRecycledBufIdx];
    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->SetupBrcConstantTable(brcConstantData));

    if (!m_histBufferBrc)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->CreateBuffer(
            &encoderBrc->m_brcBuffers.resBrcHistoryBuffer, //m_brcHistoryBufferSize,
            m_histBufferBrc));
    }

    // BRC Prev PAK statistics output buffer
    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->UpdateBuffer(
        &encoderBrc->m_brcBuffers.resBrcPakStatisticBuffer[encoderBrc->m_brcBuffers.uiCurrBrcPakStasIdxForRead],
        m_PAKStatsBufferBrc));

    // BRC HCP_PIC_STATE read
    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->UpdateBuffer(
        brcHcpStateReadBuffer,
        m_PICStateInBufferBrc));

    // BRC HCP_PIC_STATE write
    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->UpdateBuffer(
        &encoderBrc->m_brcBuffers.resBrcImageStatesWriteBuffer[encoderBrc->m_currRecycledBufIdx],
        m_PICStateOutBufferBrc));

    // BRC Data Surface
    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->UpdateSurface2D(
        &brcConstantData->OsResource,
        m_ConstDataBufferBRC));

    // Pixel MB Statistics surface
    if (!m_PixelMBStatsBufferBrc)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->CreateBuffer(
            &encoderBrc->m_resMbStatsBuffer,
            m_PixelMBStatsBufferBrc));
    }

    // Combined ENC-parameter buffer
    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->UpdateBuffer(
        &encoderBrc->m_brcInputForEncKernelBuffer->sResource,
        m_CombinedEncBufferBrc));

    return eStatus;
}

MOS_STATUS CodecHalHevcBrcG12::SetupKernelArgsBrcUpdate()
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    int idx = 0;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_cmKrnBrcUpdate->SetKernelArg(idx++, sizeof(encoderBrc->m_brcUpdateCurbeInit), &encoderBrc->curbeBrcUpdate));

    SurfaceIndex *pIndex0 = nullptr;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_histBufferBrc->GetIndex(pIndex0));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_cmKrnBrcUpdate->SetKernelArg(idx++, sizeof(SurfaceIndex), pIndex0));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_PAKStatsBufferBrc->GetIndex(pIndex0));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_cmKrnBrcUpdate->SetKernelArg(idx++, sizeof(SurfaceIndex), pIndex0));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_PICStateInBufferBrc->GetIndex(pIndex0));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_cmKrnBrcUpdate->SetKernelArg(idx++, sizeof(SurfaceIndex), pIndex0));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_PICStateOutBufferBrc->GetIndex(pIndex0));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_cmKrnBrcUpdate->SetKernelArg(idx++, sizeof(SurfaceIndex), pIndex0));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_CombinedEncBufferBrc->GetIndex(pIndex0));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_cmKrnBrcUpdate->SetKernelArg(idx++, sizeof(SurfaceIndex), pIndex0));

    //Setup Distortion 2D surface
    CmSurface2D *brcDistortion = (encoderBrc->m_pictureCodingType == I_TYPE) ? encoderBrc->m_brcBuffers.brcIntraDistortionSurface
                                                                             : encoderBrc->m_brcBuffers.meBrcDistortionSurface;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(brcDistortion->GetIndex(pIndex0));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_cmKrnBrcUpdate->SetKernelArg(idx++, sizeof(SurfaceIndex), pIndex0));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_ConstDataBufferBRC->GetIndex(pIndex0));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_cmKrnBrcUpdate->SetKernelArg(idx++, sizeof(SurfaceIndex), pIndex0));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_PixelMBStatsBufferBrc->GetIndex(pIndex0));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_cmKrnBrcUpdate->SetKernelArg(idx++, sizeof(SurfaceIndex), pIndex0));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_brcBuffers.mvAndDistortionSumSurface->GetIndex(pIndex0));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_cmKrnBrcUpdate->SetKernelArg(idx++, sizeof(SurfaceIndex), pIndex0));

    return eStatus;
}

MOS_STATUS CodecHalHevcBrcG12::EncodeBrcLcuUpdateKernel()
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
    CODECHAL_ENCODE_FUNCTION_ENTER;

    PerfTagSetting perfTag;
    perfTag.Value = 0;
    perfTag.Mode = (uint16_t)encoderBrc->m_mode & CODECHAL_ENCODE_MODE_BIT_MASK;
    perfTag.CallType = CODECHAL_ENCODE_PERFTAG_CALL_BRC_UPDATE_LCU;
    perfTag.PictureCodingType = encoderBrc->m_pictureCodingType;
    encoderBrc->GetOsInterface()->pfnSetPerfTag(encoderBrc->GetOsInterface(), perfTag.Value);
    encoderBrc->GetOsInterface()->pfnIncPerfBufferID(encoderBrc->GetOsInterface());

    CODECHAL_ENCODE_CHK_STATUS_RETURN(BrcUpdateCurbe());
    CODECHAL_ENCODE_CHK_STATUS_RETURN(SetupSurfacesBrcLcuQp());

    if (encoderBrc->m_hevcPicParams->NumROI)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->SetupROISurface());
    }

    CODECHAL_ENCODE_CHK_STATUS_RETURN(SetupKernelArgsBrcLcuQp());

    if (encoderBrc->m_resolutionChanged && m_threadSpaceBrcLCUQP)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->DestroyThreadSpace(m_threadSpaceBrcLCUQP));
        m_threadSpaceBrcLCUQP = nullptr;
    }

    if (!m_threadSpaceBrcLCUQP)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(SetupBrcLcuqpThreadSpace(m_cmKrnBrcLCUQP, m_threadSpaceBrcLCUQP));
    }

    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmTask->AddKernel(m_cmKrnBrcLCUQP));

    if (!encoderBrc->m_singleTaskPhaseSupported || encoderBrc->m_lastTaskInPhase)
    {
        CmEvent * event = CM_NO_EVENT;
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmQueue->EnqueueFast(encoderBrc->m_cmTask, event));

        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmTask->Reset());

        encoderBrc->m_lastTaskInPhase = false;
    }
    else
    {
        encoderBrc->m_cmTask->AddSync();
    }

    return eStatus;
}

MOS_STATUS CodecHalHevcBrcG12::SetupSurfacesBrcLcuQp()
{
    MOS_STATUS  eStatus = MOS_STATUS_SUCCESS;

    if (!m_histBufferBrc)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->CreateBuffer(
             &encoderBrc->m_brcBuffers.resBrcHistoryBuffer,
             m_histBufferBrc));
    }

    // Pixel MB Statistics surface
    if (!m_PixelMBStatsBufferBrc)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->CreateBuffer(
             &encoderBrc->m_resMbStatsBuffer,
             m_PixelMBStatsBufferBrc));
    }

    if (!m_BrcMbQp) {

        // BRC Data Surface
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->CreateSurface2D(
            &encoderBrc->m_brcBuffers.sBrcMbQpBuffer.OsResource,
            m_BrcMbQp));
    }

    if (!m_BrcROISurf)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->m_cmDev->CreateSurface2D(
            &encoderBrc->m_brcBuffers.sBrcRoiSurface.OsResource,
            m_BrcROISurf));
    }

    return eStatus;
}

MOS_STATUS CodecHalHevcBrcG12::SetupKernelArgsBrcLcuQp()
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    int idx = 0;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_cmKrnBrcLCUQP->SetKernelArg(idx++, sizeof(encoderBrc->m_brcUpdateCurbeInit), &encoderBrc->curbeBrcUpdate));

    SurfaceIndex *pIndex0 = nullptr;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_histBufferBrc->GetIndex(pIndex0));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_cmKrnBrcLCUQP->SetKernelArg(idx++, sizeof(SurfaceIndex), pIndex0));

    //Setup Distortion 2D surface
    CmSurface2D *brcDistortion = (encoderBrc->m_pictureCodingType == I_TYPE) ? encoderBrc->m_brcBuffers.brcIntraDistortionSurface
                                                                             : encoderBrc->m_brcBuffers.meBrcDistortionSurface;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(brcDistortion->GetIndex(pIndex0));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_cmKrnBrcLCUQP->SetKernelArg(idx++, sizeof(SurfaceIndex), pIndex0));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_PixelMBStatsBufferBrc->GetIndex(pIndex0));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_cmKrnBrcLCUQP->SetKernelArg(idx++, sizeof(SurfaceIndex), pIndex0));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_BrcMbQp->GetIndex(pIndex0));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_cmKrnBrcLCUQP->SetKernelArg(idx++, sizeof(SurfaceIndex), pIndex0));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_BrcROISurf->GetIndex(pIndex0));
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_cmKrnBrcLCUQP->SetKernelArg(idx++, sizeof(SurfaceIndex), pIndex0));
    return eStatus;
}

#if USE_CODECHAL_DEBUG_TOOL
MOS_STATUS CodecHalHevcBrcG12::DumpBrcInputBuffers()
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->GetDebugInterface()->DumpBuffer(
        &encoderBrc->m_mvAndDistortionSumSurface.sResource,
        CodechalDbgAttr::attrInput,
        "MvDistSum",
        encoderBrc->m_mvAndDistortionSumSurface.dwSize,
        0,
        CODECHAL_MEDIA_STATE_BRC_UPDATE));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->GetDebugInterface()->DumpBuffer(
        &encoderBrc->m_brcBuffers.resBrcImageStatesReadBuffer[encoderBrc->m_currRecycledBufIdx],
        CodechalDbgAttr::attrInput,
        "ImgStateRead",
        BRC_IMG_STATE_SIZE_PER_PASS * encoderBrc->GetHwInterface()->GetMfxInterface()->GetBrcNumPakPasses(),
        0,
        CODECHAL_MEDIA_STATE_BRC_UPDATE));

    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->GetDebugInterface()->DumpSurface(
        &encoderBrc->m_brcBuffers.sBrcConstantDataBuffer[encoderBrc->m_currRecycledBufIdx],
        CodechalDbgAttr::attrInput,
        "ConstData",
        CODECHAL_MEDIA_STATE_BRC_UPDATE));

    // PAK statistics buffer is only dumped for BrcUpdate kernel input
    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->GetDebugInterface()->DumpBuffer(
        &encoderBrc->m_brcBuffers.resBrcPakStatisticBuffer[encoderBrc->m_brcBuffers.uiCurrBrcPakStasIdxForRead],
        CodechalDbgAttr::attrInput,
        "PakStats",
        HEVC_BRC_PAK_STATISTCS_SIZE,
        0,
        CODECHAL_MEDIA_STATE_BRC_UPDATE));
    // HEVC maintains a ptr to its own distortion surface, as it may be a couple different surfaces
    if (encoderBrc->m_brcDistortion)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(
            encoderBrc->GetDebugInterface()->DumpBuffer(
                &encoderBrc->m_brcDistortion->OsResource,
                CodechalDbgAttr::attrInput,
                "BrcDist_BeforeFrameBrc",
                encoderBrc->m_brcBuffers.sMeBrcDistortionBuffer.dwPitch * encoderBrc->m_brcBuffers.sMeBrcDistortionBuffer.dwHeight,
                encoderBrc->m_brcBuffers.dwMeBrcDistortionBottomFieldOffset,
                CODECHAL_MEDIA_STATE_BRC_UPDATE));
    }

    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->GetDebugInterface()->DumpBuffer(&encoderBrc->m_brcBuffers.resBrcHistoryBuffer,
        CodechalDbgAttr::attrInput,
        "HistoryRead_beforeFramBRC",
        encoderBrc->m_brcHistoryBufferSize,
        0,
        CODECHAL_MEDIA_STATE_BRC_UPDATE));

    if (encoderBrc->m_brcBuffers.pMbEncKernelStateInUse)
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->GetDebugInterface()->DumpCurbe(
            CODECHAL_MEDIA_STATE_BRC_UPDATE,
            encoderBrc->m_brcBuffers.pMbEncKernelStateInUse));
    }

    CODECHAL_ENCODE_CHK_STATUS_RETURN(encoderBrc->GetDebugInterface()->DumpBuffer(&encoderBrc->m_resMbStatsBuffer,
        CodechalDbgAttr::attrInput,
        "MBStatsSurf",
        encoderBrc->GetHwInterface()->m_avcMbStatBufferSize,
        0,
        CODECHAL_MEDIA_STATE_BRC_UPDATE));

    return eStatus;
}
#endif
