]> git.vomp.tv Git - vompclient-marten.git/blob - osdopengl.cc
Switch to smaller OSD rendering buffer
[vompclient-marten.git] / osdopengl.cc
1 /*\r
2     Copyright 2004-2005 Chris Tallon, 2006,2011-2012 Marten Richter\r
3 \r
4     This file is part of VOMP.\r
5 \r
6     VOMP is free software; you can redistribute it and/or modify\r
7     it under the terms of the GNU General Public License as published by\r
8     the Free Software Foundation; either version 2 of the License, or\r
9     (at your option) any later version.\r
10 \r
11     VOMP is distributed in the hope that it will be useful,\r
12     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14     GNU General Public License for more details.\r
15 \r
16     You should have received a copy of the GNU General Public License\r
17     along with VOMP; if not, write to the Free Software\r
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\r
19 */\r
20 \r
21 \r
22 #include "osdopengl.h"\r
23 #include "mtd.h"\r
24 #include "videovpeogl.h"\r
25 #include "surfaceopengl.h"\r
26 \r
27 \r
28 #include "message.h"\r
29 #include "command.h"\r
30 \r
31 \r
32 #define  BACKBUFFER_WIDTH 1280\r
33 #define  BACKBUFFER_HEIGHT 720\r
34 \r
35 \r
36 \r
37 \r
38 \r
39 \r
40 \r
41 OsdOpenGL::OsdOpenGL()\r
42 {\r
43   glmutex.Lock();\r
44 \r
45   external_driving=false;\r
46 \r
47   lastrendertime=getTimeMS();\r
48   display_height=0;\r
49   display_width=0;\r
50 \r
51 #ifdef BENCHMARK_FPS\r
52         last_benchmark_time=getTimeMS();\r
53         num_benchmark_frames=0;\r
54 #endif\r
55 \r
56   \r
57 }\r
58 \r
59 OsdOpenGL::~OsdOpenGL()\r
60 {\r
61 \r
62   if (initted) \r
63   {\r
64           threadStop();\r
65                 shutdown();\r
66   }\r
67 \r
68 \r
69   glmutex.Unlock();\r
70 }\r
71 \r
72 int OsdOpenGL::getFD()\r
73 {\r
74   if (!initted) return 0;\r
75   return fdOsd;\r
76 }\r
77 \r
78 Surface * OsdOpenGL::createNewSurface() {\r
79         return (Surface*)new SurfaceOpenGL();\r
80 }\r
81 \r
82 int OsdOpenGL::init(void* device)\r
83 {\r
84   if (initted) return 0;\r
85   Video* video = Video::getInstance();\r
86    //window=*((HWND*)device);\r
87   \r
88    // May be this device specific part should go to a device specific child class\r
89 \r
90    //init broadcom chipset (Move to video?)\r
91    bcm_host_init();\r
92 \r
93    //First get connection to egl\r
94    egl_display=eglGetDisplay(EGL_DEFAULT_DISPLAY);\r
95 \r
96    if (egl_display==EGL_NO_DISPLAY) {\r
97            Log::getInstance()->log("OSD", Log::WARN, "Could not get egl display! %x",eglGetError());\r
98            glmutex.Unlock();\r
99            return 0;\r
100    }\r
101 \r
102 \r
103 \r
104    if (eglInitialize(egl_display, NULL, NULL)==EGL_FALSE) {\r
105            Log::getInstance()->log("OSD", Log::WARN, "Initialising display failed! %x",eglGetError());\r
106            glmutex.Unlock();\r
107            return 0;\r
108    }\r
109 \r
110    const char *query_str=eglQueryString(egl_display,EGL_CLIENT_APIS);\r
111    if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);\r
112    else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",eglGetError());\r
113    query_str=eglQueryString(egl_display,EGL_EXTENSIONS);\r
114    if (query_str)    Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);\r
115    else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",eglGetError());\r
116 \r
117    const EGLint attributs[]={\r
118                  EGL_RED_SIZE,8,EGL_GREEN_SIZE, 8,EGL_BLUE_SIZE, 8,EGL_ALPHA_SIZE, 8,\r
119          EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT,\r
120          EGL_CONFORMANT, EGL_OPENGL_ES2_BIT,\r
121          EGL_NONE\r
122    }; // Here, we might have to select the resolution!\r
123 \r
124 \r
125    EGLint number;\r
126 \r
127    if (eglChooseConfig(egl_display, attributs, &egl_ourconfig, 1, &number)==EGL_FALSE) {\r
128            Log::getInstance()->log("OSD", Log::WARN, "Choosing egl config failed! %x",eglGetError());\r
129            glmutex.Unlock();\r
130            return 0;\r
131    }\r
132 \r
133    const EGLint attr_context[]={\r
134                    EGL_CONTEXT_CLIENT_VERSION,2,\r
135           EGL_NONE\r
136       };\r
137 \r
138    egl_context=eglCreateContext(egl_display,egl_ourconfig,EGL_NO_CONTEXT,attr_context);\r
139    if (egl_context==EGL_NO_CONTEXT) {\r
140            Log::getInstance()->log("OSD", Log::WARN, "Creating egl context failed! %x",eglGetError());\r
141            glmutex.Unlock();\r
142            return 0;\r
143    }\r
144 \r
145    // warning broadcom specific, get display size!\r
146    display_width=display_height=0;\r
147    if (graphics_get_display_size(0, &display_width, &display_height)<0) {\r
148            Log::getInstance()->log("OSD", Log::WARN, "Getting display size failed! (BCM API) ");\r
149            glmutex.Unlock();\r
150            return 0;\r
151    }\r
152    Log::getInstance()->log("OSD", Log::NOTICE, "Displaysize is %d x %d ",display_width, display_height);\r
153    VC_RECT_T dst_rect ={0,0,display_width,display_height};\r
154    VC_RECT_T src_rect={0,0,BACKBUFFER_WIDTH <<16,BACKBUFFER_HEIGHT<<16};\r
155    DISPMANX_DISPLAY_HANDLE_T bcm_display;\r
156    DISPMANX_ELEMENT_HANDLE_T bcm_element;\r
157    DISPMANX_UPDATE_HANDLE_T  bcm_update;\r
158 \r
159 \r
160    bcm_display=vc_dispmanx_display_open(0);\r
161    bcm_update=vc_dispmanx_update_start(0);\r
162    bcm_element=vc_dispmanx_element_add(bcm_update,bcm_display,\r
163          2,&dst_rect, 0,\r
164          &src_rect,DISPMANX_PROTECTION_NONE,0, 0, (DISPMANX_TRANSFORM_T) 0);\r
165 \r
166    vc_dispmanx_update_submit_sync(bcm_update);\r
167    static EGL_DISPMANX_WINDOW_T nativewindow;\r
168    nativewindow.element=bcm_element;\r
169    nativewindow.height=BACKBUFFER_HEIGHT;\r
170    nativewindow.width=BACKBUFFER_WIDTH;\r
171 \r
172    egl_surface = eglCreateWindowSurface(egl_display,egl_ourconfig, &nativewindow,NULL );\r
173    if (egl_surface==EGL_NO_SURFACE) {\r
174            Log::getInstance()->log("OSD", Log::WARN, "Creating egl window surface failed!");\r
175            glmutex.Unlock();\r
176            return 0;\r
177    }\r
178 \r
179    if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context)== EGL_FALSE) {\r
180            Log::getInstance()->log("OSD", Log::WARN, "Making egl Current failed");\r
181            glmutex.Unlock();\r
182                    return 0;\r
183    }\r
184    // Test stuff\r
185 \r
186    query_str=(const char*)glGetString(GL_VERSION) ;\r
187    if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);\r
188    else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",glGetError());\r
189 \r
190    query_str=(const char*)glGetString(GL_VENDOR) ;\r
191    if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);\r
192    else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",glGetError());\r
193 \r
194    query_str=(const char*)glGetString(GL_RENDERER) ;\r
195    if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);\r
196    else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",glGetError());\r
197 \r
198    query_str=(const char*)glGetString(GL_EXTENSIONS) ;\r
199    if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);\r
200    else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",glGetError());\r
201 \r
202 \r
203 \r
204 \r
205   //Now we will create the Screen\r
206   screen = (Surface*) new SurfaceOpenGL(Surface::SCREEN);\r
207 \r
208   screen->create(video->getScreenWidth(), video->getScreenHeight());\r
209   screen->display();\r
210   initted = 1; // must set this here or create surface won't work\r
211 \r
212   //glGenBuffers(1, &vB);\r
213   //glGenBuffers(1, &iB);\r
214 \r
215   //Preparing the Shaders\r
216 \r
217   if (!osd_shader.init()) {\r
218           Log::getInstance()->log("OSD", Log::WARN, "Init Osd Shader failed");\r
219           glmutex.Unlock();\r
220           return 0;\r
221   }\r
222   if (!yuv400_shader.init()) {\r
223           Log::getInstance()->log("OSD", Log::WARN, "Init Yuv400 Shader failed");\r
224           glmutex.Unlock();\r
225           return 0;\r
226   }\r
227 \r
228   if (!yuv444_shader.init()) {\r
229           Log::getInstance()->log("OSD", Log::WARN, "Init Yuv444 Shader failed");\r
230           glmutex.Unlock();\r
231           return 0;\r
232   }\r
233 \r
234 \r
235 \r
236   glClearColor(0.0f,0.0f,0.0f,1.f);\r
237   eglSwapInterval(egl_display, 1 );\r
238 \r
239   eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );\r
240 \r
241   if (((VideoVPEOGL*)Video::getInstance())->initUsingOSDObjects()!=1) { //call Video for init opengl stuff\r
242           return 0;\r
243   }\r
244 \r
245 \r
246   glmutex.Unlock();\r
247   threadStart();\r
248 \r
249   return 1;\r
250 }\r
251 \r
252 \r
253 \r
254 \r
255         \r
256 void OsdOpenGL::InitVertexBuffer(float  scalex,float scaley)\r
257 {\r
258   Video* video=Video::getInstance();\r
259   float texx=1.f;\r
260   float texy=1.f;\r
261   OSDCOLOR osdcolor={1.f,1.f,1.f,1.f};\r
262 \r
263  // osdvertices[0].c=osdcolor;\r
264   osdvertices[0].x= (scalex);\r
265   osdvertices[0].y=-scaley;\r
266   osdvertices[0].z=0.5;\r
267   osdvertices[0].u=texx;\r
268   osdvertices[0].v=texy;\r
269  // osdvertices[1].c=osdcolor;\r
270   osdvertices[1].x=(scalex);\r
271   osdvertices[1].y=(scaley);\r
272   osdvertices[1].z=0.5f;\r
273   osdvertices[1].u=texx;\r
274   osdvertices[1].v=0.f;\r
275   //  osdvertices[0].c=osdcolor;\r
276   osdvertices[2].x=(-scalex);\r
277   osdvertices[2].y=-scaley;\r
278   osdvertices[2].z=0.5f;\r
279   osdvertices[2].u=0.f;\r
280   osdvertices[2].v=texy;\r
281  // osdvertices[3].c=osdcolor;\r
282   osdvertices[3].x=-scalex;\r
283   osdvertices[3].y=(scaley);\r
284   osdvertices[3].z=0.5f;\r
285   osdvertices[3].u=0.f;\r
286   osdvertices[3].v=0.f;\r
287   \r
288   osdindices[0]=0;\r
289   osdindices[1]=1;\r
290   osdindices[2]=2;\r
291   osdindices[3]=0;\r
292   osdindices[4]=2;\r
293   osdindices[5]=3;\r
294 \r
295   return;\r
296 }\r
297 \r
298 int OsdOpenGL::shutdown()\r
299 {\r
300   if (!initted) return 0;\r
301   initted = 0;\r
302 \r
303   (((VideoVPEOGL*)Video::getInstance())->shutdownUsingOSDObjects());\r
304 \r
305 \r
306   osd_shader.deinit();\r
307   yuv400_shader.deinit();\r
308   yuv444_shader.deinit();\r
309 \r
310   glClear(GL_COLOR_BUFFER_BIT);\r
311   eglSwapBuffers(egl_display, egl_surface);\r
312   eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );\r
313   eglDestroySurface(egl_display,egl_surface);\r
314   eglDestroyContext(egl_display,egl_context);\r
315   eglTerminate(egl_display );\r
316 \r
317   return 1;\r
318 }\r
319 \r
320 void OsdOpenGL::screenShot(const char* fileName)\r
321 {\r
322   screen->screenShot(fileName);\r
323 }\r
324 \r
325 void OsdOpenGL::threadMethod()\r
326 {\r
327         // We have to claim the gl context for this thread\r
328         //glmutex.Lock();\r
329 \r
330         //glmutex.Unlock();\r
331         VPEOGLFrame *frame=NULL;\r
332         int ts=0;\r
333         VideoVPEOGL* video =(VideoVPEOGL*) Video::getInstance();\r
334         while (true)\r
335         {\r
336                 ts=10;\r
337                 unsigned int waittime=10;\r
338 \r
339                 if (initted){\r
340 #ifdef VPE_LIBAV_SUPPORT\r
341                         if (!frame) frame=video->getReadyOGLFrame();\r
342                         if (frame) {\r
343                                 InternalRendering(frame);\r
344                         //      MILLISLEEP(1000);\r
345                                 lastrendertime=getTimeMS();\r
346                                 video->returnOGLFrame(frame); //please recycle it\r
347                                 frame=NULL;\r
348 \r
349                         }\r
350                         else\r
351 #endif\r
352                         {\r
353                                  long long time1=getTimeMS();\r
354                                  if ((time1-lastrendertime)>200) {//5 fps for OSD updates are enough, avoids tearing\r
355                                         InternalRendering(NULL);\r
356                                         lastrendertime=getTimeMS();\r
357                                  }\r
358                         }\r
359 #ifdef VPE_LIBAV_SUPPORT\r
360                         frame=video->getReadyOGLFrame();\r
361 #endif\r
362                 }\r
363 \r
364 \r
365                 if (frame) ts=0;\r
366                 threadCheckExit();\r
367                 if (ts!=0) {\r
368                         struct timespec target_time;\r
369                         clock_gettime(CLOCK_REALTIME,&target_time);\r
370                         target_time.tv_nsec+=1000000LL*ts;\r
371                         if (target_time.tv_nsec>999999999) {\r
372                                 target_time.tv_nsec-=1000000000L;\r
373                                 target_time.tv_sec+=1;\r
374                         }\r
375                         threadWaitForSignalTimed(&target_time);\r
376                 }\r
377                 //Sleep(1);\r
378         }\r
379         //eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );\r
380 }\r
381 \r
382 \r
383 void OsdOpenGL::threadPostStopCleanup()\r
384 {\r
385         //Doing nothing\r
386         //goo;\r
387 }\r
388 \r
389 \r
390 \r
391 void OsdOpenGL::InternalRendering(VPEOGLFrame* frame){\r
392     BeginPainting();\r
393 \r
394 \r
395 \r
396         //InitVertexBuffer(display_width,display_height);\r
397     InitVertexBuffer(1.f,1.f);\r
398 \r
399 \r
400         glViewport(0, 0, BACKBUFFER_WIDTH ,BACKBUFFER_HEIGHT);\r
401 \r
402         glClearColor(0.0f,0.0f,0.0f,1.f);\r
403         glClear(GL_COLOR_BUFFER_BIT);\r
404 \r
405         if (frame) {\r
406                 switch (frame->type) {\r
407                 case 1:\r
408                         yuv400_shader.PrepareRendering(frame->textures[0],frame->textures[1],frame->textures[2]);break;\r
409                 case 2:\r
410                         yuv444_shader.PrepareRendering(frame->textures[0]);break;\r
411                 }\r
412 \r
413 \r
414 \r
415 \r
416                 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,sizeof(OSDVERTEX), osdvertices);\r
417                 glEnableVertexAttribArray(0);\r
418                 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE,sizeof(OSDVERTEX), &(osdvertices[0].u));\r
419                 glEnableVertexAttribArray(1);\r
420 \r
421 \r
422                 //Log::getInstance()->log("OSD", Log::WARN, "mark8 glerror %x %x",glGetError(),osd_sampler_loc);\r
423 \r
424 \r
425 \r
426                 glDrawArrays(GL_TRIANGLE_STRIP, 0,  4);\r
427 \r
428 \r
429         }\r
430 \r
431 \r
432 \r
433 \r
434         osd_shader.PrepareRendering(((SurfaceOpenGL*)screen)->getTexture());\r
435 \r
436         glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,sizeof(OSDVERTEX), osdvertices);\r
437         glEnableVertexAttribArray(0);\r
438         glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE,sizeof(OSDVERTEX), &(osdvertices[0].u));\r
439         glEnableVertexAttribArray(1);\r
440 \r
441 \r
442         if (frame!=NULL) { //This is ok for ffmpeg rendering not OMX\r
443                 glEnable(GL_BLEND);\r
444                 glBlendFuncSeparate (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,GL_ZERO,GL_ONE);\r
445 \r
446         } else {\r
447                 glDisable(GL_BLEND);\r
448         }\r
449 \r
450 \r
451         glDrawArrays(GL_TRIANGLE_STRIP, 0,  4);\r
452 \r
453         \r
454 \r
455         //Show it to the user!\r
456         eglSwapBuffers(egl_display, egl_surface);\r
457 \r
458         EndPainting();\r
459 #ifdef BENCHMARK_FPS\r
460         num_benchmark_frames++;\r
461         if (getTimeMS()-last_benchmark_time>4000) {\r
462                 float fps=1000./(float)(getTimeMS()-last_benchmark_time);\r
463                 fps*=((float)num_benchmark_frames);\r
464                 num_benchmark_frames=0;\r
465                 Log::getInstance()->log("OSD", Log::NOTICE, "Current FPS %g", fps);\r
466                 last_benchmark_time=getTimeMS();\r
467 \r
468         }\r
469 \r
470 #endif\r
471 \r
472         \r
473 \r
474 }\r
475 \r
476 \r
477 \r
478 \r
479 void OsdOpenGL::BeginPainting() {//We synchronize calls to d3d between different threads\r
480         glmutex.Lock();\r
481         if (initted) {\r
482                 if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context)== EGL_FALSE) {\r
483                         Log::getInstance()->log("OSD", Log::WARN, "Making egl Current failed in thread %d",eglGetError());\r
484                         return;\r
485                 }\r
486         }\r
487 }\r
488 \r
489 void OsdOpenGL::EndPainting() {\r
490         eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );\r
491         glmutex.Unlock();\r
492 }\r
493 \r
494 \r
495 \r
496 void OsdOpenGL::Blank() {\r
497         BeginPainting();\r
498         glClearColor(0.15f, 0.25f, 0.35f, 1.0f); // change this to black after testing\r
499         glClear( GL_COLOR_BUFFER_BIT );\r
500         glClear( GL_DEPTH_BUFFER_BIT );\r
501         EndPainting();\r
502 }\r