]> git.vomp.tv Git - vompclient.git/blob - windowsosd.cc
Split OsdWin into OsdWinPixel and WindowsOsd as preparation for Vector
[vompclient.git] / windowsosd.cc
1 /*
2         Copyright 2014 Marten Richter
3
4         This file is part of VOMP.
5
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.
10
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.
15
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.
19 */
20
21
22 #include "windowsosd.h"
23 #include "dsallocator.h"
24 #include "video.h"
25 #include "videowin.h"
26 #include "log.h"
27
28 #define  BACKBUFFER_WIDTH 1920
29 #define  BACKBUFFER_HEIGHT 1080
30
31 typedef HRESULT(__stdcall *FCT_DXVA2CreateDirect3DDeviceManager9)(UINT* pResetToken, IDirect3DDeviceManager9** ppDeviceManager);
32 typedef HRESULT(__stdcall *FCT_MFCreateVideoSampleFromSurface)(IUnknown* pUnkSurface, IMFSample** ppSample);
33
34 FCT_DXVA2CreateDirect3DDeviceManager9 ptrDXVA2CreateDirect3DDeviceManager9 = NULL;
35 FCT_MFCreateVideoSampleFromSurface ptrMFCreateVideoSampleFromSurface = NULL;
36
37
38 WindowsOsd::WindowsOsd()
39 {
40         d3d = NULL;
41         d3ddevice = NULL;
42         d3drtsurf = NULL;
43
44         evrsupported = true;
45         filter_type = D3DTEXF_FORCE_DWORD;
46         evrstate = EVR_pres_off;
47
48         swappy = NULL;
49         swapsurf = NULL;
50         window = NULL;
51
52         external_driving = false;
53         dsallocator = NULL;
54
55         lastrendertime = timeGetTime();
56         event = CreateEvent(NULL,/*FALSE*/TRUE, FALSE, NULL);
57         d3dmutex = CreateMutex(NULL, FALSE, NULL);
58
59         /*EVR stuff*/
60         dxvadevicehandle = NULL;
61         HMODULE hlib = NULL;
62         hlib = LoadLibrary("dxva2.dll");
63         if (!hlib) {
64                 evrsupported = false;
65                 return;
66         }
67         ptrDXVA2CreateDirect3DDeviceManager9 = (FCT_DXVA2CreateDirect3DDeviceManager9)GetProcAddress(hlib, "DXVA2CreateDirect3DDeviceManager9");
68         if (!ptrDXVA2CreateDirect3DDeviceManager9){
69                 evrsupported = false;
70                 return;
71         }
72
73         hlib = LoadLibrary("evr.dll");
74         if (!hlib) {
75                 evrsupported = false;
76                 return;
77         }
78
79         ptrMFCreateVideoSampleFromSurface = (FCT_MFCreateVideoSampleFromSurface)GetProcAddress(hlib, "MFCreateVideoSampleFromSurface");
80         if (!ptrMFCreateVideoSampleFromSurface){
81                 evrsupported = false;
82                 return;
83         }
84
85 }
86
87 WindowsOsd::~WindowsOsd()
88 {
89         CloseHandle(event);
90         CloseHandle(d3dmutex);
91 }
92
93
94 void WindowsOsd::LockDevice()
95 {
96         if (!evrsupported) return;
97         if (!dxvadevicehandle)
98         {
99                 d3ddevman->OpenDeviceHandle(&dxvadevicehandle);
100         }
101         IDirect3DDevice9 *temp;
102         d3ddevman->LockDevice(dxvadevicehandle, &temp, TRUE);
103
104 }
105
106 void WindowsOsd::UnlockDevice()
107 {
108         if (!evrsupported) return;
109         if (!isInitialized()) return;
110         d3ddevman->UnlockDevice(dxvadevicehandle, TRUE);
111         if (dxvadevicehandle)
112         {
113                 d3ddevman->CloseDeviceHandle(dxvadevicehandle);
114                 dxvadevicehandle = NULL;
115         }
116
117 }
118
119 int WindowsOsd::createDirect3D9Objects()
120 {
121         Video* video = Video::getInstance();
122         //First Create Direct 3D Object
123         d3d = Direct3DCreate9(D3D_SDK_VERSION);
124         if (!d3d)
125         {
126                 Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 object!");
127                 return 0;
128         }
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!");
139                 return 0;
140         }
141         d3ddevice->GetRenderTarget(0, &d3drtsurf);
142
143         /*
144         if (!InitVertexBuffer()) {
145         Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 vertex buf!");
146         return 0;
147         }*/
148         /* We have to determine which kind of filtering is supported*/
149         D3DCAPS9 caps;
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;
155                 }
156         }
157         else {
158                 if (filter_type == D3DTEXF_LINEAR)
159                 {
160                         filter_type = D3DTEXF_POINT;
161                 }
162         }
163
164         if (((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFPOINT) != 0)
165                 && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFPOINT) != 0)) {
166                 if (filter_type == D3DTEXF_FORCE_DWORD)
167                 {
168                         filter_type = D3DTEXF_POINT;
169                 }
170         }
171         else {
172                 if (filter_type == D3DTEXF_POINT) {
173                         filter_type = D3DTEXF_NONE;
174                 }
175         }
176         if (filter_type == D3DTEXF_FORCE_DWORD) {
177                 filter_type = D3DTEXF_NONE;
178         }
179
180         if (evrsupported)
181         {
182                 if (ptrDXVA2CreateDirect3DDeviceManager9(&dxvatoken, &d3ddevman) != S_OK) evrsupported = false;
183                 else {
184                         d3ddevman->ResetDevice(d3ddevice, dxvatoken);
185                 }
186         }
187         SetEvent(event);//Now all devices are ready
188         return 1;
189 }
190
191 void WindowsOsd::startRenderLoop()
192 {
193         threadStart();
194 }
195
196 void WindowsOsd::shutdownDirect3D9Objects()
197 {
198         threadStop();
199         evrsupported = 0;
200         if (d3ddevman) d3ddevman->Release();
201         d3drtsurf->Release();
202         d3ddevice->Release();
203         d3d->Release();
204         if (swapsurf) swapsurf->Release();
205         if (swappy) swappy->Release();
206 }
207
208 void WindowsOsd::threadMethod()
209 {
210         while (true)
211         {
212                 DWORD waittime = 10;
213                 if (isInitialized()){
214                         if (evrstate == EVR_pres_off || evrstate == EVR_pres_pause)
215                         {
216                                 Render();
217                         }
218                         else if (evrstate == EVR_pres_started)
219                         {
220                                 LPDIRECT3DSURFACE9 surf;
221                                 if (dsallocator) dsallocator->GetNextSurface(&surf, &waittime);
222                                 if (surf == NULL)
223                                 {
224                                         Render();
225                                 }
226                                 else
227                                 {
228                                         RenderDS(surf);
229                                         surf->Release();
230                                         if (dsallocator) dsallocator->DiscardSurfaceandgetWait(&waittime);
231                                 }
232                         }
233                 }
234                 threadCheckExit();
235                 if (waittime != 0) Sleep(min(10, waittime));
236                 //Sleep(1);
237         }
238 }
239
240
241 void WindowsOsd::threadPostStopCleanup()
242 {
243         //Doing nothing
244         //goo;
245 }
246
247 void WindowsOsd::setExternalDriving(DsAllocator* dsall, DWORD width, DWORD height) {
248
249         if (swappy)
250         {
251                 BeginPainting();
252                 d3ddevice->StretchRect(swapsurf, NULL, d3drtsurf, NULL, filter_type);
253                 LPDIRECT3DSWAPCHAIN9 temp = swappy;
254                 LPDIRECT3DSURFACE9 tempsurf = swapsurf;
255                 swappy = NULL;
256                 swapsurf = NULL;
257                 EndPainting();
258                 tempsurf->Release();
259                 temp->Release();
260         }
261
262         if (dsall == NULL) {
263                 external_driving = false;
264                 dsallocator = NULL;
265                 return;
266         }
267         WaitForSingleObject(event, INFINITE);//We will only return if we are initted
268         BeginPainting();
269
270         if (width>BACKBUFFER_WIDTH || height>BACKBUFFER_HEIGHT)
271         {
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!");
280                         //return 0;
281                 }
282                 else {
283                         swappy->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &swapsurf);
284                 }
285                 Log::getInstance()->log("OSD", Log::INFO, "Create Additional Swap Chain %d %d!", width, height);
286         }
287
288         dsallocator = dsall;
289         external_driving = true;
290
291         EndPainting();
292 }
293
294 // This function is called from the WinMain function in order to get Screen updates
295 void WindowsOsd::Render()
296 {
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();
303                 }
304                 else {
305                         //Sleep(5); //Sleep for 5 ms, in order to avoid blocking the other threads
306                 }
307         }
308         else {
309                 DWORD time1 = timeGetTime();
310                 if ((time1 - lastrendertime)>50) {//10 fps for OSD updates are enough, avoids tearing
311                         InternalRendering(NULL);
312                         lastrendertime = timeGetTime();
313                 }
314                 else {
315                         //Sleep(5);
316
317                 }
318
319         }
320 }
321
322 void WindowsOsd::RenderDS(LPDIRECT3DSURFACE9 present){
323         if (!isInitialized()) return;
324         if (external_driving) {
325                 InternalRendering(present);
326                 lastrendertime = timeGetTime();
327         }
328 }
329
330
331 void WindowsOsd::BeginPainting() {//We synchronize calls to d3d between different threads
332         WaitForSingleObject(d3dmutex, INFINITE);
333         LockDevice();
334 }
335
336 void WindowsOsd::EndPainting() {
337         UnlockDevice();
338         ReleaseMutex(d3dmutex);
339 }
340
341 DWORD WindowsOsd::getFilterCaps()
342 {
343         if (!isInitialized()) return NULL;
344         D3DCAPS9 caps;
345         d3ddevice->GetDeviceCaps(&caps);
346         return caps.StretchRectFilterCaps;
347 }
348
349 LPDIRECT3DDEVICE9 WindowsOsd::getD3dDev() {
350         WaitForSingleObject(event, INFINITE);//We will only return if we are initted
351         return d3ddevice;
352 }
353
354 LPDIRECT3D9 WindowsOsd::getD3d() {
355         WaitForSingleObject(event, INFINITE);//We will only return if we are initted
356         return d3d;
357 }
358
359
360 void WindowsOsd::Blank() {
361         WaitForSingleObject(event, INFINITE);
362         BeginPainting();
363         d3ddevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
364         EndPainting();
365 }
366
367 void WindowsOsd::InternalRendering(LPDIRECT3DSURFACE9 present){
368         BeginPainting();
369         HRESULT losty = d3ddevice->TestCooperativeLevel();
370         if (losty == D3DERR_DEVICELOST) {
371                 //Sleep(10);
372                 EndPainting();
373                 return; //Device Lost
374         }
375         if (losty == D3DERR_DEVICENOTRESET){
376                 EndPainting();
377                 DoLost();
378                 return;
379         }
380         WaitForSingleObject(event, INFINITE);
381
382         LPDIRECT3DSURFACE9 targetsurf;
383         if (swappy)
384         {
385                 targetsurf = swapsurf;
386                 d3ddevice->SetRenderTarget(0, swapsurf);//Stupid VMR manipulates the render target
387         }
388         else
389         {
390                 targetsurf = d3drtsurf;
391                 d3ddevice->SetRenderTarget(0, d3drtsurf);//Stupid VMR manipulates the render target
392         }
393         D3DSURFACE_DESC targetdesc;
394         targetsurf->GetDesc(&targetdesc);
395
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();
404                         int addx, addy;
405                         addx = addy = 0;
406
407                         switch (vd.mode) {
408                         case Eighth:
409                                 destrect.right = destrect.right / 2;
410                                 destrect.bottom = destrect.bottom / 2;
411                         case Quarter:
412                                 destrect.right = destrect.right / 2;
413                                 destrect.bottom = destrect.bottom / 2;
414                                 break;
415                         case Window:
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);
424                                 }
425                                 else {
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);
430                                 }
431
432
433                                 break;
434                         };
435                         switch (vd.mode) {
436                         case Quarter:
437                         case Eighth:
438                         case Window:
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;
443                                 break;
444                         }
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;
455                         }
456                         d3ddevice->StretchRect(present, &sourcerect, targetsurf, &destrect, filter_type);
457
458                 }
459         }
460         else {
461                 VideoWin* video = (VideoWin*)Video::getInstance();
462                 //Clear Background
463                 if (!video->isVideoOn()) d3ddevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
464         }
465         LPDIRECT3DVERTEXBUFFER9 vb = NULL;
466         vb = InitVertexBuffer(targetdesc.Width, targetdesc.Height);
467
468         //Drawing the OSD
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);
476
477                 d3ddevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
478                 d3ddevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
479
480
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);
485
486
487                 d3ddevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
488                 d3ddevice->EndScene();
489                 //Show it to the user!
490                 HRESULT hres;
491                 if (swappy)
492                 {
493                         if (hres = swappy->Present(NULL, NULL, NULL, NULL, 0) == D3DERR_DEVICELOST){
494                                 //EndPainting();
495                                 if (!external_driving) DoLost();
496                         }
497                 }
498                 else
499                 {
500                         if (hres = d3ddevice->Present(NULL, NULL, NULL, NULL) == D3DERR_DEVICELOST){
501                                 //EndPainting();
502                                 if (!external_driving) DoLost();
503                         }
504                 }
505
506         }
507
508         vb->Release();
509         EndPainting();
510
511
512         //      if (!external_driving) {
513         //              Sleep(4);//The User can wait for 4 milliseconds to see his changes
514         //      }
515 }
516
517 bool WindowsOsd::DoLost(){
518         Log::getInstance()->log("OSD", Log::WARN, "Direct3D Device Lost! Reobtaining...");
519         ResetEvent(event);
520         if (external_driving && dsallocator != NULL) {
521                 dsallocator->LostDevice(d3ddevice, d3d); //Propagate the information through DS
522         }
523         //First we free up all resources
524         Video* video = Video::getInstance();
525         lostDestroyObjects();
526         if (d3drtsurf) d3drtsurf->Release();
527         d3drtsurf = NULL;
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;
534
535         if (swapsurf) { swapsurf->Release(); swapsurf = NULL; };
536         if (swappy) { swappy->Release(); swappy = NULL; };
537
538         if (d3ddevice->Reset(&d3dparas) != D3D_OK){
539                 return false;
540         }
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
547         // FIXME
548
549         SetEvent(event);
550
551         lostRecreateObjects();
552
553
554         return true;
555
556 }
557
558 LPDIRECT3DVERTEXBUFFER9  WindowsOsd::InitVertexBuffer(DWORD width, DWORD height)
559 {
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;
593
594         if (d3ddevice->CreateVertexBuffer(4 * sizeof(OSDVERTEX), 0, D3DFVF_OSDVERTEX, D3DPOOL_MANAGED,
595                 &ret, NULL) != D3D_OK) {
596                 return NULL;
597         }
598         void *pvertex = NULL;
599         if (ret->Lock(0, sizeof(osdvertices), &pvertex, 0) != D3D_OK) {
600                 return NULL;
601         }
602         memcpy(pvertex, osdvertices, sizeof(osdvertices));
603         ret->Unlock();
604         return ret;
605 }