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