/*
* Copyright (c) 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.
*/
//!
//! \file     media_debug_interface.cpp
//! \brief    Defines the debug interface shared by all of Media.
//! \details  The debug interface dumps output from Media based on input config file.
//!
#include "media_debug_interface.h"
#if USE_MEDIA_DEBUG_TOOL
#include "media_debug_config_manager.h"
#include "codechal_hw.h"
#include <fstream>
#include <sstream>
#include <iomanip>

#if !defined(LINUX) && !defined(ANDROID)
#include "UmdStateSeparation.h"
#endif

MediaDebugInterface::MediaDebugInterface()
{
    memset(&m_currPic, 0, sizeof(CODEC_PICTURE));
    memset(m_fileName, 0, sizeof(m_fileName));
    memset(m_path, 0, sizeof(m_path));
    InitCRCTable(m_crcTable);
}

MediaDebugInterface::~MediaDebugInterface()
{
    if (nullptr != m_configMgr)
    {
        MOS_Delete(m_configMgr);
    }

    if (!Mos_ResourceIsNull(&m_temp2DSurfForCopy.OsResource))
    {
        m_osInterface->pfnFreeResource(m_osInterface, &m_temp2DSurfForCopy.OsResource);
    }
}

MOS_STATUS MediaDebugInterface::InitDumpLocation()
{
    m_crcGoldenRefFileName = m_outputFilePath + std::string("GoldenReference.txt");
    if (m_configMgr->AttrIsEnabled(MediaDbgAttr::attrDumpToThreadFolder))
    {
        std::string ThreadSubFolder = "T" + std::to_string(MOS_GetCurrentThreadId()) + MOS_DIRECTORY_DELIMITER;
        m_outputFilePath            = m_outputFilePath + ThreadSubFolder;
        MOS_CreateDirectory(const_cast<char *>(m_outputFilePath.c_str()));
    }

    m_ddiFileName = m_outputFilePath + "ddi.par";
    std::ofstream ofs(m_ddiFileName, std::ios::out);
    ofs << "ParamFilePath"
        << " = \"" << m_fileName << "\"" << std::endl;
    ofs.close();

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::SetOutputFilePath()
{
    MOS_USER_FEATURE_VALUE_DATA       userFeatureData;
    MOS_USER_FEATURE_VALUE_WRITE_DATA userFeatureWriteData;
    MOS_USER_FEATURE_VALUE_ID         OutputPathKey = __MOS_USER_FEATURE_KEY_INVALID_ID;
    char                              stringData[MOS_MAX_PATH_LENGTH + 1];
    std::string                       DumpFilePath;

    MEDIA_DEBUG_FUNCTION_ENTER;

#ifdef LINUX
    char *customizedOutputPath = getenv("MOS_DEBUG_OUTPUT_LOCATION");
    if (customizedOutputPath != nullptr && strlen(customizedOutputPath) != 0)
    {
        m_outputFilePath = customizedOutputPath;
        m_outputFilePath.erase(m_outputFilePath.find_last_not_of(" \n\r\t") + 1);
        if (m_outputFilePath[m_outputFilePath.length() - 1] != MOS_DIRECTORY_DELIMITER)
            m_outputFilePath += MOS_DIRECTORY_DELIMITER;
    }
    else
#endif
    {
        OutputPathKey = SetOutputPathKey();
        MOS_ZeroMemory(&userFeatureData, sizeof(userFeatureData));
        userFeatureData.StringData.pStringData = stringData;
        MOS_UserFeature_ReadValue_ID(
            NULL,
            OutputPathKey,
            &userFeatureData,
            m_osInterface->pOsContext);

        if (userFeatureData.StringData.uSize == MOS_MAX_PATH_LENGTH + 1)
        {
            userFeatureData.StringData.uSize = 0;
        }

        if (userFeatureData.StringData.uSize > 0)
        {
            if (userFeatureData.StringData.pStringData[userFeatureData.StringData.uSize - 2] != MOS_DIRECTORY_DELIMITER)
            {
                userFeatureData.StringData.pStringData[userFeatureData.StringData.uSize - 1] = MOS_DIRECTORY_DELIMITER;
                userFeatureData.StringData.pStringData[userFeatureData.StringData.uSize]     = '\0';
                userFeatureData.StringData.uSize++;
            }
            m_outputFilePath = userFeatureData.StringData.pStringData;
        }
        else
        {
#if defined(LINUX) || defined(ANDROID)
            m_outputFilePath = MOS_DEBUG_DEFAULT_OUTPUT_LOCATION;
#else
            // Use state separation APIs to obtain appropriate storage location
            if (SUCCEEDED(GetDriverPersistentStorageLocation(DumpFilePath)))
            {
                m_outputFilePath = DumpFilePath.c_str();
                OutputPathKey    = InitDefaultOutput();
                MOS_ZeroMemory(&userFeatureWriteData, sizeof(userFeatureWriteData));
                userFeatureWriteData.Value.StringData.pStringData = const_cast<char *>(m_outputFilePath.c_str());
                userFeatureWriteData.Value.StringData.uSize       = m_outputFilePath.size();
                userFeatureWriteData.ValueID                      = OutputPathKey;
                MOS_UserFeature_WriteValues_ID(NULL, &userFeatureWriteData, 1, m_osInterface->pOsContext);
            }
            else
            {
                return MOS_STATUS_UNKNOWN;
            }
#endif
        }
    }
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::DumpStringStream(std::stringstream& ss, const char* bufferName, const char* attrName)
{
    MEDIA_DEBUG_FUNCTION_ENTER;

    MEDIA_DEBUG_CHK_NULL(bufferName);
    MEDIA_DEBUG_CHK_NULL(attrName);

    if (!m_configMgr->AttrIsEnabled(attrName))
    {
        return MOS_STATUS_SUCCESS;
    }

    const char* filePath = CreateFileName(bufferName, nullptr, MediaDbgExtType::txt);
    std::ofstream ofs(filePath);
    ofs << ss.str();
    ofs.close();

    return MOS_STATUS_SUCCESS;
}

bool MediaDebugInterface::DumpIsEnabled(
    const char *              attr,
    MEDIA_DEBUG_STATE_TYPE mediaState)
{
    if (nullptr == m_configMgr)
    {
        return false;
    }

    if (mediaState != CODECHAL_NUM_MEDIA_STATES)
    {
        return m_configMgr->AttrIsEnabled(mediaState, attr);
    }
    else
    {
        return m_configMgr->AttrIsEnabled(attr);
    }
}

const char *MediaDebugInterface::CreateFileName(
    const char *funcName,
    const char *bufType,
    const char *extType)
{
    if (nullptr == funcName || nullptr == extType)
    {
        return nullptr;
    }

    char frameType = 'X';
    // Sets the frameType label
    if (m_frameType == I_TYPE)
    {
        frameType = 'I';
    }
    else if (m_frameType == P_TYPE)
    {
        frameType = 'P';
    }
    else if (m_frameType == B_TYPE)
    {
        frameType = 'B';
    }
    else if (m_frameType == MIXED_TYPE)
    {
        frameType = 'M';
    }

    const char *fieldOrder;
    // Sets the Field Order label
    if (CodecHal_PictureIsTopField(m_currPic))
    {
        fieldOrder = MediaDbgFieldType::topField;
    }
    else if (CodecHal_PictureIsBottomField(m_currPic))
    {
        fieldOrder = MediaDbgFieldType::botField;
    }
    else
    {
        fieldOrder = MediaDbgFieldType::frame;
    }

    // Sets the Postfix label
    if (m_configMgr->AttrIsEnabled(MediaDbgAttr::attrDumpBufferInBinary) &&
        strcmp(extType, MediaDbgExtType::txt) == 0)
    {
        extType = MediaDbgExtType::dat;
    }

    if (bufType != nullptr &&
        !strncmp(bufType, MediaDbgBufferType::bufSlcParams, sizeof(MediaDbgBufferType::bufSlcParams) - 1) && !strncmp(funcName, "_DDIEnc", sizeof("_DDIEnc") - 1))
    {
        m_outputFileName = m_outputFilePath +
                           std::to_string(m_bufferDumpFrameNum) + '-' +
                           std::to_string(m_streamId) + '_' +
                           std::to_string(m_sliceId + 1) +
                           funcName + '_' + bufType + '_' + frameType + fieldOrder + extType;
    }
    else if (bufType != nullptr &&
             !strncmp(bufType, MediaDbgBufferType::bufEncodePar, sizeof(MediaDbgBufferType::bufEncodePar) - 1))
    {
        if (!strncmp(funcName, "EncodeSequence", sizeof("EncodeSequence") - 1))
        {
            m_outputFileName = m_outputFilePath +
                               std::to_string(m_streamId) + '_' +
                               funcName + extType;
        }
        else
        {
            m_outputFileName = m_outputFilePath +
                               std::to_string(m_bufferDumpFrameNum) + '-' +
                               std::to_string(m_streamId) + '_' +
                               funcName + frameType + fieldOrder + extType;
        }
    }
    else
    {
        if (funcName[0] == '_')
            funcName += 1;

        if (bufType != nullptr)
        {
            m_outputFileName = m_outputFilePath +
                               std::to_string(m_bufferDumpFrameNum) + '-' +
                               std::to_string(m_streamId) + '_' +
                               funcName + '_' + bufType + '_' + frameType + fieldOrder + extType;
        }
        else
        {
            m_outputFileName = m_outputFilePath +
                               std::to_string(m_bufferDumpFrameNum) + '-' +
                               std::to_string(m_streamId) + '_' +
                               funcName + '_' + frameType + fieldOrder + extType;
        }
    }

    return m_outputFileName.c_str();
}

MOS_STATUS MediaDebugInterface::DumpCmdBuffer(
    PMOS_COMMAND_BUFFER    cmdBuffer,
    MEDIA_DEBUG_STATE_TYPE mediaState,
    const char *           cmdName)
{
    MEDIA_DEBUG_FUNCTION_ENTER;

    bool attrEnabled = m_configMgr->AttrIsEnabled(MediaDbgAttr::attrCmdBufferMfx);

    if (!attrEnabled && mediaState != CODECHAL_NUM_MEDIA_STATES)
    {
        attrEnabled = m_configMgr->AttrIsEnabled(mediaState, MediaDbgAttr::attrCmdBuffer);
    }

    if (!attrEnabled)
    {
        return MOS_STATUS_SUCCESS;
    }

    bool binaryDumpEnabled = m_configMgr->AttrIsEnabled(MediaDbgAttr::attrDumpCmdBufInBinary);

    std::string funcName = cmdName ? cmdName : m_configMgr->GetMediaStateStr(mediaState);
    const char *fileName = CreateFileName(
        funcName.c_str(),
        MediaDbgBufferType::bufCmd,
        binaryDumpEnabled ? MediaDbgExtType::dat : MediaDbgExtType::txt);

    if (binaryDumpEnabled)
    {
        DumpBufferInBinary((uint8_t *)cmdBuffer->pCmdBase, (uint32_t)cmdBuffer->iOffset);
    }
    else
    {
        DumpBufferInHexDwords((uint8_t *)cmdBuffer->pCmdBase, (uint32_t)cmdBuffer->iOffset);
    }

    return MOS_STATUS_SUCCESS;
}

void MediaDebugInterface::PackGoldenReferences(std::initializer_list<std::vector<uint32_t>> goldenReference)
{
    for (auto beg = goldenReference.begin(); beg != goldenReference.end(); beg++)
    {
        m_goldenReferences.push_back(*beg);
    }
}

MOS_STATUS MediaDebugInterface::CaptureGoldenReference(uint8_t *buf, uint32_t size, uint32_t hwCrcValue)
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
    if (m_enableHwDebugHooks &&
        !m_goldenReferenceExist)
    {
        if (m_swCRC)
        {
            if (buf == nullptr)
            {
                return MOS_STATUS_NULL_POINTER;
            }
            auto crcVal = CalculateCRC(buf, size, 0);
            m_crcGoldenReference.push_back(crcVal);
        }
        else
        {
            m_crcGoldenReference.push_back(hwCrcValue);
        }
    }
    return eStatus;
}

MOS_STATUS MediaDebugInterface::FillSemaResource(std::vector<uint32_t*> &vSemaData, std::vector<uint32_t> &data)
{
    if (vSemaData.size() != data.size())
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }
    for (uint32_t i = 0; i < vSemaData.size(); i++)
    {
        *vSemaData[i] = data[i];
    }
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::LockResource(uint32_t *semaData, PMOS_RESOURCE reSemaphore)
{
    CodechalResLock semaLock(m_osInterface, reSemaphore);
    semaData = (uint32_t *)semaLock.Lock(CodechalResLock::writeOnly);
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::LockSemaResource(std::vector<uint32_t *> &vSemaData, std::vector<MOS_RESOURCE> &vResource)
{
    for (uint32_t i = 0; i < vResource.size(); i++)
    {
        CodechalResLock semaLock(m_osInterface, &vResource[i]);
        uint32_t *      smData = (uint32_t *)semaLock.Lock(CodechalResLock::writeOnly);
        LockResource(smData, &vResource[i]);
        vSemaData.push_back(smData);
    }
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::StopExecutionAtFrame(CodechalHwInterface *hwInterface, PMOS_RESOURCE statusBuffer, PMOS_COMMAND_BUFFER pCmdBuffer, uint32_t numFrame)
{
    MEDIA_DEBUG_ASSERTMESSAGE("Will stop to frame: %d!!!", numFrame);

    MEDIA_DEBUG_CHK_STATUS(hwInterface->SendHwSemaphoreWaitCmd(
        statusBuffer,
        0x7f7f7f7f,
        MHW_MI_SAD_EQUAL_SDD,
        pCmdBuffer));
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::DumpToFile(const GoldenReferences &goldenReferences)
{
    std::ofstream ofs(m_crcGoldenRefFileName, std::ios_base::out);
    if (goldenReferences.size() <= 0)
    {
        return MOS_STATUS_INVALID_PARAMETER;
    }
    for (uint32_t i = 0; i < goldenReferences[0].size(); i++)
    {
        for (auto golden : goldenReferences)
        {
            ofs << golden[i] << '\t';
        }
        ofs << '\n';
    }
    ofs.close();
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::StoreNumFrame(PMHW_MI_INTERFACE pMiInterface, PMOS_RESOURCE pResource, int32_t frameNum, PMOS_COMMAND_BUFFER pCmdBuffer)
{
    MHW_MI_STORE_DATA_PARAMS storeDataParams;
    MOS_ZeroMemory(&storeDataParams, sizeof(storeDataParams));
    storeDataParams.pOsResource      = pResource;
    storeDataParams.dwResourceOffset = 0;
    storeDataParams.dwValue          = frameNum;
    MEDIA_DEBUG_CHK_STATUS(pMiInterface->AddMiStoreDataImmCmd(pCmdBuffer, &storeDataParams));
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::DumpGoldenReference()
{
    if (m_enableHwDebugHooks && !m_goldenReferenceExist && m_goldenReferences.size() > 0)
    {
        return DumpToFile(m_goldenReferences);
    }
    else
    {
        return MOS_STATUS_UNKNOWN;
    }
}

MOS_STATUS MediaDebugInterface::DumpYUVSurfaceToBuffer(PMOS_SURFACE surface,
    uint8_t *                                                          buffer,
    uint32_t &                                                         size)
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    MOS_LOCK_PARAMS lockFlags;
    MOS_ZeroMemory(&lockFlags, sizeof(MOS_LOCK_PARAMS));
    lockFlags.ReadOnly     = 1;
    lockFlags.TiledAsTiled = 1;  // Bypass GMM CPU blit due to some issues in GMM CpuBlt function

    uint8_t *lockedAddr = (uint8_t *)m_osInterface->pfnLockResource(m_osInterface, &surface->OsResource, &lockFlags);
    if (lockedAddr == nullptr)  // Failed to lock. Try to submit copy task and dump another surface
    {
        uint32_t        sizeToBeCopied = 0;
        MOS_GFXRES_TYPE ResType;

#if LINUX
        // Linux does not have OsResource->ResType
        ResType = surface->Type;
#else
        ResType = surface->OsResource.ResType;
#endif

        GMM_RESOURCE_FLAG gmmFlags  = surface->OsResource.pGmmResInfo->GetResFlags();
        bool              allocated = false;

        MEDIA_DEBUG_CHK_STATUS(ReAllocateSurface(
            &m_temp2DSurfForCopy,
            surface,
            "Temp2DSurfForSurfDumper",
            ResType));

        // Ensure allocated buffer size contains the source surface size
        if (m_temp2DSurfForCopy.OsResource.pGmmResInfo->GetSizeMainSurface() >= surface->OsResource.pGmmResInfo->GetSizeMainSurface())
        {
            sizeToBeCopied = (uint32_t)surface->OsResource.pGmmResInfo->GetSizeMainSurface();
        }

        if (sizeToBeCopied == 0)
        {
            // Currently, MOS's pfnAllocateResource does not support allocate a surface reference to another surface.
            // When the source surface is not created from Media, it is possible that we cannot allocate the same size as source.
            // For example, on Gen9, Render target might have GMM set CCS=1 MMC=0, but MOS cannot allocate surface with such combination.
            // When Gmm allocation parameter is different, the resulting surface size/padding/pitch will be differnt.
            // Once if MOS can support allocate a surface by reference another surface, we can do a bit to bit copy without problem.
            MEDIA_DEBUG_ASSERTMESSAGE("Cannot allocate correct size, failed to copy nonlockable resource");
            return MOS_STATUS_NULL_POINTER;
        }

        MEDIA_DEBUG_VERBOSEMESSAGE("Temp2DSurfaceForCopy width %d, height %d, pitch %d, TileType %d, bIsCompressed %d, CompressionMode %d",
            m_temp2DSurfForCopy.dwWidth,
            m_temp2DSurfForCopy.dwHeight,
            m_temp2DSurfForCopy.dwPitch,
            m_temp2DSurfForCopy.TileType,
            m_temp2DSurfForCopy.bIsCompressed,
            m_temp2DSurfForCopy.CompressionMode);

        if (CopySurfaceData_Vdbox(sizeToBeCopied, &surface->OsResource, &m_temp2DSurfForCopy.OsResource) != MOS_STATUS_SUCCESS)
        {
            MEDIA_DEBUG_ASSERTMESSAGE("CopyDataSurface_Vdbox failed");
            m_osInterface->pfnFreeResource(m_osInterface, &m_temp2DSurfForCopy.OsResource);
            return MOS_STATUS_NULL_POINTER;
        }
        lockedAddr = (uint8_t *)m_osInterface->pfnLockResource(m_osInterface, &m_temp2DSurfForCopy.OsResource, &lockFlags);
        MEDIA_DEBUG_CHK_NULL(lockedAddr);
    }

    uint32_t sizeMain     = (uint32_t)(surface->OsResource.pGmmResInfo->GetSizeMainSurface());
    uint8_t *surfBaseAddr = (uint8_t *)MOS_AllocMemory(sizeMain);
    MEDIA_DEBUG_CHK_NULL(surfBaseAddr);

    Mos_SwizzleData(lockedAddr, surfBaseAddr, surface->TileType, MOS_TILE_LINEAR, sizeMain / surface->dwPitch, surface->dwPitch, 0);

    uint8_t *data = surfBaseAddr;
    data += surface->dwOffset + surface->YPlaneOffset.iYOffset * surface->dwPitch;

    uint32_t width  = surface->dwWidth;
    uint32_t height = surface->dwHeight;

    switch (surface->Format)
    {
    case Format_YUY2:
    case Format_Y216V:
    case Format_P010:
    case Format_P016:
        width = width << 1;
        break;
    case Format_Y216:
    case Format_Y210:  //422 10bit -- Y0[15:0]:U[15:0]:Y1[15:0]:V[15:0] = 32bits per pixel = 4Bytes per pixel
    case Format_Y410:  //444 10bit -- A[31:30]:V[29:20]:Y[19:10]:U[9:0] = 32bits per pixel = 4Bytes per pixel
    case Format_R10G10B10A2:
    case Format_AYUV:  //444 8bit  -- A[31:24]:Y[23:16]:U[15:8]:V[7:0] = 32bits per pixel = 4Bytes per pixel
    case Format_A8R8G8B8:
        width = width << 2;
        break;
    default:
        break;
    }

    uint32_t pitch = surface->dwPitch;
    if (surface->Format == Format_UYVY)
        pitch = width;

    if (CodecHal_PictureIsBottomField(m_currPic))
    {
        data += pitch;
    }

    if (CodecHal_PictureIsField(m_currPic))
    {
        pitch *= 2;
        height /= 2;
    }

    // write luma data to file
    for (uint32_t h = 0; h < height; h++)
    {
        MOS_SecureMemcpy(buffer, width, data, width);
        buffer += width;
        size += width;
        data += pitch;
    }

    if (surface->Format != Format_A8B8G8R8)
    {
        switch (surface->Format)
        {
        case Format_NV12:
        case Format_P010:
        case Format_P016:
            height >>= 1;
            break;
        case Format_Y416:
        case Format_AUYV:
        case Format_R10G10B10A2:
            height *= 2;
            break;
        case Format_YUY2:
        case Format_YUYV:
        case Format_YUY2V:
        case Format_Y216V:
        case Format_YVYU:
        case Format_UYVY:
        case Format_VYUY:
        case Format_Y216:  //422 16bit
        case Format_Y210:  //422 10bit
        case Format_P208:  //422 8bit
            break;
        case Format_422V:
        case Format_IMC3:
            height = height / 2;
            break;
        case Format_AYUV:
        default:
            height = 0;
            break;
        }

        uint8_t *vPlaneData = surfBaseAddr;
#ifdef LINUX
        data = surfBaseAddr + surface->UPlaneOffset.iSurfaceOffset;
        if (surface->Format == Format_422V || surface->Format == Format_IMC3)
        {
            vPlaneData = surfBaseAddr + surface->VPlaneOffset.iSurfaceOffset;
        }
#else
        data = surfBaseAddr + surface->UPlaneOffset.iLockSurfaceOffset;
        if (surface->Format == Format_422V || surface->Format == Format_IMC3)
        {
            vPlaneData = surfBaseAddr + surface->VPlaneOffset.iLockSurfaceOffset;
        }

#endif

        // write chroma data to file
        for (uint32_t h = 0; h < height; h++)
        {
            MOS_SecureMemcpy(buffer, width, data, width);
            buffer += width;
            size += width;
            data += pitch;
        }

        // write v planar data to file
        if (surface->Format == Format_422V || surface->Format == Format_IMC3)
        {
            for (uint32_t h = 0; h < height; h++)
            {
                MOS_SecureMemcpy(buffer, width, vPlaneData, width);
                buffer += width;
                size += width;
                vPlaneData += pitch;
            }
        }
    }

    m_osInterface->pfnUnlockResource(m_osInterface, &surface->OsResource);

    MOS_FreeMemory(surfBaseAddr);

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::LoadGoldenReference()
{
    std::ifstream ifs(m_crcGoldenRefFileName, std::ios_base::in);
    std::vector<uint32_t> lines;
    std::string           str;
    uint32_t              num;
    if (!ifs)
    {
        return MOS_STATUS_FILE_OPEN_FAILED;
    }
    uint32_t crc;
    while (!ifs.eof())
    {
        std::getline(ifs, str);
        std::stringstream stringin(str); 
        lines.clear();
        while (stringin >> num)
        {
            lines.push_back(num);
        }
        m_goldenReferences.push_back(lines);
    }
    ifs.close();
    m_goldenReferences.pop_back();
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::SubmitDummyWorkload(MOS_COMMAND_BUFFER *pCmdBuffer, int32_t bNullRendering)
{
    MHW_MI_FLUSH_DW_PARAMS flushDwParams;
    MOS_ZeroMemory(&flushDwParams, sizeof(flushDwParams));
    MEDIA_DEBUG_CHK_STATUS(m_miInterface->AddMiFlushDwCmd(
        pCmdBuffer,
        &flushDwParams));
    MEDIA_DEBUG_CHK_STATUS(m_miInterface->AddMiBatchBufferEnd(
        pCmdBuffer,
        nullptr));
    m_osInterface->pfnReturnCommandBuffer(m_osInterface, pCmdBuffer, 0);
    MEDIA_DEBUG_CHK_STATUS(m_osInterface->pfnSubmitCommandBuffer(m_osInterface, pCmdBuffer, bNullRendering));
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::DetectCorruptionHw(CodechalHwInterface *hwInterface, PMOS_RESOURCE frameCntRes, uint32_t curIdx, uint32_t frameCrcOffset, std::vector<MOS_RESOURCE> &vStatusBuffer, PMOS_COMMAND_BUFFER pCmdBuffer, uint32_t frameNum)
{
    if (m_enableHwDebugHooks &&
        m_goldenReferenceExist &&
        m_goldenReferences.size() > 0 &&
        vStatusBuffer.size() > 0)
    {
        for (uint32_t i = 0; i < vStatusBuffer.size(); i++)
        {
            MEDIA_DEBUG_CHK_STATUS(hwInterface->SendHwSemaphoreWaitCmd(
                &vStatusBuffer[i],
                m_goldenReferences[curIdx][i],
                MHW_MI_SAD_EQUAL_SDD,
                pCmdBuffer,
                frameCrcOffset));
        }
        StoreNumFrame(m_miInterface, frameCntRes, frameNum, pCmdBuffer);
        if (m_frameNumSpecified == frameNum)
        {
            StopExecutionAtFrame(hwInterface, &vStatusBuffer[0], pCmdBuffer, frameNum);
        }
    }
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::Dump2ndLvlBatch(
    PMHW_BATCH_BUFFER      batchBuffer,
    MEDIA_DEBUG_STATE_TYPE mediaState,
    const char *           batchName)
{
    MEDIA_DEBUG_FUNCTION_ENTER;

    bool attrEnabled = m_configMgr->AttrIsEnabled(MediaDbgAttr::attr2ndLvlBatchMfx);

    if (!attrEnabled && mediaState != CODECHAL_NUM_MEDIA_STATES)
    {
        attrEnabled = m_configMgr->AttrIsEnabled(mediaState, MediaDbgAttr::attr2ndLvlBatch);
    }

    if (!attrEnabled)
    {
        return MOS_STATUS_SUCCESS;
    }

    bool        batchLockedForDebug = !batchBuffer->bLocked;
    std::string funcName            = batchName ? batchName : m_configMgr->GetMediaStateStr(mediaState);

    if (batchLockedForDebug)
    {
        (Mhw_LockBb(m_osInterface, batchBuffer));
    }

    const char *fileName = CreateFileName(
        funcName.c_str(),
        MediaDbgBufferType::buf2ndLvl,
        MediaDbgExtType::txt);

    batchBuffer->pData += batchBuffer->dwOffset;

    DumpBufferInHexDwords(batchBuffer->pData,
        (uint32_t)batchBuffer->iLastCurrent);

    if (batchLockedForDebug)
    {
        (Mhw_UnlockBb(m_osInterface, batchBuffer, false));
    }

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::DumpCurbe(
    MEDIA_DEBUG_STATE_TYPE mediaState,
    PMHW_KERNEL_STATE      kernelState)
{
    MEDIA_DEBUG_FUNCTION_ENTER;

    if (mediaState >= CODECHAL_NUM_MEDIA_STATES ||
        !m_configMgr->AttrIsEnabled(mediaState, MediaDbgAttr::attrCurbe))
    {
        return MOS_STATUS_SUCCESS;
    }

    std::string funcName   = m_configMgr->GetMediaStateStr(mediaState);
    bool        binaryDump = m_configMgr->AttrIsEnabled(MediaDbgAttr::attrDumpBufferInBinary);

    const char *fileName = CreateFileName(
        funcName.c_str(),
        MediaDbgBufferType::bufCurbe,
        MediaDbgExtType::txt);

    return kernelState->m_dshRegion.Dump(
        fileName,
        kernelState->dwCurbeOffset,
        kernelState->KernelParams.iCurbeLength,
        binaryDump);
}

MOS_STATUS MediaDebugInterface::DumpMDFCurbe(
    MEDIA_DEBUG_STATE_TYPE mediaState,
    uint8_t *              curbeBuffer,
    uint32_t               curbeSize)
{
    MEDIA_DEBUG_FUNCTION_ENTER;

    uint8_t *  curbeAlignedData = nullptr;
    uint32_t   curbeAlignedSize = 0;
    MOS_STATUS eStatus          = MOS_STATUS_SUCCESS;

    if (mediaState >= CODECHAL_NUM_MEDIA_STATES ||
        !m_configMgr->AttrIsEnabled(mediaState, MediaDbgAttr::attrCurbe))
    {
        return eStatus;
    }

    std::string funcName   = m_configMgr->GetMediaStateStr(mediaState);
    bool        binaryDump = m_configMgr->AttrIsEnabled(MediaDbgAttr::attrDumpBufferInBinary);
    const char *extType    = binaryDump ? MediaDbgExtType::dat : MediaDbgExtType::txt;

    const char *fileName = CreateFileName(
        funcName.c_str(),
        MediaDbgBufferType::bufCurbe,
        extType);

    curbeAlignedSize = MOS_ALIGN_CEIL(curbeSize, 64);
    curbeAlignedData = (uint8_t *)malloc(curbeAlignedSize * sizeof(uint8_t));
    if (curbeAlignedData == nullptr)
    {
        eStatus = MOS_STATUS_NULL_POINTER;
        return eStatus;
    }

    MOS_ZeroMemory(curbeAlignedData, curbeAlignedSize);
    MOS_SecureMemcpy(curbeAlignedData, curbeSize, curbeBuffer, curbeSize);

    if (binaryDump)
    {
        eStatus = DumpBufferInBinary(curbeAlignedData, curbeAlignedSize);
    }
    else
    {
        eStatus = DumpBufferInHexDwords(curbeAlignedData, curbeAlignedSize);
    }

    free(curbeAlignedData);

    return eStatus;
}

MOS_STATUS MediaDebugInterface::DumpKernelRegion(
    MEDIA_DEBUG_STATE_TYPE mediaState,
    MHW_STATE_HEAP_TYPE    stateHeap,
    PMHW_KERNEL_STATE      kernelState)
{
    MEDIA_DEBUG_FUNCTION_ENTER;

    uint8_t *sshData = nullptr;
    uint32_t sshSize = 0;

    MemoryBlock *regionBlock = nullptr;
    bool         attrEnabled = false;
    const char * bufferType;
    if (stateHeap == MHW_ISH_TYPE)
    {
        regionBlock = &kernelState->m_ishRegion;
        attrEnabled = m_configMgr->AttrIsEnabled(mediaState, MediaDbgAttr::attrIsh);
        bufferType  = MediaDbgBufferType::bufISH;
    }
    else if (stateHeap == MHW_DSH_TYPE)
    {
        regionBlock = &kernelState->m_dshRegion;
        attrEnabled = m_configMgr->AttrIsEnabled(mediaState, MediaDbgAttr::attrDsh);
        bufferType  = MediaDbgBufferType::bufDSH;
    }
    else
    {
        attrEnabled = m_configMgr->AttrIsEnabled(mediaState, MediaDbgAttr::attrSsh);
        bufferType  = MediaDbgBufferType::bufSSH;

        MEDIA_DEBUG_CHK_NULL(m_osInterface);
        MEDIA_DEBUG_CHK_STATUS(m_osInterface->pfnGetIndirectStatePointer(
            m_osInterface,
            &sshData));
        sshData += kernelState->dwSshOffset;
        sshSize = kernelState->dwSshSize;
    }

    if (!attrEnabled)
    {
        return MOS_STATUS_SUCCESS;
    }

    std::string funcName = m_configMgr->GetMediaStateStr(mediaState);

    const char *fileName = CreateFileName(
        funcName.c_str(),
        bufferType,
        MediaDbgExtType::txt);

    bool binaryDump = m_configMgr->AttrIsEnabled(MediaDbgAttr::attrDumpBufferInBinary);

    if (regionBlock)
    {
        return regionBlock->Dump(fileName, 0, 0, binaryDump);
    }
    else
    {
        return DumpBufferInHexDwords(sshData, sshSize);
    }
}

MOS_STATUS MediaDebugInterface::SetSWCrcMode(bool swCrc)
{
    m_swCRC = swCrc;
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::DumpYUVSurface(
    PMOS_SURFACE           surface,
    const char *           attrName,
    const char *           surfName,
    MEDIA_DEBUG_STATE_TYPE mediaState,
    uint32_t               width_in,
    uint32_t               height_in)
{
    if (!DumpIsEnabled(attrName, mediaState))
    {
        return MOS_STATUS_SUCCESS;
    }

    MOS_LOCK_PARAMS lockFlags;
    MOS_ZeroMemory(&lockFlags, sizeof(MOS_LOCK_PARAMS));
    lockFlags.ReadOnly     = 1;
    lockFlags.TiledAsTiled = 1;  // Bypass GMM CPU blit due to some issues in GMM CpuBlt function

    uint8_t *lockedAddr = (uint8_t *)m_osInterface->pfnLockResource(m_osInterface, &surface->OsResource, &lockFlags);
    if (lockedAddr == nullptr)  // Failed to lock. Try to submit copy task and dump another surface
    {
        uint32_t        sizeToBeCopied = 0;
        MOS_GFXRES_TYPE ResType;

#if LINUX
        // Linux does not have OsResource->ResType
        ResType = surface->Type;
#else
        ResType = surface->OsResource.ResType;
#endif

        GMM_RESOURCE_FLAG gmmFlags  = surface->OsResource.pGmmResInfo->GetResFlags();
        bool              allocated = false;

        MEDIA_DEBUG_CHK_STATUS(ReAllocateSurface(
            &m_temp2DSurfForCopy,
            surface,
            "Temp2DSurfForSurfDumper",
            ResType));

        // Ensure allocated buffer size contains the source surface size
        if (m_temp2DSurfForCopy.OsResource.pGmmResInfo->GetSizeMainSurface() >= surface->OsResource.pGmmResInfo->GetSizeMainSurface())
        {
            sizeToBeCopied = (uint32_t)surface->OsResource.pGmmResInfo->GetSizeMainSurface();
        }

        if (sizeToBeCopied == 0)
        {
            // Currently, MOS's pfnAllocateResource does not support allocate a surface reference to another surface.
            // When the source surface is not created from Media, it is possible that we cannot allocate the same size as source.
            // For example, on Gen9, Render target might have GMM set CCS=1 MMC=0, but MOS cannot allocate surface with such combination.
            // When Gmm allocation parameter is different, the resulting surface size/padding/pitch will be differnt.
            // Once if MOS can support allocate a surface by reference another surface, we can do a bit to bit copy without problem.
            MEDIA_DEBUG_ASSERTMESSAGE("Cannot allocate correct size, failed to copy nonlockable resource");
            return MOS_STATUS_NULL_POINTER;
        }

        MEDIA_DEBUG_VERBOSEMESSAGE("Temp2DSurfaceForCopy width %d, height %d, pitch %d, TileType %d, bIsCompressed %d, CompressionMode %d",
            m_temp2DSurfForCopy.dwWidth,
            m_temp2DSurfForCopy.dwHeight,
            m_temp2DSurfForCopy.dwPitch,
            m_temp2DSurfForCopy.TileType,
            m_temp2DSurfForCopy.bIsCompressed,
            m_temp2DSurfForCopy.CompressionMode);

        if (CopySurfaceData_Vdbox(sizeToBeCopied, &surface->OsResource, &m_temp2DSurfForCopy.OsResource) != MOS_STATUS_SUCCESS)
        {
            MEDIA_DEBUG_ASSERTMESSAGE("CopyDataSurface_Vdbox failed");
            m_osInterface->pfnFreeResource(m_osInterface, &m_temp2DSurfForCopy.OsResource);
            return MOS_STATUS_NULL_POINTER;
        }
        lockedAddr = (uint8_t *)m_osInterface->pfnLockResource(m_osInterface, &m_temp2DSurfForCopy.OsResource, &lockFlags);
        MEDIA_DEBUG_CHK_NULL(lockedAddr);

        if (DumpIsEnabled(MediaDbgAttr::attrDisableSwizzleForDumps))
        {
            if (CodecHal_PictureIsField(m_currPic))
            {
                return MOS_STATUS_INVALID_PARAMETER;
            }
            else
            {
                return DumpNotSwizzled(surfName, m_temp2DSurfForCopy, lockedAddr, sizeToBeCopied);
            }
        }
    }

    uint32_t sizeMain = (uint32_t)(surface->OsResource.pGmmResInfo->GetSizeMainSurface());
    if (DumpIsEnabled(MediaDbgAttr::attrDisableSwizzleForDumps))
    {
        if (CodecHal_PictureIsField(m_currPic))
        {
            return MOS_STATUS_INVALID_PARAMETER;
        }
        else
        {
            return DumpNotSwizzled(surfName, *surface, lockedAddr, sizeMain);
        }
    }

    uint8_t *surfBaseAddr = (uint8_t *)MOS_AllocMemory(sizeMain);
    MEDIA_DEBUG_CHK_NULL(surfBaseAddr);

    if (DumpIsEnabled(MediaDbgAttr::attrForceYUVDumpWithMemcpy))
    {
        MOS_SecureMemcpy(surfBaseAddr, sizeMain, lockedAddr, sizeMain);  // Firstly, copy to surfBaseAddr to faster unlock resource
        m_osInterface->pfnUnlockResource(m_osInterface, &surface->OsResource);
        lockedAddr   = surfBaseAddr;
        surfBaseAddr = (uint8_t *)MOS_AllocMemory(sizeMain);
        MEDIA_DEBUG_CHK_NULL(surfBaseAddr);
    }

    // Always use MOS swizzle instead of GMM Cpu blit
    MEDIA_DEBUG_CHK_NULL(surfBaseAddr);
    Mos_SwizzleData(lockedAddr, surfBaseAddr, surface->TileType, MOS_TILE_LINEAR, sizeMain / surface->dwPitch, surface->dwPitch, 0);

    uint8_t *data = surfBaseAddr;
    data += surface->dwOffset + surface->YPlaneOffset.iYOffset * surface->dwPitch;

    uint32_t width  = width_in ? width_in : surface->dwWidth;
    uint32_t height = height_in ? height_in : surface->dwHeight;

    switch (surface->Format)
    {
    case Format_YUY2:
    case Format_Y216V:
    case Format_P010:
    case Format_P016:
        width = width << 1;
        break;
    case Format_Y216:
    case Format_Y210:  //422 10bit -- Y0[15:0]:U[15:0]:Y1[15:0]:V[15:0] = 32bits per pixel = 4Bytes per pixel
    case Format_Y410:  //444 10bit -- A[31:30]:V[29:20]:Y[19:10]:U[9:0] = 32bits per pixel = 4Bytes per pixel
    case Format_R10G10B10A2:
    case Format_AYUV:  //444 8bit  -- A[31:24]:Y[23:16]:U[15:8]:V[7:0] = 32bits per pixel = 4Bytes per pixel
    case Format_A8R8G8B8:
        width = width << 2;
        break;
    default:
        break;
    }

    uint32_t pitch = surface->dwPitch;
    if (surface->Format == Format_UYVY)
        pitch = width;

    if (CodecHal_PictureIsBottomField(m_currPic))
    {
        data += pitch;
    }

    if (CodecHal_PictureIsField(m_currPic))
    {
        pitch *= 2;
        height /= 2;
    }

    const char *funcName = (m_mediafunction == MEDIA_FUNCTION_VP) ? "_VP" : ((m_mediafunction == MEDIA_FUNCTION_ENCODE) ? "_ENC" : "_DEC");
    std::string bufName  = std::string(surfName) + "_w[" + std::to_string(surface->dwWidth) + "]_h[" + std::to_string(surface->dwHeight) + "]_p[" + std::to_string(pitch) + "]";
    const char *filePath = CreateFileName(funcName, bufName.c_str(), MediaDbgExtType::yuv);

    std::ofstream ofs(filePath, std::ios_base::out | std::ios_base::binary);
    if (ofs.fail())
    {
        return MOS_STATUS_UNKNOWN;
    }

    // write luma data to file
    for (uint32_t h = 0; h < height; h++)
    {
        ofs.write((char *)data, width);
        data += pitch;
    }

    if (surface->Format != Format_A8B8G8R8)
    {
        switch (surface->Format)
        {
        case Format_NV12:
        case Format_P010:
        case Format_P016:
            height >>= 1;
            break;
        case Format_Y416:
        case Format_AUYV:
        case Format_R10G10B10A2:
            height *= 2;
            break;
        case Format_YUY2:
        case Format_YUYV:
        case Format_YUY2V:
        case Format_Y216V:
        case Format_YVYU:
        case Format_UYVY:
        case Format_VYUY:
        case Format_Y216:  //422 16bit
        case Format_Y210:  //422 10bit
        case Format_P208:  //422 8bit
            break;
        case Format_422V:
        case Format_IMC3:
            height = height / 2;
            break;
        case Format_AYUV:
        default:
            height = 0;
            break;
        }

        uint8_t *vPlaneData = surfBaseAddr;
#ifdef LINUX
        data = surfBaseAddr + surface->UPlaneOffset.iSurfaceOffset;
        if (surface->Format == Format_422V || surface->Format == Format_IMC3)
        {
            vPlaneData = surfBaseAddr + surface->VPlaneOffset.iSurfaceOffset;
        }
#else
        data = surfBaseAddr + surface->UPlaneOffset.iLockSurfaceOffset;
        if (surface->Format == Format_422V || surface->Format == Format_IMC3)
        {
            vPlaneData = surfBaseAddr + surface->VPlaneOffset.iLockSurfaceOffset;
        }

#endif

        // write chroma data to file
        for (uint32_t h = 0; h < height; h++)
        {
            ofs.write((char *)data, width);
            data += pitch;
        }

        // write v planar data to file
        if (surface->Format == Format_422V || surface->Format == Format_IMC3)
        {
            for (uint32_t h = 0; h < height; h++)
            {
                ofs.write((char *)vPlaneData, width);
                vPlaneData += pitch;
            }
        }
    }
    ofs.close();

    if (DumpIsEnabled(MediaDbgAttr::attrForceYUVDumpWithMemcpy))
    {
        MOS_FreeMemory(lockedAddr);
    }
    else
    {
        m_osInterface->pfnUnlockResource(m_osInterface, &surface->OsResource);
    }
    MOS_FreeMemory(surfBaseAddr);

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::DumpBuffer(
    PMOS_RESOURCE          resource,
    const char *           attrName,
    const char *           bufferName,
    uint32_t               size,
    uint32_t               offset,
    MEDIA_DEBUG_STATE_TYPE mediaState)
{
    MEDIA_DEBUG_FUNCTION_ENTER;

    MEDIA_DEBUG_CHK_NULL(resource);
    MEDIA_DEBUG_CHK_NULL(bufferName);

    if (size == 0)
    {
        return MOS_STATUS_SUCCESS;
    }

    if (attrName)
    {
        bool attrEnabled = false;

        if (mediaState == CODECHAL_NUM_MEDIA_STATES)
        {
            attrEnabled = m_configMgr->AttrIsEnabled(attrName);
        }
        else
        {
            attrEnabled = m_configMgr->AttrIsEnabled(mediaState, attrName);
        }

        if (!attrEnabled)
        {
            return MOS_STATUS_SUCCESS;
        }
    }

    MOS_LOCK_PARAMS lockFlags;
    MOS_ZeroMemory(&lockFlags, sizeof(MOS_LOCK_PARAMS));
    lockFlags.ReadOnly = 1;
    uint8_t *data      = (uint8_t *)m_osInterface->pfnLockResource(m_osInterface, resource, &lockFlags);
    MEDIA_DEBUG_CHK_NULL(data);
    data += offset;

    const char *fileName;
    bool        binaryDump = m_configMgr->AttrIsEnabled(MediaDbgAttr::attrDumpBufferInBinary);
    const char *extType    = binaryDump ? MediaDbgExtType::dat : MediaDbgExtType::txt;

    if (mediaState == CODECHAL_NUM_MEDIA_STATES)
    {
        fileName = CreateFileName(bufferName, attrName, extType);
    }
    else
    {
        std::string kernelName = m_configMgr->GetMediaStateStr(mediaState);
        fileName               = CreateFileName(kernelName.c_str(), bufferName, extType);
    }

    MOS_STATUS status;
    if (binaryDump)
    {
        status = DumpBufferInBinary(data, size);
    }
    else
    {
        status = DumpBufferInHexDwords(data, size);
    }

    if (data)
    {
        m_osInterface->pfnUnlockResource(m_osInterface, resource);
    }

    return status;
}

MOS_STATUS MediaDebugInterface::DumpSurface(
    PMOS_SURFACE              surface,
    const char *              attrName,
    const char *              surfaceName,
    MEDIA_DEBUG_STATE_TYPE    mediaState)
{
    MEDIA_DEBUG_FUNCTION_ENTER;

    MEDIA_DEBUG_CHK_NULL(surface);
    MEDIA_DEBUG_CHK_NULL(attrName);
    MEDIA_DEBUG_CHK_NULL(surfaceName);

    bool attrEnabled = false;

    if (mediaState == CODECHAL_NUM_MEDIA_STATES)
    {
        attrEnabled = m_configMgr->AttrIsEnabled(attrName);
    }
    else
    {
        attrEnabled = m_configMgr->AttrIsEnabled(mediaState, attrName);
    }

    if (!attrEnabled)
    {
        return MOS_STATUS_SUCCESS;
    }

    bool        binaryDump = m_configMgr->AttrIsEnabled(MediaDbgAttr::attrDumpBufferInBinary);
    const char *extType    = binaryDump ? MediaDbgExtType::dat : MediaDbgExtType::txt;

    MOS_LOCK_PARAMS lockFlags;
    MOS_ZeroMemory(&lockFlags, sizeof(MOS_LOCK_PARAMS));
    lockFlags.ReadOnly = 1;
    uint8_t *data      = (uint8_t *)m_osInterface->pfnLockResource(m_osInterface, &surface->OsResource, &lockFlags);
    MEDIA_DEBUG_CHK_NULL(data);

    std::string bufName = std::string(surfaceName) + "_w[" + std::to_string(surface->dwWidth) + "]_h[" + std::to_string(surface->dwHeight) + "]_p[" + std::to_string(surface->dwPitch) + "]";
    const char *fileName;
    if (mediaState == CODECHAL_NUM_MEDIA_STATES)
    {
        fileName = CreateFileName(bufName.c_str(), nullptr, extType);
    }
    else
    {
        std::string kernelName = m_configMgr->GetMediaStateStr(mediaState);
        fileName               = CreateFileName(kernelName.c_str(), bufName.c_str(), extType);
    }

    MOS_STATUS status;
    if (binaryDump)
    {
        status = Dump2DBufferInBinary(data, surface->dwWidth, surface->dwHeight, surface->dwPitch);
    }
    else
    {
        status = DumpBufferInHexDwords(data, surface->dwHeight * surface->dwPitch);
    }

    if (data)
    {
        m_osInterface->pfnUnlockResource(m_osInterface, &surface->OsResource);
    }

    return status;
}

MOS_STATUS MediaDebugInterface::DumpData(
    void *      data,
    uint32_t    size,
    const char *attrName,
    const char *bufferName)
{
    MEDIA_DEBUG_FUNCTION_ENTER;

    MEDIA_DEBUG_CHK_NULL(data);
    MEDIA_DEBUG_CHK_NULL(attrName);
    MEDIA_DEBUG_CHK_NULL(bufferName);

    if (!m_configMgr->AttrIsEnabled(attrName))
    {
        return MOS_STATUS_SUCCESS;
    }

    bool        binaryDump = m_configMgr->AttrIsEnabled(MediaDbgAttr::attrDumpBufferInBinary);
    const char *fileName   = CreateFileName(bufferName, nullptr, binaryDump ? MediaDbgExtType::dat : MediaDbgExtType::txt);

    if (binaryDump)
    {
        DumpBufferInBinary((uint8_t *)data, size);
    }
    else
    {
        DumpBufferInHexDwords((uint8_t *)data, size);
    }

    return MOS_STATUS_SUCCESS;
}

#define FIELD_TO_OFS(name, shift) ofs << shift #name << ": " << (int64_t)ptr->name << std::endl;
#define EMPTY_TO_OFS()
#define UNION_STRUCT_START_TO_OFS()     ofs << "union"      << std::endl \
                                            << "{"          << std::endl \
                                            << "    struct" << std::endl \
                                            << "    {"      << std::endl;

#define UNION_STRUCT_FIELD_TO_OFS(name) ofs << "        "#name << ": " << ptr->name << std::endl;
#define UNION_END_TO_OFS(name)          ofs << "    }"      << std::endl \
                                            << "    "#name << ": " << ptr->name << std::endl \
                                            << "}" << std::endl;
#define OFFSET_FIELD_TO_OFS(class_name, f_name, shift) << shift "                 "#f_name": " << ptr->class_name.f_name << std::endl
#define PLANE_OFFSET_TO_OFS(name) ofs << "MOS_PLANE_OFFSET "#name << std::endl \
                                                            OFFSET_FIELD_TO_OFS(name, iSurfaceOffset,)    \
                                                            OFFSET_FIELD_TO_OFS(name, iXOffset,)          \
                                                            OFFSET_FIELD_TO_OFS(name, iYOffset,)          \
                                                            OFFSET_FIELD_TO_OFS(name, iLockSurfaceOffset,);
#define RESOURCE_OFFSET_TO_OFS(name, shift) ofs << shift "MOS_RESOURCE_OFFSETS "#name << std::endl \
                                                                                OFFSET_FIELD_TO_OFS(name, BaseOffset, shift) \
                                                                                OFFSET_FIELD_TO_OFS(name, XOffset, shift)    \
                                                                                OFFSET_FIELD_TO_OFS(name, YOffset, shift);
MOS_STATUS MediaDebugInterface::DumpSurfaceInfo(
    PMOS_SURFACE surface,
    const char* surfaceName)
{
    MEDIA_DEBUG_FUNCTION_ENTER;

    MEDIA_DEBUG_CHK_NULL(surface);
    MEDIA_DEBUG_CHK_NULL(surfaceName);

    if (!m_configMgr->AttrIsEnabled(MediaDbgAttr::attrSurfaceInfo))
    {
        return MOS_STATUS_SUCCESS;
    }

    const char* funcName = (m_mediafunction == MEDIA_FUNCTION_VP) ? "_VP" : ((m_mediafunction == MEDIA_FUNCTION_ENCODE) ? "_ENC" : "_DEC");
    const char* filePath = CreateFileName(funcName, surfaceName, MediaDbgExtType::txt);
    std::ofstream ofs(filePath);
    PMOS_SURFACE ptr = surface;

    if (ofs.fail())
    {
        return MOS_STATUS_UNKNOWN;
    }

    ofs << "Surface name: " << surfaceName << std::endl;

    EMPTY_TO_OFS();
    ofs << "MOS_SURFACE:" << std::endl;
    FIELD_TO_OFS(dwArraySlice, );
    FIELD_TO_OFS(dwMipSlice, );
    FIELD_TO_OFS(S3dChannel, );

    EMPTY_TO_OFS();
    FIELD_TO_OFS(Type, );
    FIELD_TO_OFS(bOverlay, );
    FIELD_TO_OFS(bFlipChain, );

#if !defined(LINUX)
    EMPTY_TO_OFS();
    UNION_STRUCT_START_TO_OFS();
    UNION_STRUCT_FIELD_TO_OFS(dwFirstArraySlice);
    UNION_STRUCT_FIELD_TO_OFS(dwFirstMipSlice);
    UNION_END_TO_OFS(dwSubResourceIndex);
#endif

    EMPTY_TO_OFS();
    FIELD_TO_OFS(dwWidth, );
    FIELD_TO_OFS(dwHeight, );
    FIELD_TO_OFS(dwSize, );
    FIELD_TO_OFS(dwDepth, );
    FIELD_TO_OFS(dwArraySize, );
    FIELD_TO_OFS(dwLockPitch, );
    FIELD_TO_OFS(dwPitch, );
    FIELD_TO_OFS(dwSlicePitch, );
    FIELD_TO_OFS(dwQPitch, );
    FIELD_TO_OFS(TileType, );
    FIELD_TO_OFS(TileModeGMM, );
    FIELD_TO_OFS(bGMMTileEnabled, );
    FIELD_TO_OFS(Format, );
    FIELD_TO_OFS(bArraySpacing, );
    FIELD_TO_OFS(bCompressible, );

    EMPTY_TO_OFS();
    FIELD_TO_OFS(dwOffset, );
    PLANE_OFFSET_TO_OFS(YPlaneOffset);
    PLANE_OFFSET_TO_OFS(UPlaneOffset);
    PLANE_OFFSET_TO_OFS(VPlaneOffset);

    EMPTY_TO_OFS();
    UNION_STRUCT_START_TO_OFS();
    RESOURCE_OFFSET_TO_OFS(RenderOffset.YUV.Y, "    ");
    RESOURCE_OFFSET_TO_OFS(RenderOffset.YUV.U, "    ");
    RESOURCE_OFFSET_TO_OFS(RenderOffset.YUV.V, "    ");
    ofs << "    } YUV;" << std::endl;
    RESOURCE_OFFSET_TO_OFS(RenderOffset.RGB, );
    ofs << "}" << std::endl;

    EMPTY_TO_OFS();
    UNION_STRUCT_START_TO_OFS();
    UNION_STRUCT_FIELD_TO_OFS(LockOffset.YUV.Y);
    UNION_STRUCT_FIELD_TO_OFS(LockOffset.YUV.U);
    UNION_STRUCT_FIELD_TO_OFS(LockOffset.YUV.V);
    UNION_END_TO_OFS(LockOffset.RGB);

    EMPTY_TO_OFS();
    FIELD_TO_OFS(bIsCompressed, );
    FIELD_TO_OFS(CompressionMode, );
    FIELD_TO_OFS(CompressionFormat, );
    FIELD_TO_OFS(YoffsetForUplane, );
    FIELD_TO_OFS(YoffsetForVplane, );

    EMPTY_TO_OFS();
    EMPTY_TO_OFS();
    MOS_STATUS sts = DumpMosSpecificResourceInfoToOfs(&surface->OsResource, ofs);
    ofs.close();

    return sts;
}

#define FIELD_TO_OFS_8SHIFT(name) FIELD_TO_OFS(name, "        ")
MOS_STATUS MediaDebugInterface::DumpMosSpecificResourceInfoToOfs(
    PMOS_RESOURCE pOsResource,
    std::ofstream& ofs)
{
    MEDIA_DEBUG_FUNCTION_ENTER;
    if (Mos_ResourceIsNull(pOsResource))
    {
        MEDIA_DEBUG_ASSERTMESSAGE("pOsResource is null");
        return MOS_STATUS_INVALID_PARAMETER;
    }

    PMOS_RESOURCE ptr = pOsResource;
    ofs << "MOS_RESOURCE:" << std::endl;
#if !defined(LINUX)
    FIELD_TO_OFS(RunTimeHandle, );
#else
    FIELD_TO_OFS(iWidth, );
    FIELD_TO_OFS(iHeight, );
    FIELD_TO_OFS(iSize, );
    FIELD_TO_OFS(iPitch, );
    FIELD_TO_OFS(iDepth, );
    FIELD_TO_OFS(Format, );
    FIELD_TO_OFS(iCount, );
    FIELD_TO_OFS(dwGfxAddress, );
    FIELD_TO_OFS(isTiled, );
    FIELD_TO_OFS(TileType, );
    FIELD_TO_OFS(bMapped, );

    EMPTY_TO_OFS();
    FIELD_TO_OFS(bo->size, );
    FIELD_TO_OFS(bo->align, );
    FIELD_TO_OFS(bo->offset, );
    FIELD_TO_OFS(bo->handle, );
    FIELD_TO_OFS(bo->offset64, );
    FIELD_TO_OFS(bo->aux_mapped, );
#endif
    ofs << "iAllocationIndex[MOS_GPU_CONTEXT_MAX == " << (uint32_t)MOS_GPU_CONTEXT_MAX << "]: {";
    for (int i = 0; i < MOS_GPU_CONTEXT_MAX; ++i)
        ofs << ptr->iAllocationIndex[i] << ", ";
    ofs << "}" << std::endl;

    {
        PGMM_RESOURCE_INFO ptr = pOsResource->pGmmResInfo;
        EMPTY_TO_OFS();
        ofs << "GMM_RESOURCE_INFO:" << std::endl;
        FIELD_TO_OFS(GetResourceType(), );
        FIELD_TO_OFS(GetResourceFormat(), );
        FIELD_TO_OFS(GetBitsPerPixel(), );

        {
            GMM_RESOURCE_FLAG flags = pOsResource->pGmmResInfo->GetResFlags();
            GMM_RESOURCE_FLAG* ptr = &flags;

            EMPTY_TO_OFS();
            ofs << "    GMM_RESOURCE_FLAG:" << std::endl;
            FIELD_TO_OFS_8SHIFT(Gpu.CameraCapture);
            FIELD_TO_OFS_8SHIFT(Gpu.CCS);
            FIELD_TO_OFS_8SHIFT(Gpu.ColorDiscard);
            FIELD_TO_OFS_8SHIFT(Gpu.ColorSeparation);
            FIELD_TO_OFS_8SHIFT(Gpu.ColorSeparationRGBX);
            FIELD_TO_OFS_8SHIFT(Gpu.Constant);
            FIELD_TO_OFS_8SHIFT(Gpu.Depth);
            FIELD_TO_OFS_8SHIFT(Gpu.FlipChain);
            FIELD_TO_OFS_8SHIFT(Gpu.FlipChainPreferred);
            FIELD_TO_OFS_8SHIFT(Gpu.HistoryBuffer);
            FIELD_TO_OFS_8SHIFT(Gpu.HiZ);
            FIELD_TO_OFS_8SHIFT(Gpu.Index);
            FIELD_TO_OFS_8SHIFT(Gpu.IndirectClearColor);
            FIELD_TO_OFS_8SHIFT(Gpu.InstructionFlat);
            FIELD_TO_OFS_8SHIFT(Gpu.InterlacedScan);
            FIELD_TO_OFS_8SHIFT(Gpu.MCS);
            FIELD_TO_OFS_8SHIFT(Gpu.MMC);
            FIELD_TO_OFS_8SHIFT(Gpu.MotionComp);
            FIELD_TO_OFS_8SHIFT(Gpu.NoRestriction);
            FIELD_TO_OFS_8SHIFT(Gpu.Overlay);
            FIELD_TO_OFS_8SHIFT(Gpu.Presentable);
            FIELD_TO_OFS_8SHIFT(Gpu.ProceduralTexture);
            FIELD_TO_OFS_8SHIFT(Gpu.Query);
            FIELD_TO_OFS_8SHIFT(Gpu.RenderTarget);
            FIELD_TO_OFS_8SHIFT(Gpu.S3d);
            FIELD_TO_OFS_8SHIFT(Gpu.S3dDx);
            FIELD_TO_OFS_8SHIFT(Gpu.__S3dNonPacked);
            FIELD_TO_OFS_8SHIFT(Gpu.ScratchFlat);
            FIELD_TO_OFS_8SHIFT(Gpu.SeparateStencil);
            FIELD_TO_OFS_8SHIFT(Gpu.State);
            FIELD_TO_OFS_8SHIFT(Gpu.Stream);
            FIELD_TO_OFS_8SHIFT(Gpu.TextApi);
            FIELD_TO_OFS_8SHIFT(Gpu.Texture);
            FIELD_TO_OFS_8SHIFT(Gpu.TiledResource);
            FIELD_TO_OFS_8SHIFT(Gpu.TilePool);
            FIELD_TO_OFS_8SHIFT(Gpu.UnifiedAuxSurface);
            FIELD_TO_OFS_8SHIFT(Gpu.Vertex);
            FIELD_TO_OFS_8SHIFT(Gpu.Video);
            FIELD_TO_OFS_8SHIFT(Gpu.__NonMsaaTileXCcs);
            FIELD_TO_OFS_8SHIFT(Gpu.__NonMsaaTileYCcs);
            FIELD_TO_OFS_8SHIFT(Gpu.__MsaaTileMcs);
            FIELD_TO_OFS_8SHIFT(Gpu.__NonMsaaLinearCCS);
            FIELD_TO_OFS_8SHIFT(Gpu.__Remaining);

            EMPTY_TO_OFS();
            FIELD_TO_OFS_8SHIFT(Info.AllowVirtualPadding);
            FIELD_TO_OFS_8SHIFT(Info.BigPage);
            FIELD_TO_OFS_8SHIFT(Info.Cacheable);
            FIELD_TO_OFS_8SHIFT(Info.ContigPhysMemoryForiDART);
            FIELD_TO_OFS_8SHIFT(Info.CornerTexelMode);
            FIELD_TO_OFS_8SHIFT(Info.ExistingSysMem);
            FIELD_TO_OFS_8SHIFT(Info.ForceResidency);
            FIELD_TO_OFS_8SHIFT(Info.Gfdt);
            FIELD_TO_OFS_8SHIFT(Info.GttMapType);
            FIELD_TO_OFS_8SHIFT(Info.HardwareProtected);
            FIELD_TO_OFS_8SHIFT(Info.KernelModeMapped);
            FIELD_TO_OFS_8SHIFT(Info.LayoutBelow);
            FIELD_TO_OFS_8SHIFT(Info.LayoutMono);
            FIELD_TO_OFS_8SHIFT(Info.LayoutRight);
            FIELD_TO_OFS_8SHIFT(Info.LocalOnly);
            FIELD_TO_OFS_8SHIFT(Info.Linear);
            FIELD_TO_OFS_8SHIFT(Info.MediaCompressed);
            FIELD_TO_OFS_8SHIFT(Info.NoOptimizationPadding);
            FIELD_TO_OFS_8SHIFT(Info.NoPhysMemory);
            FIELD_TO_OFS_8SHIFT(Info.NotLockable);
            FIELD_TO_OFS_8SHIFT(Info.NonLocalOnly);
            FIELD_TO_OFS_8SHIFT(Info.StdSwizzle);
            FIELD_TO_OFS_8SHIFT(Info.PseudoStdSwizzle);
            FIELD_TO_OFS_8SHIFT(Info.Undefined64KBSwizzle);
            FIELD_TO_OFS_8SHIFT(Info.RedecribedPlanes);
            FIELD_TO_OFS_8SHIFT(Info.RenderCompressed);
            FIELD_TO_OFS_8SHIFT(Info.Rotated);
            FIELD_TO_OFS_8SHIFT(Info.Shared);
            FIELD_TO_OFS_8SHIFT(Info.SoftwareProtected);
            FIELD_TO_OFS_8SHIFT(Info.SVM);
#if !defined(LINUX)
            FIELD_TO_OFS_8SHIFT(Info.Tile4);
            FIELD_TO_OFS_8SHIFT(Info.Tile64);
#endif
            FIELD_TO_OFS_8SHIFT(Info.TiledW);
            FIELD_TO_OFS_8SHIFT(Info.TiledX);
            FIELD_TO_OFS_8SHIFT(Info.TiledY);
            FIELD_TO_OFS_8SHIFT(Info.TiledYf);
            FIELD_TO_OFS_8SHIFT(Info.TiledYs);
            FIELD_TO_OFS_8SHIFT(Info.XAdapter);
            FIELD_TO_OFS_8SHIFT(Info.__PreallocatedResInfo);
#if !defined(LINUX)
            FIELD_TO_OFS_8SHIFT(Info.LMemBarPreferred);
            FIELD_TO_OFS_8SHIFT(Info.LMemBarOrNonlocalOnly);
            FIELD_TO_OFS_8SHIFT(Info.LMemBarIndifferent);
            FIELD_TO_OFS_8SHIFT(Info.CpuVisibleOnDemand);
            FIELD_TO_OFS_8SHIFT(Info.DwmFbrResource);
#endif

            EMPTY_TO_OFS();
            FIELD_TO_OFS_8SHIFT(Wa.GTMfx2ndLevelBatchRingSizeAlign);
            FIELD_TO_OFS_8SHIFT(Wa.ILKNeedAvcMprRowStore32KAlign);
            FIELD_TO_OFS_8SHIFT(Wa.ILKNeedAvcDmvBuffer32KAlign);
            FIELD_TO_OFS_8SHIFT(Wa.NoBufferSamplerPadding);
            FIELD_TO_OFS_8SHIFT(Wa.NoLegacyPlanarLinearVideoRestrictions);
            FIELD_TO_OFS_8SHIFT(Wa.CHVAstcSkipVirtualMips);
            FIELD_TO_OFS_8SHIFT(Wa.DisablePackedMipTail);
            FIELD_TO_OFS_8SHIFT(Wa.__ForceOtherHVALIGN4);
            FIELD_TO_OFS_8SHIFT(Wa.DisableDisplayCcsClearColor);
            FIELD_TO_OFS_8SHIFT(Wa.DisableDisplayCcsCompression);
            FIELD_TO_OFS_8SHIFT(Wa.PreGen12FastClearOnly);
#if !defined(LINUX)
            FIELD_TO_OFS_8SHIFT(Wa.ForceStdAllocAlign);
#endif
        }

        FIELD_TO_OFS(GetBaseWidth(), );
        FIELD_TO_OFS(GetBaseHeight(), );
        FIELD_TO_OFS(GetBaseDepth(), );
        FIELD_TO_OFS(GetMaxLod(), );
        FIELD_TO_OFS(GetArraySize(), );
        FIELD_TO_OFS(GetSetCpSurfTag(0, 0), );
        FIELD_TO_OFS(GetCachePolicyUsage(), );
        FIELD_TO_OFS(GetNumSamples(), );
        FIELD_TO_OFS(GetSamplePattern(), );

        EMPTY_TO_OFS();
        FIELD_TO_OFS(IsArraySpacingSingleLod(), );
        FIELD_TO_OFS(GetBaseAlignment(), );
        FIELD_TO_OFS(GetHAlign(), );
        FIELD_TO_OFS(GetVAlign(), );
        FIELD_TO_OFS(GetMipTailStartLodSurfaceState(), );
        FIELD_TO_OFS(GetQPitch(), );

        EMPTY_TO_OFS();
        ofs << "MmcMode[GMM_MAX_MMC_INDEX == " << GMM_MAX_MMC_INDEX << "]: {";
        for (uint32_t i = 0; i < GMM_MAX_MMC_INDEX; ++i)
            ofs << (uint32_t)ptr->GetMmcMode(i) << ", ";
        ofs << "}" << std::endl;

        ofs << "MmcHint[GMM_MAX_MMC_INDEX == " << GMM_MAX_MMC_INDEX << "]: {";
        for (uint32_t i = 0; i < GMM_MAX_MMC_INDEX; ++i)
            ofs << (uint32_t)ptr->GetMmcHint(i) << ", ";
        ofs << "}" << std::endl;

        FIELD_TO_OFS(GetRenderPitch(), );
        FIELD_TO_OFS(GetSizeMainSurface(), );
        FIELD_TO_OFS(GmmGetTileMode(), );

#if !defined(LINUX)
        EMPTY_TO_OFS();
        FIELD_TO_OFS(GetMultiTileArch().Enable, );
#endif
    }

    return MOS_STATUS_SUCCESS;
}
#undef FIELD_TO_OFS_8SHIFT
#undef RESOURCE_OFFSET_TO_OFS
#undef PLANE_OFFSET_TO_OFS
#undef OFFSET_FIELD_TO_OFS
#undef UNION_END_TO_OFS
#undef UNION_STRUCT_FIELD_TO_OFS
#undef UNION_STRUCT_START_TO_OFS
#undef EMPTY_TO_OFS
#undef FIELD_TO_OFS

MOS_STATUS MediaDebugInterface::DumpBltOutput(
    PMOS_SURFACE surface,
    const char * attrName)
{
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::DeleteCfgLinkNode(uint32_t frameIdx)
{
    return m_configMgr->DeleteCfgNode(frameIdx);
}

MOS_STATUS MediaDebugInterface::ReAllocateSurface(
    PMOS_SURFACE    pSurface,
    PMOS_SURFACE    pSrcSurf,
    PCCHAR          pSurfaceName,
    MOS_GFXRES_TYPE DefaultResType)
{
    MOS_ALLOC_GFXRES_PARAMS AllocParams;

    MEDIA_DEBUG_ASSERT(m_osInterface);
    MEDIA_DEBUG_ASSERT(&pSurface->OsResource);

    // bCompressible should be compared with bCompressible since it is inited by bCompressible in previous call
    // TileType of surface should be compared since we need to reallocate surface if TileType changes
    if (!Mos_ResourceIsNull(&pSurface->OsResource) &&
        (pSurface->dwWidth == pSrcSurf->dwWidth) &&
        (pSurface->dwHeight == pSrcSurf->dwHeight) &&
        (pSurface->Format == pSrcSurf->Format) &&
        (pSurface->bCompressible == pSrcSurf->bCompressible) &&
        (pSurface->CompressionMode == pSrcSurf->CompressionMode) &&
        (pSurface->TileType == pSrcSurf->TileType))
    {
        return MOS_STATUS_SUCCESS;
    }
    MOS_ZeroMemory(&AllocParams, sizeof(MOS_ALLOC_GFXRES_PARAMS));

#if !EMUL
    //  Need to reallocate surface according to expected tiletype instead of tiletype of the surface what we have
    if ((pSurface->OsResource.pGmmResInfo != nullptr) &&
        (pSurface->TileType == pSrcSurf->TileType))
    {
        // Reallocate but use same tile type and resource type as current
        AllocParams.TileType = pSurface->OsResource.TileType;
        AllocParams.Type     = DefaultResType;
    }
    else
#endif
    {
        // First time allocation. Caller must specify default params
        AllocParams.TileType = pSrcSurf->TileType;
        AllocParams.Type     = DefaultResType;
    }

    AllocParams.dwWidth         = pSrcSurf->dwWidth;
    AllocParams.dwHeight        = pSrcSurf->dwHeight;
    AllocParams.Format          = pSrcSurf->Format;
    AllocParams.bIsCompressible = pSrcSurf->bCompressible;
    AllocParams.CompressionMode = pSrcSurf->CompressionMode;
    AllocParams.pBufName        = pSurfaceName;
    AllocParams.dwArraySize     = 1;

    // Delete resource if already allocated
    m_osInterface->pfnFreeResource(m_osInterface, &(pSurface->OsResource));

    // Allocate surface
    CODECHAL_PUBLIC_CHK_STATUS_RETURN(m_osInterface->pfnAllocateResource(
        m_osInterface,
        &AllocParams,
        &pSurface->OsResource));

    pSurface->dwWidth         = pSrcSurf->dwWidth;
    pSurface->dwHeight        = pSrcSurf->dwHeight;
    pSurface->dwPitch         = pSrcSurf->dwPitch;
    pSurface->dwDepth         = pSrcSurf->dwDepth;
    pSurface->dwQPitch        = pSrcSurf->dwQPitch;
    pSurface->bArraySpacing   = pSrcSurf->bArraySpacing;
    pSurface->bCompressible   = pSrcSurf->bCompressible;
    pSurface->CompressionMode = pSrcSurf->CompressionMode;
    pSurface->bIsCompressed   = pSrcSurf->bIsCompressed;

    if (!m_osInterface->apoMosEnabled)
    {
        MOS_SURFACE details;
        MOS_ZeroMemory(&details, sizeof(details));
        details.Format = Format_Invalid;

        MEDIA_DEBUG_CHK_STATUS(m_osInterface->pfnGetResourceInfo(m_osInterface, &pSurface->OsResource, &details));

        pSurface->Format                      = details.Format;
        pSurface->TileType                    = details.TileType;
        pSurface->dwOffset                    = details.RenderOffset.YUV.Y.BaseOffset;
        pSurface->YPlaneOffset.iSurfaceOffset = details.RenderOffset.YUV.Y.BaseOffset;
        pSurface->YPlaneOffset.iXOffset       = details.RenderOffset.YUV.Y.XOffset;
        pSurface->YPlaneOffset.iYOffset =
            (pSurface->YPlaneOffset.iSurfaceOffset - pSurface->dwOffset) / pSurface->dwPitch +
            details.RenderOffset.YUV.Y.YOffset;
        pSurface->UPlaneOffset.iSurfaceOffset = details.RenderOffset.YUV.U.BaseOffset;
        pSurface->UPlaneOffset.iXOffset       = details.RenderOffset.YUV.U.XOffset;
        pSurface->UPlaneOffset.iYOffset =
            (pSurface->UPlaneOffset.iSurfaceOffset - pSurface->dwOffset) / pSurface->dwPitch +
            details.RenderOffset.YUV.U.YOffset;
        pSurface->UPlaneOffset.iLockSurfaceOffset = details.LockOffset.YUV.U;
        pSurface->VPlaneOffset.iSurfaceOffset     = details.RenderOffset.YUV.V.BaseOffset;
        pSurface->VPlaneOffset.iXOffset           = details.RenderOffset.YUV.V.XOffset;
        pSurface->VPlaneOffset.iYOffset =
            (pSurface->VPlaneOffset.iSurfaceOffset - pSurface->dwOffset) / pSurface->dwPitch +
            details.RenderOffset.YUV.V.YOffset;
        pSurface->VPlaneOffset.iLockSurfaceOffset = details.LockOffset.YUV.V;
    }
    else
    {
        pSurface->Format = Format_Invalid;
        MEDIA_DEBUG_CHK_STATUS(m_osInterface->pfnGetResourceInfo(m_osInterface, &pSurface->OsResource, pSurface));
    }

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::CopySurfaceData_Vdbox(
    uint32_t      dwDataSize,
    PMOS_RESOURCE presSourceSurface,
    PMOS_RESOURCE presCopiedSurface)
{
    MOS_COMMAND_BUFFER        CmdBuffer;
    MHW_MI_FLUSH_DW_PARAMS    FlushDwParams;
    MHW_GENERIC_PROLOG_PARAMS genericPrologParams;
    MHW_CP_COPY_PARAMS        cpCopyParams;
    MOS_NULL_RENDERING_FLAGS  NullRenderingFlags;
    MOS_GPU_CONTEXT           orgGpuContext;

    if (!m_vdboxContextCreated)
    {
        MOS_GPUCTX_CREATOPTIONS createOption;

        MEDIA_DEBUG_CHK_STATUS(m_osInterface->pfnCreateGpuContext(
            m_osInterface,
            MOS_GPU_CONTEXT_VIDEO,
            MOS_GPU_NODE_VIDEO,
            &createOption));

        // Register VDbox GPU context with the Batch Buffer completion event
        MEDIA_DEBUG_CHK_STATUS(m_osInterface->pfnRegisterBBCompleteNotifyEvent(
            m_osInterface,
            MOS_GPU_CONTEXT_VIDEO));

        m_vdboxContextCreated = true;
    }

    MEDIA_DEBUG_CHK_NULL(m_cpInterface);
    MEDIA_DEBUG_CHK_NULL(m_osInterface);
    MEDIA_DEBUG_CHK_NULL(m_miInterface);
    MEDIA_DEBUG_CHK_NULL(m_osInterface->pfnGetWaTable(m_osInterface));

    orgGpuContext = m_osInterface->CurrentGpuContextOrdinal;

    // Due to VDBOX cryto copy limitation, the size must be Cache line aligned
    if (!MOS_IS_ALIGNED(dwDataSize, MHW_CACHELINE_SIZE))
    {
        MEDIA_DEBUG_ASSERTMESSAGE("Size is not CACHE line aligned, cannot use VDBOX to copy.");
        return MOS_STATUS_INVALID_PARAMETER;
    }

    MEDIA_DEBUG_CHK_STATUS(m_osInterface->pfnSetGpuContext(m_osInterface, MOS_GPU_CONTEXT_VIDEO));
    m_osInterface->pfnResetOsStates(m_osInterface);

    // Register the target resource
    MEDIA_DEBUG_CHK_STATUS(m_osInterface->pfnRegisterResource(
        m_osInterface,
        presCopiedSurface,
        true,
        true));

    // Register the source resource
    MEDIA_DEBUG_CHK_STATUS(m_osInterface->pfnRegisterResource(
        m_osInterface,
        presSourceSurface,
        false,
        true));

    MEDIA_DEBUG_CHK_STATUS(m_osInterface->pfnGetCommandBuffer(m_osInterface, &CmdBuffer, 0));

    MOS_ZeroMemory(&genericPrologParams, sizeof(genericPrologParams));
    genericPrologParams.pOsInterface  = m_osInterface;
    genericPrologParams.pvMiInterface = m_miInterface;
    genericPrologParams.bMmcEnabled   = false;
    MEDIA_DEBUG_CHK_STATUS(Mhw_SendGenericPrologCmd(&CmdBuffer, &genericPrologParams));

    MOS_ZeroMemory(&cpCopyParams, sizeof(cpCopyParams));
    cpCopyParams.size          = dwDataSize;
    cpCopyParams.presSrc       = presSourceSurface;
    cpCopyParams.presDst       = presCopiedSurface;
    cpCopyParams.isEncodeInUse = false;

    MEDIA_DEBUG_CHK_STATUS(m_cpInterface->SetCpCopy(m_osInterface, &CmdBuffer, &cpCopyParams));

    // MI_FLUSH
    MOS_ZeroMemory(&FlushDwParams, sizeof(FlushDwParams));
    MEDIA_DEBUG_CHK_STATUS(m_miInterface->AddMiFlushDwCmd(
        &CmdBuffer,
        &FlushDwParams));

    MEDIA_DEBUG_CHK_STATUS(m_miInterface->AddMiBatchBufferEnd(
        &CmdBuffer,
        nullptr));

    m_osInterface->pfnReturnCommandBuffer(m_osInterface, &CmdBuffer, 0);

    NullRenderingFlags = m_osInterface->pfnGetNullHWRenderFlags(m_osInterface);

    MEDIA_DEBUG_CHK_STATUS(m_osInterface->pfnSubmitCommandBuffer(
        m_osInterface,
        &CmdBuffer,
        NullRenderingFlags.CtxVideo || NullRenderingFlags.CodecGlobal || NullRenderingFlags.CtxVideo || NullRenderingFlags.VPGobal));

    MEDIA_DEBUG_CHK_STATUS(m_osInterface->pfnSetGpuContext(m_osInterface, orgGpuContext));
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::DumpNotSwizzled(
    std::string  surfName,
    MOS_SURFACE &surf,
    uint8_t *    lockedAddr,
    int32_t      size)
{
    const char *funcName = (m_mediafunction == MEDIA_FUNCTION_VP) ? "_VP" : ((m_mediafunction == MEDIA_FUNCTION_ENCODE) ? "_ENC" : "_DEC");
    int         YOffset  = surf.dwOffset + surf.YPlaneOffset.iYOffset * surf.dwPitch;

#ifdef LINUX
    int UOffset = surf.UPlaneOffset.iSurfaceOffset;
    int VOffset = surf.VPlaneOffset.iSurfaceOffset;
#else
    int UOffset = surf.UPlaneOffset.iLockSurfaceOffset;
    int VOffset = surf.VPlaneOffset.iLockSurfaceOffset;
#endif

    std::string bufName = std::string(surfName) + "NotSwizzled_format[" + std::to_string((int)surf.Format) + "]_w[" + std::to_string(surf.dwWidth) + "]_h[" + std::to_string(surf.dwHeight) + "]_p[" + std::to_string(surf.dwPitch) + "]_srcTiling[" + std::to_string((int)surf.TileType) + "]_sizeMain[" + std::to_string(size) + "]_YOffset[" + std::to_string(YOffset) + "]_UOffset[" + std::to_string(UOffset) + "]_VOffset[" + std::to_string(VOffset) + "]";

    const char *  filePath = CreateFileName(funcName, bufName.c_str(), MediaDbgExtType::yuv);
    std::ofstream ofs(filePath, std::ios_base::out | std::ios_base::binary);
    if (ofs.fail())
    {
        return MOS_STATUS_UNKNOWN;
    }

    uint8_t *data = lockedAddr;
    ofs.write((char *)data, size);
    ofs.close();

    m_osInterface->pfnUnlockResource(m_osInterface, &surf.OsResource);
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::Dump2DBufferInBinary(
    uint8_t *data,
    uint32_t width,
    uint32_t height,
    uint32_t pitch)
{
    MEDIA_DEBUG_CHK_NULL(data);

    const char *filePath = m_outputFileName.c_str();

    if (width == 0 || height == 0 || pitch == 0)
    {
        return MOS_STATUS_UNKNOWN;
    }

    std::ofstream ofs(filePath, std::ios_base::out | std::ios_base::binary);
    if (ofs.fail())
    {
        return MOS_STATUS_UNKNOWN;
    }

    for (uint32_t h = 0; h < height; h++)
    {
        ofs.write((char *)data, width);
        data += pitch;
    }

    ofs.close();
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::DumpBufferInBinary(uint8_t *data, uint32_t size)
{
    MEDIA_DEBUG_CHK_NULL(data);

    const char *filePath = m_outputFileName.c_str();

    if (size == 0)
    {
        return MOS_STATUS_UNKNOWN;
    }

    std::ofstream ofs(filePath, std::ios_base::out | std::ios_base::binary);
    if (ofs.fail())
    {
        return MOS_STATUS_UNKNOWN;
    }

    ofs.write((char *)data, size);
    ofs.close();
    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::DumpBufferInHexDwords(uint8_t *data, uint32_t size)
{
    MEDIA_DEBUG_CHK_NULL(data);

    const char *filePath = m_outputFileName.c_str();

    if (size == 0)
    {
        return MOS_STATUS_UNKNOWN;
    }

    std::ofstream ofs(filePath);

    if (ofs.fail())
    {
        return MOS_STATUS_UNKNOWN;
    }

    uint32_t dwordSize  = size / sizeof(uint32_t);
    uint32_t remainSize = size % sizeof(uint32_t);

    uint32_t *dwordData = (uint32_t *)data;
    uint32_t  i;
    for (i = 0; i < dwordSize; i++)
    {
        ofs << std::hex << std::setw(8) << std::setfill('0') << +dwordData[i] << " ";
        if (i % 4 == 3)
        {
            ofs << std::endl;
        }
    }

    if (remainSize > 0)
    {
        uint32_t lastWord = dwordData[i] & (0xFFFFFFFF << ((8 - remainSize * 2) * 4));
        ofs << std::hex << std::setw(8) << std::setfill('0') << +lastWord << std::endl;
    }

    ofs.close();

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaDebugInterface::InitCRCTable(uint32_t crcTable[256])
{
    uint32_t polynomial = 0xEDB88320;
    for (uint32_t i = 0; i < 256; i++)
    {
        uint32_t c = i;
        for (size_t j = 0; j < 8; j++)
        {
            if (c & 1)
            {
                c = polynomial ^ (c >> 1);
            }
            else
            {
                c >>= 1;
            }
        }
        crcTable[i] = c;
    }
    return MOS_STATUS_SUCCESS;
}

uint32_t MediaDebugInterface::CalculateCRC(const void *buf, size_t len, uint32_t initial)
{
    uint32_t       c = initial ^ 0xFFFFFFFF;
    const uint8_t *u = static_cast<const uint8_t *>(buf);
    for (size_t i = 0; i < len; ++i)
    {
        c = m_crcTable[(c ^ u[i]) & 0xFF] ^ (c >> 8);
    }
    return c ^ 0xFFFFFFFF;
}

#endif  // USE_MEDIA_DEBUG_TOOL
