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();
232 #pragma warning(disable : 4703)
235 void WindowsOsd::threadMethod()
240 if (isInitialized()){
241 if (evrstate == EVR_pres_off || evrstate == EVR_pres_pause)
245 else if (evrstate == EVR_pres_started)
247 LPDIRECT3DSURFACE9 surf;
248 if (dsallocator) dsallocator->GetNextSurface(&surf, &waittime);
257 if (dsallocator) dsallocator->DiscardSurfaceandgetWait(&waittime);
262 if (waittime != 0) Sleep(min(10, waittime));
268 void WindowsOsd::threadPostStopCleanup()
274 void WindowsOsd::setExternalDriving(DsAllocator* dsall, DWORD width, DWORD height) {
279 d3ddevice->StretchRect(swapsurf, NULL, d3drtsurf, NULL, filter_type);
280 LPDIRECT3DSWAPCHAIN9 temp = swappy;
281 LPDIRECT3DSURFACE9 tempsurf = swapsurf;
290 external_driving = false;
294 WaitForSingleObject(event, INFINITE);//We will only return if we are initted
297 if (width>BACKBUFFER_WIDTH || height>BACKBUFFER_HEIGHT)
299 D3DPRESENT_PARAMETERS d3dparas;
300 ZeroMemory(&d3dparas, sizeof(d3dparas));
301 d3dparas.BackBufferWidth = width;
302 d3dparas.BackBufferHeight = height;
303 d3dparas.Windowed = TRUE;
304 d3dparas.SwapEffect = D3DSWAPEFFECT_COPY;
305 if (d3ddevice->CreateAdditionalSwapChain(&d3dparas, &swappy) != D3D_OK){
306 Log::getInstance()->log("OSD", Log::WARN, "Could not create Swap Chain!");
310 swappy->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &swapsurf);
312 Log::getInstance()->log("OSD", Log::INFO, "Create Additional Swap Chain %d %d!", width, height);
316 external_driving = true;
321 // This function is called from the WinMain function in order to get Screen updates
322 void WindowsOsd::Render()
324 if (!isInitialized()) return;
325 if (external_driving) {
326 DWORD time1 = timeGetTime(); //Updates the Menue
327 if ((time1 - lastrendertime)>200) {//5 fps for OSD updates are enough, avoids tearing
329 InternalRendering(NULL);
330 lastosdrendertime = lastrendertime = timeGetTime();
333 //Sleep(5); //Sleep for 5 ms, in order to avoid blocking the other threads
337 DWORD time1 = timeGetTime();
338 if ((time1 - lastrendertime)>50) {//10 fps for OSD updates are enough, avoids tearing
340 InternalRendering(NULL);
341 lastosdrendertime = lastrendertime = timeGetTime();
351 void WindowsOsd::RenderDS(LPDIRECT3DSURFACE9 present){
352 if (!isInitialized()) return;
353 if (external_driving) {
354 InternalRendering(present);
355 lastrendertime = timeGetTime();
357 if ((lastrendertime - lastosdrendertime) > 50) {
359 lastosdrendertime = timeGetTime();
364 void WindowsOsd::BeginPainting() {//We synchronize calls to d3d between different threads
365 WaitForSingleObject(d3dmutex, INFINITE);
369 void WindowsOsd::EndPainting() {
371 ReleaseMutex(d3dmutex);
374 DWORD WindowsOsd::getFilterCaps()
376 if (!isInitialized()) return NULL;
378 d3ddevice->GetDeviceCaps(&caps);
379 return caps.StretchRectFilterCaps;
382 LPDIRECT3DDEVICE9 WindowsOsd::getD3dDev() {
383 WaitForSingleObject(event, INFINITE);//We will only return if we are initted
387 LPDIRECT3D9 WindowsOsd::getD3d() {
388 WaitForSingleObject(event, INFINITE);//We will only return if we are initted
393 void WindowsOsd::Blank() {
394 WaitForSingleObject(event, INFINITE);
396 d3ddevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
400 void WindowsOsd::InternalRendering(LPDIRECT3DSURFACE9 present){
402 HRESULT losty = d3ddevice->TestCooperativeLevel();
403 if (losty == D3DERR_DEVICELOST) {
406 return; //Device Lost
408 if (losty == D3DERR_DEVICENOTRESET){
413 WaitForSingleObject(event, INFINITE);
415 LPDIRECT3DSURFACE9 targetsurf;
418 targetsurf = swapsurf;
419 d3ddevice->SetRenderTarget(0, swapsurf);//Stupid VMR manipulates the render target
423 targetsurf = d3drtsurf;
424 d3ddevice->SetRenderTarget(0, d3drtsurf);//Stupid VMR manipulates the render target
426 D3DSURFACE_DESC targetdesc;
427 targetsurf->GetDesc(&targetdesc);
429 d3ddevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
430 LPDIRECT3DVERTEXBUFFER9 vb2 = NULL;
431 vb2 = InitBackgroundVertexBuffer(targetdesc.Width, targetdesc.Height);
432 if (d3ddevice->BeginScene() == D3D_OK) {
433 d3ddevice->SetStreamSource(0, vb2, 0, sizeof(OSDVERTEX));
434 d3ddevice->SetFVF(D3DFVF_OSDVERTEX);
435 d3ddevice->SetTexture(0, NULL);
437 d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
438 d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
439 d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
440 d3ddevice->SetRenderState(D3DRS_LIGHTING, FALSE);
442 d3ddevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
443 d3ddevice->EndScene();
448 if (external_driving) {
449 //Copy video to Backbuffer
450 if (present != NULL) {
451 VideoWin* video = (VideoWin*)Video::getInstance();
452 /*calculating destination rect */
453 RECT destrect = { 0, 0,/*video->getScreenWidth()*/ targetdesc.Width,
454 /*video->getScreenHeight()*/targetdesc.Height };
455 const VideoDisplay &vd = video->getVideoDisplay();
461 destrect.right = destrect.right / 2;
462 destrect.bottom = destrect.bottom / 2;
464 destrect.right = destrect.right / 2;
465 destrect.bottom = destrect.bottom / 2;
468 float imageaspect = 720.f / 576.f;
469 float boxaspect = ((float)vd.width) / ((float)vd.height);
470 float videoaspect = ((float)targetdesc.Width) / ((float)targetdesc.Height);
471 if (imageaspect > boxaspect) {
472 destrect.right = (int)(((float)destrect.right) * ((float)vd.width) / 720.f);
473 destrect.bottom = (int)(((float)destrect.right) / videoaspect);
474 addy += (((float)vd.height) - ((float)vd.width) / imageaspect)*0.5f / 576.f
475 *((float)targetdesc.Height);
478 destrect.bottom = (int)(((float)destrect.bottom) * ((float)vd.height) / 576.f);
479 destrect.right = (int)(((float)destrect.bottom)*videoaspect);
480 addx += (((float)vd.width) - ((float)vd.height) * imageaspect)*0.5f / 720.f
481 *((float)targetdesc.Width);
491 destrect.left = (int)(((float)vd.x*targetdesc.Width) / 720.f) + addx;
492 destrect.top = (int)(((float)vd.y*targetdesc.Height) / 576.f) + addy;
493 destrect.right += destrect.left;
494 destrect.bottom += destrect.top;
499 D3DSURFACE_DESC surf_desc;
500 present->GetDesc(&surf_desc);//for chop sides
501 RECT sourcerect = { 0, 0, surf_desc.Width, surf_desc.Height };
502 if (video->getPseudoTVsize() == Video::ASPECT4X3
503 && video->getMode() == Video::NORMAL
504 && video->getAspectRatio() == Video::ASPECT16X9) {
505 unsigned int correction = ((double)(surf_desc.Width))*4.*9. / 3. / 16.;
506 sourcerect.left = (surf_desc.Width - correction) / 2;
507 sourcerect.right = sourcerect.left + correction;
509 d3ddevice->StretchRect(present, &sourcerect, targetsurf, &destrect, filter_type);
514 LPDIRECT3DVERTEXBUFFER9 vb1 = NULL;
515 vb1 = InitVertexBuffer(targetdesc.Width, targetdesc.Height);
519 if (d3ddevice->BeginScene() == D3D_OK) {
520 d3ddevice->SetStreamSource(0, vb1, 0, sizeof(OSDVERTEX));
521 d3ddevice->SetFVF(D3DFVF_OSDVERTEX);
522 d3ddevice->SetTexture(0, NULL);
523 LPDIRECT3DTEXTURE9 osdtex = getNextOsdTexture();
524 d3ddevice->SetTexture(0, osdtex);
525 //d3ddevice->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_MODULATE);
526 d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
528 d3ddevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
529 d3ddevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
532 d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
533 d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
534 d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
535 d3ddevice->SetRenderState(D3DRS_LIGHTING, FALSE);
538 d3ddevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
539 d3ddevice->EndScene();
540 //Show it to the user!
544 if (hres = swappy->Present(NULL, NULL, NULL, NULL, 0) == D3DERR_DEVICELOST){
546 if (!external_driving) DoLost();
551 if (hres = d3ddevice->Present(NULL, NULL, NULL, NULL) == D3DERR_DEVICELOST){
553 if (!external_driving) DoLost();
563 // if (!external_driving) {
564 // Sleep(4);//The User can wait for 4 milliseconds to see his changes
568 bool WindowsOsd::DoLost(){
569 Log::getInstance()->log("OSD", Log::WARN, "Direct3D Device Lost! Reobtaining...");
571 if (external_driving && dsallocator != NULL) {
572 dsallocator->LostDevice(d3ddevice, d3d); //Propagate the information through DS
574 //First we free up all resources
575 Video* video = Video::getInstance();
576 lostDestroyObjects();
577 if (d3drtsurf) d3drtsurf->Release();
579 D3DPRESENT_PARAMETERS d3dparas;
580 ZeroMemory(&d3dparas, sizeof(d3dparas));
581 d3dparas.BackBufferWidth = BACKBUFFER_WIDTH;
582 d3dparas.BackBufferHeight = BACKBUFFER_HEIGHT;
583 d3dparas.Windowed = TRUE;
584 d3dparas.SwapEffect = D3DSWAPEFFECT_COPY;
586 if (swapsurf) { swapsurf->Release(); swapsurf = NULL; };
587 if (swappy) { swappy->Release(); swappy = NULL; };
589 if (d3ddevice->Reset(&d3dparas) != D3D_OK){
592 d3ddevice->GetRenderTarget(0, &d3drtsurf);
593 if (d3ddevman) d3ddevman->ResetDevice(d3ddevice, dxvatoken);
594 //InitVertexBuffer();
595 //Redraw Views, Chris could you add a member function to BoxStack, so that
596 // I can cause it to completely redraw the Views?
597 // Otherwise the OSD would be distorted after Device Lost
602 lostRecreateObjects();
609 LPDIRECT3DVERTEXBUFFER9 WindowsOsd::InitVertexBuffer(DWORD width, DWORD height)
611 LPDIRECT3DVERTEXBUFFER9 ret = NULL;
614 getTextureCoordinates(&texx, &texy);
616 D3DCOLOR osdcolor = D3DCOLOR_RGBA(255, 255, 255, 255);
617 OSDVERTEX osdvertices[4];
618 osdvertices[0].c = osdcolor;
619 osdvertices[0].x = 0.f - 0.5f;
620 osdvertices[0].y = 0.f - 0.5f;
621 osdvertices[0].z = 0.5f;
622 osdvertices[0].rhw = 1.f;
623 osdvertices[0].u = 0.f;
624 osdvertices[0].v = 0.f;
625 osdvertices[1].c = osdcolor;
626 osdvertices[1].x = ((float)width) - 0.5f;
627 osdvertices[1].y = 0.f - 0.5f;
628 osdvertices[1].z = 0.5f;
629 osdvertices[1].u = texx;
630 osdvertices[1].v = 0.f;
631 osdvertices[1].rhw = 1.f;
632 osdvertices[2].c = osdcolor;
633 osdvertices[2].x = ((float)width) - 0.5f;
634 osdvertices[2].y = ((float)height) - 0.5f;
635 osdvertices[2].z = 0.5f;
636 osdvertices[2].rhw = 1.f;
637 osdvertices[2].u = texx;
638 osdvertices[2].v = texy;
639 osdvertices[3].c = osdcolor;
640 osdvertices[3].x = 0.f - 0.5f;
641 osdvertices[3].y = ((float)height) - 0.5f;
642 osdvertices[3].z = 0.5f;
643 osdvertices[3].rhw = 1.f;
644 osdvertices[3].u = 0.f;
645 osdvertices[3].v = texy;
647 if (d3ddevice->CreateVertexBuffer(4 * sizeof(OSDVERTEX), 0, D3DFVF_OSDVERTEX, D3DPOOL_DEFAULT,
648 &ret, NULL) != D3D_OK) {
651 void *pvertex = NULL;
652 if (ret->Lock(0, sizeof(osdvertices), &pvertex, 0) != D3D_OK) {
655 memcpy(pvertex, osdvertices, sizeof(osdvertices));
660 LPDIRECT3DVERTEXBUFFER9 WindowsOsd::InitBackgroundVertexBuffer(DWORD width, DWORD height)
662 LPDIRECT3DVERTEXBUFFER9 ret = NULL;
665 getTextureCoordinates(&texx, &texy);
667 DrawStyle bg = DrawStyle::WALLPAPER;
668 if (bg.ft != DrawStyle::GradientLinear) {
673 OSDVERTEX osdvertices[4];
674 osdvertices[0].c = D3DCOLOR_RGBA(bg.red,bg.green,bg.blue,0xff);
675 osdvertices[0].x = 0.f - 0.5f;
676 osdvertices[0].y = 0.f - 0.5f;
677 osdvertices[0].z = 0.5f;
678 osdvertices[0].rhw = 1.f;
679 osdvertices[0].u = 0.f;
680 osdvertices[0].v = 0.f;
681 osdvertices[1].c = D3DCOLOR_RGBA(bg.red, bg.green, bg.blue, 0xff);
682 osdvertices[1].x = ((float)width) - 0.5f;
683 osdvertices[1].y = 0.f - 0.5f;
684 osdvertices[1].z = 0.5f;
685 osdvertices[1].u = texx;
686 osdvertices[1].v = 0.f;
687 osdvertices[1].rhw = 1.f;
688 osdvertices[2].c = D3DCOLOR_RGBA(bg.grad_col[0].red, bg.grad_col[0].green,
689 bg.grad_col[0].blue, 0xff);
690 osdvertices[2].x = ((float)width) - 0.5f;
691 osdvertices[2].y = ((float)height) - 0.5f;
692 osdvertices[2].z = 0.5f;
693 osdvertices[2].rhw = 1.f;
694 osdvertices[2].u = texx;
695 osdvertices[2].v = texy;
696 osdvertices[3].c = D3DCOLOR_RGBA(bg.grad_col[0].red, bg.grad_col[0].green,
697 bg.grad_col[0].blue, 0xff);
698 osdvertices[3].x = 0.f - 0.5f;
699 osdvertices[3].y = ((float)height) - 0.5f;
700 osdvertices[3].z = 0.5f;
701 osdvertices[3].rhw = 1.f;
702 osdvertices[3].u = 0.f;
703 osdvertices[3].v = texy;
705 if (d3ddevice->CreateVertexBuffer(4 * sizeof(OSDVERTEX), 0, D3DFVF_OSDVERTEX, D3DPOOL_DEFAULT,
706 &ret, NULL) != D3D_OK) {
709 void *pvertex = NULL;
710 if (ret->Lock(0, sizeof(osdvertices), &pvertex, 0) != D3D_OK) {
713 memcpy(pvertex, osdvertices, sizeof(osdvertices));