blob: e5545a64b615a126bff474e597dacbc99baa5b10 [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.h
//! \brief Abstraction for MDF related operations.
//! \details It is a thin wrapper layer based on MDF APIs.
//!
#ifndef __VPHAL_MDF_WRAPPER_H__
#define __VPHAL_MDF_WRAPPER_H__
#include <fstream>
#include <vector>
#include <type_traits>
#include <string>
#include <unordered_map>
#include "cm_rt_umd.h"
#include "vphal.h"
template <typename CmSurfType>
class VpCmSurfaceHolder;
class CmContext;
class EventListener
{
public:
virtual void OnEventAvailable(CmEvent *event, const std::string &name) = 0;
};
class EventManager : public EventListener
{
public:
EventManager(const std::string &owner, CmContext *cmContext) :
mOwner(owner),
m_cmContext(cmContext)
{
}
virtual ~EventManager()
{
Clear();
}
void OnEventAvailable(CmEvent *event, const std::string &name) override;
CmEvent* GetLastEvent() const;
private:
void AddEvent(const std::string &name, CmEvent *event);
void Clear();
void Profiling() const;
std::unordered_map<std::string, std::vector<CmEvent *> > mEventMap;
const std::string mOwner;
int mEventCount = 0;
CmEvent *mLastEvent = nullptr;
bool mReport = false;
CmContext *m_cmContext = nullptr;
};
// This is not multi-threads safe. Is it a good idea to use singleton here?
class CmContext
{
public:
// noncopyable
CmContext(const CmContext&) = delete;
CmContext& operator=(const CmContext&) = delete;
CmContext(PMOS_CONTEXT OsContext);
virtual ~CmContext();
void Destroy();
CmDevice* GetCmDevice() const
{
return mCmDevice;
}
CmQueue* GetCmQueue() const
{
return mCmQueue;
}
CmVebox* GetCmVebox() const
{
return mCmVebox;
}
void ConnectEventListener(EventListener *listener)
{
mEventListener = listener;
}
CmKernel* CloneKernel(CmKernel *kernel);
void BatchKernel(CmKernel *kernel, CmThreadSpace *threadSpace, bool bFence);
void FlushBatchTask(bool waitForFinish);
void RunSingleKernel(
CmKernel *kernel,
CmThreadSpace *threadSpace,
const std::string &name,
bool waitForFinish);
void BeginConditionalExecute(VpCmSurfaceHolder<CmBuffer> *conditionalBatchBuffer)
{
FlushBatchTask(false);
mConditionalBatchBuffer = conditionalBatchBuffer;
}
void EndConditionalExecute()
{
FlushBatchTask(false);
mConditionalBatchBuffer = nullptr;
}
private:
void EnqueueTask(CmTask *task, CmThreadSpace *threadSpace, const std::string &name, bool waitForFinish);
int mRefCount;
CmDevice *mCmDevice;
CmQueue *mCmQueue;
CmVebox *mCmVebox;
CmTask *mBatchTask;
std::vector<CmKernel *> mAddedKernels;
std::vector<CmKernel *> mKernelsToPurge;
std::vector<CmThreadSpace *> mThreadSpacesToPurge;
bool mHasBatchedTask;
VpCmSurfaceHolder<CmBuffer> *mConditionalBatchBuffer; // CmContext does NOT own this.
CM_CONDITIONAL_END_PARAM mCondParam;
EventListener *mEventListener; // CmContext does NOT own this.
};
static inline
GMM_RESOURCE_FORMAT ConvertMosFmtToGmmFmt(MOS_FORMAT format)
{
switch (format)
{
case Format_X8R8G8B8: return GMM_FORMAT_B8G8R8X8_UNORM_TYPE;
case Format_A8R8G8B8: return GMM_FORMAT_B8G8R8A8_UNORM_TYPE;
case Format_NV12: return GMM_FORMAT_NV12_TYPE;
case Format_A8: return GMM_FORMAT_A8_UNORM_TYPE;
case Format_YUY2: return GMM_FORMAT_R8G8_UNORM_TYPE;
case Format_R8G8UN: return GMM_FORMAT_R8G8_UNORM_TYPE;
case Format_R32F: return GMM_FORMAT_R32_FLOAT_TYPE;
case Format_AYUV: return GMM_FORMAT_AYUV_TYPE;
case Format_Buffer: return GMM_FORMAT_A8_UNORM_TYPE;
// Format_A16R16G16B16 and Format_A16B16G16R16 are using the same surface layout.
case Format_A16R16G16B16: return GMM_FORMAT_R16G16B16A16_UNORM_TYPE;
case Format_A16B16G16R16: return GMM_FORMAT_R16G16B16A16_UNORM_TYPE;
default:
{
VPHAL_RENDER_ASSERTMESSAGE("Unsupported format %d\n", format);
return GMM_FORMAT_INVALID;
}
}
}
static inline
MOS_FORMAT ConvertGmmFmtToMosFmt(GMM_RESOURCE_FORMAT format)
{
switch (format)
{
case GMM_FORMAT_B8G8R8X8_UNORM_TYPE : return Format_X8R8G8B8;
case GMM_FORMAT_B8G8R8A8_UNORM_TYPE : return Format_A8R8G8B8;
case GMM_FORMAT_NV12_TYPE: return Format_NV12;
case GMM_FORMAT_A8_UNORM_TYPE : return Format_A8;
case GMM_FORMAT_R8G8_UNORM_TYPE : return Format_R8G8UN;
case GMM_FORMAT_R32_FLOAT_TYPE : return Format_R32F;
case GMM_FORMAT_AYUV_TYPE : return Format_AYUV;
// WA for GMM and MDF issue. Will revisit it after fixing the issue.
case GMM_FORMAT_R16G16B16A16_UNORM_TYPE: return Format_A16B16G16R16;
default:
{
VPHAL_RENDER_ASSERTMESSAGE("Unsupported format %d\n", format);
return Format_Invalid;
}
}
}
static inline
int GetBitsPerPixel(GMM_RESOURCE_FORMAT format)
{
switch (format)
{
case GMM_FORMAT_B8G8R8X8_UNORM_TYPE: return 32;
case GMM_FORMAT_B8G8R8A8_UNORM_TYPE: return 32;
case GMM_FORMAT_NV12_TYPE: return 12;
case GMM_FORMAT_A8_UNORM_TYPE: return 8;
case GMM_FORMAT_R8G8_UNORM_TYPE: return 16;
case GMM_FORMAT_R32_FLOAT_TYPE: return 32;
case GMM_FORMAT_AYUV_TYPE: return 32;
case GMM_FORMAT_R16G16B16A16_UNORM_TYPE: return 64;
case GMM_FORMAT_R16G16B16X16_UNORM_TYPE: return 64;
default:
{
VPHAL_RENDER_ASSERTMESSAGE("Unsupported format %d\n", format);
return 0;
}
}
}
template <typename CmSurfType>
class VpCmSurfaceHolder
{
public:
static_assert(
std::is_same<CmSurfType, CmBuffer >::value ||
std::is_same<CmSurfType, CmSurface2D>::value ||
std::is_same<CmSurfType, CmSurface3D>::value,
"CmSurfType need to be one of CmBuffer, CmSurface2D or CmSurface3D.");
VpCmSurfaceHolder(PVPHAL_SURFACE vpSurf, CmContext *cmContext):
mCmSurface(nullptr),
mSurfaceIndex(nullptr),
mSamplerSurfaceIndex(nullptr),
mSampler8x8SurfaceIndex(nullptr),
mWidth(vpSurf->dwWidth),
mHeight(vpSurf->dwHeight),
mDepth(vpSurf->dwDepth),
mFormat(ConvertMosFmtToGmmFmt(vpSurf->Format)),
m_cmContext(cmContext)
{
int result = CreateCmSurfaceSpecialized(vpSurf, mCmSurface);
if ((result != CM_SUCCESS) || (!mCmSurface))
{
VPHAL_RENDER_ASSERTMESSAGE("Failed to create VpCmSurfaceHolder from VP Surface!\n");
return;
}
mCmSurface->GetIndex(mSurfaceIndex);
}
VpCmSurfaceHolder(int width, int height, int depth, GMM_RESOURCE_FORMAT format, CmContext *cmContext) :
mCmSurface(nullptr),
mSurfaceIndex(nullptr),
mSamplerSurfaceIndex(nullptr),
mSampler8x8SurfaceIndex(nullptr),
mWidth(width),
mHeight(height),
mDepth(depth),
mFormat(format),
m_cmContext(cmContext)
{
int result = CreateCmSurfaceSpecialized(width, height, depth, format, mCmSurface);
if ((result != CM_SUCCESS) || (!mCmSurface))
{
VPHAL_RENDER_ASSERTMESSAGE("Failed to create VpCmSurfaceHolder!\n");
return;
}
mCmSurface->GetIndex(mSurfaceIndex);
}
virtual ~VpCmSurfaceHolder()
{
VPHAL_RENDER_CHK_NULL_NO_STATUS_RETURN(m_cmContext);
CmDevice *dev = m_cmContext->GetCmDevice();
if (mSampler8x8SurfaceIndex)
{
dev->DestroySampler8x8Surface(mSampler8x8SurfaceIndex);
}
if (mSamplerSurfaceIndex)
{
dev->DestroySamplerSurface(mSamplerSurfaceIndex);
}
if (mCmSurface)
{
dev->DestroySurface(mCmSurface);
}
}
CmSurfType* GetCmSurface() const
{
return mCmSurface;
}
SurfaceIndex* GetCmSurfaceIndex()
{
if (!mSurfaceIndex)
{
mCmSurface->GetIndex(mSurfaceIndex);
}
return mSurfaceIndex;
}
SurfaceIndex* GetCmSamplerSurfaceIndex()
{
if (!mSamplerSurfaceIndex)
{
if (!m_cmContext)
{
return mSamplerSurfaceIndex;
}
int result = m_cmContext->GetCmDevice()->CreateSamplerSurface2D(mCmSurface, mSamplerSurfaceIndex);
if (result != CM_SUCCESS)
{
VPHAL_RENDER_ASSERTMESSAGE("Failed in CreateSamplerSurface2D!\n");
}
}
return mSamplerSurfaceIndex;
}
SurfaceIndex* GetCmSampler8x8SurfaceIndex()
{
if (!mSampler8x8SurfaceIndex)
{
if (!m_cmContext)
{
return mSampler8x8SurfaceIndex;
}
int result = m_cmContext->GetCmDevice()->CreateSampler8x8Surface(mCmSurface, mSampler8x8SurfaceIndex, CM_AVS_SURFACE, CM_SURFACE_CLAMP);
if (result != CM_SUCCESS)
{
VPHAL_RENDER_ASSERTMESSAGE("Failed in CreateSampler8x8Surface!\n");
}
}
return mSampler8x8SurfaceIndex;
}
void GetSurfaceDimentions(int &width, int &height, int &depth)
{
width = mWidth;
height = mHeight;
depth = mDepth;
}
int GetSurfaceSize() const
{
return (mWidth * mHeight * mDepth * GetBitsPerPixel(mFormat)) >> 3;
}
GMM_RESOURCE_FORMAT GetFormat() const
{
return mFormat;
}
void InitSurfaceFromFile(const std::string &fileName)
{
std::ifstream blob(fileName, std::ifstream::ate | std::ifstream::binary);
if (!blob.is_open())
{
VPHAL_RENDER_ASSERTMESSAGE("Error in opening raw data file: %s.\n", fileName.c_str());
return;
}
const int fileSize = static_cast<int>(blob.tellg());
if (fileSize == 0)
{
VPHAL_RENDER_ASSERTMESSAGE("file size is 0.\n");
return;
}
blob.seekg(0, blob.beg);
std::vector<char> temp(GetSurfaceSize());
blob.read(temp.data(), MOS_MIN(fileSize, GetSurfaceSize()));
mCmSurface->WriteSurface((unsigned char *)temp.data(), nullptr);
}
void DumpSurfaceToFile(const std::string &fileName)
{
std::ofstream blob(fileName, std::ofstream::out | std::ifstream::binary);
if (!blob.is_open())
{
VPHAL_RENDER_ASSERTMESSAGE("Error in opening raw data file: %s.\n", fileName.c_str());
return;
}
const int size = GetSurfaceSize();
std::vector<char> temp(size);
mCmSurface->ReadSurface((unsigned char*)temp.data(), nullptr, size);
blob.write(temp.data(), size);
}
private:
VpCmSurfaceHolder(const VpCmSurfaceHolder &) = delete;
VpCmSurfaceHolder& operator=(const VpCmSurfaceHolder &) = delete;
inline int CreateCmSurfaceSpecialized(PVPHAL_SURFACE vpSurf, CmSurfType* &surf)
{
return 0;
}
inline int CreateCmSurfaceSpecialized(int width, int height, int depth, GMM_RESOURCE_FORMAT format, CmSurfType* &surf)
{
return 0;
}
PVPHAL_SURFACE mVphalSurface;
CmSurfType *mCmSurface;
SurfaceIndex *mSurfaceIndex;
SurfaceIndex *mSamplerSurfaceIndex;
SurfaceIndex *mSampler8x8SurfaceIndex;
const int mWidth;
const int mHeight;
const int mDepth;
const GMM_RESOURCE_FORMAT mFormat;
CmContext *m_cmContext = nullptr;
};
template <>
inline int VpCmSurfaceHolder<CmBuffer>::CreateCmSurfaceSpecialized(PVPHAL_SURFACE vpSurf, CmBuffer* &surf)
{
if (!m_cmContext)
{
return CM_NULL_POINTER;
}
return m_cmContext->GetCmDevice()->CreateBuffer(&vpSurf->OsResource, surf);
}
template <>
inline int VpCmSurfaceHolder<CmSurface2D>::CreateCmSurfaceSpecialized(PVPHAL_SURFACE vpSurf, CmSurface2D* &surf)
{
if (!m_cmContext)
{
return CM_NULL_POINTER;
}
return m_cmContext->GetCmDevice()->CreateSurface2D(&vpSurf->OsResource, surf);
}
template <>
inline int VpCmSurfaceHolder<CmBuffer>::CreateCmSurfaceSpecialized(int width, int height, int depth, GMM_RESOURCE_FORMAT format, CmBuffer* &surf)
{
if (!m_cmContext)
{
return CM_NULL_POINTER;
}
return m_cmContext->GetCmDevice()->CreateBuffer(width, surf);
}
template <>
inline int VpCmSurfaceHolder<CmSurface2D>::CreateCmSurfaceSpecialized(int width, int height, int depth, GMM_RESOURCE_FORMAT format, CmSurface2D* &surf)
{
if (!m_cmContext)
{
return CM_NULL_POINTER;
}
return m_cmContext->GetCmDevice()->CreateSurface2D(width, height, ConvertGmmFmtToMosFmt(format), surf);
}
template <>
inline int VpCmSurfaceHolder<CmSurface3D>::CreateCmSurfaceSpecialized(int width, int height, int depth, GMM_RESOURCE_FORMAT format, CmSurface3D* &surf)
{
if (!m_cmContext)
{
return CM_NULL_POINTER;
}
return m_cmContext->GetCmDevice()->CreateSurface3D(width, height, depth, ConvertGmmFmtToMosFmt(format), surf);
}
class VPRender
{
public:
virtual ~VPRender()
{
}
virtual void Render(void *payload) = 0;
};
// A simple CM kernel render wrapper.
// All methods derived classes need to implement are private.
// This means the calling order of such methods is fixed, and they will
// get hooked to the Render() entry method in this base class.
class VPCmRenderer: public VPRender
{
public:
VPCmRenderer(const std::string &name, CmContext *cmContext);
virtual ~VPCmRenderer();
void Render(void *payload) override;
void SetmBlockingMode(bool enable)
{
mBlockingMode = enable;
}
void EnableDump(bool enable)
{
mEnableDump = enable;
}
void EnableBatchDispatch(bool enable)
{
mBatchDispatch = enable;
}
protected:
CmProgram* LoadProgram(const void *binary, int size);
CmProgram* LoadProgram(const std::string& binaryFileName);
const std::string mName;
CmContext *m_cmContext = nullptr;
private:
virtual void AttachPayload(void *) = 0;
virtual CmKernel* GetKernelToRun(std::string &name) = 0;
virtual void GetThreadSpaceDimension(int &tsWidth, int &tsHeight, int &tsColor) = 0;
virtual void PrepareKernel(CmKernel *kernel) = 0;
// Derived class can override this method if special setup needs be performed on thread space,
// like walking pattern, dependency pattern(scoreboard), etc.
virtual void SetupThreadSpace(CmThreadSpace *threadSpace, int /*tsWidth*/, int /*tsHeight*/, int /*tsColor*/)
{
threadSpace->SelectMediaWalkingPattern(CM_WALK_VERTICAL);
}
// This method will take effect only if this renderer works in batch dispatching mode(multi-kernels in one task).
// Be careful if derived class want to override it. Return true is always safe and workable.
// Return false if its kernel has no dependency against previous ones,
// thus will enable kernel parallel execution, which can improve performance in some cases.
virtual bool NeedAddSync()
{
return true;
}
virtual bool CannotAssociateThreadSpace()
{
return true;
}
virtual void Dump()
{
}
bool mBatchDispatch;
bool mBlockingMode;
bool mEnableDump;
};
#endif // __VPHAL_MDF_WRAPPER_H__