2 Copyright 2004-2005 Chris Tallon
4 This file is part of VOMP.
6 VOMP is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 VOMP is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with VOMP; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 #include "surfacewin.h"
27 #include "dsallocator.h"
31 #define BACKBUFFER_WIDTH 1280
32 #define BACKBUFFER_HEIGHT 720
36 typedef HRESULT (__stdcall *FCT_DXVA2CreateDirect3DDeviceManager9)(UINT* pResetToken, IDirect3DDeviceManager9** ppDeviceManager);
37 typedef HRESULT (__stdcall *FCT_MFCreateVideoSampleFromSurface)(IUnknown* pUnkSurface, IMFSample** ppSample);
39 FCT_DXVA2CreateDirect3DDeviceManager9 ptrDXVA2CreateDirect3DDeviceManager9=NULL;
40 FCT_MFCreateVideoSampleFromSurface ptrMFCreateVideoSampleFromSurface=NULL;
42 //This is stuff for rendering the OSD
52 evrstate=EVR_pres_off;
55 external_driving=false;
57 filter_type=D3DTEXF_FORCE_DWORD;
58 lastrendertime=timeGetTime();
59 event = CreateEvent(NULL,/*FALSE*/TRUE,FALSE,NULL);
60 d3dmutex = CreateMutex(NULL,FALSE,NULL);
62 dxvadevicehandle=NULL;
65 hlib=LoadLibrary("dxva2.dll");
70 ptrDXVA2CreateDirect3DDeviceManager9=(FCT_DXVA2CreateDirect3DDeviceManager9)GetProcAddress(hlib, "DXVA2CreateDirect3DDeviceManager9");
71 if (!ptrDXVA2CreateDirect3DDeviceManager9){
76 hlib=LoadLibrary("evr.dll");
82 ptrMFCreateVideoSampleFromSurface = (FCT_MFCreateVideoSampleFromSurface)GetProcAddress(hlib,"MFCreateVideoSampleFromSurface");
83 if (!ptrMFCreateVideoSampleFromSurface){
99 CloseHandle(d3dmutex);
104 if (!initted) return 0;
108 int OsdWin::init(void* device)
110 if (initted) return 0;
111 Video* video = Video::getInstance();
112 window=*((HWND*)device);
113 //First Create Direct 3D Object
114 d3d=Direct3DCreate9(D3D_SDK_VERSION);
117 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 object!");
120 // then create the Device
121 D3DPRESENT_PARAMETERS d3dparas;
122 ZeroMemory(&d3dparas,sizeof(d3dparas));
123 d3dparas.BackBufferWidth=BACKBUFFER_WIDTH;
124 d3dparas.BackBufferHeight=BACKBUFFER_HEIGHT;
125 d3dparas.Windowed=TRUE;
126 d3dparas.SwapEffect=D3DSWAPEFFECT_COPY;
127 if (d3d->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,*((HWND*) device),
128 D3DCREATE_SOFTWARE_VERTEXPROCESSING |D3DCREATE_MULTITHREADED,&d3dparas,&d3ddevice)!=D3D_OK) {
129 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 device!");
132 d3ddevice->GetRenderTarget(0,&d3drtsurf);
135 if (!InitVertexBuffer()) {
136 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 vertex buf!");
139 /* We have to determine which kind of filtering is supported*/
141 d3ddevice->GetDeviceCaps(&caps);
142 if ( ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFLINEAR)!=0)
143 && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR)!=0)) {
144 if (filter_type==D3DTEXF_FORCE_DWORD) {
145 filter_type=D3DTEXF_LINEAR;
148 if (filter_type==D3DTEXF_LINEAR)
150 filter_type=D3DTEXF_POINT;
154 if ( ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFPOINT)!=0)
155 && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFPOINT)!=0)) {
156 if (filter_type==D3DTEXF_FORCE_DWORD)
158 filter_type=D3DTEXF_POINT;
161 if (filter_type==D3DTEXF_POINT) {
162 filter_type=D3DTEXF_NONE;
165 if (filter_type==D3DTEXF_FORCE_DWORD) {
166 filter_type=D3DTEXF_NONE;
171 if (ptrDXVA2CreateDirect3DDeviceManager9(&dxvatoken,&d3ddevman)!=S_OK) evrsupported=false;
173 d3ddevman->ResetDevice(d3ddevice,dxvatoken);
180 //Now we will create the Screen
181 screen = new SurfaceWin(Surface::SCREEN);
182 SetEvent(event);//Now all devices are ready
183 screen->create(video->getScreenWidth(), video->getScreenHeight());
185 initted = 1; // must set this here or create surface won't work
191 void OsdWin::LockDevice()
193 if (!evrsupported) return;
194 if (!dxvadevicehandle)
196 d3ddevman->OpenDeviceHandle(&dxvadevicehandle);
198 IDirect3DDevice9 *temp;
199 d3ddevman->LockDevice(dxvadevicehandle,&temp,TRUE);
203 void OsdWin::UnlockDevice()
205 if (!evrsupported) return;
206 if (!initted) return;
207 d3ddevman->UnlockDevice(dxvadevicehandle,TRUE);
208 if (dxvadevicehandle)
210 d3ddevman->CloseDeviceHandle(dxvadevicehandle);
211 dxvadevicehandle=NULL;
216 DWORD OsdWin::getFilterCaps()
218 if (!initted) return NULL;
220 d3ddevice->GetDeviceCaps(&caps);
221 return caps.StretchRectFilterCaps;
224 LPDIRECT3DVERTEXBUFFER9 OsdWin::InitVertexBuffer(DWORD width, DWORD height)
226 LPDIRECT3DVERTEXBUFFER9 ret =NULL;
227 Video* video=Video::getInstance();
228 FLOAT texx=((float)video->getScreenWidth())/1024.f;
229 FLOAT texy=((float)video->getScreenHeight())/1024.f;
230 D3DCOLOR osdcolor=D3DCOLOR_RGBA(255,255,255,255);
231 osdvertices[0].c=osdcolor;
232 osdvertices[0].x=0.f-0.5f;
233 osdvertices[0].y=0.f-0.5f;
234 osdvertices[0].z=0.5f;
235 osdvertices[0].rhw=1.f;
236 osdvertices[0].u=0.f;
237 osdvertices[0].v=0.f;
238 osdvertices[1].c=osdcolor;
239 osdvertices[1].x=((float)width)-0.5f;-0.5f;
240 osdvertices[1].y=0.f-0.5f;
241 osdvertices[1].z=0.5f;
242 osdvertices[1].u=texx;
243 osdvertices[1].v=0.f;
244 osdvertices[1].rhw=1.f;
245 osdvertices[2].c=osdcolor;
246 osdvertices[2].x=((float)width)-0.5f;
247 osdvertices[2].y=((float)height)-0.5f;
248 osdvertices[2].z=0.5f;
249 osdvertices[2].rhw=1.f;
250 osdvertices[2].u=texx;
251 osdvertices[2].v=texy;
252 osdvertices[3].c=osdcolor;
253 osdvertices[3].x=0.f-0.5f;
254 osdvertices[3].y=((float)height)-0.5f;
255 osdvertices[3].z=0.5f;
256 osdvertices[3].rhw=1.f;
257 osdvertices[3].u=0.f;
258 osdvertices[3].v=texy;
260 if (d3ddevice->CreateVertexBuffer(4*sizeof(OSDVERTEX),0,D3DFVF_OSDVERTEX,D3DPOOL_MANAGED,
261 &ret,NULL)!=D3D_OK) {
265 if (ret->Lock(0,sizeof(osdvertices),&pvertex,0)!=D3D_OK) {
268 memcpy(pvertex,osdvertices,sizeof(osdvertices));
273 int OsdWin::shutdown()
275 if (!initted) return 0;
278 if (d3ddevman) d3ddevman->Release();
279 d3drtsurf->Release();
280 d3ddevice->Release();
282 if (swapsurf) swapsurf->Release();
283 if (swappy) swappy->Release();
288 void OsdWin::screenShot(char* fileName)
290 screen->screenShot(fileName);
293 void OsdWin::threadMethod()
299 if (evrstate==EVR_pres_off || evrstate==EVR_pres_pause)
302 } else if (evrstate==EVR_pres_started)
304 LPDIRECT3DSURFACE9 surf;
305 if (dsallocator) dsallocator->GetNextSurface(&surf,&waittime);
314 if (dsallocator) dsallocator->DiscardSurfaceandgetWait(&waittime);
319 if (waittime!=0) Sleep(min(10,waittime));
325 void OsdWin::threadPostStopCleanup()
332 // This function is called from the WinMain function in order to get Screen updates
333 void OsdWin::Render()
335 if (!initted) return ;
336 if (external_driving) {
337 DWORD time1=timeGetTime(); //Updates the Menue
338 if ((time1-lastrendertime)>200) {//5 fps for OSD updates are enough, avoids tearing
339 InternalRendering(NULL);
340 lastrendertime=timeGetTime();
342 //Sleep(5); //Sleep for 5 ms, in order to avoid blocking the other threads
345 DWORD time1=timeGetTime();
346 if ((time1-lastrendertime)>50) {//10 fps for OSD updates are enough, avoids tearing
347 InternalRendering(NULL);
348 lastrendertime=timeGetTime();
357 void OsdWin::RenderDS(LPDIRECT3DSURFACE9 present){
358 if (!initted) return;
359 if (external_driving) {
360 InternalRendering(present);
361 lastrendertime=timeGetTime();
366 void OsdWin::InternalRendering(LPDIRECT3DSURFACE9 present){
368 HRESULT losty=d3ddevice->TestCooperativeLevel();
369 if (losty==D3DERR_DEVICELOST) {
372 return; //Device Lost
374 if (losty==D3DERR_DEVICENOTRESET){
379 WaitForSingleObject(event,INFINITE);
384 LPDIRECT3DSURFACE9 targetsurf;
388 d3ddevice->SetRenderTarget(0,swapsurf);//Stupid VMR manipulates the render target
392 targetsurf=d3drtsurf;
393 d3ddevice->SetRenderTarget(0,d3drtsurf);//Stupid VMR manipulates the render target
395 D3DSURFACE_DESC targetdesc;
396 targetsurf->GetDesc(&targetdesc);
398 if (external_driving) {
399 //Copy video to Backbuffer
400 if (present!=NULL ) {
401 VideoWin* video =(VideoWin*) Video::getInstance();
402 /*calculating destination rect */
403 RECT destrect={0,0,/*video->getScreenWidth()*/ targetdesc.Width,
404 /*video->getScreenHeight()*/targetdesc.Height};
405 UCHAR mode=video->getMode();
408 destrect.right=destrect.right/2;
409 destrect.bottom=destrect.bottom/2;
411 destrect.right=destrect.right/2+video->getPosx()*2;
412 destrect.bottom=destrect.bottom/2+video->getPosy()*2;
413 destrect.left=video->getPosx()*2;
414 destrect.top=video->getPosy()*2;
415 d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0);
418 D3DSURFACE_DESC surf_desc;
419 present->GetDesc(&surf_desc);//for chop sides
420 RECT sourcerect= {0,0,surf_desc.Width,surf_desc.Height};
421 if (video->getPseudoTVsize()==Video::ASPECT4X3
422 && video->getMode()==Video::NORMAL
423 && video->getAspectRatio()==Video::ASPECT16X9) {
424 unsigned int correction=((double) (surf_desc.Width))*4.*9./3./16.;
425 sourcerect.left=(surf_desc.Width-correction)/2;
426 sourcerect.right=sourcerect.left+correction;
428 d3ddevice->StretchRect(present,&sourcerect,targetsurf ,&destrect,filter_type);
432 VideoWin* video =(VideoWin*) Video::getInstance();
434 if (!video->isVideoOn()) d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0);
436 LPDIRECT3DVERTEXBUFFER9 vb=NULL;
437 vb=InitVertexBuffer(targetdesc.Width,targetdesc.Height);
440 if (d3ddevice->BeginScene()==D3D_OK) {
441 d3ddevice->SetStreamSource(0,vb,0,sizeof(OSDVERTEX));
442 d3ddevice->SetFVF(D3DFVF_OSDVERTEX);
443 d3ddevice->SetTexture(0,((SurfaceWin*)screen)->getD3dtexture());
444 //d3ddevice->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_MODULATE);
445 d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP,D3DTOP_MODULATE);
447 d3ddevice->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
448 d3ddevice->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
451 d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);
452 d3ddevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
453 d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
454 d3ddevice->SetRenderState(D3DRS_LIGHTING,FALSE);
457 d3ddevice->DrawPrimitive(D3DPT_TRIANGLEFAN,0,2);
458 d3ddevice->EndScene();
459 //Show it to the user!
463 if (hres=swappy->Present(NULL,NULL,NULL,NULL,0)==D3DERR_DEVICELOST){
465 if (!external_driving) DoLost();
470 if (hres=d3ddevice->Present(NULL,NULL,NULL,NULL)==D3DERR_DEVICELOST){
472 if (!external_driving) DoLost();
482 // if (!external_driving) {
483 // Sleep(4);//The User can wait for 4 milliseconds to see his changes
487 bool OsdWin::DoLost(){
488 Log::getInstance()->log("OSD", Log::WARN, "Direct3D Device Lost! Reobtaining...");
490 if (external_driving && dsallocator!=NULL) {
491 dsallocator->LostDevice(d3ddevice,d3d); //Propagate the information through DS
493 //First we free up all resources
494 Video* video = Video::getInstance();
495 ((SurfaceWin*)screen)->ReleaseSurface();
496 if (d3drtsurf) d3drtsurf->Release();
498 D3DPRESENT_PARAMETERS d3dparas;
499 ZeroMemory(&d3dparas,sizeof(d3dparas));
500 d3dparas.BackBufferWidth=BACKBUFFER_WIDTH;
501 d3dparas.BackBufferHeight=BACKBUFFER_HEIGHT;
502 d3dparas.Windowed=TRUE;
503 d3dparas.SwapEffect=D3DSWAPEFFECT_COPY;
505 if (swapsurf) {swapsurf->Release();swapsurf=NULL;};
506 if (swappy) {swappy->Release();swappy=NULL;};
508 if (d3ddevice->Reset(&d3dparas)!=D3D_OK){
511 d3ddevice->GetRenderTarget(0,&d3drtsurf);
512 if (d3ddevman) d3ddevman->ResetDevice(d3ddevice,dxvatoken);
513 //InitVertexBuffer();
514 //Redraw Views, Chris could you add a member function to BoxStack, so that
515 // I can cause it to completely redraw the Views?
516 // Otherwise the OSD would be distorted after Device Lost
522 screen->create(video->getScreenWidth(), video->getScreenHeight());
528 LPDIRECT3DDEVICE9 OsdWin::getD3dDev() {
529 WaitForSingleObject(event,INFINITE);//We will only return if we are initted
533 LPDIRECT3D9 OsdWin::getD3d() {
534 WaitForSingleObject(event,INFINITE);//We will only return if we are initted
538 void OsdWin::BeginPainting() {//We synchronize calls to d3d between different threads
539 WaitForSingleObject(d3dmutex,INFINITE);
543 void OsdWin::EndPainting() {
545 ReleaseMutex(d3dmutex);
548 void OsdWin::setExternalDriving(DsAllocator* dsall,DWORD width, DWORD height) {
553 d3ddevice->StretchRect(swapsurf,NULL,d3drtsurf,NULL,filter_type);
554 LPDIRECT3DSWAPCHAIN9 temp=swappy;
555 LPDIRECT3DSURFACE9 tempsurf=swapsurf;
564 external_driving=false;
568 WaitForSingleObject(event,INFINITE);//We will only return if we are initted
571 if (width>BACKBUFFER_WIDTH || height>BACKBUFFER_HEIGHT)
573 D3DPRESENT_PARAMETERS d3dparas;
574 ZeroMemory(&d3dparas,sizeof(d3dparas));
575 d3dparas.BackBufferWidth=width;
576 d3dparas.BackBufferHeight=height;
577 d3dparas.Windowed=TRUE;
578 d3dparas.SwapEffect=D3DSWAPEFFECT_COPY;
579 if (d3ddevice->CreateAdditionalSwapChain(&d3dparas,&swappy)!=D3D_OK){
580 Log::getInstance()->log("OSD", Log::WARN, "Could not create Swap Chain!");
583 swappy->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&swapsurf);
585 Log::getInstance()->log("OSD", Log::INFO, "Create Additional Swap Chain %d %d!",width,height);
589 external_driving=true;
594 void OsdWin::Blank() {
595 WaitForSingleObject(event,INFINITE);
597 d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0);