Click here to show toolbars of the Web Online Help System: show toolbars |
/********************************************************************************/ /* * File name: mdisplay.cpp * Location: See Matrox Example Launcher in the MIL Control Center * * * Synopsis: This class manages a display output. * * * A thread is created for each display. * When a DisplayBuffer() is called, * the buffer is inserted in a FIFO that * is consumed by the created thread. * * To calculate the latency between the output and a input, * a black pixel containing the display index is inserted on the output image * at offset 0,0. The black pixel is sent for 10 frames then a white pixel is sent. * When the white pixel is grabbed, the time difference is taken which is the latency. * * */ #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; // Private. // Initialisation. 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; //m_pSwapChain = 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; } // Public. // Allocates the display. 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, // D3D device (INT) m_SizeY/50, // Height (UINT) m_SizeX/160, // Width FW_BOLD, // Weight 1, // MipLevels, 0 = autogen mipmaps FALSE, // Italic DEFAULT_CHARSET, // CharSet OUT_DEFAULT_PRECIS, // OutputPrecision DEFAULT_QUALITY, // Quality DEFAULT_PITCH | FF_DONTCARE, // PitchAndFamily MIL_TEXT("Arial"), // pFaceName &m_pD3DFont); // ppFont 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); } // Is everything allocated. if(m_UpdateEvent && m_pD3DDevice && m_pD3DFont && m_pSwapChain && m_ThreadId) Success = true; } m_IsAllocated = Success; return Success; } // Private. // Frees everything. 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; } } // Public. // Sets the display input source ID, reallocates internal buffers matching the input size. void CDisplay::SetDisplaySource(MIL_INT64 iSourceId, MIL_INT iSizeX, MIL_INT iSizeY) { EnterCriticalSection(&m_ReallocationLock); EnterCriticalSection(&m_CSLock); // Empty fifos. 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); } // Public. // Sets the display overlay input source ID, reallocates internal buffers matching the input size. void CDisplay::SetDisplayOverlaySource(MIL_INT64 iSourceId, MIL_INT iSizeX, MIL_INT iSizeY) { EnterCriticalSection(&m_ReallocationLock); EnterCriticalSection(&m_CSLock); m_DisplayOverlayEnable = false; // Empty fifos. 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); } // Public. // Reset the statistics. void CDisplay::ResetStatistic() { m_DisplayFrameRate = 0; m_DisplayCount = 0; m_DisplayOverlayCount = 0; m_FrameSkip = 0; } // Public. // Returns the display statistics. 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; } // Public. // This function update the display with the buffer. bool CDisplay::DisplayBuffer(MIL_ID iBuffer) { if(m_DisplayQueue.size() < MAX_DISPLAY_BUFFERING) { EnterCriticalSection(&m_CSLock); m_DisplayQueue.push(iBuffer); LeaveCriticalSection(&m_CSLock); // Trigger display update. MthrControl(m_UpdateEvent, M_EVENT_SET, M_SIGNALED); return true; } else { m_FrameSkip++; return false; } } // Public. // This function update the overlay with the buffer. bool CDisplay::DisplayOverlayBuffer(MIL_ID iBuffer) { // Update the overlay at half the rate. 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; } // Public. // Calculates the latency. The unmodified grab buffer is used to read the latency tag. bool CDisplay::UpdateLatency(MIL_ID iBuffer) { bool IsBufferFromThisDisplay = false; // Calculate latency. 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; // Is buffer from this display. if(m_Index == DisplayIndex) IsBufferFromThisDisplay = true; // White image, now lets measure the latency. if((TagValue == 0xf0f0) && IsBufferFromThisDisplay) m_Latency.State = eLATENCY_READ_LATENCY; } return IsBufferFromThisDisplay; } // Public. // Enable/disable latency calculation. void CDisplay::Latency(bool State) { m_Latency.Init(); m_Latency.Enable = State; } // Public. // Returns if latency calculation is enabled. bool CDisplay::Latency() { return m_Latency.Enable; } // Public. // Returns latency results. 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; } // Public. // Enable/disable Direct3D effects. void CDisplay::D3DEffect(bool State) { m_EnableD3DEffect = State; } // Public. // Returns if Direct3D effects is enabled. bool CDisplay::D3DEffect() { return m_EnableD3DEffect; } // Private. // Copies the image from the host to the board for display. 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); // Copy the buffers (main image and overlay) into on-board video memory. 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; // Buffer is YUV16 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) { // When the pitchs are the same, use one memcpy. memcpy(LockRect.pBits, pSrcData, LockRect.Pitch * pDisp->m_SourceSizeY[i]); } else { // The pitchs are not the same, use memcpy per line. 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); } // Draw text. 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 no overlay buffer is present, use the last one. if(OverlayBuf == NULL) pDest[1] = m_DisplayOverlayLastBuffer; pDisp->m_pD3DDevice->StretchRect(pDest[1], NULL, pBackBuffer, &rect, D3DTEXF_NONE); m_DisplayOverlayLastBuffer = pDest[1]; } // Calculate latency. 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) { // Now output 10 black images. The display index value is the R value // of the RGB output. 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) { // Now output one white 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) { // Output one white image; 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) { // Now we read the 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); }