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.
21 #include "windowsosd.h"
22 #include "dsallocator.h"
28 typedef HRESULT(__stdcall *FCT_DXVA2CreateDirect3DDeviceManager9)(u4* pResetToken, IDirect3DDeviceManager9** ppDeviceManager);
29 typedef HRESULT(__stdcall *FCT_MFCreateVideoSampleFromSurface)(IUnknown* pUnkSurface, IMFSample** ppSample);
31 FCT_DXVA2CreateDirect3DDeviceManager9 ptrDXVA2CreateDirect3DDeviceManager9 = NULL;
32 FCT_MFCreateVideoSampleFromSurface ptrMFCreateVideoSampleFromSurface = NULL;
34 WindowsOsd::WindowsOsd()
41 filter_type = D3DTEXF_FORCE_DWORD;
42 evrstate = EVR_pres_off;
48 external_driving = false;
51 lastrendertime = timeGetTime();
52 event = CreateEvent(NULL,/*FALSE*/TRUE, FALSE, NULL);
55 dxvadevicehandle = NULL;
57 hlib = LoadLibrary("dxva2.dll");
62 ptrDXVA2CreateDirect3DDeviceManager9 = (FCT_DXVA2CreateDirect3DDeviceManager9)GetProcAddress(hlib, "DXVA2CreateDirect3DDeviceManager9");
63 if (!ptrDXVA2CreateDirect3DDeviceManager9){
68 hlib = LoadLibrary("evr.dll");
74 ptrMFCreateVideoSampleFromSurface = (FCT_MFCreateVideoSampleFromSurface)GetProcAddress(hlib, "MFCreateVideoSampleFromSurface");
75 if (!ptrMFCreateVideoSampleFromSurface){
82 WindowsOsd::~WindowsOsd()
88 void WindowsOsd::LockDevice()
90 if (!evrsupported) return;
91 if (!dxvadevicehandle)
93 d3ddevman->OpenDeviceHandle(&dxvadevicehandle);
95 IDirect3DDevice9 *temp;
96 d3ddevman->LockDevice(dxvadevicehandle, &temp, TRUE);
100 void WindowsOsd::UnlockDevice()
102 if (!evrsupported) return;
103 if (!isInitialized()) return;
104 d3ddevman->UnlockDevice(dxvadevicehandle, TRUE);
105 if (dxvadevicehandle)
107 d3ddevman->CloseDeviceHandle(dxvadevicehandle);
108 dxvadevicehandle = NULL;
113 int WindowsOsd::createDirect3D9Objects()
115 Video* video = Video::getInstance();
116 //First Create Direct 3D Object
117 #ifdef WINDOWS_LEGACY
118 d3d = Direct3DCreate9(D3D_SDK_VERSION);
121 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 object!");
125 HRESULT err = Direct3DCreate9Ex(D3D_SDK_VERSION, &d3d);
128 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9Ex object! %d",err);
133 // then create the Device
134 D3DPRESENT_PARAMETERS d3dparas;
135 ZeroMemory(&d3dparas, sizeof(d3dparas));
136 d3dparas.BackBufferWidth = BACKBUFFER_WIDTH;
137 d3dparas.BackBufferHeight = BACKBUFFER_HEIGHT;
138 d3dparas.Windowed = TRUE;
139 d3dparas.SwapEffect = D3DSWAPEFFECT_COPY;
140 #ifdef WINDOWS_LEGACY
141 if (d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window,
142 D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED, &d3dparas, &d3ddevice) != D3D_OK) {
143 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 device!");
147 if (d3d->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window,
148 D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED, &d3dparas, NULL, &d3ddevice) != D3D_OK) {
149 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 device!");
153 d3ddevice->GetRenderTarget(0, &d3drtsurf);
156 if (!InitVertexBuffer()) {
157 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 vertex buf!");
160 /* We have to determine which kind of filtering is supported*/
162 d3ddevice->GetDeviceCaps(&caps);
163 if (((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFLINEAR) != 0)
164 && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR) != 0)) {
165 if (filter_type == D3DTEXF_FORCE_DWORD) {
166 filter_type = D3DTEXF_LINEAR;
170 if (filter_type == D3DTEXF_LINEAR)
172 filter_type = D3DTEXF_POINT;
176 if (((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFPOINT) != 0)
177 && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFPOINT) != 0)) {
178 if (filter_type == D3DTEXF_FORCE_DWORD)
180 filter_type = D3DTEXF_POINT;
184 if (filter_type == D3DTEXF_POINT) {
185 filter_type = D3DTEXF_NONE;
188 if (filter_type == D3DTEXF_FORCE_DWORD) {
189 filter_type = D3DTEXF_NONE;
194 if (ptrDXVA2CreateDirect3DDeviceManager9(&dxvatoken, &d3ddevman) != S_OK) evrsupported = false;
196 d3ddevman->ResetDevice(d3ddevice, dxvatoken);
199 SetEvent(event);//Now all devices are ready
203 void WindowsOsd::startRenderLoop()
208 void WindowsOsd::stopRenderLoop()
213 void WindowsOsd::shutdownDirect3D9Objects()
217 if (d3ddevman) d3ddevman->Release();
218 d3drtsurf->Release();
219 d3ddevice->Release();
221 if (swapsurf) swapsurf->Release();
222 if (swappy) swappy->Release();
227 #pragma warning(disable : 4703)
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));
262 void WindowsOsd::setExternalDriving(DsAllocator* dsall, DWORD width, DWORD height) {
267 d3ddevice->StretchRect(swapsurf, NULL, d3drtsurf, NULL, filter_type);
268 LPDIRECT3DSWAPCHAIN9 temp = swappy;
269 LPDIRECT3DSURFACE9 tempsurf = swapsurf;
278 external_driving = false;
282 WaitForSingleObject(event, INFINITE);//We will only return if we are initted
285 if (width>BACKBUFFER_WIDTH || height>BACKBUFFER_HEIGHT)
287 D3DPRESENT_PARAMETERS d3dparas;
288 ZeroMemory(&d3dparas, sizeof(d3dparas));
289 d3dparas.BackBufferWidth = width;
290 d3dparas.BackBufferHeight = height;
291 d3dparas.Windowed = TRUE;
292 d3dparas.SwapEffect = D3DSWAPEFFECT_COPY;
293 if (d3ddevice->CreateAdditionalSwapChain(&d3dparas, &swappy) != D3D_OK){
294 Log::getInstance()->log("OSD", Log::WARN, "Could not create Swap Chain!");
298 swappy->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &swapsurf);
300 Log::getInstance()->log("OSD", Log::INFO, "Create Additional Swap Chain %d %d!", width, height);
304 external_driving = true;
309 // This function is called from the WinMain function in order to get Screen updates
310 void WindowsOsd::Render()
312 if (!isInitialized()) return;
313 if (external_driving) {
314 DWORD time1 = timeGetTime(); //Updates the Menue
315 if ((time1 - lastrendertime)>200) {//5 fps for OSD updates are enough, avoids tearing
317 InternalRendering(NULL);
318 lastosdrendertime = lastrendertime = timeGetTime();
321 //Sleep(5); //Sleep for 5 ms, in order to avoid blocking the other threads
325 DWORD time1 = timeGetTime();
326 if ((time1 - lastrendertime)>50) {//10 fps for OSD updates are enough, avoids tearing
328 InternalRendering(NULL);
329 lastosdrendertime = lastrendertime = timeGetTime();
339 void WindowsOsd::RenderDS(LPDIRECT3DSURFACE9 present){
340 if (!isInitialized()) return;
341 if (external_driving) {
342 InternalRendering(present);
343 lastrendertime = timeGetTime();
345 if ((lastrendertime - lastosdrendertime) > 50) {
347 lastosdrendertime = timeGetTime();
352 void WindowsOsd::BeginPainting() {//We synchronize calls to d3d between different threads
357 void WindowsOsd::EndPainting() {
362 DWORD WindowsOsd::getFilterCaps()
364 if (!isInitialized()) return NULL;
366 d3ddevice->GetDeviceCaps(&caps);
367 return caps.StretchRectFilterCaps;
370 LPDIRECT3DDEVICE9 WindowsOsd::getD3dDev() {
371 WaitForSingleObject(event, INFINITE);//We will only return if we are initted
375 LPDIRECT3D9 WindowsOsd::getD3d() {
376 WaitForSingleObject(event, INFINITE);//We will only return if we are initted
381 void WindowsOsd::Blank() {
382 WaitForSingleObject(event, INFINITE);
384 d3ddevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
388 void WindowsOsd::InternalRendering(LPDIRECT3DSURFACE9 present){
390 HRESULT losty = d3ddevice->TestCooperativeLevel();
391 if (losty == D3DERR_DEVICELOST) {
394 return; //Device Lost
396 if (losty == D3DERR_DEVICENOTRESET){
401 WaitForSingleObject(event, INFINITE);
403 LPDIRECT3DSURFACE9 targetsurf;
406 targetsurf = swapsurf;
407 d3ddevice->SetRenderTarget(0, swapsurf);//Stupid VMR manipulates the render target
411 targetsurf = d3drtsurf;
412 d3ddevice->SetRenderTarget(0, d3drtsurf);//Stupid VMR manipulates the render target
414 D3DSURFACE_DESC targetdesc;
415 targetsurf->GetDesc(&targetdesc);
417 d3ddevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
418 LPDIRECT3DVERTEXBUFFER9 vb2 = NULL;
419 vb2 = InitBackgroundVertexBuffer(targetdesc.Width, targetdesc.Height);
420 if (d3ddevice->BeginScene() == D3D_OK) {
421 d3ddevice->SetStreamSource(0, vb2, 0, sizeof(OSDVERTEX));
422 d3ddevice->SetFVF(D3DFVF_OSDVERTEX);
423 d3ddevice->SetTexture(0, NULL);
425 d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
426 d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
427 d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
428 d3ddevice->SetRenderState(D3DRS_LIGHTING, FALSE);
430 d3ddevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
431 d3ddevice->EndScene();
436 if (external_driving) {
437 //Copy video to Backbuffer
438 if (present != NULL) {
439 VideoWin* video = (VideoWin*)Video::getInstance();
440 /*calculating destination rect */
441 RECT destrect = { 0, 0,/*video->getScreenWidth()*/ targetdesc.Width,
442 /*video->getScreenHeight()*/targetdesc.Height };
443 const VideoDisplay &vd = video->getVideoDisplay();
449 destrect.right = destrect.right / 2;
450 destrect.bottom = destrect.bottom / 2;
452 destrect.right = destrect.right / 2;
453 destrect.bottom = destrect.bottom / 2;
456 float imageaspect = 720.f / 576.f;
457 float boxaspect = ((float)vd.width) / ((float)vd.height);
458 float videoaspect = ((float)targetdesc.Width) / ((float)targetdesc.Height);
459 if (imageaspect > boxaspect) {
460 destrect.right = (int)(((float)destrect.right) * ((float)vd.width) / 720.f);
461 destrect.bottom = (int)(((float)destrect.right) / videoaspect);
462 addy += (((float)vd.height) - ((float)vd.width) / imageaspect)*0.5f / 576.f
463 *((float)targetdesc.Height);
466 destrect.bottom = (int)(((float)destrect.bottom) * ((float)vd.height) / 576.f);
467 destrect.right = (int)(((float)destrect.bottom)*videoaspect);
468 addx += (((float)vd.width) - ((float)vd.height) * imageaspect)*0.5f / 720.f
469 *((float)targetdesc.Width);
479 destrect.left = (int)(((float)vd.x*targetdesc.Width) / 720.f) + addx;
480 destrect.top = (int)(((float)vd.y*targetdesc.Height) / 576.f) + addy;
481 destrect.right += destrect.left;
482 destrect.bottom += destrect.top;
487 D3DSURFACE_DESC surf_desc;
488 present->GetDesc(&surf_desc);//for chop sides
489 RECT sourcerect = { 0, 0, surf_desc.Width, surf_desc.Height };
490 if (video->getPseudoTVsize() == Video::ASPECT4X3
491 && video->getMode() == Video::NORMAL
492 && video->getAspectRatio() == Video::ASPECT16X9) {
493 unsigned int correction = ((double)(surf_desc.Width))*4.*9. / 3. / 16.;
494 sourcerect.left = (surf_desc.Width - correction) / 2;
495 sourcerect.right = sourcerect.left + correction;
497 d3ddevice->StretchRect(present, &sourcerect, targetsurf, &destrect, filter_type);
502 LPDIRECT3DVERTEXBUFFER9 vb1 = NULL;
503 vb1 = InitVertexBuffer(targetdesc.Width, targetdesc.Height);
507 if (d3ddevice->BeginScene() == D3D_OK) {
508 d3ddevice->SetStreamSource(0, vb1, 0, sizeof(OSDVERTEX));
509 d3ddevice->SetFVF(D3DFVF_OSDVERTEX);
510 d3ddevice->SetTexture(0, NULL);
511 LPDIRECT3DTEXTURE9 osdtex = getNextOsdTexture();
512 d3ddevice->SetTexture(0, osdtex);
513 //d3ddevice->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_MODULATE);
514 d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
516 d3ddevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
517 d3ddevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
520 d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
521 d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
522 d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
523 d3ddevice->SetRenderState(D3DRS_LIGHTING, FALSE);
526 d3ddevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
527 d3ddevice->EndScene();
528 //Show it to the user!
532 if (hres = swappy->Present(NULL, NULL, NULL, NULL, 0) == D3DERR_DEVICELOST){
534 if (!external_driving) DoLost();
539 if (hres = d3ddevice->Present(NULL, NULL, NULL, NULL) == D3DERR_DEVICELOST){
541 if (!external_driving) DoLost();
551 // if (!external_driving) {
552 // Sleep(4);//The User can wait for 4 milliseconds to see his changes
556 bool WindowsOsd::DoLost(){
557 Log::getInstance()->log("OSD", Log::WARN, "Direct3D Device Lost! Reobtaining...");
559 if (external_driving && dsallocator != NULL) {
560 dsallocator->LostDevice(d3ddevice, d3d); //Propagate the information through DS
562 //First we free up all resources
563 Video* video = Video::getInstance();
564 lostDestroyObjects();
565 if (d3drtsurf) d3drtsurf->Release();
567 D3DPRESENT_PARAMETERS d3dparas;
568 ZeroMemory(&d3dparas, sizeof(d3dparas));
569 d3dparas.BackBufferWidth = BACKBUFFER_WIDTH;
570 d3dparas.BackBufferHeight = BACKBUFFER_HEIGHT;
571 d3dparas.Windowed = TRUE;
572 d3dparas.SwapEffect = D3DSWAPEFFECT_COPY;
574 if (swapsurf) { swapsurf->Release(); swapsurf = NULL; };
575 if (swappy) { swappy->Release(); swappy = NULL; };
577 if (d3ddevice->Reset(&d3dparas) != D3D_OK){
580 d3ddevice->GetRenderTarget(0, &d3drtsurf);
581 if (d3ddevman) d3ddevman->ResetDevice(d3ddevice, dxvatoken);
582 //InitVertexBuffer();
583 //Redraw Views, Chris could you add a member function to BoxStack, so that
584 // I can cause it to completely redraw the Views?
585 // Otherwise the OSD would be distorted after Device Lost
590 lostRecreateObjects();
597 LPDIRECT3DVERTEXBUFFER9 WindowsOsd::InitVertexBuffer(DWORD width, DWORD height)
599 LPDIRECT3DVERTEXBUFFER9 ret = NULL;
602 getTextureCoordinates(&texx, &texy);
604 D3DCOLOR osdcolor = D3DCOLOR_RGBA(255, 255, 255, 255);
605 OSDVERTEX osdvertices[4];
606 osdvertices[0].c = osdcolor;
607 osdvertices[0].x = 0.f - 0.5f;
608 osdvertices[0].y = 0.f - 0.5f;
609 osdvertices[0].z = 0.5f;
610 osdvertices[0].rhw = 1.f;
611 osdvertices[0].u = 0.f;
612 osdvertices[0].v = 0.f;
613 osdvertices[1].c = osdcolor;
614 osdvertices[1].x = ((float)width) - 0.5f;
615 osdvertices[1].y = 0.f - 0.5f;
616 osdvertices[1].z = 0.5f;
617 osdvertices[1].u = texx;
618 osdvertices[1].v = 0.f;
619 osdvertices[1].rhw = 1.f;
620 osdvertices[2].c = osdcolor;
621 osdvertices[2].x = ((float)width) - 0.5f;
622 osdvertices[2].y = ((float)height) - 0.5f;
623 osdvertices[2].z = 0.5f;
624 osdvertices[2].rhw = 1.f;
625 osdvertices[2].u = texx;
626 osdvertices[2].v = texy;
627 osdvertices[3].c = osdcolor;
628 osdvertices[3].x = 0.f - 0.5f;
629 osdvertices[3].y = ((float)height) - 0.5f;
630 osdvertices[3].z = 0.5f;
631 osdvertices[3].rhw = 1.f;
632 osdvertices[3].u = 0.f;
633 osdvertices[3].v = texy;
635 if (d3ddevice->CreateVertexBuffer(4 * sizeof(OSDVERTEX), 0, D3DFVF_OSDVERTEX, D3DPOOL_DEFAULT,
636 &ret, NULL) != D3D_OK) {
639 void *pvertex = NULL;
640 if (ret->Lock(0, sizeof(osdvertices), &pvertex, 0) != D3D_OK) {
643 memcpy(pvertex, osdvertices, sizeof(osdvertices));
648 LPDIRECT3DVERTEXBUFFER9 WindowsOsd::InitBackgroundVertexBuffer(DWORD width, DWORD height)
650 LPDIRECT3DVERTEXBUFFER9 ret = NULL;
653 getTextureCoordinates(&texx, &texy);
655 DrawStyle bg = DrawStyle::WALLPAPER;
656 if (bg.ft != DrawStyle::GradientLinear) {
661 OSDVERTEX osdvertices[4];
662 osdvertices[0].c = D3DCOLOR_RGBA(bg.red,bg.green,bg.blue,0xff);
663 osdvertices[0].x = 0.f - 0.5f;
664 osdvertices[0].y = 0.f - 0.5f;
665 osdvertices[0].z = 0.5f;
666 osdvertices[0].rhw = 1.f;
667 osdvertices[0].u = 0.f;
668 osdvertices[0].v = 0.f;
669 osdvertices[1].c = D3DCOLOR_RGBA(bg.red, bg.green, bg.blue, 0xff);
670 osdvertices[1].x = ((float)width) - 0.5f;
671 osdvertices[1].y = 0.f - 0.5f;
672 osdvertices[1].z = 0.5f;
673 osdvertices[1].u = texx;
674 osdvertices[1].v = 0.f;
675 osdvertices[1].rhw = 1.f;
676 osdvertices[2].c = D3DCOLOR_RGBA(bg.grad_col[0].red, bg.grad_col[0].green,
677 bg.grad_col[0].blue, 0xff);
678 osdvertices[2].x = ((float)width) - 0.5f;
679 osdvertices[2].y = ((float)height) - 0.5f;
680 osdvertices[2].z = 0.5f;
681 osdvertices[2].rhw = 1.f;
682 osdvertices[2].u = texx;
683 osdvertices[2].v = texy;
684 osdvertices[3].c = D3DCOLOR_RGBA(bg.grad_col[0].red, bg.grad_col[0].green,
685 bg.grad_col[0].blue, 0xff);
686 osdvertices[3].x = 0.f - 0.5f;
687 osdvertices[3].y = ((float)height) - 0.5f;
688 osdvertices[3].z = 0.5f;
689 osdvertices[3].rhw = 1.f;
690 osdvertices[3].u = 0.f;
691 osdvertices[3].v = texy;
693 if (d3ddevice->CreateVertexBuffer(4 * sizeof(OSDVERTEX), 0, D3DFVF_OSDVERTEX, D3DPOOL_DEFAULT,
694 &ret, NULL) != D3D_OK) {
697 void *pvertex = NULL;
698 if (ret->Lock(0, sizeof(osdvertices), &pvertex, 0) != D3D_OK) {
701 memcpy(pvertex, osdvertices, sizeof(osdvertices));