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"
30 typedef HRESULT(__stdcall *FCT_DXVA2CreateDirect3DDeviceManager9)(UINT* pResetToken, IDirect3DDeviceManager9** ppDeviceManager);
31 typedef HRESULT(__stdcall *FCT_MFCreateVideoSampleFromSurface)(IUnknown* pUnkSurface, IMFSample** ppSample);
33 FCT_DXVA2CreateDirect3DDeviceManager9 ptrDXVA2CreateDirect3DDeviceManager9 = NULL;
34 FCT_MFCreateVideoSampleFromSurface ptrMFCreateVideoSampleFromSurface = NULL;
37 WindowsOsd::WindowsOsd()
44 filter_type = D3DTEXF_FORCE_DWORD;
45 evrstate = EVR_pres_off;
51 external_driving = false;
54 lastrendertime = timeGetTime();
55 event = CreateEvent(NULL,/*FALSE*/TRUE, FALSE, NULL);
56 d3dmutex = CreateMutex(NULL, FALSE, NULL);
59 dxvadevicehandle = NULL;
61 hlib = LoadLibrary("dxva2.dll");
66 ptrDXVA2CreateDirect3DDeviceManager9 = (FCT_DXVA2CreateDirect3DDeviceManager9)GetProcAddress(hlib, "DXVA2CreateDirect3DDeviceManager9");
67 if (!ptrDXVA2CreateDirect3DDeviceManager9){
72 hlib = LoadLibrary("evr.dll");
78 ptrMFCreateVideoSampleFromSurface = (FCT_MFCreateVideoSampleFromSurface)GetProcAddress(hlib, "MFCreateVideoSampleFromSurface");
79 if (!ptrMFCreateVideoSampleFromSurface){
86 WindowsOsd::~WindowsOsd()
89 CloseHandle(d3dmutex);
93 void WindowsOsd::LockDevice()
95 if (!evrsupported) return;
96 if (!dxvadevicehandle)
98 d3ddevman->OpenDeviceHandle(&dxvadevicehandle);
100 IDirect3DDevice9 *temp;
101 d3ddevman->LockDevice(dxvadevicehandle, &temp, TRUE);
105 void WindowsOsd::UnlockDevice()
107 if (!evrsupported) return;
108 if (!isInitialized()) return;
109 d3ddevman->UnlockDevice(dxvadevicehandle, TRUE);
110 if (dxvadevicehandle)
112 d3ddevman->CloseDeviceHandle(dxvadevicehandle);
113 dxvadevicehandle = NULL;
118 int WindowsOsd::createDirect3D9Objects()
120 Video* video = Video::getInstance();
121 //First Create Direct 3D Object
122 #ifdef WINDOWS_LEGACY
123 d3d = Direct3DCreate9(D3D_SDK_VERSION);
126 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 object!");
130 HRESULT err = Direct3DCreate9Ex(D3D_SDK_VERSION, &d3d);
133 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9Ex object! %d",err);
138 // then create the Device
139 D3DPRESENT_PARAMETERS d3dparas;
140 ZeroMemory(&d3dparas, sizeof(d3dparas));
141 d3dparas.BackBufferWidth = BACKBUFFER_WIDTH;
142 d3dparas.BackBufferHeight = BACKBUFFER_HEIGHT;
143 d3dparas.Windowed = TRUE;
144 d3dparas.SwapEffect = D3DSWAPEFFECT_COPY;
145 #ifdef WINDOWS_LEGACY
146 if (d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window,
147 D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED, &d3dparas, &d3ddevice) != D3D_OK) {
148 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 device!");
152 if (d3d->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window,
153 D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED, &d3dparas, NULL, &d3ddevice) != D3D_OK) {
154 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 device!");
158 d3ddevice->GetRenderTarget(0, &d3drtsurf);
161 if (!InitVertexBuffer()) {
162 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 vertex buf!");
165 /* We have to determine which kind of filtering is supported*/
167 d3ddevice->GetDeviceCaps(&caps);
168 if (((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFLINEAR) != 0)
169 && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR) != 0)) {
170 if (filter_type == D3DTEXF_FORCE_DWORD) {
171 filter_type = D3DTEXF_LINEAR;
175 if (filter_type == D3DTEXF_LINEAR)
177 filter_type = D3DTEXF_POINT;
181 if (((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFPOINT) != 0)
182 && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFPOINT) != 0)) {
183 if (filter_type == D3DTEXF_FORCE_DWORD)
185 filter_type = D3DTEXF_POINT;
189 if (filter_type == D3DTEXF_POINT) {
190 filter_type = D3DTEXF_NONE;
193 if (filter_type == D3DTEXF_FORCE_DWORD) {
194 filter_type = D3DTEXF_NONE;
199 if (ptrDXVA2CreateDirect3DDeviceManager9(&dxvatoken, &d3ddevman) != S_OK) evrsupported = false;
201 d3ddevman->ResetDevice(d3ddevice, dxvatoken);
204 SetEvent(event);//Now all devices are ready
208 void WindowsOsd::startRenderLoop()
213 void WindowsOsd::stopRenderLoop()
218 void WindowsOsd::shutdownDirect3D9Objects()
222 if (d3ddevman) d3ddevman->Release();
223 d3drtsurf->Release();
224 d3ddevice->Release();
226 if (swapsurf) swapsurf->Release();
227 if (swappy) swappy->Release();
230 void WindowsOsd::threadMethod()
235 if (isInitialized()){
236 if (evrstate == EVR_pres_off || evrstate == EVR_pres_pause)
240 else if (evrstate == EVR_pres_started)
242 LPDIRECT3DSURFACE9 surf;
243 if (dsallocator) dsallocator->GetNextSurface(&surf, &waittime);
252 if (dsallocator) dsallocator->DiscardSurfaceandgetWait(&waittime);
257 if (waittime != 0) Sleep(min(10, waittime));
263 void WindowsOsd::threadPostStopCleanup()
269 void WindowsOsd::setExternalDriving(DsAllocator* dsall, DWORD width, DWORD height) {
274 d3ddevice->StretchRect(swapsurf, NULL, d3drtsurf, NULL, filter_type);
275 LPDIRECT3DSWAPCHAIN9 temp = swappy;
276 LPDIRECT3DSURFACE9 tempsurf = swapsurf;
285 external_driving = false;
289 WaitForSingleObject(event, INFINITE);//We will only return if we are initted
292 if (width>BACKBUFFER_WIDTH || height>BACKBUFFER_HEIGHT)
294 D3DPRESENT_PARAMETERS d3dparas;
295 ZeroMemory(&d3dparas, sizeof(d3dparas));
296 d3dparas.BackBufferWidth = width;
297 d3dparas.BackBufferHeight = height;
298 d3dparas.Windowed = TRUE;
299 d3dparas.SwapEffect = D3DSWAPEFFECT_COPY;
300 if (d3ddevice->CreateAdditionalSwapChain(&d3dparas, &swappy) != D3D_OK){
301 Log::getInstance()->log("OSD", Log::WARN, "Could not create Swap Chain!");
305 swappy->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &swapsurf);
307 Log::getInstance()->log("OSD", Log::INFO, "Create Additional Swap Chain %d %d!", width, height);
311 external_driving = true;
316 // This function is called from the WinMain function in order to get Screen updates
317 void WindowsOsd::Render()
319 if (!isInitialized()) return;
320 if (external_driving) {
321 DWORD time1 = timeGetTime(); //Updates the Menue
322 if ((time1 - lastrendertime)>200) {//5 fps for OSD updates are enough, avoids tearing
324 InternalRendering(NULL);
325 lastosdrendertime = lastrendertime = timeGetTime();
328 //Sleep(5); //Sleep for 5 ms, in order to avoid blocking the other threads
332 DWORD time1 = timeGetTime();
333 if ((time1 - lastrendertime)>50) {//10 fps for OSD updates are enough, avoids tearing
335 InternalRendering(NULL);
336 lastosdrendertime = lastrendertime = timeGetTime();
346 void WindowsOsd::RenderDS(LPDIRECT3DSURFACE9 present){
347 if (!isInitialized()) return;
348 if (external_driving) {
349 InternalRendering(present);
350 lastrendertime = timeGetTime();
352 if ((lastrendertime - lastosdrendertime) > 50) {
354 lastosdrendertime = timeGetTime();
359 void WindowsOsd::BeginPainting() {//We synchronize calls to d3d between different threads
360 WaitForSingleObject(d3dmutex, INFINITE);
364 void WindowsOsd::EndPainting() {
366 ReleaseMutex(d3dmutex);
369 DWORD WindowsOsd::getFilterCaps()
371 if (!isInitialized()) return NULL;
373 d3ddevice->GetDeviceCaps(&caps);
374 return caps.StretchRectFilterCaps;
377 LPDIRECT3DDEVICE9 WindowsOsd::getD3dDev() {
378 WaitForSingleObject(event, INFINITE);//We will only return if we are initted
382 LPDIRECT3D9 WindowsOsd::getD3d() {
383 WaitForSingleObject(event, INFINITE);//We will only return if we are initted
388 void WindowsOsd::Blank() {
389 WaitForSingleObject(event, INFINITE);
391 d3ddevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
395 void WindowsOsd::InternalRendering(LPDIRECT3DSURFACE9 present){
397 HRESULT losty = d3ddevice->TestCooperativeLevel();
398 if (losty == D3DERR_DEVICELOST) {
401 return; //Device Lost
403 if (losty == D3DERR_DEVICENOTRESET){
408 WaitForSingleObject(event, INFINITE);
410 LPDIRECT3DSURFACE9 targetsurf;
413 targetsurf = swapsurf;
414 d3ddevice->SetRenderTarget(0, swapsurf);//Stupid VMR manipulates the render target
418 targetsurf = d3drtsurf;
419 d3ddevice->SetRenderTarget(0, d3drtsurf);//Stupid VMR manipulates the render target
421 D3DSURFACE_DESC targetdesc;
422 targetsurf->GetDesc(&targetdesc);
424 d3ddevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
425 LPDIRECT3DVERTEXBUFFER9 vb2 = NULL;
426 vb2 = InitBackgroundVertexBuffer(targetdesc.Width, targetdesc.Height);
427 if (d3ddevice->BeginScene() == D3D_OK) {
428 d3ddevice->SetStreamSource(0, vb2, 0, sizeof(OSDVERTEX));
429 d3ddevice->SetFVF(D3DFVF_OSDVERTEX);
430 d3ddevice->SetTexture(0, NULL);
432 d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
433 d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
434 d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
435 d3ddevice->SetRenderState(D3DRS_LIGHTING, FALSE);
437 d3ddevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
438 d3ddevice->EndScene();
443 if (external_driving) {
444 //Copy video to Backbuffer
445 if (present != NULL) {
446 VideoWin* video = (VideoWin*)Video::getInstance();
447 /*calculating destination rect */
448 RECT destrect = { 0, 0,/*video->getScreenWidth()*/ targetdesc.Width,
449 /*video->getScreenHeight()*/targetdesc.Height };
450 const VideoDisplay &vd = video->getVideoDisplay();
456 destrect.right = destrect.right / 2;
457 destrect.bottom = destrect.bottom / 2;
459 destrect.right = destrect.right / 2;
460 destrect.bottom = destrect.bottom / 2;
463 float imageaspect = 720.f / 576.f;
464 float boxaspect = ((float)vd.width) / ((float)vd.height);
465 float videoaspect = ((float)targetdesc.Width) / ((float)targetdesc.Height);
466 if (imageaspect > boxaspect) {
467 destrect.right = (int)(((float)destrect.right) * ((float)vd.width) / 720.f);
468 destrect.bottom = (int)(((float)destrect.right) / videoaspect);
469 addy += (((float)vd.height) - ((float)vd.width) / imageaspect)*0.5f / 576.f
470 *((float)targetdesc.Height);
473 destrect.bottom = (int)(((float)destrect.bottom) * ((float)vd.height) / 576.f);
474 destrect.right = (int)(((float)destrect.bottom)*videoaspect);
475 addx += (((float)vd.width) - ((float)vd.height) * imageaspect)*0.5f / 720.f
476 *((float)targetdesc.Width);
486 destrect.left = (int)(((float)vd.x*targetdesc.Width) / 720.f) + addx;
487 destrect.top = (int)(((float)vd.y*targetdesc.Height) / 576.f) + addy;
488 destrect.right += destrect.left;
489 destrect.bottom += destrect.top;
494 D3DSURFACE_DESC surf_desc;
495 present->GetDesc(&surf_desc);//for chop sides
496 RECT sourcerect = { 0, 0, surf_desc.Width, surf_desc.Height };
497 if (video->getPseudoTVsize() == Video::ASPECT4X3
498 && video->getMode() == Video::NORMAL
499 && video->getAspectRatio() == Video::ASPECT16X9) {
500 unsigned int correction = ((double)(surf_desc.Width))*4.*9. / 3. / 16.;
501 sourcerect.left = (surf_desc.Width - correction) / 2;
502 sourcerect.right = sourcerect.left + correction;
504 d3ddevice->StretchRect(present, &sourcerect, targetsurf, &destrect, filter_type);
509 LPDIRECT3DVERTEXBUFFER9 vb1 = NULL;
510 vb1 = InitVertexBuffer(targetdesc.Width, targetdesc.Height);
514 if (d3ddevice->BeginScene() == D3D_OK) {
515 d3ddevice->SetStreamSource(0, vb1, 0, sizeof(OSDVERTEX));
516 d3ddevice->SetFVF(D3DFVF_OSDVERTEX);
517 d3ddevice->SetTexture(0, NULL);
518 LPDIRECT3DTEXTURE9 osdtex = getNextOsdTexture();
519 d3ddevice->SetTexture(0, osdtex);
520 //d3ddevice->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_MODULATE);
521 d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
523 d3ddevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
524 d3ddevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
527 d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
528 d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
529 d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
530 d3ddevice->SetRenderState(D3DRS_LIGHTING, FALSE);
533 d3ddevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
534 d3ddevice->EndScene();
535 //Show it to the user!
539 if (hres = swappy->Present(NULL, NULL, NULL, NULL, 0) == D3DERR_DEVICELOST){
541 if (!external_driving) DoLost();
546 if (hres = d3ddevice->Present(NULL, NULL, NULL, NULL) == D3DERR_DEVICELOST){
548 if (!external_driving) DoLost();
558 // if (!external_driving) {
559 // Sleep(4);//The User can wait for 4 milliseconds to see his changes
563 bool WindowsOsd::DoLost(){
564 Log::getInstance()->log("OSD", Log::WARN, "Direct3D Device Lost! Reobtaining...");
566 if (external_driving && dsallocator != NULL) {
567 dsallocator->LostDevice(d3ddevice, d3d); //Propagate the information through DS
569 //First we free up all resources
570 Video* video = Video::getInstance();
571 lostDestroyObjects();
572 if (d3drtsurf) d3drtsurf->Release();
574 D3DPRESENT_PARAMETERS d3dparas;
575 ZeroMemory(&d3dparas, sizeof(d3dparas));
576 d3dparas.BackBufferWidth = BACKBUFFER_WIDTH;
577 d3dparas.BackBufferHeight = BACKBUFFER_HEIGHT;
578 d3dparas.Windowed = TRUE;
579 d3dparas.SwapEffect = D3DSWAPEFFECT_COPY;
581 if (swapsurf) { swapsurf->Release(); swapsurf = NULL; };
582 if (swappy) { swappy->Release(); swappy = NULL; };
584 if (d3ddevice->Reset(&d3dparas) != D3D_OK){
587 d3ddevice->GetRenderTarget(0, &d3drtsurf);
588 if (d3ddevman) d3ddevman->ResetDevice(d3ddevice, dxvatoken);
589 //InitVertexBuffer();
590 //Redraw Views, Chris could you add a member function to BoxStack, so that
591 // I can cause it to completely redraw the Views?
592 // Otherwise the OSD would be distorted after Device Lost
597 lostRecreateObjects();
604 LPDIRECT3DVERTEXBUFFER9 WindowsOsd::InitVertexBuffer(DWORD width, DWORD height)
606 LPDIRECT3DVERTEXBUFFER9 ret = NULL;
609 getTextureCoordinates(&texx, &texy);
611 D3DCOLOR osdcolor = D3DCOLOR_RGBA(255, 255, 255, 255);
612 OSDVERTEX osdvertices[4];
613 osdvertices[0].c = osdcolor;
614 osdvertices[0].x = 0.f - 0.5f;
615 osdvertices[0].y = 0.f - 0.5f;
616 osdvertices[0].z = 0.5f;
617 osdvertices[0].rhw = 1.f;
618 osdvertices[0].u = 0.f;
619 osdvertices[0].v = 0.f;
620 osdvertices[1].c = osdcolor;
621 osdvertices[1].x = ((float)width) - 0.5f;
622 osdvertices[1].y = 0.f - 0.5f;
623 osdvertices[1].z = 0.5f;
624 osdvertices[1].u = texx;
625 osdvertices[1].v = 0.f;
626 osdvertices[1].rhw = 1.f;
627 osdvertices[2].c = osdcolor;
628 osdvertices[2].x = ((float)width) - 0.5f;
629 osdvertices[2].y = ((float)height) - 0.5f;
630 osdvertices[2].z = 0.5f;
631 osdvertices[2].rhw = 1.f;
632 osdvertices[2].u = texx;
633 osdvertices[2].v = texy;
634 osdvertices[3].c = osdcolor;
635 osdvertices[3].x = 0.f - 0.5f;
636 osdvertices[3].y = ((float)height) - 0.5f;
637 osdvertices[3].z = 0.5f;
638 osdvertices[3].rhw = 1.f;
639 osdvertices[3].u = 0.f;
640 osdvertices[3].v = texy;
642 if (d3ddevice->CreateVertexBuffer(4 * sizeof(OSDVERTEX), 0, D3DFVF_OSDVERTEX, D3DPOOL_DEFAULT,
643 &ret, NULL) != D3D_OK) {
646 void *pvertex = NULL;
647 if (ret->Lock(0, sizeof(osdvertices), &pvertex, 0) != D3D_OK) {
650 memcpy(pvertex, osdvertices, sizeof(osdvertices));
655 LPDIRECT3DVERTEXBUFFER9 WindowsOsd::InitBackgroundVertexBuffer(DWORD width, DWORD height)
657 LPDIRECT3DVERTEXBUFFER9 ret = NULL;
660 getTextureCoordinates(&texx, &texy);
662 DrawStyle bg = DrawStyle::WALLPAPER;
663 if (bg.ft != DrawStyle::GradientLinear) {
668 OSDVERTEX osdvertices[4];
669 osdvertices[0].c = D3DCOLOR_RGBA(bg.red,bg.green,bg.blue,0xff);
670 osdvertices[0].x = 0.f - 0.5f;
671 osdvertices[0].y = 0.f - 0.5f;
672 osdvertices[0].z = 0.5f;
673 osdvertices[0].rhw = 1.f;
674 osdvertices[0].u = 0.f;
675 osdvertices[0].v = 0.f;
676 osdvertices[1].c = D3DCOLOR_RGBA(bg.red, bg.green, bg.blue, 0xff);
677 osdvertices[1].x = ((float)width) - 0.5f;
678 osdvertices[1].y = 0.f - 0.5f;
679 osdvertices[1].z = 0.5f;
680 osdvertices[1].u = texx;
681 osdvertices[1].v = 0.f;
682 osdvertices[1].rhw = 1.f;
683 osdvertices[2].c = D3DCOLOR_RGBA(bg.grad_col[0].red, bg.grad_col[0].green,
684 bg.grad_col[0].blue, 0xff);
685 osdvertices[2].x = ((float)width) - 0.5f;
686 osdvertices[2].y = ((float)height) - 0.5f;
687 osdvertices[2].z = 0.5f;
688 osdvertices[2].rhw = 1.f;
689 osdvertices[2].u = texx;
690 osdvertices[2].v = texy;
691 osdvertices[3].c = D3DCOLOR_RGBA(bg.grad_col[0].red, bg.grad_col[0].green,
692 bg.grad_col[0].blue, 0xff);
693 osdvertices[3].x = 0.f - 0.5f;
694 osdvertices[3].y = ((float)height) - 0.5f;
695 osdvertices[3].z = 0.5f;
696 osdvertices[3].rhw = 1.f;
697 osdvertices[3].u = 0.f;
698 osdvertices[3].v = texy;
700 if (d3ddevice->CreateVertexBuffer(4 * sizeof(OSDVERTEX), 0, D3DFVF_OSDVERTEX, D3DPOOL_DEFAULT,
701 &ret, NULL) != D3D_OK) {
704 void *pvertex = NULL;
705 if (ret->Lock(0, sizeof(osdvertices), &pvertex, 0) != D3D_OK) {
708 memcpy(pvertex, osdvertices, sizeof(osdvertices));