/*
* Copyright (c) 2018, 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_cmd_task.cpp
//! \brief    Defines the interface for media cmd task
//! \details  The media cmd task is dedicated for command buffer submission
//!
#include <stdint.h>
#include <memory>
#include <vector>
#include "media_scalability.h"
#include "media_scalability_defs.h"
#include "mos_utilities.h"
#include "media_cmd_task.h"
#include "media_packet.h"
#include "media_utils.h"

CmdTask::CmdTask(PMOS_INTERFACE osInterface)
    : m_osInterface(osInterface)
{

}

MOS_STATUS CmdTask::CalculateCmdBufferSizeFromActivePackets()
{
    uint32_t curCommandBufferSize      = 0;
    uint32_t curRequestedPatchListSize = 0;

    m_cmdBufSize    = 0;
    m_patchListSize = 0;
    for (auto prop : m_packets)
    {
        // Calculate total size based on pipe 0
        if (prop.stateProperty.currentPipe == 0)
        {
            auto packetId = prop.packetId;
            auto packet   = prop.packet;

            MEDIA_CHK_NULL_RETURN(packet);

            curCommandBufferSize      = 0;
            curRequestedPatchListSize = 0;

            packet->CalculateCommandSize(curCommandBufferSize, curRequestedPatchListSize);
            m_cmdBufSize += curCommandBufferSize;
            m_patchListSize += curRequestedPatchListSize;
        }
    }

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS CmdTask::SubmitToLevelzeroRuntime()
{
    for (auto &prop : m_packets)
    {
        auto    packet      = prop.packet;
        uint8_t packetPhase = MediaPacket::otherPacket;
        MEDIA_CHK_NULL_RETURN(packet);

        MEDIA_CHK_STATUS_RETURN(packet->Prepare());
        MEDIA_CHK_STATUS_RETURN(packet->Submit(nullptr, packetPhase));
    }

#if (_DEBUG || _RELEASE_INTERNAL)
    for (auto prop : m_packets)
    {
        auto packet = prop.packet;
        MEDIA_CHK_NULL_RETURN(packet);
        MEDIA_CHK_STATUS_RETURN(packet->DumpOutput());
    }
#endif  // _DEBUG || _RELEASE_INTERNAL

    // clear the packet lists since all commands are submitted
    m_packets.clear();

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS CmdTask::Submit(bool immediateSubmit, MediaScalability *scalability, CodechalDebugInterface *debugInterface, bool levelzeroRuntimeInUse)
{
    if (levelzeroRuntimeInUse)
    {
        return SubmitToLevelzeroRuntime();
    }
    else
    {
        return Submit(immediateSubmit, scalability, debugInterface);
    }
}

MOS_STATUS CmdTask::Submit(bool immediateSubmit, MediaScalability *scalability, CodechalDebugInterface *debugInterface)
{
    MEDIA_CHK_NULL_RETURN(scalability);

    // Algin this variable in pipeline, packet and scalability.
    bool singleTaskPhaseSupportedInPak = false;
    MEDIA_CHK_STATUS_RETURN(CalculateCmdBufferSizeFromActivePackets());

    // prepare cmd buffer
    MOS_COMMAND_BUFFER cmdBuffer;
    // initialize the command buffer struct
    MOS_ZeroMemory(&cmdBuffer, sizeof(MOS_COMMAND_BUFFER));

    if (m_packets.size() > 0)
    {
        MEDIA_CHK_STATUS_RETURN(scalability->UpdateState(&m_packets[0].stateProperty));

        // VerifyCmdBuffer could be called for duplicated times for singleTaskPhase mult-pass cases
        // Each task submit verify only once
        MEDIA_CHK_STATUS_RETURN(scalability->VerifyCmdBuffer(m_cmdBufSize, m_patchListSize, singleTaskPhaseSupportedInPak));
    }
    else
    {
        SCALABILITY_ASSERTMESSAGE("No packets to execute in the task!");
        return MOS_STATUS_INVALID_PARAMETER;
    }

    int8_t curPipe = -1;

    for (auto& prop : m_packets)
    {
        MEDIA_CHK_STATUS_RETURN(scalability->UpdateState(&prop.stateProperty));

        auto packet = prop.packet;
        uint8_t packetPhase = MediaPacket::otherPacket;
        MEDIA_CHK_NULL_RETURN(packet);

        MEDIA_CHK_STATUS_RETURN(packet->Prepare());

        MEDIA_CHK_STATUS_RETURN(scalability->GetCmdBuffer(&cmdBuffer, prop.frameTrackingRequested));
        //Set first packet for each pipe in the first pass, used for prolog & forcewakeup insertion
        bool isFirstPacket = scalability->GetCurrentPass() == 0 && curPipe < scalability->GetCurrentPipe();
        if (isFirstPacket)
        {
            packetPhase = MediaPacket::firstPacket;
        }

        if (!prop.skipOcaBBStartInCmdTask && (isFirstPacket || !prop.stateProperty.singleTaskPhaseSupported))
        {
            scalability->Oca1stLevelBBStart(cmdBuffer);
        }

        curPipe = scalability->GetCurrentPipe();

        MEDIA_CHK_STATUS_RETURN(packet->Submit(&cmdBuffer, packetPhase));

        MEDIA_CHK_STATUS_RETURN(scalability->ReturnCmdBuffer(&cmdBuffer));
    }

#if (_DEBUG || _RELEASE_INTERNAL) && !EMUL
    MEDIA_CHK_STATUS_RETURN(DumpCmdBufferAllPipes(&cmdBuffer, debugInterface, scalability));
#endif  // _DEBUG || _RELEASE_INTERNAL

    // submit cmd buffer
    MEDIA_CHK_STATUS_RETURN(scalability->SubmitCmdBuffer(&cmdBuffer));

#if (_DEBUG || _RELEASE_INTERNAL)
    for (auto prop : m_packets)
    {
        MEDIA_CHK_STATUS_RETURN(scalability->UpdateState(&prop.stateProperty));

        auto packet = prop.packet;
        MEDIA_CHK_NULL_RETURN(packet);
        MEDIA_CHK_STATUS_RETURN(packet->DumpOutput());
    }
#endif // _DEBUG || _RELEASE_INTERNAL

    // clear the packet lists since all commands are composed into command buffer
    m_packets.clear();

    return MOS_STATUS_SUCCESS;
}

#if ((_DEBUG || _RELEASE_INTERNAL) && !EMUL)
MOS_STATUS CmdTask::DumpCmdBuffer(PMOS_COMMAND_BUFFER cmdBuffer, CodechalDebugInterface *debugInterface, uint8_t pipeIdx)
{
    MEDIA_CHK_NULL_RETURN(cmdBuffer);

    if (debugInterface)
    {
        std::string packetName = "";
        for (auto prop : m_packets)
        {
            // Construct cmd buffer dump name only from packets in pipe 0
            if (prop.stateProperty.currentPipe == 0)
            {
                packetName += prop.packet->GetPacketName();
                break;
            }
        }

        // Put pipe index to file name
        std::stringstream pipeIdxStrStream;
        pipeIdxStrStream << "_" << uint32_t(pipeIdx);
        packetName += pipeIdxStrStream.str();

        MEDIA_CHK_STATUS_RETURN(debugInterface->DumpCmdBuffer(
            cmdBuffer,
            CODECHAL_NUM_MEDIA_STATES,
            packetName.data()));
    }

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS CmdTask::DumpCmdBufferAllPipes(PMOS_COMMAND_BUFFER cmdBuffer, CodechalDebugInterface *debugInterface, MediaScalability *scalability)
{
    MEDIA_CHK_NULL_RETURN(cmdBuffer);
    MEDIA_CHK_NULL_RETURN(scalability);

    if (debugInterface)
    {
        auto pipeNum = scalability->GetPipeNumber();
        auto lastPipeIndex = scalability->GetCurrentPipe();
        for (uint8_t pipeIdx = 0; pipeIdx < pipeNum; pipeIdx++)
        {
            scalability->SetCurrentPipeIndex(pipeIdx);
            MEDIA_CHK_STATUS_RETURN(scalability->GetCmdBuffer(cmdBuffer));
            MEDIA_CHK_STATUS_RETURN(DumpCmdBuffer(cmdBuffer, debugInterface, pipeIdx));
            MEDIA_CHK_STATUS_RETURN(scalability->ReturnCmdBuffer(cmdBuffer));
        }

        // Recover scalability current pipe index after dump
        scalability->SetCurrentPipeIndex(lastPipeIndex);
    }

    return MOS_STATUS_SUCCESS;
}
#endif // _DEBUG || _RELEASE_INTERNAL
