1 /*
2  *  Copyright (C) 2010-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "LinuxRendererGLES.h"
10 
11 #include "Application.h"
12 #include "RenderCapture.h"
13 #include "RenderFactory.h"
14 #include "ServiceBroker.h"
15 #include "VideoShaders/VideoFilterShaderGLES.h"
16 #include "VideoShaders/YUV2RGBShaderGLES.h"
17 #include "cores/IPlayer.h"
18 #include "guilib/Texture.h"
19 #include "rendering/MatrixGL.h"
20 #include "rendering/gles/RenderSystemGLES.h"
21 #include "settings/AdvancedSettings.h"
22 #include "settings/DisplaySettings.h"
23 #include "settings/MediaSettings.h"
24 #include "settings/Settings.h"
25 #include "settings/SettingsComponent.h"
26 #include "threads/SingleLock.h"
27 #include "utils/GLUtils.h"
28 #include "utils/MathUtils.h"
29 #include "utils/log.h"
30 #include "windowing/WinSystem.h"
31 
32 
33 using namespace Shaders;
34 
CLinuxRendererGLES()35 CLinuxRendererGLES::CLinuxRendererGLES()
36 {
37   m_textureTarget = GL_TEXTURE_2D;
38   m_format = AV_PIX_FMT_NONE;
39 
40   m_fullRange = !CServiceBroker::GetWinSystem()->UseLimitedColor();
41 
42   m_renderSystem = dynamic_cast<CRenderSystemGLES*>(CServiceBroker::GetRenderSystem());
43 
44 #if HAS_GLES >= 3
45   unsigned int verMajor, verMinor;
46   m_renderSystem->GetRenderVersion(verMajor, verMinor);
47 
48   if (verMajor >= 3)
49   {
50     m_pixelStoreKey = GL_UNPACK_ROW_LENGTH;
51   }
52 #endif
53 
54 #if defined (GL_UNPACK_ROW_LENGTH_EXT)
55   if (m_renderSystem->IsExtSupported("GL_EXT_unpack_subimage"))
56   {
57     m_pixelStoreKey = GL_UNPACK_ROW_LENGTH_EXT;
58   }
59 #endif
60 }
61 
~CLinuxRendererGLES()62 CLinuxRendererGLES::~CLinuxRendererGLES()
63 {
64   UnInit();
65 
66   ReleaseShaders();
67 
68   free(m_planeBuffer);
69   m_planeBuffer = nullptr;
70 }
71 
Create(CVideoBuffer * buffer)72 CBaseRenderer* CLinuxRendererGLES::Create(CVideoBuffer *buffer)
73 {
74   return new CLinuxRendererGLES();
75 }
76 
Register()77 bool CLinuxRendererGLES::Register()
78 {
79   VIDEOPLAYER::CRendererFactory::RegisterRenderer("default", CLinuxRendererGLES::Create);
80   return true;
81 }
82 
ValidateRenderTarget()83 bool CLinuxRendererGLES::ValidateRenderTarget()
84 {
85   if (!m_bValidated)
86   {
87     // function pointer for texture might change in
88     // call to LoadShaders
89     glFinish();
90 
91     for (int i = 0 ; i < NUM_BUFFERS ; i++)
92     {
93       DeleteTexture(i);
94     }
95 
96      // create the yuv textures
97     UpdateVideoFilter();
98     LoadShaders();
99 
100     if (m_renderMethod < 0)
101     {
102       return false;
103     }
104 
105     for (int i = 0 ; i < m_NumYV12Buffers ; i++)
106     {
107       CreateTexture(i);
108     }
109 
110     m_bValidated = true;
111 
112     return true;
113   }
114 
115   return false;
116 }
117 
Configure(const VideoPicture & picture,float fps,unsigned int orientation)118 bool CLinuxRendererGLES::Configure(const VideoPicture &picture, float fps, unsigned int orientation)
119 {
120   CLog::Log(LOGDEBUG, "LinuxRendererGLES::Configure: fps: %0.3f", fps);
121   m_format = picture.videoBuffer->GetFormat();
122   m_sourceWidth = picture.iWidth;
123   m_sourceHeight = picture.iHeight;
124   m_renderOrientation = orientation;
125 
126   m_srcPrimaries = GetSrcPrimaries(static_cast<AVColorPrimaries>(picture.color_primaries),
127                                    picture.iWidth, picture.iHeight);
128   m_toneMap = false;
129 
130   // Calculate the input frame aspect ratio.
131   CalculateFrameAspectRatio(picture.iDisplayWidth, picture.iDisplayHeight);
132   SetViewMode(m_videoSettings.m_ViewMode);
133   ManageRenderArea();
134 
135   m_bConfigured = true;
136   m_scalingMethodGui = (ESCALINGMETHOD)-1;
137 
138   // Ensure that textures are recreated and rendering starts only after the 1st
139   // frame is loaded after every call to Configure().
140   m_bValidated = false;
141 
142   // setup the background colour
143   m_clearColour = CServiceBroker::GetWinSystem()->UseLimitedColor() ? (16.0f / 0xff) : 0.0f;
144 
145   if (picture.hasDisplayMetadata && picture.hasLightMetadata)
146   {
147     m_passthroughHDR = CServiceBroker::GetWinSystem()->SetHDR(&picture);
148     CLog::Log(LOGDEBUG, "LinuxRendererGLES::Configure: HDR passthrough: %s", m_passthroughHDR ? "on" : "off");
149   }
150 
151   return true;
152 }
153 
ConfigChanged(const VideoPicture & picture)154 bool CLinuxRendererGLES::ConfigChanged(const VideoPicture &picture)
155 {
156   if (picture.videoBuffer->GetFormat() != m_format)
157   {
158     return true;
159   }
160 
161   return false;
162 }
163 
NextYV12Texture()164 int CLinuxRendererGLES::NextYV12Texture()
165 {
166   return (m_iYV12RenderBuffer + 1) % m_NumYV12Buffers;
167 }
168 
AddVideoPicture(const VideoPicture & picture,int index)169 void CLinuxRendererGLES::AddVideoPicture(const VideoPicture &picture, int index)
170 {
171   CPictureBuffer &buf = m_buffers[index];
172   if (buf.videoBuffer)
173   {
174     CLog::LogF(LOGERROR, "unreleased video buffer");
175     buf.videoBuffer->Release();
176   }
177   buf.videoBuffer = picture.videoBuffer;
178   buf.videoBuffer->Acquire();
179   buf.loaded = false;
180   buf.m_srcPrimaries = static_cast<AVColorPrimaries>(picture.color_primaries);
181   buf.m_srcColSpace = static_cast<AVColorSpace>(picture.color_space);
182   buf.m_srcFullRange = picture.color_range == 1;
183   buf.m_srcBits = picture.colorBits;
184 
185   buf.hasDisplayMetadata = picture.hasDisplayMetadata;
186   buf.displayMetadata = picture.displayMetadata;
187   buf.lightMetadata = picture.lightMetadata;
188   if (picture.hasLightMetadata && picture.lightMetadata.MaxCLL)
189   {
190     buf.hasLightMetadata = picture.hasLightMetadata;
191   }
192 }
193 
ReleaseBuffer(int idx)194 void CLinuxRendererGLES::ReleaseBuffer(int idx)
195 {
196   CPictureBuffer &buf = m_buffers[idx];
197   if (buf.videoBuffer)
198   {
199     buf.videoBuffer->Release();
200     buf.videoBuffer = nullptr;
201   }
202 }
203 
CalculateTextureSourceRects(int source,int num_planes)204 void CLinuxRendererGLES::CalculateTextureSourceRects(int source, int num_planes)
205 {
206   CPictureBuffer& buf = m_buffers[source];
207   YuvImage* im = &buf.image;
208 
209   // calculate the source rectangle
210   for(int field = 0; field < 3; field++)
211   {
212     for(int plane = 0; plane < num_planes; plane++)
213     {
214       CYuvPlane& p = buf.fields[field][plane];
215 
216       p.rect = m_sourceRect;
217       p.width  = im->width;
218       p.height = im->height;
219 
220       if(field != FIELD_FULL)
221       {
222         // correct for field offsets and chroma offsets
223         float offset_y = 0.5;
224         if(plane != 0)
225         {
226           offset_y += 0.5;
227         }
228 
229         if(field == FIELD_BOT)
230         {
231           offset_y *= -1;
232         }
233 
234         p.rect.y1 += offset_y;
235         p.rect.y2 += offset_y;
236 
237         // half the height if this is a field
238         p.height  *= 0.5f;
239         p.rect.y1 *= 0.5f;
240         p.rect.y2 *= 0.5f;
241       }
242 
243       if(plane != 0)
244       {
245         p.width   /= 1 << im->cshift_x;
246         p.height  /= 1 << im->cshift_y;
247 
248         p.rect.x1 /= 1 << im->cshift_x;
249         p.rect.x2 /= 1 << im->cshift_x;
250         p.rect.y1 /= 1 << im->cshift_y;
251         p.rect.y2 /= 1 << im->cshift_y;
252       }
253 
254       // protect against division by zero
255       if (p.texheight == 0 || p.texwidth == 0 ||
256           p.pixpertex_x == 0 || p.pixpertex_y == 0)
257       {
258         continue;
259       }
260 
261       p.height  /= p.pixpertex_y;
262       p.rect.y1 /= p.pixpertex_y;
263       p.rect.y2 /= p.pixpertex_y;
264       p.width   /= p.pixpertex_x;
265       p.rect.x1 /= p.pixpertex_x;
266       p.rect.x2 /= p.pixpertex_x;
267 
268       if (m_textureTarget == GL_TEXTURE_2D)
269       {
270         p.height  /= p.texheight;
271         p.rect.y1 /= p.texheight;
272         p.rect.y2 /= p.texheight;
273         p.width   /= p.texwidth;
274         p.rect.x1 /= p.texwidth;
275         p.rect.x2 /= p.texwidth;
276       }
277     }
278   }
279 }
280 
LoadPlane(CYuvPlane & plane,int type,unsigned width,unsigned height,int stride,int bpp,void * data)281 void CLinuxRendererGLES::LoadPlane(CYuvPlane& plane, int type,
282                                    unsigned width, unsigned height,
283                                    int stride, int bpp, void* data)
284 {
285   const GLvoid *pixelData = data;
286   int bps = bpp * glFormatElementByteCount(type);
287 
288   glBindTexture(m_textureTarget, plane.id);
289 
290   bool pixelStoreChanged = false;
291   if (stride != static_cast<int>(width * bps))
292   {
293     if (m_pixelStoreKey > 0)
294     {
295       pixelStoreChanged = true;
296       glPixelStorei(m_pixelStoreKey, stride);
297     }
298     else
299     {
300       size_t planeSize = width * height * bps;
301       if (m_planeBufferSize < planeSize)
302       {
303         m_planeBuffer = static_cast<unsigned char*>(realloc(m_planeBuffer, planeSize));
304         m_planeBufferSize = planeSize;
305       }
306 
307       unsigned char *src(static_cast<unsigned char*>(data)),
308                     *dst(m_planeBuffer);
309 
310       for (unsigned int y = 0; y < height; ++y, src += stride, dst += width * bps)
311         memcpy(dst, src, width * bps);
312 
313       pixelData = m_planeBuffer;
314     }
315   }
316   glTexSubImage2D(m_textureTarget, 0, 0, 0, width, height, type, GL_UNSIGNED_BYTE, pixelData);
317 
318   if (m_pixelStoreKey > 0 && pixelStoreChanged)
319     glPixelStorei(m_pixelStoreKey, 0);
320 
321   // check if we need to load any border pixels
322   if (height < plane.texheight)
323   {
324     glTexSubImage2D(m_textureTarget, 0,
325                     0, height, width, 1,
326                     type, GL_UNSIGNED_BYTE,
327                     static_cast<const unsigned char*>(pixelData) + stride * (height - 1));
328   }
329 
330   if (width  < plane.texwidth)
331   {
332     glTexSubImage2D(m_textureTarget, 0,
333                     width, 0, 1, height,
334                     type, GL_UNSIGNED_BYTE,
335                     static_cast<const unsigned char*>(pixelData) + bps * (width - 1));
336   }
337 
338   glBindTexture(m_textureTarget, 0);
339 }
340 
Flush(bool saveBuffers)341 bool CLinuxRendererGLES::Flush(bool saveBuffers)
342 {
343   glFinish();
344 
345   for (int i = 0 ; i < m_NumYV12Buffers ; i++)
346   {
347     DeleteTexture(i);
348   }
349 
350   glFinish();
351   m_bValidated = false;
352   m_fbo.fbo.Cleanup();
353   m_iYV12RenderBuffer = 0;
354 
355   return false;
356 }
357 
Update()358 void CLinuxRendererGLES::Update()
359 {
360   if (!m_bConfigured)
361   {
362     return;
363   }
364 
365   ManageRenderArea();
366   ValidateRenderTarget();
367 }
368 
DrawBlackBars()369 void CLinuxRendererGLES::DrawBlackBars()
370 {
371   CRect windowRect(0, 0, CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth(),
372                    CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight());
373 
374   auto quads = windowRect.SubtractRect(m_destRect);
375 
376   struct Svertex
377   {
378     float x, y;
379   };
380 
381   std::vector<Svertex> vertices(6 * quads.size());
382 
383   GLubyte count = 0;
384   for (const auto& quad : quads)
385   {
386     vertices[count + 1].x = quad.x1;
387     vertices[count + 1].y = quad.y1;
388 
389     vertices[count + 0].x = vertices[count + 5].x = quad.x1;
390     vertices[count + 0].y = vertices[count + 5].y = quad.y2;
391 
392     vertices[count + 2].x = vertices[count + 3].x = quad.x2;
393     vertices[count + 2].y = vertices[count + 3].y = quad.y1;
394 
395     vertices[count + 4].x = quad.x2;
396     vertices[count + 4].y = quad.y2;
397 
398     count += 6;
399   }
400 
401   glDisable(GL_BLEND);
402 
403   CRenderSystemGLES* renderSystem =
404       dynamic_cast<CRenderSystemGLES*>(CServiceBroker::GetRenderSystem());
405   if (!renderSystem)
406     return;
407 
408   renderSystem->EnableGUIShader(SM_DEFAULT);
409   GLint posLoc = renderSystem->GUIShaderGetPos();
410   GLint uniCol = renderSystem->GUIShaderGetUniCol();
411 
412   glUniform4f(uniCol, m_clearColour / 255.0f, m_clearColour / 255.0f, m_clearColour / 255.0f, 1.0f);
413 
414   GLuint vertexVBO;
415   glGenBuffers(1, &vertexVBO);
416   glBindBuffer(GL_ARRAY_BUFFER, vertexVBO);
417   glBufferData(GL_ARRAY_BUFFER, sizeof(Svertex) * vertices.size(), vertices.data(), GL_STATIC_DRAW);
418 
419   glVertexAttribPointer(posLoc, 2, GL_FLOAT, GL_FALSE, sizeof(Svertex), 0);
420   glEnableVertexAttribArray(posLoc);
421 
422   glDrawArrays(GL_TRIANGLES, 0, vertices.size());
423 
424   glDisableVertexAttribArray(posLoc);
425   glBindBuffer(GL_ARRAY_BUFFER, 0);
426   glDeleteBuffers(1, &vertexVBO);
427 
428   renderSystem->DisableGUIShader();
429 }
430 
RenderUpdate(int index,int index2,bool clear,unsigned int flags,unsigned int alpha)431 void CLinuxRendererGLES::RenderUpdate(int index, int index2, bool clear, unsigned int flags, unsigned int alpha)
432 {
433   m_iYV12RenderBuffer = index;
434 
435   if (!m_bConfigured)
436   {
437     return;
438   }
439 
440   // if its first pass, just init textures and return
441   if (ValidateRenderTarget())
442   {
443     return;
444   }
445 
446   if (!IsGuiLayer())
447   {
448     RenderUpdateVideo(clear, flags, alpha);
449     return;
450   }
451 
452   CPictureBuffer& buf = m_buffers[index];
453 
454   if (!buf.fields[FIELD_FULL][0].id)
455   {
456     return;
457   }
458 
459   ManageRenderArea();
460 
461   if (clear)
462   {
463     if (alpha == 255)
464       DrawBlackBars();
465     else
466     {
467       glClearColor(m_clearColour, m_clearColour, m_clearColour, 0);
468       glClear(GL_COLOR_BUFFER_BIT);
469       glClearColor(0, 0, 0, 0);
470     }
471   }
472 
473   if (alpha < 255)
474   {
475     glEnable(GL_BLEND);
476     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
477     if (m_pYUVProgShader)
478     {
479       m_pYUVProgShader->SetAlpha(alpha / 255.0f);
480     }
481 
482     if (m_pYUVBobShader)
483     {
484       m_pYUVBobShader->SetAlpha(alpha / 255.0f);
485     }
486   }
487   else
488   {
489     glDisable(GL_BLEND);
490     if (m_pYUVProgShader)
491     {
492       m_pYUVProgShader->SetAlpha(1.0f);
493     }
494 
495     if (m_pYUVBobShader)
496     {
497       m_pYUVBobShader->SetAlpha(1.0f);
498     }
499   }
500 
501   Render(flags, index);
502 
503   VerifyGLState();
504   glEnable(GL_BLEND);
505 }
506 
RenderUpdateVideo(bool clear,unsigned int flags,unsigned int alpha)507 void CLinuxRendererGLES::RenderUpdateVideo(bool clear, unsigned int flags, unsigned int alpha)
508 {
509   if (!m_bConfigured)
510   {
511     return;
512   }
513 
514   if (IsGuiLayer())
515   {
516     return;
517   }
518 }
519 
UpdateVideoFilter()520 void CLinuxRendererGLES::UpdateVideoFilter()
521 {
522   CRect srcRect;
523   CRect dstRect;
524   CRect viewRect;
525   GetVideoRect(srcRect, dstRect, viewRect);
526 
527   if (m_scalingMethodGui == m_videoSettings.m_ScalingMethod &&
528       viewRect.Height() == m_viewRect.Height() &&
529       viewRect.Width() == m_viewRect.Width())
530   {
531     return;
532   }
533 
534   m_scalingMethodGui = m_videoSettings.m_ScalingMethod;
535   m_scalingMethod = m_scalingMethodGui;
536   m_viewRect = viewRect;
537 
538   if(!Supports(m_scalingMethod))
539   {
540     CLog::Log(LOGWARNING, "CLinuxRendererGLES::UpdateVideoFilter - chosen scaling method %d, is not supported by renderer", static_cast<int>(m_scalingMethod));
541     m_scalingMethod = VS_SCALINGMETHOD_LINEAR;
542   }
543 
544   if (m_pVideoFilterShader)
545   {
546     delete m_pVideoFilterShader;
547     m_pVideoFilterShader = nullptr;
548   }
549 
550   m_fbo.fbo.Cleanup();
551 
552   VerifyGLState();
553 
554   switch (m_scalingMethod)
555   {
556   case VS_SCALINGMETHOD_NEAREST:
557   {
558     CLog::Log(LOGINFO, "GLES: Selecting single pass rendering");
559     SetTextureFilter(GL_NEAREST);
560     m_renderQuality = RQ_SINGLEPASS;
561     return;
562   }
563   case VS_SCALINGMETHOD_LINEAR:
564   {
565     CLog::Log(LOGINFO, "GLES: Selecting single pass rendering");
566     SetTextureFilter(GL_LINEAR);
567     m_renderQuality = RQ_SINGLEPASS;
568     return;
569   }
570   case VS_SCALINGMETHOD_LANCZOS2:
571   case VS_SCALINGMETHOD_SPLINE36_FAST:
572   case VS_SCALINGMETHOD_LANCZOS3_FAST:
573   case VS_SCALINGMETHOD_SPLINE36:
574   case VS_SCALINGMETHOD_LANCZOS3:
575   case VS_SCALINGMETHOD_CUBIC_MITCHELL:
576   {
577     if (m_renderMethod & RENDER_GLSL)
578     {
579       if (!m_fbo.fbo.Initialize())
580       {
581         CLog::Log(LOGERROR, "GLES: Error initializing FBO");
582         break;
583       }
584 
585       if (!m_fbo.fbo.CreateAndBindToTexture(GL_TEXTURE_2D, m_sourceWidth, m_sourceHeight, GL_RGBA))
586       {
587         CLog::Log(LOGERROR, "GLES: Error creating texture and binding to FBO");
588         break;
589       }
590     }
591 
592     m_pVideoFilterShader = new ConvolutionFilterShader(m_scalingMethod);
593     if (!m_pVideoFilterShader->CompileAndLink())
594     {
595       CLog::Log(LOGERROR, "GLES: Error compiling and linking video filter shader");
596       break;
597     }
598 
599     CLog::Log(LOGINFO, "GLES: Selecting multi pass rendering");
600     SetTextureFilter(GL_LINEAR);
601     m_renderQuality = RQ_MULTIPASS;
602       return;
603   }
604   case VS_SCALINGMETHOD_CUBIC_B_SPLINE:
605   case VS_SCALINGMETHOD_CUBIC_CATMULL:
606   case VS_SCALINGMETHOD_CUBIC_0_075:
607   case VS_SCALINGMETHOD_CUBIC_0_1:
608   case VS_SCALINGMETHOD_BICUBIC_SOFTWARE:
609   case VS_SCALINGMETHOD_LANCZOS_SOFTWARE:
610   case VS_SCALINGMETHOD_SINC_SOFTWARE:
611   case VS_SCALINGMETHOD_SINC8:
612   {
613     CLog::Log(LOGERROR, "GLES: TODO: This scaler has not yet been implemented");
614     break;
615   }
616   default:
617     break;
618   }
619 
620   CLog::Log(LOGERROR, "GLES: Falling back to bilinear due to failure to init scaler");
621   if (m_pVideoFilterShader)
622   {
623     delete m_pVideoFilterShader;
624     m_pVideoFilterShader = nullptr;
625   }
626 
627   m_fbo.fbo.Cleanup();
628 
629   SetTextureFilter(GL_LINEAR);
630   m_renderQuality = RQ_SINGLEPASS;
631 }
632 
LoadShaders(int field)633 void CLinuxRendererGLES::LoadShaders(int field)
634 {
635   m_reloadShaders = 0;
636 
637   if (!LoadShadersHook())
638   {
639     int requestedMethod = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_RENDERMETHOD);
640     CLog::Log(LOGDEBUG, "GLES: Requested render method: %d", requestedMethod);
641 
642     ReleaseShaders();
643 
644     switch(requestedMethod)
645     {
646       case RENDER_METHOD_AUTO:
647       case RENDER_METHOD_GLSL:
648       {
649         // Try GLSL shaders if supported and user requested auto or GLSL.
650         if (glCreateProgram())
651         {
652           // create regular scan shader
653           CLog::Log(LOGINFO, "GLES: Selecting YUV 2 RGB shader");
654 
655           EShaderFormat shaderFormat = GetShaderFormat();
656           m_pYUVProgShader = new YUV2RGBProgressiveShader(shaderFormat, m_passthroughHDR ? m_srcPrimaries : AVColorPrimaries::AVCOL_PRI_BT709, m_srcPrimaries, m_toneMap);
657           m_pYUVProgShader->SetConvertFullColorRange(m_fullRange);
658           m_pYUVBobShader = new YUV2RGBBobShader(shaderFormat, m_passthroughHDR ? m_srcPrimaries : AVColorPrimaries::AVCOL_PRI_BT709, m_srcPrimaries, m_toneMap);
659           m_pYUVBobShader->SetConvertFullColorRange(m_fullRange);
660 
661           if ((m_pYUVProgShader && m_pYUVProgShader->CompileAndLink())
662               && (m_pYUVBobShader && m_pYUVBobShader->CompileAndLink()))
663           {
664             m_renderMethod = RENDER_GLSL;
665             UpdateVideoFilter();
666             break;
667           }
668           else
669           {
670             ReleaseShaders();
671             CLog::Log(LOGERROR, "GLES: Error enabling YUV2RGB GLSL shader");
672             m_renderMethod = -1;
673             break;
674           }
675         }
676 
677         break;
678       }
679       default:
680       {
681         m_renderMethod = -1 ;
682         CLog::Log(LOGERROR, "GLES: render method not supported");
683       }
684     }
685   }
686 }
687 
ReleaseShaders()688 void CLinuxRendererGLES::ReleaseShaders()
689 {
690   if (m_pYUVProgShader)
691   {
692     delete m_pYUVProgShader;
693     m_pYUVProgShader = nullptr;
694   }
695 
696   if (m_pYUVBobShader)
697   {
698     delete m_pYUVBobShader;
699     m_pYUVBobShader = nullptr;
700   }
701 }
702 
UnInit()703 void CLinuxRendererGLES::UnInit()
704 {
705   CLog::Log(LOGDEBUG, "LinuxRendererGLES: Cleaning up GLES resources");
706   CSingleLock lock(CServiceBroker::GetWinSystem()->GetGfxContext());
707 
708   glFinish();
709 
710   // YV12 textures
711   for (int i = 0; i < NUM_BUFFERS; ++i)
712   {
713     DeleteTexture(i);
714   }
715 
716   // cleanup framebuffer object if it was in use
717   m_fbo.fbo.Cleanup();
718   m_bValidated = false;
719   m_bConfigured = false;
720 
721   CServiceBroker::GetWinSystem()->SetHDR(nullptr);
722 }
723 
CreateTexture(int index)724 bool CLinuxRendererGLES::CreateTexture(int index)
725 {
726   if (m_format == AV_PIX_FMT_NV12)
727   {
728     return CreateNV12Texture(index);
729   }
730   else
731   {
732     return CreateYV12Texture(index);
733   }
734 }
735 
DeleteTexture(int index)736 void CLinuxRendererGLES::DeleteTexture(int index)
737 {
738   ReleaseBuffer(index);
739 
740   if (m_format == AV_PIX_FMT_NV12)
741   {
742     DeleteNV12Texture(index);
743   }
744   else
745   {
746     DeleteYV12Texture(index);
747   }
748 }
749 
UploadTexture(int index)750 bool CLinuxRendererGLES::UploadTexture(int index)
751 {
752   if (!m_buffers[index].videoBuffer)
753   {
754     return false;
755   }
756 
757   if (m_buffers[index].loaded)
758   {
759     return true;
760   }
761 
762   bool ret{false};
763 
764   YuvImage &dst = m_buffers[index].image;
765   m_buffers[index].videoBuffer->GetPlanes(dst.plane);
766   m_buffers[index].videoBuffer->GetStrides(dst.stride);
767 
768   if (m_format == AV_PIX_FMT_NV12)
769   {
770     ret = UploadNV12Texture(index);
771   }
772   else
773   {
774     // default to YV12 texture handlers
775     ret = UploadYV12Texture(index);
776   }
777 
778   if (ret)
779   {
780     m_buffers[index].loaded = true;
781   }
782 
783   return ret;
784 }
785 
Render(unsigned int flags,int index)786 void CLinuxRendererGLES::Render(unsigned int flags, int index)
787 {
788   // obtain current field, if interlaced
789   if( flags & RENDER_FLAG_TOP)
790   {
791     m_currentField = FIELD_TOP;
792   }
793   else if (flags & RENDER_FLAG_BOT)
794   {
795     m_currentField = FIELD_BOT;
796   }
797   else
798   {
799     m_currentField = FIELD_FULL;
800   }
801 
802   // call texture load function
803   if (!UploadTexture(index))
804   {
805     return;
806   }
807 
808   if (RenderHook(index))
809   {
810     ;
811   }
812   else if (m_renderMethod & RENDER_GLSL)
813   {
814     UpdateVideoFilter();
815     switch(m_renderQuality)
816     {
817     case RQ_LOW:
818     case RQ_SINGLEPASS:
819     {
820       RenderSinglePass(index, m_currentField);
821       VerifyGLState();
822       break;
823     }
824     case RQ_MULTIPASS:
825     {
826       RenderToFBO(index, m_currentField);
827       RenderFromFBO();
828       VerifyGLState();
829       break;
830     }
831     default:
832       break;
833     }
834   }
835 
836   AfterRenderHook(index);
837 }
838 
RenderSinglePass(int index,int field)839 void CLinuxRendererGLES::RenderSinglePass(int index, int field)
840 {
841   CPictureBuffer &buf = m_buffers[index];
842   CYuvPlane (&planes)[YuvImage::MAX_PLANES] = m_buffers[index].fields[field];
843 
844   AVColorPrimaries srcPrim = GetSrcPrimaries(buf.m_srcPrimaries, buf.image.width, buf.image.height);
845   if (srcPrim != m_srcPrimaries)
846   {
847     m_srcPrimaries = srcPrim;
848     m_reloadShaders = true;
849   }
850 
851   bool toneMap = false;
852 
853   if (!m_passthroughHDR && m_videoSettings.m_ToneMapMethod != VS_TONEMAPMETHOD_OFF)
854   {
855     if (buf.hasLightMetadata || (buf.hasDisplayMetadata && buf.displayMetadata.has_luminance))
856     {
857       toneMap = true;
858     }
859   }
860 
861   if (toneMap != m_toneMap)
862   {
863     m_reloadShaders = true;
864   }
865 
866   m_toneMap = toneMap;
867 
868   if (m_reloadShaders)
869   {
870     LoadShaders(field);
871   }
872 
873   glDisable(GL_DEPTH_TEST);
874 
875   // Y
876   glActiveTexture(GL_TEXTURE0);
877   glBindTexture(m_textureTarget, planes[0].id);
878 
879   // U
880   glActiveTexture(GL_TEXTURE1);
881   glBindTexture(m_textureTarget, planes[1].id);
882 
883   // V
884   glActiveTexture(GL_TEXTURE2);
885   glBindTexture(m_textureTarget, planes[2].id);
886 
887   glActiveTexture(GL_TEXTURE0);
888   VerifyGLState();
889 
890   Shaders::BaseYUV2RGBGLSLShader *pYUVShader;
891   if (field != FIELD_FULL)
892   {
893     pYUVShader = m_pYUVBobShader;
894   }
895   else
896   {
897     pYUVShader = m_pYUVProgShader;
898   }
899 
900   pYUVShader->SetBlack(m_videoSettings.m_Brightness * 0.01f - 0.5f);
901   pYUVShader->SetContrast(m_videoSettings.m_Contrast * 0.02f);
902   pYUVShader->SetWidth(planes[0].texwidth);
903   pYUVShader->SetHeight(planes[0].texheight);
904   pYUVShader->SetColParams(buf.m_srcColSpace, buf.m_srcBits, !buf.m_srcFullRange, buf.m_srcTextureBits);
905   pYUVShader->SetDisplayMetadata(buf.hasDisplayMetadata, buf.displayMetadata,
906                                  buf.hasLightMetadata, buf.lightMetadata);
907   pYUVShader->SetToneMapParam(m_videoSettings.m_ToneMapParam);
908 
909   if (field == FIELD_TOP)
910   {
911     pYUVShader->SetField(1);
912   }
913   else if(field == FIELD_BOT)
914   {
915     pYUVShader->SetField(0);
916   }
917 
918   pYUVShader->SetMatrices(glMatrixProject.Get(), glMatrixModview.Get());
919   pYUVShader->Enable();
920 
921   GLubyte idx[4] = {0, 1, 3, 2}; // determines order of triangle strip
922   GLfloat m_vert[4][3];
923   GLfloat m_tex[3][4][2];
924 
925   GLint vertLoc = pYUVShader->GetVertexLoc();
926   GLint Yloc = pYUVShader->GetYcoordLoc();
927   GLint Uloc = pYUVShader->GetUcoordLoc();
928   GLint Vloc = pYUVShader->GetVcoordLoc();
929 
930   glVertexAttribPointer(vertLoc, 3, GL_FLOAT, 0, 0, m_vert);
931   glVertexAttribPointer(Yloc, 2, GL_FLOAT, 0, 0, m_tex[0]);
932   glVertexAttribPointer(Uloc, 2, GL_FLOAT, 0, 0, m_tex[1]);
933   glVertexAttribPointer(Vloc, 2, GL_FLOAT, 0, 0, m_tex[2]);
934 
935   glEnableVertexAttribArray(vertLoc);
936   glEnableVertexAttribArray(Yloc);
937   glEnableVertexAttribArray(Uloc);
938   glEnableVertexAttribArray(Vloc);
939 
940   // Setup vertex position values
941   for(int i = 0; i < 4; i++)
942   {
943     m_vert[i][0] = m_rotatedDestCoords[i].x;
944     m_vert[i][1] = m_rotatedDestCoords[i].y;
945     m_vert[i][2] = 0.0f;// set z to 0
946   }
947 
948   // Setup texture coordinates
949   for (int i = 0; i < 3; i++)
950   {
951     m_tex[i][0][0] = m_tex[i][3][0] = planes[i].rect.x1;
952     m_tex[i][0][1] = m_tex[i][1][1] = planes[i].rect.y1;
953     m_tex[i][1][0] = m_tex[i][2][0] = planes[i].rect.x2;
954     m_tex[i][2][1] = m_tex[i][3][1] = planes[i].rect.y2;
955   }
956 
957   glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, idx);
958 
959   VerifyGLState();
960 
961   pYUVShader->Disable();
962   VerifyGLState();
963 
964   glDisableVertexAttribArray(vertLoc);
965   glDisableVertexAttribArray(Yloc);
966   glDisableVertexAttribArray(Uloc);
967   glDisableVertexAttribArray(Vloc);
968 
969   VerifyGLState();
970 }
971 
RenderToFBO(int index,int field)972 void CLinuxRendererGLES::RenderToFBO(int index, int field)
973 {
974   CPictureBuffer &buf = m_buffers[index];
975   CYuvPlane (&planes)[YuvImage::MAX_PLANES] = m_buffers[index].fields[field];
976 
977   AVColorPrimaries srcPrim = GetSrcPrimaries(buf.m_srcPrimaries, buf.image.width, buf.image.height);
978   if (srcPrim != m_srcPrimaries)
979   {
980     m_srcPrimaries = srcPrim;
981     m_reloadShaders = true;
982   }
983 
984   bool toneMap = false;
985   if (m_videoSettings.m_ToneMapMethod != VS_TONEMAPMETHOD_OFF)
986   {
987     if (buf.hasLightMetadata || (buf.hasDisplayMetadata && buf.displayMetadata.has_luminance))
988     {
989       toneMap = true;
990     }
991   }
992 
993   if (toneMap != m_toneMap)
994   {
995     m_reloadShaders = true;
996   }
997 
998   m_toneMap = toneMap;
999 
1000   if (m_reloadShaders)
1001   {
1002     m_reloadShaders = 0;
1003     LoadShaders(m_currentField);
1004   }
1005 
1006   if (!m_fbo.fbo.IsValid())
1007   {
1008     if (!m_fbo.fbo.Initialize())
1009     {
1010       CLog::Log(LOGERROR, "GLES: Error initializing FBO");
1011       return;
1012     }
1013 
1014     if (!m_fbo.fbo.CreateAndBindToTexture(GL_TEXTURE_2D, m_sourceWidth, m_sourceHeight, GL_RGBA))
1015     {
1016       CLog::Log(LOGERROR, "GLES: Error creating texture and binding to FBO");
1017       return;
1018     }
1019   }
1020 
1021   glDisable(GL_DEPTH_TEST);
1022 
1023   // Y
1024   glActiveTexture(GL_TEXTURE0);
1025   glBindTexture(m_textureTarget, planes[0].id);
1026   VerifyGLState();
1027 
1028   // U
1029   glActiveTexture(GL_TEXTURE1);
1030   glBindTexture(m_textureTarget, planes[1].id);
1031   VerifyGLState();
1032 
1033   // V
1034   glActiveTexture(GL_TEXTURE2);
1035   glBindTexture(m_textureTarget, planes[2].id);
1036   VerifyGLState();
1037 
1038   glActiveTexture(GL_TEXTURE0);
1039   VerifyGLState();
1040 
1041   Shaders::BaseYUV2RGBGLSLShader *pYUVShader = m_pYUVProgShader;
1042   // make sure the yuv shader is loaded and ready to go
1043   if (!pYUVShader || (!pYUVShader->OK()))
1044   {
1045     CLog::Log(LOGERROR, "GLES: YUV shader not active, cannot do multipass render");
1046     return;
1047   }
1048 
1049   m_fbo.fbo.BeginRender();
1050   VerifyGLState();
1051 
1052   pYUVShader->SetBlack(m_videoSettings.m_Brightness * 0.01f - 0.5f);
1053   pYUVShader->SetContrast(m_videoSettings.m_Contrast * 0.02f);
1054   pYUVShader->SetWidth(planes[0].texwidth);
1055   pYUVShader->SetHeight(planes[0].texheight);
1056   pYUVShader->SetColParams(buf.m_srcColSpace, buf.m_srcBits, !buf.m_srcFullRange, buf.m_srcTextureBits);
1057   pYUVShader->SetDisplayMetadata(buf.hasDisplayMetadata, buf.displayMetadata,
1058                                  buf.hasLightMetadata, buf.lightMetadata);
1059   pYUVShader->SetToneMapParam(m_videoSettings.m_ToneMapParam);
1060 
1061   if (field == FIELD_TOP)
1062   {
1063     pYUVShader->SetField(1);
1064   }
1065   else if(field == FIELD_BOT)
1066   {
1067     pYUVShader->SetField(0);
1068   }
1069 
1070   VerifyGLState();
1071 
1072   glMatrixModview.Push();
1073   glMatrixModview->LoadIdentity();
1074   glMatrixModview.Load();
1075 
1076   glMatrixProject.Push();
1077   glMatrixProject->LoadIdentity();
1078   glMatrixProject->Ortho2D(0, m_sourceWidth, 0, m_sourceHeight);
1079   glMatrixProject.Load();
1080 
1081   pYUVShader->SetMatrices(glMatrixProject.Get(), glMatrixModview.Get());
1082 
1083   CRect viewport;
1084   m_renderSystem->GetViewPort(viewport);
1085   glViewport(0, 0, m_sourceWidth, m_sourceHeight);
1086   glScissor(0, 0, m_sourceWidth, m_sourceHeight);
1087 
1088   if (!pYUVShader->Enable())
1089   {
1090     CLog::Log(LOGERROR, "GLES: Error enabling YUV shader");
1091   }
1092 
1093   m_fbo.width  = planes[0].rect.x2 - planes[0].rect.x1;
1094   m_fbo.height = planes[0].rect.y2 - planes[0].rect.y1;
1095 
1096   if (m_textureTarget == GL_TEXTURE_2D)
1097   {
1098     m_fbo.width  *= planes[0].texwidth;
1099     m_fbo.height *= planes[0].texheight;
1100   }
1101 
1102   m_fbo.width  *= planes[0].pixpertex_x;
1103   m_fbo.height *= planes[0].pixpertex_y;
1104 
1105   // 1st Pass to video frame size
1106   GLubyte idx[4] = {0, 1, 3, 2}; // determines order of triangle strip
1107   GLfloat vert[4][3];
1108   GLfloat tex[3][4][2];
1109 
1110   GLint vertLoc = pYUVShader->GetVertexLoc();
1111   GLint Yloc = pYUVShader->GetYcoordLoc();
1112   GLint Uloc = pYUVShader->GetUcoordLoc();
1113   GLint Vloc = pYUVShader->GetVcoordLoc();
1114 
1115   glVertexAttribPointer(vertLoc, 3, GL_FLOAT, 0, 0, vert);
1116   glVertexAttribPointer(Yloc, 2, GL_FLOAT, 0, 0, tex[0]);
1117   glVertexAttribPointer(Uloc, 2, GL_FLOAT, 0, 0, tex[1]);
1118   glVertexAttribPointer(Vloc, 2, GL_FLOAT, 0, 0, tex[2]);
1119 
1120   glEnableVertexAttribArray(vertLoc);
1121   glEnableVertexAttribArray(Yloc);
1122   glEnableVertexAttribArray(Uloc);
1123   glEnableVertexAttribArray(Vloc);
1124 
1125   // Setup vertex position values
1126   // Set vertex coordinates
1127   vert[0][0] = vert[3][0] = 0.0f;
1128   vert[0][1] = vert[1][1] = 0.0f;
1129   vert[1][0] = vert[2][0] = m_fbo.width;
1130   vert[2][1] = vert[3][1] = m_fbo.height;
1131   vert[0][2] = vert[1][2] = vert[2][2] = vert[3][2] = 0.0f;
1132 
1133   // Setup texture coordinates
1134   for (int i = 0; i < 3; i++)
1135   {
1136     tex[i][0][0] = tex[i][3][0] = planes[i].rect.x1;
1137     tex[i][0][1] = tex[i][1][1] = planes[i].rect.y1;
1138     tex[i][1][0] = tex[i][2][0] = planes[i].rect.x2;
1139     tex[i][2][1] = tex[i][3][1] = planes[i].rect.y2;
1140   }
1141 
1142   glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, idx);
1143 
1144   VerifyGLState();
1145 
1146   pYUVShader->Disable();
1147 
1148   glMatrixModview.PopLoad();
1149   glMatrixProject.PopLoad();
1150 
1151   VerifyGLState();
1152 
1153   glDisableVertexAttribArray(vertLoc);
1154   glDisableVertexAttribArray(Yloc);
1155   glDisableVertexAttribArray(Uloc);
1156   glDisableVertexAttribArray(Vloc);
1157 
1158   m_renderSystem->SetViewPort(viewport);
1159 
1160   m_fbo.fbo.EndRender();
1161 
1162   VerifyGLState();
1163 }
1164 
RenderFromFBO()1165 void CLinuxRendererGLES::RenderFromFBO()
1166 {
1167   glActiveTexture(GL_TEXTURE0);
1168   glBindTexture(GL_TEXTURE_2D, m_fbo.fbo.Texture());
1169   VerifyGLState();
1170 
1171   // Use regular normalized texture coordinates
1172 
1173   // 2nd Pass to screen size with optional video filter
1174 
1175   if (m_pVideoFilterShader)
1176   {
1177     GLint filter;
1178     if (!m_pVideoFilterShader->GetTextureFilter(filter))
1179     {
1180       filter = m_scalingMethod == VS_SCALINGMETHOD_NEAREST ? GL_NEAREST : GL_LINEAR;
1181     }
1182 
1183     m_fbo.fbo.SetFiltering(GL_TEXTURE_2D, filter);
1184     m_pVideoFilterShader->SetSourceTexture(0);
1185     m_pVideoFilterShader->SetWidth(m_sourceWidth);
1186     m_pVideoFilterShader->SetHeight(m_sourceHeight);
1187     m_pVideoFilterShader->SetAlpha(1.0f);
1188     m_pVideoFilterShader->SetMatrices(glMatrixProject.Get(), glMatrixModview.Get());
1189     m_pVideoFilterShader->Enable();
1190   }
1191   else
1192   {
1193     GLint filter = m_scalingMethod == VS_SCALINGMETHOD_NEAREST ? GL_NEAREST : GL_LINEAR;
1194     m_fbo.fbo.SetFiltering(GL_TEXTURE_2D, filter);
1195   }
1196 
1197   VerifyGLState();
1198 
1199   float imgwidth = m_fbo.width / m_sourceWidth;
1200   float imgheight = m_fbo.height / m_sourceHeight;
1201 
1202   GLubyte idx[4] = {0, 1, 3, 2}; // determines order of triangle strip
1203   GLfloat vert[4][3];
1204   GLfloat tex[4][2];
1205 
1206   GLint vertLoc = m_pVideoFilterShader->GetVertexLoc();
1207   GLint loc = m_pVideoFilterShader->GetcoordLoc();
1208 
1209   glVertexAttribPointer(vertLoc, 3, GL_FLOAT, 0, 0, vert);
1210   glVertexAttribPointer(loc, 2, GL_FLOAT, 0, 0, tex);
1211 
1212   glEnableVertexAttribArray(vertLoc);
1213   glEnableVertexAttribArray(loc);
1214 
1215   // Setup vertex position values
1216   for(int i = 0; i < 4; i++)
1217   {
1218     vert[i][0] = m_rotatedDestCoords[i].x;
1219     vert[i][1] = m_rotatedDestCoords[i].y;
1220     vert[i][2] = 0.0f; // set z to 0
1221   }
1222 
1223   // Setup texture coordinates
1224   tex[0][0] = tex[3][0] = 0.0f;
1225   tex[0][1] = tex[1][1] = 0.0f;
1226   tex[1][0] = tex[2][0] = imgwidth;
1227   tex[2][1] = tex[3][1] = imgheight;
1228 
1229   glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, idx);
1230 
1231   VerifyGLState();
1232 
1233   if (m_pVideoFilterShader)
1234   {
1235     m_pVideoFilterShader->Disable();
1236   }
1237 
1238   VerifyGLState();
1239 
1240   glBindTexture(GL_TEXTURE_2D, 0);
1241   VerifyGLState();
1242 }
1243 
RenderCapture(CRenderCapture * capture)1244 bool CLinuxRendererGLES::RenderCapture(CRenderCapture* capture)
1245 {
1246   if (!m_bValidated)
1247   {
1248     return false;
1249   }
1250 
1251   // save current video rect
1252   CRect saveSize = m_destRect;
1253   saveRotatedCoords(); // backup current m_rotatedDestCoords
1254 
1255   // new video rect is thumbnail size
1256   m_destRect.SetRect(0, 0, static_cast<float>(capture->GetWidth()), static_cast<float>(capture->GetHeight()));
1257   MarkDirty();
1258   syncDestRectToRotatedPoints(); // syncs the changed destRect to m_rotatedDestCoords
1259 
1260   // clear framebuffer and invert Y axis to get non-inverted image
1261   glDisable(GL_BLEND);
1262 
1263   glMatrixModview.Push();
1264   glMatrixModview->Translatef(0.0f, capture->GetHeight(), 0.0f);
1265   glMatrixModview->Scalef(1.0f, -1.0f, 1.0f);
1266   glMatrixModview.Load();
1267 
1268   capture->BeginRender();
1269 
1270   Render(RENDER_FLAG_NOOSD, m_iYV12RenderBuffer);
1271   // read pixels
1272   glReadPixels(0, CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight() - capture->GetHeight(), capture->GetWidth(), capture->GetHeight(),
1273                GL_RGBA, GL_UNSIGNED_BYTE, capture->GetRenderBuffer());
1274 
1275   // OpenGLES returns in RGBA order but CRenderCapture needs BGRA order
1276   // XOR Swap RGBA -> BGRA
1277   unsigned char* pixels = static_cast<unsigned char*>(capture->GetRenderBuffer());
1278   for (unsigned int i = 0; i < capture->GetWidth() * capture->GetHeight(); i++, pixels += 4)
1279   {
1280     std::swap(pixels[0], pixels[2]);
1281   }
1282 
1283   capture->EndRender();
1284 
1285   // revert model view matrix
1286   glMatrixModview.PopLoad();
1287 
1288   // restore original video rect
1289   m_destRect = saveSize;
1290   restoreRotatedCoords(); // restores the previous state of the rotated dest coords
1291 
1292   return true;
1293 }
1294 
1295 //********************************************************************************************************/
1296 // YV12 Texture creation, deletion, copying + clearing
1297 //********************************************************************************************************/
UploadYV12Texture(int source)1298 bool CLinuxRendererGLES::UploadYV12Texture(int source)
1299 {
1300   CPictureBuffer& buf = m_buffers[source];
1301   YuvImage* im = &buf.image;
1302 
1303   VerifyGLState();
1304 
1305   glPixelStorei(GL_UNPACK_ALIGNMENT,1);
1306 
1307   // load Y plane
1308   LoadPlane(buf.fields[FIELD_FULL][0], GL_LUMINANCE,
1309             im->width, im->height,
1310             im->stride[0], im->bpp, im->plane[0]);
1311 
1312   // load U plane
1313   LoadPlane(buf.fields[FIELD_FULL][1], GL_LUMINANCE,
1314             im->width >> im->cshift_x, im->height >> im->cshift_y,
1315             im->stride[1], im->bpp, im->plane[1]);
1316 
1317   // load V plane
1318   LoadPlane(buf.fields[FIELD_FULL][2], GL_ALPHA,
1319             im->width >> im->cshift_x, im->height >> im->cshift_y,
1320             im->stride[2], im->bpp, im->plane[2]);
1321 
1322   VerifyGLState();
1323 
1324   CalculateTextureSourceRects(source, 3);
1325 
1326   return true;
1327 }
1328 
DeleteYV12Texture(int index)1329 void CLinuxRendererGLES::DeleteYV12Texture(int index)
1330 {
1331   YuvImage &im = m_buffers[index].image;
1332 
1333   if (m_buffers[index].fields[FIELD_FULL][0].id == 0)
1334   {
1335     return;
1336   }
1337 
1338   // finish up all textures, and delete them
1339   for(int f = 0; f < MAX_FIELDS; f++)
1340   {
1341     for(int p = 0; p < YuvImage::MAX_PLANES; p++)
1342     {
1343       if (m_buffers[index].fields[f][p].id)
1344       {
1345         if (glIsTexture(m_buffers[index].fields[f][p].id))
1346         {
1347           glDeleteTextures(1, &m_buffers[index].fields[f][p].id);
1348         }
1349 
1350         m_buffers[index].fields[f][p].id = 0;
1351       }
1352     }
1353   }
1354 
1355   for(int p = 0; p < YuvImage::MAX_PLANES; p++)
1356   {
1357     im.plane[p] = nullptr;
1358   }
1359 }
1360 
CreateYV12Texture(int index)1361 bool CLinuxRendererGLES::CreateYV12Texture(int index)
1362 {
1363   // since we also want the field textures, pitch must be texture aligned
1364   unsigned p;
1365   YuvImage &im = m_buffers[index].image;
1366 
1367   DeleteYV12Texture(index);
1368 
1369   im.height = m_sourceHeight;
1370   im.width = m_sourceWidth;
1371   im.cshift_x = 1;
1372   im.cshift_y = 1;
1373   im.bpp = 1;
1374 
1375   im.stride[0] = im.bpp * im.width;
1376   im.stride[1] = im.bpp * (im.width >> im.cshift_x);
1377   im.stride[2] = im.bpp * (im.width >> im.cshift_x);
1378 
1379   im.planesize[0] = im.stride[0] * im.height;
1380   im.planesize[1] = im.stride[1] * (im.height >> im.cshift_y);
1381   im.planesize[2] = im.stride[2] * (im.height >> im.cshift_y);
1382 
1383   for (int i = 0; i < 3; i++)
1384   {
1385     im.plane[i] = nullptr; // will be set in UploadTexture()
1386   }
1387 
1388   for(int f = 0; f < MAX_FIELDS; f++)
1389   {
1390     for(p = 0; p < YuvImage::MAX_PLANES; p++)
1391     {
1392       if (!glIsTexture(m_buffers[index].fields[f][p].id))
1393       {
1394         glGenTextures(1, &m_buffers[index].fields[f][p].id);
1395         VerifyGLState();
1396       }
1397     }
1398   }
1399 
1400   // YUV
1401   for (int f = FIELD_FULL; f <= FIELD_BOT ; f++)
1402   {
1403     int fieldshift = (f == FIELD_FULL) ? 0 : 1;
1404     CYuvPlane (&planes)[YuvImage::MAX_PLANES] = m_buffers[index].fields[f];
1405 
1406     planes[0].texwidth  = im.width;
1407     planes[0].texheight = im.height >> fieldshift;
1408 
1409     planes[1].texwidth  = planes[0].texwidth  >> im.cshift_x;
1410     planes[1].texheight = planes[0].texheight >> im.cshift_y;
1411     planes[2].texwidth  = planes[0].texwidth  >> im.cshift_x;
1412     planes[2].texheight = planes[0].texheight >> im.cshift_y;
1413 
1414     for (int p = 0; p < 3; p++)
1415     {
1416       planes[p].pixpertex_x = 1;
1417       planes[p].pixpertex_y = 1;
1418     }
1419 
1420     for(int p = 0; p < 3; p++)
1421     {
1422       CYuvPlane &plane = planes[p];
1423       if (plane.texwidth * plane.texheight == 0)
1424       {
1425         continue;
1426       }
1427 
1428       glBindTexture(m_textureTarget, plane.id);
1429 
1430       GLint format;
1431       if (p == 2) // V plane needs an alpha texture
1432       {
1433         format = GL_ALPHA;
1434       }
1435       else
1436       {
1437         format = GL_LUMINANCE;
1438       }
1439 
1440       glTexImage2D(m_textureTarget, 0, format, plane.texwidth, plane.texheight, 0, format, GL_UNSIGNED_BYTE, nullptr);
1441       glTexParameteri(m_textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1442       glTexParameteri(m_textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1443       glTexParameteri(m_textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1444       glTexParameteri(m_textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1445       VerifyGLState();
1446     }
1447   }
1448   return true;
1449 }
1450 
1451 //********************************************************************************************************
1452 // NV12 Texture loading, creation and deletion
1453 //********************************************************************************************************
UploadNV12Texture(int source)1454 bool CLinuxRendererGLES::UploadNV12Texture(int source)
1455 {
1456   CPictureBuffer& buf = m_buffers[source];
1457   YuvImage* im = &buf.image;
1458 
1459   bool deinterlacing;
1460   if (m_currentField == FIELD_FULL)
1461   {
1462     deinterlacing = false;
1463   }
1464   else
1465   {
1466     deinterlacing = true;
1467   }
1468 
1469   VerifyGLState();
1470 
1471   glPixelStorei(GL_UNPACK_ALIGNMENT, im->bpp);
1472 
1473   if (deinterlacing)
1474   {
1475     // Load Odd Y field
1476     LoadPlane(buf.fields[FIELD_TOP][0] , GL_LUMINANCE,
1477               im->width, im->height >> 1,
1478               im->stride[0]*2, im->bpp, im->plane[0]);
1479 
1480     // Load Even Y field
1481     LoadPlane(buf.fields[FIELD_BOT][0], GL_LUMINANCE,
1482               im->width, im->height >> 1,
1483               im->stride[0]*2, im->bpp, im->plane[0] + im->stride[0]) ;
1484 
1485     // Load Odd UV Fields
1486     LoadPlane(buf.fields[FIELD_TOP][1], GL_LUMINANCE_ALPHA,
1487               im->width >> im->cshift_x, im->height >> (im->cshift_y + 1),
1488               im->stride[1]*2, im->bpp, im->plane[1]);
1489 
1490     // Load Even UV Fields
1491     LoadPlane(buf.fields[FIELD_BOT][1], GL_LUMINANCE_ALPHA,
1492               im->width >> im->cshift_x, im->height >> (im->cshift_y + 1),
1493               im->stride[1]*2, im->bpp, im->plane[1] + im->stride[1]);
1494 
1495   }
1496   else
1497   {
1498     // Load Y plane
1499     LoadPlane(buf. fields[FIELD_FULL][0], GL_LUMINANCE,
1500               im->width, im->height,
1501               im->stride[0], im->bpp, im->plane[0]);
1502 
1503     // Load UV plane
1504     LoadPlane(buf.fields[FIELD_FULL][1], GL_LUMINANCE_ALPHA,
1505               im->width >> im->cshift_x, im->height >> im->cshift_y,
1506               im->stride[1], im->bpp, im->plane[1]);
1507   }
1508 
1509   VerifyGLState();
1510 
1511   CalculateTextureSourceRects(source, 3);
1512 
1513   return true;
1514 }
1515 
CreateNV12Texture(int index)1516 bool CLinuxRendererGLES::CreateNV12Texture(int index)
1517 {
1518   // since we also want the field textures, pitch must be texture aligned
1519   CPictureBuffer& buf = m_buffers[index];
1520   YuvImage &im = buf.image;
1521 
1522   // Delete any old texture
1523   DeleteNV12Texture(index);
1524 
1525   im.height = m_sourceHeight;
1526   im.width  = m_sourceWidth;
1527   im.cshift_x = 1;
1528   im.cshift_y = 1;
1529   im.bpp = 1;
1530 
1531   im.stride[0] = im.width;
1532   im.stride[1] = im.width;
1533   im.stride[2] = 0;
1534 
1535   im.plane[0] = nullptr;
1536   im.plane[1] = nullptr;
1537   im.plane[2] = nullptr;
1538 
1539   // Y plane
1540   im.planesize[0] = im.stride[0] * im.height;
1541   // packed UV plane
1542   im.planesize[1] = im.stride[1] * im.height / 2;
1543   // third plane is not used
1544   im.planesize[2] = 0;
1545 
1546   for (int i = 0; i < 2; i++)
1547   {
1548     im.plane[i] = nullptr; // will be set in UploadTexture()
1549   }
1550 
1551   for(int f = 0; f < MAX_FIELDS; f++)
1552   {
1553     for(int p = 0; p < 2; p++)
1554     {
1555       if (!glIsTexture(buf.fields[f][p].id))
1556       {
1557         glGenTextures(1, &buf.fields[f][p].id);
1558         VerifyGLState();
1559       }
1560     }
1561 
1562     buf.fields[f][2].id = buf.fields[f][1].id;
1563   }
1564 
1565   // YUV
1566   for (int f = FIELD_FULL; f <= FIELD_BOT; f++)
1567   {
1568     int fieldshift = (f == FIELD_FULL) ? 0 : 1;
1569     CYuvPlane (&planes)[YuvImage::MAX_PLANES] = buf.fields[f];
1570 
1571     planes[0].texwidth  = im.width;
1572     planes[0].texheight = im.height >> fieldshift;
1573 
1574     planes[1].texwidth  = planes[0].texwidth  >> im.cshift_x;
1575     planes[1].texheight = planes[0].texheight >> im.cshift_y;
1576     planes[2].texwidth  = planes[1].texwidth;
1577     planes[2].texheight = planes[1].texheight;
1578 
1579     for (int p = 0; p < 3; p++)
1580     {
1581       planes[p].pixpertex_x = 1;
1582       planes[p].pixpertex_y = 1;
1583     }
1584 
1585     for(int p = 0; p < 2; p++)
1586     {
1587       CYuvPlane &plane = planes[p];
1588       if (plane.texwidth * plane.texheight == 0)
1589       {
1590         continue;
1591       }
1592 
1593       glBindTexture(m_textureTarget, plane.id);
1594 
1595       if (p == 1)
1596       {
1597         glTexImage2D(m_textureTarget, 0, GL_LUMINANCE_ALPHA, plane.texwidth, plane.texheight, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, nullptr);
1598       }
1599       else
1600       {
1601         glTexImage2D(m_textureTarget, 0, GL_LUMINANCE, plane.texwidth, plane.texheight, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, nullptr);
1602       }
1603 
1604       glTexParameteri(m_textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1605       glTexParameteri(m_textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1606       glTexParameteri(m_textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1607       glTexParameteri(m_textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1608       VerifyGLState();
1609     }
1610   }
1611 
1612   return true;
1613 }
1614 
DeleteNV12Texture(int index)1615 void CLinuxRendererGLES::DeleteNV12Texture(int index)
1616 {
1617   CPictureBuffer& buf = m_buffers[index];
1618   YuvImage &im = buf.image;
1619 
1620   if (buf.fields[FIELD_FULL][0].id == 0)
1621   {
1622     return;
1623   }
1624 
1625   // finish up all textures, and delete them
1626   for(int f = 0; f < MAX_FIELDS; f++)
1627   {
1628     for(int p = 0; p < 2; p++)
1629     {
1630       if (buf.fields[f][p].id)
1631       {
1632         if (glIsTexture(buf.fields[f][p].id))
1633         {
1634           glDeleteTextures(1, &buf.fields[f][p].id);
1635         }
1636 
1637         buf.fields[f][p].id = 0;
1638       }
1639     }
1640 
1641     buf.fields[f][2].id = 0;
1642   }
1643 
1644   for(int p = 0; p < 2; p++)
1645   {
1646     im.plane[p] = nullptr;
1647   }
1648 }
1649 
1650 //********************************************************************************************************
1651 // SurfaceTexture creation, deletion, copying + clearing
1652 //********************************************************************************************************
SetTextureFilter(GLenum method)1653 void CLinuxRendererGLES::SetTextureFilter(GLenum method)
1654 {
1655   for (int i = 0 ; i < m_NumYV12Buffers; i++)
1656   {
1657     CPictureBuffer& buf = m_buffers[i];
1658 
1659     for (int f = FIELD_FULL; f <= FIELD_BOT; f++)
1660     {
1661       for (int p = 0; p < 3; p++)
1662       {
1663         if(glIsTexture(buf.fields[f][p].id))
1664         {
1665           glBindTexture(m_textureTarget, buf.fields[f][p].id);
1666           glTexParameteri(m_textureTarget, GL_TEXTURE_MIN_FILTER, method);
1667           glTexParameteri(m_textureTarget, GL_TEXTURE_MAG_FILTER, method);
1668           VerifyGLState();
1669         }
1670       }
1671     }
1672   }
1673 }
1674 
Supports(ERENDERFEATURE feature)1675 bool CLinuxRendererGLES::Supports(ERENDERFEATURE feature)
1676 {
1677   if (feature == RENDERFEATURE_GAMMA ||
1678       feature == RENDERFEATURE_NOISE ||
1679       feature == RENDERFEATURE_SHARPNESS ||
1680       feature == RENDERFEATURE_NONLINSTRETCH)
1681   {
1682     return false;
1683   }
1684 
1685   if (feature == RENDERFEATURE_STRETCH ||
1686       feature == RENDERFEATURE_ZOOM ||
1687       feature == RENDERFEATURE_VERTICAL_SHIFT ||
1688       feature == RENDERFEATURE_PIXEL_RATIO ||
1689       feature == RENDERFEATURE_POSTPROCESS ||
1690       feature == RENDERFEATURE_ROTATION ||
1691       feature == RENDERFEATURE_BRIGHTNESS ||
1692       feature == RENDERFEATURE_CONTRAST ||
1693       feature == RENDERFEATURE_TONEMAP)
1694   {
1695     return true;
1696   }
1697 
1698   return false;
1699 }
1700 
SupportsMultiPassRendering()1701 bool CLinuxRendererGLES::SupportsMultiPassRendering()
1702 {
1703   return true;
1704 }
1705 
Supports(ESCALINGMETHOD method)1706 bool CLinuxRendererGLES::Supports(ESCALINGMETHOD method)
1707 {
1708   if(method == VS_SCALINGMETHOD_NEAREST ||
1709      method == VS_SCALINGMETHOD_LINEAR)
1710   {
1711     return true;
1712   }
1713 
1714   if(method == VS_SCALINGMETHOD_CUBIC_MITCHELL ||
1715      method == VS_SCALINGMETHOD_LANCZOS2 ||
1716      method == VS_SCALINGMETHOD_SPLINE36_FAST ||
1717      method == VS_SCALINGMETHOD_LANCZOS3_FAST ||
1718      method == VS_SCALINGMETHOD_SPLINE36 ||
1719      method == VS_SCALINGMETHOD_LANCZOS3)
1720   {
1721     // if scaling is below level, avoid hq scaling
1722     float scaleX = fabs((static_cast<float>(m_sourceWidth) - m_destRect.Width()) / m_sourceWidth) * 100;
1723     float scaleY = fabs((static_cast<float>(m_sourceHeight) - m_destRect.Height()) / m_sourceHeight) * 100;
1724     int minScale = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_HQSCALERS);
1725     if (scaleX < minScale && scaleY < minScale)
1726     {
1727       return false;
1728     }
1729 
1730     if (m_renderMethod & RENDER_GLSL)
1731     {
1732       // spline36 and lanczos3 are only allowed through advancedsettings.xml
1733       if(method != VS_SCALINGMETHOD_SPLINE36 &&
1734          method != VS_SCALINGMETHOD_LANCZOS3)
1735       {
1736         return true;
1737       }
1738       else
1739       {
1740         return CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoEnableHighQualityHwScalers;
1741       }
1742     }
1743   }
1744 
1745   return false;
1746 }
1747 
GetRenderInfo()1748 CRenderInfo CLinuxRendererGLES::GetRenderInfo()
1749 {
1750   CRenderInfo info;
1751   info.max_buffer_size = NUM_BUFFERS;
1752 
1753   return info;
1754 }
1755 
IsGuiLayer()1756 bool CLinuxRendererGLES::IsGuiLayer()
1757 {
1758   return true;
1759 }
1760 
GetSrcPrimaries(AVColorPrimaries srcPrimaries,unsigned int width,unsigned int height)1761 AVColorPrimaries CLinuxRendererGLES::GetSrcPrimaries(AVColorPrimaries srcPrimaries, unsigned int width, unsigned int height)
1762 {
1763   AVColorPrimaries ret = srcPrimaries;
1764   if (ret == AVCOL_PRI_UNSPECIFIED)
1765   {
1766     if (width > 1024 || height >= 600)
1767     {
1768       ret = AVCOL_PRI_BT709;
1769     }
1770     else
1771     {
1772       ret = AVCOL_PRI_BT470BG;
1773     }
1774   }
1775 
1776   return ret;
1777 }
1778