blob: d5b60a31014e1275b9ffe6f1a1f83d0b79b949a5 [file] [log] [blame]
/*
* Copyright (c) 2009-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 vphal_mdf_wrapper.cpp
//! \brief Abstraction for MDF related operations.
//! \details It is a thin wrapper layer based on MDF APIs.
//!
#include "vphal_mdf_wrapper.h"
#include <algorithm>
#include <cstdio>
void EventManager::OnEventAvailable(CmEvent *event, const std::string &name)
{
AddEvent(name, event);
}
void EventManager::AddEvent(const std::string &name, CmEvent *event)
{
if (mEventCount >= (128 * 1024) / sizeof(CmEvent))
{
if (mReport)
{
Profiling();
}
Clear();
}
mEventMap[name].push_back(event);
mLastEvent = event;
mEventCount++;
}
void EventManager::Clear()
{
VPHAL_RENDER_CHK_NULL_NO_STATUS_RETURN(m_cmContext);
CmQueue *queue = m_cmContext->GetCmQueue();
VPHAL_RENDER_CHK_NULL_NO_STATUS_RETURN(queue);
for (auto it : mEventMap)
{
for (CmEvent *event : it.second)
{
queue->DestroyEvent(event);
}
}
mEventMap.clear();
mEventCount = 0;
mLastEvent = nullptr;
}
void EventManager::Profiling() const
{
VPHAL_RENDER_NORMALMESSAGE("------------------------%s Profiling Report------------------------\n", mOwner.c_str());
for (auto it : mEventMap)
{
int count = 0;
double totalTimeInMS = 0.0;
for (CmEvent *event : it.second)
{
uint64_t executionTimeInNS = 0;
int result = event->GetExecutionTime(executionTimeInNS);
if (result != CM_SUCCESS)
{
VPHAL_RENDER_ASSERTMESSAGE("[%s]: CM GetExecutionTime error: %d\n", it.first.c_str(), result);
continue;
}
totalTimeInMS += executionTimeInNS / 1000000.0;
count++;
}
VPHAL_RENDER_NORMALMESSAGE("[%s]: execution count %llu, average time %f ms.\n", it.first.c_str(), it.second.size(), totalTimeInMS / count);
}
VPHAL_RENDER_NORMALMESSAGE("------------------------%s Profiling Report End------------------------\n", mOwner.c_str());
}
CmEvent* EventManager::GetLastEvent() const
{
return mLastEvent;
}
CmContext::CmContext(PMOS_CONTEXT OsContext) :
mRefCount(0),
mCmDevice(nullptr),
mCmQueue(nullptr),
mCmVebox(nullptr),
mBatchTask(nullptr),
mHasBatchedTask(false),
mConditionalBatchBuffer(nullptr),
mCondParam({ 0 }),
mEventListener(nullptr)
{
VPHAL_RENDER_ASSERT(OsContext);
const unsigned int MDF_DEVICE_CREATE_OPTION =
((CM_DEVICE_CREATE_OPTION_SCRATCH_SPACE_DISABLE) |
(CM_DEVICE_CONFIG_DSH_DISABLE_MASK) |
(CM_DEVICE_CONFIG_TASK_NUM_16 << CM_DEVICE_CONFIG_TASK_NUM_OFFSET) |
(CM_DEVICE_CONFIG_MEDIA_RESET_ENABLE) |
(CM_DEVICE_CONFIG_EXTRA_TASK_NUM_4 << CM_DEVICE_CONFIG_EXTRA_TASK_NUM_OFFSET) |
(CM_DEVICE_CONFIG_GPUCONTEXT_ENABLE) |
(32 << CM_DEVICE_CONFIG_KERNELBINARYGSH_OFFSET));
int result = CreateCmDevice(OsContext, mCmDevice, MDF_DEVICE_CREATE_OPTION);
if (result != CM_SUCCESS)
{
VPHAL_RENDER_ASSERTMESSAGE("CmDevice creation error %d\n", result);
return;
}
result = mCmDevice->CreateQueue(mCmQueue);
if (result != CM_SUCCESS)
{
VPHAL_RENDER_ASSERTMESSAGE("CmQueue creation error %d\n", result);
return;
}
result = mCmDevice->CreateVebox(mCmVebox);
if (result != CM_SUCCESS)
{
VPHAL_RENDER_ASSERTMESSAGE("CmVebox creation error %d\n", result);
return;
}
#if (_DEBUG || _RELEASE_INTERNAL)
result = mCmDevice->InitPrintBuffer(32768);
if (result != CM_SUCCESS)
{
VPHAL_RENDER_ASSERTMESSAGE("Init printf error: %d\n", result);
return;
}
#endif
result = mCmDevice->CreateTask(mBatchTask);
if (result != CM_SUCCESS)
{
VPHAL_RENDER_ASSERTMESSAGE("Create batch task error: %d\n", result);
return;
}
}
CmKernel* CmContext::CloneKernel(CmKernel *kernel)
{
auto it = std::find(mAddedKernels.begin(), mAddedKernels.end(), kernel);
if (it != mAddedKernels.end())
{
CmKernel *newKernel = nullptr;
int result = mCmDevice->CloneKernel(newKernel, kernel);
if (result != CM_SUCCESS)
{
// Clone kernel failed, try to use the old one.
VPHAL_RENDER_ASSERTMESSAGE("Clone kernel failed: %d\n", result);
return kernel;
}
mKernelsToPurge.push_back(newKernel);
return newKernel;
}
else
{
return kernel;
}
}
void CmContext::BatchKernel(CmKernel *kernel, CmThreadSpace *threadSpace, bool bFence)
{
int result;
if (mConditionalBatchBuffer && mAddedKernels.empty())
{
result = mBatchTask->AddConditionalEnd(mConditionalBatchBuffer->GetCmSurfaceIndex(), 0, &mCondParam);
if (result != CM_SUCCESS)
{
VPHAL_RENDER_ASSERTMESSAGE("Batch task AddConditionalEnd error: %d\n", result);
return;
}
}
if (bFence)
{
result = mBatchTask->AddSync();
if (result != CM_SUCCESS)
{
VPHAL_RENDER_ASSERTMESSAGE("Batch task add sync error: %d\n", result);
return;
}
}
result = mBatchTask->AddKernel(kernel);
if (result == CM_EXCEED_MAX_KERNEL_PER_ENQUEUE)
{
// Reach max kernels per task, flush and try again.
bool needAddBack = false;
if (mKernelsToPurge.back() == kernel)
{
mKernelsToPurge.pop_back();
needAddBack = true;
}
FlushBatchTask(false);
BatchKernel(kernel, threadSpace, false);
if (needAddBack)
{
mKernelsToPurge.push_back(kernel);
}
return;
}
else if (result != CM_SUCCESS)
{
VPHAL_RENDER_ASSERTMESSAGE("Batch task add sync error: %d\n", result);
return;
}
mAddedKernels.push_back(kernel);
mThreadSpacesToPurge.push_back(threadSpace);
mHasBatchedTask = true;
}
void CmContext::FlushBatchTask(bool waitForFinish)
{
if (mAddedKernels.empty())
{
return;
}
EnqueueTask(mBatchTask, nullptr, "BatchTask", waitForFinish);
for(auto it : mThreadSpacesToPurge)
{
mCmDevice->DestroyThreadSpace(it);
}
for(auto it : mKernelsToPurge)
{
mCmDevice->DestroyKernel(it);
}
mThreadSpacesToPurge.clear();
mKernelsToPurge.clear();
mAddedKernels.clear();
mBatchTask->Reset();
}
void CmContext::RunSingleKernel(
CmKernel *kernel,
CmThreadSpace *threadSpace,
const std::string &name,
bool waitForFinish)
{
FlushBatchTask(false);
CmTask *task = nullptr;
int result = mCmDevice->CreateTask(task);
if (result != CM_SUCCESS)
{
VPHAL_RENDER_ASSERTMESSAGE("[%s]: CmDevice CreateTask error: %d\n", name.c_str(), result);
return;
}
if (mConditionalBatchBuffer)
{
result = task->AddConditionalEnd(mConditionalBatchBuffer->GetCmSurfaceIndex(), 0, &mCondParam);
if (result != CM_SUCCESS)
{
VPHAL_RENDER_ASSERTMESSAGE("[%s]: AddConditionalEnd error: %d\n", name.c_str(), result);
mCmDevice->DestroyTask(task);
return;
}
}
result = task->AddKernel(kernel);
if (result != CM_SUCCESS)
{
VPHAL_RENDER_ASSERTMESSAGE("[%s]: CmDevice AddKernel error: %d\n", name.c_str(), result);
mCmDevice->DestroyTask(task);
return;
}
EnqueueTask(task, threadSpace, name, waitForFinish);
}
void CmContext::EnqueueTask(CmTask *task, CmThreadSpace *threadSpace, const std::string &name, bool waitForFinish)
{
CmEvent *event = nullptr;
int result = mCmQueue->Enqueue(task, event, threadSpace);
if (result != CM_SUCCESS)
{
VPHAL_RENDER_ASSERTMESSAGE("[%s]: CmDevice enqueue error: %d\n", name.c_str(), result);
return;
}
if (waitForFinish)
{
event->WaitForTaskFinished(-1);
#if (_DEBUG || _RELEASE_INTERNAL)
result = mCmDevice->FlushPrintBuffer();
if (result != CM_SUCCESS)
{
VPHAL_RENDER_ASSERTMESSAGE("[%s]: Flush printf buffer error: %d", name.c_str(), result);
}
std::fflush(stdout);
#endif
}
if (mEventListener)
{
mEventListener->OnEventAvailable(event, name);
}
}
void CmContext::Destroy()
{
FlushBatchTask(false);
if (mBatchTask)
{
mCmDevice->DestroyTask(mBatchTask);
}
if (mCmVebox)
{
mCmDevice->DestroyVebox(mCmVebox);
}
if (mCmDevice)
{
DestroyCmDevice(mCmDevice);
}
mBatchTask = nullptr;
mCmVebox = nullptr;
mCmDevice = nullptr;
}
CmContext::~CmContext()
{
Destroy();
}
VPCmRenderer::VPCmRenderer(const std::string &name, CmContext *cmContext) :
mName(name),
m_cmContext(cmContext),
mBatchDispatch(true),
mBlockingMode(false),
mEnableDump(false)
{
}
VPCmRenderer::~VPCmRenderer()
{
}
CmProgram* VPCmRenderer::LoadProgram(const std::string& binaryFileName)
{
std::ifstream isa(binaryFileName, std::ifstream::ate | std::ifstream::binary);
if (!isa.is_open())
{
VPHAL_RENDER_ASSERTMESSAGE("Error in opening ISA file: %s.\n", binaryFileName.c_str());
return nullptr;
}
int size = static_cast<int>(isa.tellg());
if (size == 0)
{
VPHAL_RENDER_ASSERTMESSAGE("Code size is 0.\n");
return nullptr;
}
isa.seekg(0, isa.beg);
std::vector<char> code(size);
isa.read(code.data(), size);
CmProgram *program = nullptr;
if (!m_cmContext)
{
return nullptr;
}
CmDevice *dev = m_cmContext->GetCmDevice();
if (!dev)
{
return nullptr;
}
int result = dev->LoadProgram(code.data(), size, program, "-nojitter");
if (result != CM_SUCCESS)
{
VPHAL_RENDER_ASSERTMESSAGE("[%s]: CM LoadProgram error %d\n", mName.c_str(), result);
return nullptr;
}
return program;
}
CmProgram* VPCmRenderer::LoadProgram(const void *binary, int size)
{
if (!binary || size == 0)
{
VPHAL_RENDER_ASSERTMESSAGE("Invalid program to load.\n");
return nullptr;
}
CmProgram *program = nullptr;
if (!m_cmContext)
{
return nullptr;
}
CmDevice *dev = m_cmContext->GetCmDevice();
if (!dev)
{
return nullptr;
}
int result = dev->LoadProgram(const_cast<void *>(binary), size, program, "-nojitter");
if (result != CM_SUCCESS)
{
VPHAL_RENDER_ASSERTMESSAGE("[%s]: CM LoadProgram error %d\n", mName.c_str(), result);
return nullptr;
}
return program;
}
void VPCmRenderer::Render(void *payload)
{
AttachPayload(payload);
std::string kernelName;
CmKernel *kernel = GetKernelToRun(kernelName);
if (!kernel)
{
VPHAL_RENDER_ASSERTMESSAGE("[%s]: Did not find proper kernel to run\n", mName.c_str());
return;
}
int tsWidth, tsHeight, tsColor;
GetThreadSpaceDimension(tsWidth, tsHeight, tsColor);
if (!tsWidth || !tsHeight || !tsColor)
{
VPHAL_RENDER_ASSERTMESSAGE("[%s]: Degenerate thread space, return immediately.\n", mName.c_str());
return;
}
VPHAL_RENDER_CHK_NULL_NO_STATUS_RETURN(m_cmContext);
CmThreadSpace *threadSpace = nullptr;
CmDevice *dev = m_cmContext->GetCmDevice();
VPHAL_RENDER_CHK_NULL_NO_STATUS_RETURN(dev);
int result = dev->CreateThreadSpace(tsWidth, tsHeight, threadSpace);
if (result != CM_SUCCESS)
{
VPHAL_RENDER_ASSERTMESSAGE("[%s]: CM Create ThreadSpace error: %d\n", mName.c_str(), result);
return;
}
SetupThreadSpace(threadSpace, tsWidth, tsHeight, tsColor);
// We need to use CloneKernel API to add the same kernel multiple times into one task.
bool bBatch = mBatchDispatch && !mBlockingMode && !mEnableDump && !CannotAssociateThreadSpace();
if (bBatch)
{
kernel = m_cmContext->CloneKernel(kernel);
}
kernel->SetThreadCount(tsWidth * tsHeight * tsColor);
if (!CannotAssociateThreadSpace())
{
kernel->AssociateThreadSpace(threadSpace);
}
PrepareKernel(kernel);
if (bBatch)
{
m_cmContext->BatchKernel(kernel, threadSpace, NeedAddSync());
}
else
{
m_cmContext->RunSingleKernel(kernel, CannotAssociateThreadSpace() ? threadSpace : nullptr, kernelName, mBlockingMode);
dev->DestroyThreadSpace(threadSpace);
}
if (mEnableDump)
{
Dump();
}
AttachPayload(nullptr);
}