#include <windows.h>
#include <oleauto.h>
#include <commdlg.h>
#include <mil.h>
#include <queue>
#include <list>
#include "d3d9.h"
#include "md3ddisplayeffect.h"
#include "mdisplay.h"
using namespace std;
void CDisplay::Init()
{
m_Index = 0;
m_SizeX = 0;
m_SizeY = 0;
memset(m_SourceSizeX, 0, sizeof(m_SourceSizeX));
memset(m_SourceSizeY, 0, sizeof(m_SourceSizeY));
m_DisplayFrameRate = 0.0;
m_DisplayCount = 0;
m_DisplayOverlayCount = 0;
m_FrameSkip = 0;
m_DisplayStartTime = 0;
m_pD3DDevice = NULL;
m_ThreadId = M_NULL;
memset(m_pDst, 0, sizeof(m_pDst));
memset(m_pDstOverlay, 0, sizeof(m_pDstOverlay));
m_DisplayOverlayLastBuffer = NULL;
m_DisplayOverlayEnable = false;
m_pD3DFont = NULL;
memset(m_SourceId, 0, sizeof(m_SourceId));
m_Latency.Init();
m_EnableD3DEffect = false;
m_D3DEffect.Init();
m_DrawDisplayInfo = DRAW_DISPLAYINFO;
m_UpdateEvent = M_NULL;
m_Exit = false;
m_IsAllocated = false;
}
bool CDisplay::Allocate(MIL_ID iMilSystem, MIL_INT iIndex, LPDIRECT3D9EX pD3D)
{
bool Success = false;
Init();
m_DisplayModeEx.Size = sizeof(D3DDISPLAYMODEEX);
HRESULT hr = pD3D->GetAdapterDisplayModeEx((UINT)iIndex + 1, &m_DisplayModeEx, NULL);
if(hr == D3D_OK)
{
m_SizeX = m_DisplayModeEx.Width;
m_SizeY = m_DisplayModeEx.Height;
m_Index = iIndex;
MthrAlloc(iMilSystem, M_EVENT, M_DEFAULT, M_NULL, M_NULL, &m_UpdateEvent);
D3DPRESENT_PARAMETERS d3dpp;
memset(&d3dpp, 0, sizeof(d3dpp));
d3dpp.Windowed = FALSE;
d3dpp.SwapEffect = D3DSWAPEFFECT_FLIPEX;
d3dpp.BackBufferWidth = m_DisplayModeEx.Width;
d3dpp.BackBufferHeight = m_DisplayModeEx.Height;
d3dpp.BackBufferFormat = m_DisplayModeEx.Format;
d3dpp.FullScreen_RefreshRateInHz = m_DisplayModeEx.RefreshRate;
d3dpp.BackBufferCount = MAX_DISPLAY_BUFFERING;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
d3dpp.hDeviceWindow = GetDesktopWindow();
hr = pD3D->CreateDeviceEx((UINT)(iIndex+1), D3DDEVTYPE_HAL, GetDesktopWindow(), D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_ENABLE_PRESENTSTATS | D3DCREATE_MULTITHREADED , &d3dpp, &m_DisplayModeEx, &m_pD3DDevice);
if(hr == D3D_OK)
{
D3DXCreateFont(m_pD3DDevice,
(INT) m_SizeY/50,
(UINT) m_SizeX/160,
FW_BOLD,
1,
FALSE,
DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE,
MIL_TEXT("Arial"),
&m_pD3DFont);
m_pD3DDevice->GetSwapChain(0,&m_pSwapChain);
m_pSwapChain->QueryInterface(IID_IDirect3DSwapChain9Ex,reinterpret_cast<void **>(&m_pSwapChainEx));
InitializeCriticalSection(&m_CSLock);
InitializeCriticalSection(&m_ReallocationLock);
MthrAlloc(iMilSystem, M_THREAD, M_DEFAULT, &DispUpdateThread, (void *)this, &m_ThreadId);
MosPrintf(MIL_TEXT("Allocating display %d (%d x %d @ %dHz %s)\n"),iIndex, m_DisplayModeEx.Width, m_DisplayModeEx.Height, m_DisplayModeEx.RefreshRate, (m_DisplayModeEx.ScanLineOrdering == D3DSCANLINEORDERING_INTERLACED)?MIL_TEXT("interlaced"):MIL_TEXT("progressive"), m_DisplayModeEx.RefreshRate);
}
if(m_UpdateEvent && m_pD3DDevice && m_pD3DFont && m_pSwapChain && m_ThreadId)
Success = true;
}
m_IsAllocated = Success;
return Success;
}
void CDisplay::Free()
{
m_Exit = true;
if(m_IsAllocated)
{
MthrControl(m_UpdateEvent, M_EVENT_SET, M_SIGNALED);
MthrWait(m_ThreadId, M_THREAD_END_WAIT, M_NULL);
MthrFree(m_UpdateEvent);
MthrFree(m_ThreadId);
m_pD3DDevice->Release();
for(MIL_INT i = 0; i < MAX_DISPLAY_BUFFERING; i++)
{
if(m_pDst[i])
m_pDst[i]->Release();
if(m_pDstOverlay[i])
m_pDstOverlay[i]->Release();
}
m_pD3DFont->Release();
DeleteCriticalSection(&m_CSLock);
DeleteCriticalSection(&m_ReallocationLock);
m_D3DEffect.Free();
Init();
m_IsAllocated = false;
}
}
void CDisplay::SetDisplaySource(MIL_INT64 iSourceId, MIL_INT iSizeX,
MIL_INT iSizeY)
{
EnterCriticalSection(&m_ReallocationLock);
EnterCriticalSection(&m_CSLock);
while(!m_DisplayQueue.empty())
m_DisplayQueue.pop();
for(MIL_INT i = 0; i < MAX_DISPLAY_BUFFERING; i++)
{
if(m_pDst[i])
{
m_pDst[i]->Release();
m_pDst[i] = NULL;
}
}
for(MIL_INT i = 0; i < MAX_DISPLAY_BUFFERING; i++)
{
if(iSourceId && iSizeX && iSizeY)
{
m_SourceSizeX[0] = iSizeX;
m_SourceSizeY[0] = iSizeY;
m_pD3DDevice->CreateOffscreenPlainSurface((UINT)m_SourceSizeX[0], (UINT)m_SourceSizeY[0], D3DFMT_YUY2, D3DPOOL_DEFAULT, &m_pDst[i], NULL);
}
}
m_SourceId[0] = iSourceId;
ResetStatistic();
LPDIRECT3DSURFACE9 pBackBuffer;
m_pSwapChainEx->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
if(m_pDst[0])
{
D3DLOCKED_RECT LockRect;
LockRect.pBits = 0;
while(LockRect.pBits == 0)
m_pDst[0]->LockRect(&LockRect, NULL, 0);
memset(LockRect.pBits, 127, LockRect.Pitch * m_SourceSizeY[0]);
m_pDst[0]->UnlockRect();
}
m_pD3DDevice->StretchRect(m_pDst[0], NULL, pBackBuffer, NULL, D3DTEXF_NONE);
m_pD3DDevice->PresentEx(0, 0, 0, 0, 0);
LeaveCriticalSection(&m_CSLock);
LeaveCriticalSection(&m_ReallocationLock);
}
void CDisplay::SetDisplayOverlaySource(MIL_INT64 iSourceId, MIL_INT iSizeX, MIL_INT iSizeY)
{
EnterCriticalSection(&m_ReallocationLock);
EnterCriticalSection(&m_CSLock);
m_DisplayOverlayEnable = false;
while(!m_DisplayOverlayQueue.empty())
m_DisplayOverlayQueue.pop();
for(MIL_INT i = 0; i < MAX_DISPLAY_BUFFERING; i++)
{
if(m_pDstOverlay[i])
{
m_pDstOverlay[i]->Release();
m_pDstOverlay[i] = NULL;
}
}
for(MIL_INT i = 0; i < MAX_DISPLAY_BUFFERING; i++)
{
if(iSourceId && iSizeX && iSizeY)
{
m_DisplayOverlayEnable = true;
m_SourceSizeX[1] = iSizeX;
m_SourceSizeY[1] = iSizeY;
m_pD3DDevice->CreateOffscreenPlainSurface((UINT)m_SourceSizeX[1], (UINT)m_SourceSizeY[1], D3DFMT_YUY2, D3DPOOL_DEFAULT, &m_pDstOverlay[i], NULL);
}
}
m_SourceId[1] = iSourceId;
m_DisplayOverlayLastBuffer = NULL;
LeaveCriticalSection(&m_CSLock);
LeaveCriticalSection(&m_ReallocationLock);
}
void CDisplay::ResetStatistic()
{
m_DisplayFrameRate = 0;
m_DisplayCount = 0;
m_DisplayOverlayCount = 0;
m_FrameSkip = 0;
}
void CDisplay::GetStatistic(MIL_DOUBLE *FrameRate, MIL_INT *FrameCount, MIL_INT *FramesSkipped)
{
if(FrameRate)
*FrameRate = m_DisplayFrameRate;
if(FrameCount)
*FrameCount = m_DisplayCount;
if(*FramesSkipped)
*FramesSkipped = m_FrameSkip;
}
bool CDisplay::DisplayBuffer(MIL_ID iBuffer)
{
if(m_DisplayQueue.size() < MAX_DISPLAY_BUFFERING)
{
EnterCriticalSection(&m_CSLock);
m_DisplayQueue.push(iBuffer);
LeaveCriticalSection(&m_CSLock);
MthrControl(m_UpdateEvent, M_EVENT_SET, M_SIGNALED);
return true;
}
else
{
m_FrameSkip++;
return false;
}
}
bool CDisplay::DisplayOverlayBuffer(MIL_ID iBuffer)
{
if(m_DisplayOverlayEnable)
{
m_DisplayOverlayCount++;
if(m_DisplayOverlayCount % 2 == 0)
return false;
}
if(m_DisplayOverlayQueue.size() < MAX_DISPLAY_BUFFERING)
{
EnterCriticalSection(&m_CSLock);
m_DisplayOverlayQueue.push(iBuffer);
LeaveCriticalSection(&m_CSLock);
return true;
}
return false;
}
bool CDisplay::UpdateLatency(MIL_ID iBuffer)
{
bool IsBufferFromThisDisplay = false;
if(m_Latency.Enable && (m_Latency.State == eLATENCY_LATCH_WAITING_FOR_TAG))
{
MIL_UINT32 PixelValue = 0;
MbufGet2d(iBuffer, 0, 0, 1, 1, &PixelValue);
MIL_INT TagValue = ((PixelValue>>8) & 0xffff);
MIL_INT DisplayIndex = ((PixelValue) & 0xff) - 10;
if(m_Index == DisplayIndex)
IsBufferFromThisDisplay = true;
if((TagValue == 0xf0f0) && IsBufferFromThisDisplay)
m_Latency.State = eLATENCY_READ_LATENCY;
}
return IsBufferFromThisDisplay;
}
void CDisplay::Latency(bool State)
{
m_Latency.Init();
m_Latency.Enable = State;
}
bool CDisplay::Latency()
{
return m_Latency.Enable;
}
bool CDisplay::GetLatency(MIL_DOUBLE *Cur, MIL_DOUBLE *Min, MIL_DOUBLE *Max, MIL_DOUBLE *Average, MIL_INT *CurInFrames, MIL_INT *Count)
{
*Cur = 0.0;
*Min = 0.0;
*Max = 0.0;
*Average = 0.0;
*CurInFrames = 0;
*Count = 0;
if(m_Latency.Enable)
{
if(Cur)
*Cur = m_Latency.Cur;
if(Min)
*Min = m_Latency.Min;
if(Max)
*Max = m_Latency.Max;
if(Average && m_Latency.AverageCount)
{
*Average = (m_Latency.Average / (MIL_DOUBLE)m_Latency.AverageCount);
}
if(CurInFrames)
*CurInFrames = m_Latency.LatencyInFrames;
if(Count)
*Count = m_Latency.AverageCount;
}
return m_Latency.Enable;
}
void CDisplay::D3DEffect(bool State)
{
m_EnableD3DEffect = State;
}
bool CDisplay::D3DEffect()
{
return m_EnableD3DEffect;
}
void CDisplay::UpdateDisplay(CDisplay *pDisp, MIL_ID SourceBuf, MIL_ID OverlayBuf)
{
unsigned char *pSrcData = 0;
MIL_INT SrcSizeX, SrcSizeY, SrcPitchByte;
D3DLOCKED_RECT LockRect;
MIL_ID SrcBuffers[2] = {SourceBuf, OverlayBuf};
MIL_INT BufferIndex = pDisp->m_DisplayCount%MAX_DISPLAY_BUFFERING;
if(!pDisp->m_SourceId)
return;
MIL_DOUBLE lCurTime = 0;
MappTimer(M_TIMER_READ + M_GLOBAL, &lCurTime);
if(pDisp->m_DisplayCount == 0)
pDisp->m_DisplayStartTime = lCurTime;
LPDIRECT3DSURFACE9 pDest[2] = {pDisp->m_pDst[BufferIndex], pDisp->m_pDstOverlay[BufferIndex]};
if(!pDest[0])
return;
pDisp->m_DisplayCount++;
m_DisplayFrameRate = (pDisp->m_DisplayCount)/(lCurTime - pDisp->m_DisplayStartTime);
for(MIL_INT i = 0; (i < 2) && SrcBuffers[i] && pDest[i]; i++)
{
MbufInquire(SrcBuffers[i], M_HOST_ADDRESS, &pSrcData);
MbufInquire(SrcBuffers[i], M_SIZE_X, &SrcSizeX);
MbufInquire(SrcBuffers[i], M_SIZE_Y, &SrcSizeY);
MbufInquire(SrcBuffers[i], M_PITCH_BYTE, &SrcPitchByte);
LockRect.pBits = 0;
if((SrcSizeX == pDisp->m_SourceSizeX[i]) && (SrcSizeY == pDisp->m_SourceSizeY[i]))
{
while(LockRect.pBits == 0)
{
pDest[i]->LockRect(&LockRect, NULL, 0);
}
if(SrcPitchByte == LockRect.Pitch)
{
memcpy(LockRect.pBits, pSrcData, LockRect.Pitch * pDisp->m_SourceSizeY[i]);
}
else
{
for(MIL_INT Line = 0; Line < SrcSizeY; Line++)
{
unsigned char *pdestline = (unsigned char *) (((unsigned char *) LockRect.pBits) + (LockRect.Pitch * Line));
unsigned char *psrcline = pSrcData + (SrcPitchByte * Line);
memcpy(pdestline, psrcline, SrcSizeX * 2 );
}
}
pDest[i]->UnlockRect();
}
}
LPDIRECT3DSURFACE9 pBackBuffer;
pDisp->m_pSwapChainEx->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
if(pDisp->m_EnableD3DEffect)
{
DX9Processing(&pDisp->m_D3DEffect, pDisp->m_pD3DDevice, pDest[0], pBackBuffer);
}
else
{
pDisp->m_pD3DDevice->StretchRect(pDest[0], NULL, pBackBuffer, NULL, D3DTEXF_NONE);
}
if(pDisp->m_DrawDisplayInfo)
{
MIL_TEXT_CHAR Buf[MAX_PATH+1];
MosSprintf(Buf, MAX_PATH, MIL_TEXT("Display:%d "), pDisp->m_Index);
RECT rc;
SetRect( &rc, 10, 5, 0, 0);
m_pD3DDevice->BeginScene();
pDisp->m_pD3DFont->DrawText( NULL, Buf, -1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f ) );
m_pD3DDevice->EndScene();
}
if(pDisp->m_DisplayOverlayEnable)
{
RECT rect;
rect.right = (LONG)pDisp->SizeX()/3;
rect.bottom = (LONG)pDisp->SizeY()/3;
rect.top = 90;
rect.left = 50;
if(OverlayBuf == NULL)
pDest[1] = m_DisplayOverlayLastBuffer;
pDisp->m_pD3DDevice->StretchRect(pDest[1], NULL, pBackBuffer, &rect, D3DTEXF_NONE);
m_DisplayOverlayLastBuffer = pDest[1];
}
if(pDisp->m_Latency.Enable)
{
RECT rect;
rect.bottom = 1;
rect.top = 0;
rect.left = 0;
rect.right = 1;
if(pDisp->m_Latency.State == eLATENCY_DISABLE)
{
pDisp->m_Latency.StartCount = 10;
pDisp->m_Latency.StartTime = 0.0;
pDisp->m_Latency.State = eLATENCY_COUNTING;
pDisp->m_Latency.LatencyInFramesCounter = 1;
}
else if(pDisp->m_Latency.State == eLATENCY_COUNTING)
{
pDisp->m_Latency.StartCount--;
pDisp->m_pD3DDevice->ColorFill(pBackBuffer, &rect, D3DCOLOR_XRGB(10 + pDisp->m_Index, 10, 10));
if(pDisp->m_Latency.StartCount == 0)
pDisp->m_Latency.State = eLATENCY_LATCHING_TAG_IMAGE;
}
else if(pDisp->m_Latency.State == eLATENCY_LATCHING_TAG_IMAGE)
{
pDisp->m_pD3DDevice->ColorFill(pBackBuffer, &rect, D3DCOLOR_XRGB(10 + pDisp->m_Index, 240, 240));
if(pDisp->m_Latency.StartTime == 0.0)
pDisp->m_Latency.StartTime = lCurTime;
pDisp->m_Latency.State = eLATENCY_LATCH_WAITING_FOR_TAG;
pDisp->m_Latency.LatencyInFramesCounter = 1;
}
else if(pDisp->m_Latency.State == eLATENCY_LATCH_WAITING_FOR_TAG)
{
pDisp->m_pD3DDevice->ColorFill(pBackBuffer, &rect, D3DCOLOR_XRGB(10 + pDisp->m_Index, 240, 240));
pDisp->m_Latency.LatencyInFramesCounter++;
}
else if(pDisp->m_Latency.State == eLATENCY_READ_LATENCY)
{
pDisp->m_Latency.EndTime = lCurTime;
MIL_DOUBLE lLatency = pDisp->m_Latency.EndTime - pDisp->m_Latency.StartTime;
if((pDisp->m_Latency.Min == 0) || (lLatency < pDisp->m_Latency.Min))
pDisp->m_Latency.Min = lLatency;
if(lLatency > pDisp->m_Latency.Max)
pDisp->m_Latency.Max = lLatency;
pDisp->m_Latency.Cur = lLatency;
pDisp->m_Latency.Average += lLatency;
pDisp->m_Latency.AverageCount++;
pDisp->m_Latency.LatencyInFrames = pDisp->m_Latency.LatencyInFramesCounter;
pDisp->m_Latency.State = eLATENCY_DISABLE;
}
}
pDisp->m_pD3DDevice->PresentEx(0, 0, 0, 0, D3DPRESENT_DONOTWAIT);
}