2 Copyright 2014 Marten Richter
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.
22 #include "windowsosd.h"
23 #include "dsallocator.h"
28 #define BACKBUFFER_WIDTH 1920
29 #define BACKBUFFER_HEIGHT 1080
31 typedef HRESULT(__stdcall *FCT_DXVA2CreateDirect3DDeviceManager9)(UINT* pResetToken, IDirect3DDeviceManager9** ppDeviceManager);
32 typedef HRESULT(__stdcall *FCT_MFCreateVideoSampleFromSurface)(IUnknown* pUnkSurface, IMFSample** ppSample);
34 FCT_DXVA2CreateDirect3DDeviceManager9 ptrDXVA2CreateDirect3DDeviceManager9 = NULL;
35 FCT_MFCreateVideoSampleFromSurface ptrMFCreateVideoSampleFromSurface = NULL;
38 WindowsOsd::WindowsOsd()
45 filter_type = D3DTEXF_FORCE_DWORD;
46 evrstate = EVR_pres_off;
52 external_driving = false;
55 lastrendertime = timeGetTime();
56 event = CreateEvent(NULL,/*FALSE*/TRUE, FALSE, NULL);
57 d3dmutex = CreateMutex(NULL, FALSE, NULL);
60 dxvadevicehandle = NULL;
62 hlib = LoadLibrary("dxva2.dll");
67 ptrDXVA2CreateDirect3DDeviceManager9 = (FCT_DXVA2CreateDirect3DDeviceManager9)GetProcAddress(hlib, "DXVA2CreateDirect3DDeviceManager9");
68 if (!ptrDXVA2CreateDirect3DDeviceManager9){
73 hlib = LoadLibrary("evr.dll");
79 ptrMFCreateVideoSampleFromSurface = (FCT_MFCreateVideoSampleFromSurface)GetProcAddress(hlib, "MFCreateVideoSampleFromSurface");
80 if (!ptrMFCreateVideoSampleFromSurface){
87 WindowsOsd::~WindowsOsd()
90 CloseHandle(d3dmutex);
94 void WindowsOsd::LockDevice()
96 if (!evrsupported) return;
97 if (!dxvadevicehandle)
99 d3ddevman->OpenDeviceHandle(&dxvadevicehandle);
101 IDirect3DDevice9 *temp;
102 d3ddevman->LockDevice(dxvadevicehandle, &temp, TRUE);
106 void WindowsOsd::UnlockDevice()
108 if (!evrsupported) return;
109 if (!isInitialized()) return;
110 d3ddevman->UnlockDevice(dxvadevicehandle, TRUE);
111 if (dxvadevicehandle)
113 d3ddevman->CloseDeviceHandle(dxvadevicehandle);
114 dxvadevicehandle = NULL;
119 int WindowsOsd::createDirect3D9Objects()
121 Video* video = Video::getInstance();
122 //First Create Direct 3D Object
123 d3d = Direct3DCreate9(D3D_SDK_VERSION);
126 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 object!");
129 // then create the Device
130 D3DPRESENT_PARAMETERS d3dparas;
131 ZeroMemory(&d3dparas, sizeof(d3dparas));
132 d3dparas.BackBufferWidth = BACKBUFFER_WIDTH;
133 d3dparas.BackBufferHeight = BACKBUFFER_HEIGHT;
134 d3dparas.Windowed = TRUE;
135 d3dparas.SwapEffect = D3DSWAPEFFECT_COPY;
136 if (d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window,
137 D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED, &d3dparas, &d3ddevice) != D3D_OK) {
138 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 device!");
141 d3ddevice->GetRenderTarget(0, &d3drtsurf);
144 if (!InitVertexBuffer()) {
145 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 vertex buf!");
148 /* We have to determine which kind of filtering is supported*/
150 d3ddevice->GetDeviceCaps(&caps);
151 if (((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFLINEAR) != 0)
152 && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR) != 0)) {
153 if (filter_type == D3DTEXF_FORCE_DWORD) {
154 filter_type = D3DTEXF_LINEAR;
158 if (filter_type == D3DTEXF_LINEAR)
160 filter_type = D3DTEXF_POINT;
164 if (((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFPOINT) != 0)
165 && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFPOINT) != 0)) {
166 if (filter_type == D3DTEXF_FORCE_DWORD)
168 filter_type = D3DTEXF_POINT;
172 if (filter_type == D3DTEXF_POINT) {
173 filter_type = D3DTEXF_NONE;
176 if (filter_type == D3DTEXF_FORCE_DWORD) {
177 filter_type = D3DTEXF_NONE;
182 if (ptrDXVA2CreateDirect3DDeviceManager9(&dxvatoken, &d3ddevman) != S_OK) evrsupported = false;
184 d3ddevman->ResetDevice(d3ddevice, dxvatoken);
187 SetEvent(event);//Now all devices are ready
191 void WindowsOsd::startRenderLoop()
196 void WindowsOsd::shutdownDirect3D9Objects()
200 if (d3ddevman) d3ddevman->Release();
201 d3drtsurf->Release();
202 d3ddevice->Release();
204 if (swapsurf) swapsurf->Release();
205 if (swappy) swappy->Release();
208 void WindowsOsd::threadMethod()
213 if (isInitialized()){
214 if (evrstate == EVR_pres_off || evrstate == EVR_pres_pause)
218 else if (evrstate == EVR_pres_started)
220 LPDIRECT3DSURFACE9 surf;
221 if (dsallocator) dsallocator->GetNextSurface(&surf, &waittime);
230 if (dsallocator) dsallocator->DiscardSurfaceandgetWait(&waittime);
235 if (waittime != 0) Sleep(min(10, waittime));
241 void WindowsOsd::threadPostStopCleanup()
247 void WindowsOsd::setExternalDriving(DsAllocator* dsall, DWORD width, DWORD height) {
252 d3ddevice->StretchRect(swapsurf, NULL, d3drtsurf, NULL, filter_type);
253 LPDIRECT3DSWAPCHAIN9 temp = swappy;
254 LPDIRECT3DSURFACE9 tempsurf = swapsurf;
263 external_driving = false;
267 WaitForSingleObject(event, INFINITE);//We will only return if we are initted
270 if (width>BACKBUFFER_WIDTH || height>BACKBUFFER_HEIGHT)
272 D3DPRESENT_PARAMETERS d3dparas;
273 ZeroMemory(&d3dparas, sizeof(d3dparas));
274 d3dparas.BackBufferWidth = width;
275 d3dparas.BackBufferHeight = height;
276 d3dparas.Windowed = TRUE;
277 d3dparas.SwapEffect = D3DSWAPEFFECT_COPY;
278 if (d3ddevice->CreateAdditionalSwapChain(&d3dparas, &swappy) != D3D_OK){
279 Log::getInstance()->log("OSD", Log::WARN, "Could not create Swap Chain!");
283 swappy->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &swapsurf);
285 Log::getInstance()->log("OSD", Log::INFO, "Create Additional Swap Chain %d %d!", width, height);
289 external_driving = true;
294 // This function is called from the WinMain function in order to get Screen updates
295 void WindowsOsd::Render()
297 if (!isInitialized()) return;
298 if (external_driving) {
299 DWORD time1 = timeGetTime(); //Updates the Menue
300 if ((time1 - lastrendertime)>200) {//5 fps for OSD updates are enough, avoids tearing
301 InternalRendering(NULL);
302 lastrendertime = timeGetTime();
305 //Sleep(5); //Sleep for 5 ms, in order to avoid blocking the other threads
309 DWORD time1 = timeGetTime();
310 if ((time1 - lastrendertime)>50) {//10 fps for OSD updates are enough, avoids tearing
311 InternalRendering(NULL);
312 lastrendertime = timeGetTime();
322 void WindowsOsd::RenderDS(LPDIRECT3DSURFACE9 present){
323 if (!isInitialized()) return;
324 if (external_driving) {
325 InternalRendering(present);
326 lastrendertime = timeGetTime();
331 void WindowsOsd::BeginPainting() {//We synchronize calls to d3d between different threads
332 WaitForSingleObject(d3dmutex, INFINITE);
336 void WindowsOsd::EndPainting() {
338 ReleaseMutex(d3dmutex);
341 DWORD WindowsOsd::getFilterCaps()
343 if (!isInitialized()) return NULL;
345 d3ddevice->GetDeviceCaps(&caps);
346 return caps.StretchRectFilterCaps;
349 LPDIRECT3DDEVICE9 WindowsOsd::getD3dDev() {
350 WaitForSingleObject(event, INFINITE);//We will only return if we are initted
354 LPDIRECT3D9 WindowsOsd::getD3d() {
355 WaitForSingleObject(event, INFINITE);//We will only return if we are initted
360 void WindowsOsd::Blank() {
361 WaitForSingleObject(event, INFINITE);
363 d3ddevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
367 void WindowsOsd::InternalRendering(LPDIRECT3DSURFACE9 present){
369 HRESULT losty = d3ddevice->TestCooperativeLevel();
370 if (losty == D3DERR_DEVICELOST) {
373 return; //Device Lost
375 if (losty == D3DERR_DEVICENOTRESET){
380 WaitForSingleObject(event, INFINITE);
382 LPDIRECT3DSURFACE9 targetsurf;
385 targetsurf = swapsurf;
386 d3ddevice->SetRenderTarget(0, swapsurf);//Stupid VMR manipulates the render target
390 targetsurf = d3drtsurf;
391 d3ddevice->SetRenderTarget(0, d3drtsurf);//Stupid VMR manipulates the render target
393 D3DSURFACE_DESC targetdesc;
394 targetsurf->GetDesc(&targetdesc);
396 if (external_driving) {
397 //Copy video to Backbuffer
398 if (present != NULL) {
399 VideoWin* video = (VideoWin*)Video::getInstance();
400 /*calculating destination rect */
401 RECT destrect = { 0, 0,/*video->getScreenWidth()*/ targetdesc.Width,
402 /*video->getScreenHeight()*/targetdesc.Height };
403 const VideoDisplay &vd = video->getVideoDisplay();
409 destrect.right = destrect.right / 2;
410 destrect.bottom = destrect.bottom / 2;
412 destrect.right = destrect.right / 2;
413 destrect.bottom = destrect.bottom / 2;
416 float imageaspect = 720.f / 576.f;
417 float boxaspect = ((float)vd.width) / ((float)vd.height);
418 float videoaspect = ((float)targetdesc.Width) / ((float)targetdesc.Height);
419 if (imageaspect > boxaspect) {
420 destrect.right = (int)(((float)destrect.right) * ((float)vd.width) / 720.f);
421 destrect.bottom = (int)(((float)destrect.right) / videoaspect);
422 addy += (((float)vd.height) - ((float)vd.width) / imageaspect)*0.5f / 576.f
423 *((float)targetdesc.Height);
426 destrect.bottom = (int)(((float)destrect.bottom) * ((float)vd.height) / 576.f);
427 destrect.right = (int)(((float)destrect.bottom)*videoaspect);
428 addx += (((float)vd.width) - ((float)vd.height) * imageaspect)*0.5f / 720.f
429 *((float)targetdesc.Width);
439 destrect.left = (int)(((float)vd.x*targetdesc.Width) / 720.f) + addx;
440 destrect.top = (int)(((float)vd.y*targetdesc.Height) / 576.f) + addy;
441 destrect.right += destrect.left;
442 destrect.bottom += destrect.top;
445 d3ddevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
446 D3DSURFACE_DESC surf_desc;
447 present->GetDesc(&surf_desc);//for chop sides
448 RECT sourcerect = { 0, 0, surf_desc.Width, surf_desc.Height };
449 if (video->getPseudoTVsize() == Video::ASPECT4X3
450 && video->getMode() == Video::NORMAL
451 && video->getAspectRatio() == Video::ASPECT16X9) {
452 unsigned int correction = ((double)(surf_desc.Width))*4.*9. / 3. / 16.;
453 sourcerect.left = (surf_desc.Width - correction) / 2;
454 sourcerect.right = sourcerect.left + correction;
456 d3ddevice->StretchRect(present, &sourcerect, targetsurf, &destrect, filter_type);
461 VideoWin* video = (VideoWin*)Video::getInstance();
463 if (!video->isVideoOn()) d3ddevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
465 LPDIRECT3DVERTEXBUFFER9 vb = NULL;
466 vb = InitVertexBuffer(targetdesc.Width, targetdesc.Height);
469 if (d3ddevice->BeginScene() == D3D_OK) {
470 d3ddevice->SetStreamSource(0, vb, 0, sizeof(OSDVERTEX));
471 d3ddevice->SetFVF(D3DFVF_OSDVERTEX);
472 LPDIRECT3DTEXTURE9 osdtex = getNextOsdTexture();
473 d3ddevice->SetTexture(0, osdtex);
474 //d3ddevice->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_MODULATE);
475 d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
477 d3ddevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
478 d3ddevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
481 d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
482 d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
483 d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
484 d3ddevice->SetRenderState(D3DRS_LIGHTING, FALSE);
487 d3ddevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
488 d3ddevice->EndScene();
489 //Show it to the user!
493 if (hres = swappy->Present(NULL, NULL, NULL, NULL, 0) == D3DERR_DEVICELOST){
495 if (!external_driving) DoLost();
500 if (hres = d3ddevice->Present(NULL, NULL, NULL, NULL) == D3DERR_DEVICELOST){
502 if (!external_driving) DoLost();
512 // if (!external_driving) {
513 // Sleep(4);//The User can wait for 4 milliseconds to see his changes
517 bool WindowsOsd::DoLost(){
518 Log::getInstance()->log("OSD", Log::WARN, "Direct3D Device Lost! Reobtaining...");
520 if (external_driving && dsallocator != NULL) {
521 dsallocator->LostDevice(d3ddevice, d3d); //Propagate the information through DS
523 //First we free up all resources
524 Video* video = Video::getInstance();
525 lostDestroyObjects();
526 if (d3drtsurf) d3drtsurf->Release();
528 D3DPRESENT_PARAMETERS d3dparas;
529 ZeroMemory(&d3dparas, sizeof(d3dparas));
530 d3dparas.BackBufferWidth = BACKBUFFER_WIDTH;
531 d3dparas.BackBufferHeight = BACKBUFFER_HEIGHT;
532 d3dparas.Windowed = TRUE;
533 d3dparas.SwapEffect = D3DSWAPEFFECT_COPY;
535 if (swapsurf) { swapsurf->Release(); swapsurf = NULL; };
536 if (swappy) { swappy->Release(); swappy = NULL; };
538 if (d3ddevice->Reset(&d3dparas) != D3D_OK){
541 d3ddevice->GetRenderTarget(0, &d3drtsurf);
542 if (d3ddevman) d3ddevman->ResetDevice(d3ddevice, dxvatoken);
543 //InitVertexBuffer();
544 //Redraw Views, Chris could you add a member function to BoxStack, so that
545 // I can cause it to completely redraw the Views?
546 // Otherwise the OSD would be distorted after Device Lost
551 lostRecreateObjects();
558 LPDIRECT3DVERTEXBUFFER9 WindowsOsd::InitVertexBuffer(DWORD width, DWORD height)
560 LPDIRECT3DVERTEXBUFFER9 ret = NULL;
561 Video* video = Video::getInstance();
562 FLOAT texx = ((float)video->getScreenWidth()) / 1024.f;
563 FLOAT texy = ((float)video->getScreenHeight()) / 1024.f;
564 D3DCOLOR osdcolor = D3DCOLOR_RGBA(255, 255, 255, 255);
565 osdvertices[0].c = osdcolor;
566 osdvertices[0].x = 0.f - 0.5f;
567 osdvertices[0].y = 0.f - 0.5f;
568 osdvertices[0].z = 0.5f;
569 osdvertices[0].rhw = 1.f;
570 osdvertices[0].u = 0.f;
571 osdvertices[0].v = 0.f;
572 osdvertices[1].c = osdcolor;
573 osdvertices[1].x = ((float)width) - 0.5f;
574 osdvertices[1].y = 0.f - 0.5f;
575 osdvertices[1].z = 0.5f;
576 osdvertices[1].u = texx;
577 osdvertices[1].v = 0.f;
578 osdvertices[1].rhw = 1.f;
579 osdvertices[2].c = osdcolor;
580 osdvertices[2].x = ((float)width) - 0.5f;
581 osdvertices[2].y = ((float)height) - 0.5f;
582 osdvertices[2].z = 0.5f;
583 osdvertices[2].rhw = 1.f;
584 osdvertices[2].u = texx;
585 osdvertices[2].v = texy;
586 osdvertices[3].c = osdcolor;
587 osdvertices[3].x = 0.f - 0.5f;
588 osdvertices[3].y = ((float)height) - 0.5f;
589 osdvertices[3].z = 0.5f;
590 osdvertices[3].rhw = 1.f;
591 osdvertices[3].u = 0.f;
592 osdvertices[3].v = texy;
594 if (d3ddevice->CreateVertexBuffer(4 * sizeof(OSDVERTEX), 0, D3DFVF_OSDVERTEX, D3DPOOL_MANAGED,
595 &ret, NULL) != D3D_OK) {
598 void *pvertex = NULL;
599 if (ret->Lock(0, sizeof(osdvertices), &pvertex, 0) != D3D_OK) {
602 memcpy(pvertex, osdvertices, sizeof(osdvertices));