MythTV master
mythopenglvideo.cpp
Go to the documentation of this file.
1// std
2#include <utility>
3
4// Qt
5#include <QPen>
6
7// MythTV
8#include "libmythbase/mythconfig.h"
12#include "mythavutil.h"
16#include "tv.h"
17
18// FFmpeg
19extern "C" {
20#include "libavutil/stereo3d.h"
21}
22
23#define LOC QString("GLVid: ")
24// static constexpr int8_t MAX_VIDEO_TEXTURES { 10 }; // YV12 Kernel deinterlacer + 1
25
37 MythVideoBounds* Bounds, const MythVideoProfilePtr& VideoProfile, const QString& Profile)
38 : MythVideoGPU(Render, ColourSpace, Bounds, VideoProfile, Profile),
39 m_openglRender(Render)
40{
42 {
43 LOG(VB_GENERAL, LOG_ERR, LOC + "Fatal error");
44 return;
45 }
46
48 if (m_openglRender->isOpenGLES())
49 m_gles = m_openglRender->format().majorVersion();
50
51 // Set OpenGL feature support
54 m_valid = true;
55
56 m_chromaUpsamplingFilter = gCoreContext->GetBoolSetting("ChromaUpsamplingFilter", true);
57 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Chroma upsampling filter %1")
58 .arg(m_chromaUpsamplingFilter ? "enabled" : "disabled"));
59}
60
62{
63 if (!m_openglRender)
64 return;
65
68 delete m_toneMap;
70}
71
72void MythOpenGLVideo::ColourSpaceUpdate(bool PrimariesChanged)
73{
75
76 // if input/output type are unset - we haven't created the shaders yet
77 if (PrimariesChanged && (m_outputType != FMT_NONE))
78 {
79 LOG(VB_GENERAL, LOG_INFO, LOC + "Primaries conversion changed - recreating shaders");
81 }
82
83 float colourgamma = m_videoColourSpace->GetColourGamma();
84 float displaygamma = 1.0F / m_videoColourSpace->GetDisplayGamma();
85 QMatrix4x4 primary = m_videoColourSpace->GetPrimaryMatrix();
86 for (size_t i = Progressive; i < ShaderCount; ++i)
87 {
89 m_openglRender->SetShaderProgramParams(m_shaders[i], primary, "m_primaryMatrix");
90 if (m_shaders[i])
91 {
92 m_shaders[i]->setUniformValue("m_colourGamma", colourgamma);
93 m_shaders[i]->setUniformValue("m_displayGamma", displaygamma);
94 }
95 }
96}
97
99{
100 if (m_inputTextureSize.isEmpty())
101 return;
102
104 bool rect = m_textureTarget == QOpenGLTexture::TargetRectangle;
105 GLfloat lineheight = rect ? 1.0F : 1.0F / m_inputTextureSize.height();
106 GLfloat maxheight = rect ? m_videoDispDim.height() : m_videoDispDim.height() /
107 static_cast<GLfloat>(m_inputTextureSize.height());
108 GLfloat fieldsize = rect ? 0.5F : m_inputTextureSize.height() / 2.0F;
109 QVector4D parameters(lineheight, /* lineheight */
110 static_cast<GLfloat>(m_inputTextureSize.width()), /* 'Y' select */
111 maxheight - lineheight, /* maxheight */
112 fieldsize /* fieldsize */);
113
114 for (size_t i = Progressive; i < ShaderCount; ++i)
115 {
116 if (m_shaders[i])
117 {
119 m_shaders[i]->setUniformValue("m_frameData", parameters);
120 if (BicubicUpsize == i)
121 {
122 QVector2D size { rect ? 1.0F : static_cast<GLfloat>(m_videoDim.width()),
123 rect ? 1.0F : static_cast<GLfloat>(m_videoDim.height()) };
124 m_shaders[i]->setUniformValue("m_textureSize", size);
125 }
126 }
127 }
128}
129
131{
135}
136
138{
139 // If switching off/from basic deinterlacing, then we need to delete and
140 // recreate the input textures and sometimes the shaders as well - so start
141 // from scratch
143 {
144 // Note. Textures will be created with linear filtering - which matches
145 // no resizing - which should be the case for the basic deinterlacer - and
146 // the call to SetupFrameFormat will reset resizing anyway
147 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Removing single field textures");
148 // revert to YUY2 if preferred
149 if ((m_inputType == FMT_YV12) && (m_profile == "opengl"))
152 emit OutputChanged(m_videoDim, m_videoDim, -1.0F);
153 }
156 m_deinterlacer2x = false;
157}
158
160 MythDeintType Filter /* = DEINT_SHADER */,
161 bool CreateReferences /* = true */)
162{
163 if (!Frame)
164 return false;
165
166 // do we want an opengl shader?
167 // shaders trump CPU deinterlacers if selected and driver deinterlacers will only
168 // be available under restricted circumstances
169 // N.B. there should in theory be no situation in which shader deinterlacing is not
170 // available for software formats, hence there should be no need to fallback to cpu
171
172 if (!is_interlaced(Scan) || Frame->m_alreadyDeinterlaced)
173 {
175 return false;
176 }
177
178 m_deinterlacer2x = true;
179 MythDeintType deinterlacer = Frame->GetDoubleRateOption(Filter);
180 MythDeintType other = Frame->GetDoubleRateOption(DEINT_DRIVER);
181 if (other) // another double rate deinterlacer is enabled
182 {
184 return false;
185 }
186
187 if (!deinterlacer)
188 {
189 m_deinterlacer2x = false;
190 deinterlacer = Frame->GetSingleRateOption(Filter);
191 other = Frame->GetSingleRateOption(DEINT_DRIVER);
192 if (!deinterlacer || other) // no shader deinterlacer needed
193 {
195 return false;
196 }
197 }
198
199 // if we get this far, we cannot use driver deinterlacers, shader deints
200 // are preferred over cpu, we have a deinterlacer but don't actually care whether
201 // it is single or double rate
202 if (m_deinterlacer == deinterlacer || (m_fallbackDeinterlacer && (m_fallbackDeinterlacer == deinterlacer)))
203 return true;
204
205 // Lock
207
208 // delete old reference textures
211
212 // For basic deinterlacing of software frames, we now create 2 sets of field
213 // based textures - which is the same approach taken by the CPU based onefield/bob
214 // deinterlacer and the EGL basic deinterlacer. The advantages of this
215 // approach are:-
216 // - no dependent texturing in the samplers (it is just a basic YUV to RGB conversion
217 // in the shader)
218 // - better quality (the onefield shader line doubles but does not have the
219 // implicit interpolation/smoothing of using separate textures directly,
220 // which leads to 'blockiness').
221 // - as we are not sampling other fields, there is no need to use an intermediate
222 // framebuffer to ensure accurate sampling - so we can skip the resize stage.
223 //
224 // YUYV formats are currently not supported as it does not work correctly - force YV12 instead.
225
226 if (deinterlacer == DEINT_BASIC && MythVideoFrame::YUVFormat(m_inputType))
227 {
228 if (m_outputType == FMT_YUY2)
229 {
230 LOG(VB_GENERAL, LOG_INFO, LOC + "Forcing OpenGL YV12 for basic deinterlacer");
232 }
234 QSize size(m_videoDim.width(), m_videoDim.height() >> 1);
235 std::vector<QSize> sizes;
236 sizes.emplace_back(size);
237 // N.B. If we are currently resizing, it will be turned off for this
238 // deinterlacer, so the default linear texture filtering is OK.
240 // nextTextures will hold the other field
242 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Created %1 single field textures")
243 .arg(m_inputTextures.size() * 2));
244 // Con MythVideoBounds into display the field only
245 emit OutputChanged(m_videoDim, size, -1.0F);
246 }
247
248 // sanity check max texture units. Should only be an issue on old hardware (e.g. Pi)
250 uint refstocreate = ((deinterlacer == DEINT_HIGH) && CreateReferences) ? 2 : 0;
251 int totaltextures = static_cast<int>(MythVideoFrame::GetNumPlanes(m_outputType)) * static_cast<int>(refstocreate + 1);
252 if (totaltextures > max)
253 {
254 m_fallbackDeinterlacer = deinterlacer;
255 LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Insufficent texture units for deinterlacer '%1' (%2 < %3)")
256 .arg(MythVideoFrame::DeinterlacerName(deinterlacer | DEINT_SHADER, m_deinterlacer2x)).arg(max).arg(totaltextures));
257 deinterlacer = DEINT_BASIC;
258 LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Falling back to '%1'")
260 }
261
262 // create new deinterlacers - the old ones will be deleted
263 if (!(CreateVideoShader(InterlacedBot, deinterlacer) && CreateVideoShader(InterlacedTop, deinterlacer)))
264 return false;
265
266 // create the correct number of reference textures
267 if (refstocreate)
268 {
269 std::vector<QSize> sizes;
270 sizes.emplace_back(m_videoDim);
273 // ensure we use GL_NEAREST if resizing is already active and needed
274 if ((m_resizing & Sampling) == Sampling)
275 {
278 }
279 }
280
281 // ensure they work correctly
282 UpdateColourSpace(false);
284 m_deinterlacer = deinterlacer;
285
286 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Created deinterlacer '%1' (%2->%3)")
290 return true;
291}
292
301{
302 if (!m_openglRender || !(m_features & QOpenGLFunctions::Shaders))
303 return false;
304
305 // delete the old
306 if (m_shaders[Type])
308 m_shaders[Type] = nullptr;
309
310 QStringList defines;
311 QString vertex = DefaultVertexShader;
312 QString fragment;
313 int cost = 1;
314
316 defines << "EXTOES";
317
318 if ((Default == Type) || (BicubicUpsize == Type) || (!MythVideoFrame::YUVFormat(m_outputType)))
319 {
320 QString glsldefines;
321 for (const QString& define : std::as_const(defines))
322 glsldefines += QString("#define MYTHTV_%1\n").arg(define);
323 fragment = glsldefines + YUVFragmentExtensions + ((BicubicUpsize == Type) ? BicubicShader : RGBFragmentShader);
324
325#if CONFIG_MEDIACODEC
327 vertex = MediaCodecVertexShader;
328#endif
329 }
330 // no interlaced shaders yet (i.e. interlaced chroma upsampling - not deinterlacers)
331 else
332 {
333 fragment = YUVFragmentShader;
334 QString extensions = YUVFragmentExtensions;
335 QString glsldefines;
336
337 // Any software frames that are not 8bit need to use unsigned integer
338 // samplers with GLES3.x - which need more modern shaders
340 {
341 static const QString glsl300("#version 300 es\n");
342 fragment = GLSL300YUVFragmentShader;
343 extensions = GLSL300YUVFragmentExtensions;
344 vertex = glsl300 + GLSL300VertexShader;
345 glsldefines.append(glsl300);
346 }
347
348 bool kernel = false;
349 bool topfield = InterlacedTop == Type;
350 bool progressive = (Progressive == Type) || (Deint == DEINT_NONE);
352 {
353 defines << "YV12";
354 cost = 3;
355 }
357 {
358 defines << "NV12";
359 cost = 2;
360 }
361 else if (FMT_YUY2 == m_outputType)
362 {
363 defines << "YUY2";
364 }
365
366#if CONFIG_VIDEOTOOLBOX
367 // N.B. Rectangular texture support is only currently used for VideoToolBox
368 // video frames which are NV12. Do not use rectangular textures for the 'default'
369 // shaders as it breaks video resizing and would require changes to our
370 // FramebufferObject code.
371 if ((m_textureTarget == QOpenGLTexture::TargetRectangle) && (Default != Type))
372 defines << "RECTS";
373#endif
374 if (!progressive)
375 {
376 bool basic = Deint == DEINT_BASIC && MythVideoFrame::YUVFormat(m_inputType);
377 // Chroma upsampling filter
379 m_chromaUpsamplingFilter && !basic)
380 {
381 defines << "CUE";
382 }
383
384 // field
385 if (topfield && !basic)
386 defines << "TOPFIELD";
387
388 switch (Deint)
389 {
390 case DEINT_BASIC:
391 if (!basic)
392 {
393 cost *= 2;
394 defines << "ONEFIELD";
395 }
396 break;
397 case DEINT_MEDIUM: cost *= 5; defines << "LINEARBLEND"; break;
398 case DEINT_HIGH: cost *= 15; defines << "KERNEL"; kernel = true; break;
399 default: break;
400 }
401 }
402
403 // Start building the new fragment shader
404 // We do this in code otherwise the shader string becomes monolithic
405
406 // 'expand' calls to sampleYUV for multiple planes
407 // do this before we add the samplers
408 int count = static_cast<int>(MythVideoFrame::GetNumPlanes(m_outputType));
409 for (int i = (kernel ? 2 : 0); (i >= 0) && count; i--)
410 {
411 QString find = QString("s_texture%1").arg(i);
412 QStringList replacelist;
413 for (int j = (i * count); j < ((i + 1) * count); ++j)
414 replacelist << QString("s_texture%1").arg(j);
415 fragment.replace(find, replacelist.join(", "));
416 }
417
418 // 'expand' calls to the kernel function
419 if (kernel && count)
420 {
421 for (int i = 1 ; i >= 0; i--)
422 {
423 QString find1 = QString("sampler2D kernelTex%1").arg(i);
424 QString find2 = QString("kernelTex%1").arg(i);
425 QStringList replacelist1;
426 QStringList replacelist2;
427 for (int j = 0; j < count; ++j)
428 {
429 replacelist1 << QString("sampler2D kernelTexture%1%2").arg(i).arg(j);
430 replacelist2 << QString("kernelTexture%1%2").arg(i).arg(j);
431 }
432 fragment.replace(find1, replacelist1.join(", "));
433 fragment.replace(find2, replacelist2.join(", "));
434 }
435 }
436
437 // Retrieve colour mappping defines
439
440 // Add defines
441 for (const QString& define : std::as_const(defines))
442 glsldefines += QString("#define MYTHTV_%1\n").arg(define);
443
444 // Add the required samplers
445 int start = 0;
446 int end = count;
447 if (kernel)
448 {
449 end *= 3;
450 if (topfield)
451 start += count;
452 else
453 end -= count;
454 }
455 QString glslsamplers;
456 for (int i = start; i < end; ++i)
457 glslsamplers += QString("uniform sampler2D s_texture%1;\n").arg(i);
458
459 // construct the final shader string
460 fragment = glsldefines + extensions + glslsamplers + fragment;
461 }
462
463 m_shaderCost[Type] = cost;
464 QOpenGLShaderProgram *program = m_openglRender->CreateShaderProgram(vertex, fragment);
465 if (!program)
466 return false;
467
468 m_shaders[Type] = program;
469 return true;
470}
471
473 QSize Size, GLenum TextureTarget)
474{
475 QString texnew { "2D" };
476 if (TextureTarget == QOpenGLTexture::TargetRectangle)
477 texnew = "Rect";
478 else if (TextureTarget == GL_TEXTURE_EXTERNAL_OES)
479 texnew = "OES";
480
481 QString texold { "2D" };
482 if (m_textureTarget == QOpenGLTexture::TargetRectangle)
483 texold = "Rect";
485 texold = "OES";
486
487 LOG(VB_GENERAL, LOG_INFO, LOC +
488 QString("New frame format: %1:%2 %3x%4 (Tex: %5) -> %6:%7 %8x%9 (Tex: %10)")
491 QString::number(m_videoDim.width()),
492 QString::number(m_videoDim.height()),
493 texold,
496 QString::number(Size.width()),
497 QString::number(Size.height()))
498 .arg(texnew));
499
501
502 m_inputType = InputType;
503 m_outputType = OutputType;
504 m_textureTarget = TextureTarget;
505 m_videoDim = Size;
506
507 // This is only currently needed for RGBA32 frames from composed DRM_PRIME
508 // textures that may be half height for simple bob deinterlacing
510 emit OutputChanged(m_videoDim, m_videoDim, -1.0F);
511
512 if (!MythVideoFrame::HardwareFormat(InputType))
513 {
514 std::vector<QSize> sizes;
515 sizes.push_back(Size);
517 if (m_inputTextures.empty())
518 {
519 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create input textures");
520 return false;
521 }
522
523 m_inputTextureSize = m_inputTextures[0]->m_totalSize;
524 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Created %1 input textures for '%2'")
525 .arg(m_inputTextures.size()).arg(GetProfile()));
526 }
527 else
528 {
529 m_inputTextureSize = Size;
530 }
531
532 // Create shaders
534 return false;
535
536 UpdateColourSpace(false);
538 return true;
539}
540
542{
543 for (auto & shader : m_shaders)
544 if (shader)
546 m_shaders.fill(nullptr);
547 m_shaderCost.fill(1);
551 m_textureTarget = QOpenGLTexture::Target2D;
555 m_frameBuffer = nullptr;
556 m_frameBufferTexture = nullptr;
557
559}
560
563{
564 if (Frame->m_type == FMT_NONE)
565 return;
566
567 // Hardware frames are retrieved/updated in PrepareFrame but we need to
568 // reset software frames now if necessary
570 {
572 {
573 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Resetting input format");
575 }
576 return;
577 }
578
579 // Sanitise frame
580 if ((Frame->m_width < 1) || (Frame->m_height < 1) || !Frame->m_buffer)
581 {
582 LOG(VB_GENERAL, LOG_ERR, LOC + "Invalid software frame");
583 return;
584 }
585
586 // Can we render this frame format
587 if (!MythVideoFrame::YUVFormat(Frame->m_type))
588 {
589 LOG(VB_GENERAL, LOG_ERR, LOC + "Frame format is not supported");
590 return;
591 }
592
593 // lock
595
596 // check for input changes
597 if ((Frame->m_width != m_videoDim.width()) ||
598 (Frame->m_height != m_videoDim.height()) ||
599 (Frame->m_type != m_inputType))
600 {
601 VideoFrameType frametype = Frame->m_type;
602 if ((frametype == FMT_YV12) && (m_profile == "opengl"))
603 frametype = FMT_YUY2;
604 QSize size(Frame->m_width, Frame->m_height);
605 if (!SetupFrameFormat(Frame->m_type, frametype, size, QOpenGLTexture::Target2D))
606 return;
607 }
608
609 // Setup deinterlacing if required
610 AddDeinterlacer(Frame, Scan);
611
612 if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
613 m_openglRender->logDebugMarker(LOC + "UPDATE_FRAME_START");
614
616
617 // Rotate textures if necessary
618 bool current = true;
620 {
621 if (!m_nextTextures.empty() && !m_prevTextures.empty())
622 {
623 if (qAbs(Frame->m_frameCounter - m_discontinuityCounter) > 1)
625 std::vector<MythVideoTextureOpenGL*> temp = m_prevTextures;
628 m_nextTextures = temp;
629 current = false;
630 }
631 }
632
633 m_discontinuityCounter = Frame->m_frameCounter;
634
636 {
637 // first field. Fake the pitches
638 FramePitches pitches = Frame->m_pitches;
639 Frame->m_pitches[0] = Frame->m_pitches[0] << 1;
640 Frame->m_pitches[1] = Frame->m_pitches[1] << 1;
641 Frame->m_pitches[2] = Frame->m_pitches[2] << 1;
643 // second field. Fake the offsets as well.
644 FrameOffsets offsets = Frame->m_offsets;
645 Frame->m_offsets[0] = Frame->m_offsets[0] + pitches[0];
646 Frame->m_offsets[1] = Frame->m_offsets[1] + pitches[1];
647 Frame->m_offsets[2] = Frame->m_offsets[2] + pitches[2];
649 Frame->m_pitches = pitches;
650 Frame->m_offsets = offsets;
651 }
652 else
653 {
655 }
656
657 if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
658 m_openglRender->logDebugMarker(LOC + "UPDATE_FRAME_END");
659}
660
662 StereoscopicMode StereoOverride, bool DrawBorder)
663{
664 if (!m_openglRender)
665 return;
666
668
669 if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
670 m_openglRender->logDebugMarker(LOC + "RENDER_FRAME_START");
671
672 // Set required input textures for the last stage
673 // ProcessFrame is always called first, which will create/destroy software
674 // textures as needed
675 bool hwframes = false;
676 bool useframebufferimage = false;
677
678 // for tiled renderers (e.g. Pi), enable scissoring to try and improve performance
679 // when not full screen and avoid the resize stage unless absolutely necessary
680 bool tiled = m_extraFeatures & kGLTiled;
681
682 // We lose the pause frame when seeking and using VDPAU/VAAPI/NVDEC direct rendering.
683 // As a workaround, we always use the resize stage so the last displayed frame
684 // should be retained in the Framebuffer used for resizing. If there is
685 // nothing to display, then fallback to this framebuffer.
686 // N.B. this is now strictly necessary with v4l2 and DRM PRIME direct rendering
687 // but ignore now for performance reasons
688 VideoResizing resize;
689 if (Frame)
691 else
693
694 std::vector<MythVideoTextureOpenGL*> inputtextures = m_inputTextures;
695 if (inputtextures.empty())
696 {
697 // This is experimental support for direct rendering to a framebuffer (e.g. DRM).
698 // It may be removed or refactored (e.g. pass presentation details through to
699 // the interop).
700 if (Frame)
701 {
702 Frame->m_displayed = false;
703 Frame->m_srcRect = m_videoRect;
704 Frame->m_dstRect = m_displayVideoRect;
705 }
706
707 // Pull in any hardware frames
709
710 if (Frame && Frame->m_displayed)
711 return;
712
713 if (!inputtextures.empty())
714 {
715 hwframes = true;
716 QSize newsize = inputtextures[0]->m_size;
717 VideoFrameType newsourcetype = inputtextures[0]->m_frameType;
718 VideoFrameType newtargettype = inputtextures[0]->m_frameFormat;
719 GLenum newtargettexture = inputtextures[0]->m_target;
720 if ((m_outputType != newtargettype) || (m_textureTarget != newtargettexture) ||
721 (m_inputType != newsourcetype) || (newsize != m_inputTextureSize))
722 {
723 SetupFrameFormat(newsourcetype, newtargettype, newsize, newtargettexture);
724 }
725
726#if CONFIG_MEDIACODEC
727 // Set the texture transform for mediacodec
729 {
730 if (inputtextures[0]->m_transform && m_shaders[Default])
731 {
733 m_shaders[Default]->setUniformValue("u_transform", *inputtextures[0]->m_transform);
734 }
735 }
736#endif
737 // Enable deinterlacing for NVDEC, VTB and VAAPI DRM if VPP is not available
738 if (inputtextures[0]->m_allowGLSLDeint)
739 AddDeinterlacer(Frame, Scan, DEINT_SHADER | DEINT_CPU, false); // pickup shader or cpu prefs
740 }
741 else
742 {
743 if ((resize == Framebuffer) && m_frameBuffer && m_frameBufferTexture)
744 {
745 LOG(VB_PLAYBACK, LOG_DEBUG, "Using existing framebuffer");
746 useframebufferimage = true;
747 }
748 else
749 {
750 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "Nothing to display");
751 // if this is live tv startup and the window rect has changed we
752 // must set the viewport
754 return;
755 }
756 }
757 }
758
759 // Determine which shader to use. This helps optimise the resize check.
760 bool deinterlacing = false;
761 bool basicdeinterlacing = false;
762 bool yuvoutput = MythVideoFrame::YUVFormat(m_outputType);
763 VideoShaderType program = yuvoutput ? Progressive : Default;
765 {
766 if (Scan == kScan_Interlaced)
767 {
768 program = TopFieldFirst ? InterlacedTop : InterlacedBot;
769 deinterlacing = true;
770 }
771 else if (Scan == kScan_Intr2ndField)
772 {
773 program = TopFieldFirst ? InterlacedBot : InterlacedTop;
774 deinterlacing = true;
775 }
776
777 // select the correct field for the basic deinterlacer
779 {
780 basicdeinterlacing = true;
781 if (program == InterlacedBot)
782 inputtextures = m_nextTextures;
783 }
784 }
785
786 // Set deinterlacer type for debug OSD
787 if (deinterlacing && Frame)
788 {
789 Frame->m_deinterlaceInuse = m_deinterlacer | DEINT_SHADER;
790 Frame->m_deinterlaceInuse2x = m_deinterlacer2x;
791 }
792
793 // Tonemapping can only render to a texture
794 if (m_toneMap)
795 resize |= ToneMap;
796
797 // Decide whether to use render to texture - for performance or quality
798 if (yuvoutput && !resize)
799 {
800 // ensure deinterlacing works correctly when down scaling in height
801 // N.B. not needed for the basic deinterlacer
802 if (deinterlacing && !basicdeinterlacing && (m_videoDispDim.height() > m_displayVideoRect.height()))
803 resize |= Deinterlacer;
804
805 // NB GL_NEAREST introduces some 'minor' chroma sampling errors
806 // for the following 2 cases. For YUY2 this may be better handled in the
807 // shader. For GLES3.0 10bit textures - Vulkan is probably the better solution.
808
809 // UYVY packed pixels must be sampled exactly with GL_NEAREST
810 if (FMT_YUY2 == m_outputType)
811 resize |= Sampling;
812 // unsigned integer texture formats need GL_NEAREST sampling
814 resize |= Sampling;
815
816 // don't enable resizing if the cost of a framebuffer switch may be
817 // prohibitive (e.g. Raspberry Pi/tiled renderers) or for basic deinterlacing,
818 // where we are trying to simplify/optimise rendering (and the framebuffer
819 // sizing gets confused by the change to m_videoDispDim)
820 if (!resize && !tiled && !basicdeinterlacing)
821 {
822 // improve performance. This is an educated guess on the relative cost
823 // of render to texture versus straight rendering.
824 int totexture = m_videoDispDim.width() * m_videoDispDim.height() * m_shaderCost[program];
825 int blitcost = m_displayVideoRect.width() * m_displayVideoRect.height() * m_shaderCost[Default];
826 int noresizecost = m_displayVideoRect.width() * m_displayVideoRect.height() * m_shaderCost[program];
827 if ((totexture + blitcost) < noresizecost)
828 resize |= Performance;
829 }
830 }
831
832 // Bicubic upsizing - test this after all other resize options have been checked
833 // to ensure it is not the only flag set
834 if (m_bicubicUpsize)
835 SetupBicubic(resize);
836
837 // We don't need an extra stage prior to bicubic if the frame is already RGB (e.g. VDPAU, MediaCodec)
838 // So bypass if we only set resize for bicubic.
839 bool needresize = resize && (!MythVideoFrame::FormatIsRGB(m_outputType) || (resize != Bicubic));
840
841 // set software frame filtering if resizing has changed
842 if (!needresize && m_resizing)
843 {
844 // remove framebuffer
846 {
848 m_frameBufferTexture = nullptr;
849 }
850 if (m_frameBuffer)
851 {
853 m_frameBuffer = nullptr;
854 }
855 // set filtering
860 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Disabled resizing");
861 }
862 else if (!m_resizing && needresize)
863 {
864 // framebuffer will be created as needed below
865 QOpenGLTexture::Filter filter = ((resize & Sampling) == Sampling) ? QOpenGLTexture::Nearest : QOpenGLTexture::Linear;
869 m_resizing = resize;
870 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Resizing from %1x%2 to %3x%4 for %5")
871 .arg(m_videoDispDim.width()).arg(m_videoDispDim.height())
872 .arg(m_displayVideoRect.width()).arg(m_displayVideoRect.height())
873 .arg(VideoResizeToString(resize)));
874 }
875
876 // check hardware frames have the correct filtering
877 if (hwframes)
878 {
879 QOpenGLTexture::Filter filter = (resize.testFlag(Sampling)) ? QOpenGLTexture::Nearest : QOpenGLTexture::Linear;
880 if (inputtextures[0]->m_filter != filter)
882 }
883
884 // texture coordinates
885 QRect trect(m_videoRect);
886
887 if (needresize)
888 {
889 MythVideoTextureOpenGL* nexttexture = nullptr;
890
891 // only render to the framebuffer if there is something to update
892 if (useframebufferimage)
893 {
894 if (m_toneMap)
895 {
896 nexttexture = m_toneMap->GetTexture();
897 trect = QRect(QPoint(0, 0), m_displayVideoRect.size());
898 }
899 else
900 {
901 nexttexture = m_frameBufferTexture;
902 }
903 }
904 else if (m_toneMap)
905 {
906 if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
907 m_openglRender->logDebugMarker(LOC + "RENDER_TO_TEXTURE");
908 nexttexture = m_toneMap->Map(inputtextures, m_displayVideoRect.size());
909 trect = QRect(QPoint(0, 0), m_displayVideoRect.size());
910 }
911 else
912 {
913 // render to texture stage
914 if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
915 m_openglRender->logDebugMarker(LOC + "RENDER_TO_TEXTURE");
916
917 // we need a framebuffer and associated texture
918 if (!m_frameBuffer)
919 {
921 (fbo != nullptr) && (tex != nullptr))
922 {
923 delete m_frameBuffer;
925 m_frameBuffer = fbo;
927 m_openglRender->SetTextureFilters(m_frameBufferTexture, QOpenGLTexture::Linear);
928 }
929 }
930
932 return;
933
934 // coordinates
935 QRect vrect(QPoint(0, 0), m_videoDispDim);
936 QRect trect2 = vrect;
937 if (FMT_YUY2 == m_outputType)
938 trect2.setWidth(m_videoDispDim.width() >> 1);
939
940 // framebuffer
943
944 // bind correct textures
945 std::vector<MythGLTexture*> textures {};
946 BindTextures(deinterlacing, inputtextures, textures);
947
948 // render
949 m_openglRender->DrawBitmap(textures, m_frameBuffer, trect2, vrect,
950 m_shaders[program], 0);
951 nexttexture = m_frameBufferTexture;
952 }
953
954 // reset for next stage
955 inputtextures.clear();
956 inputtextures.push_back(nexttexture);
957 program = Default;
958 deinterlacing = false;
959 }
960
961 // Use the bicubic shader if necessary
962 if (resize.testFlag(Bicubic))
963 program = BicubicUpsize;
964
965 // render to default framebuffer/screen
966 if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
967 m_openglRender->logDebugMarker(LOC + "RENDER_TO_SCREEN");
968
969 // discard stereoscopic fields
970 StereoscopicMode stereo = StereoOverride;
971 m_lastStereo = Frame ? Frame->m_stereo3D : m_lastStereo;
972 // N.B. kStereoscopicModeSideBySideDiscard is a proxy here for discard of all types
973 if ((stereo == kStereoscopicModeAuto) &&
975 (m_lastStereo != AV_STEREO3D_2D))
976 {
977 if (m_lastStereo == AV_STEREO3D_SIDEBYSIDE)
979 else if (m_lastStereo == AV_STEREO3D_TOPBOTTOM)
981 }
982
984 trect = QRect(trect.left() >> 1, trect.top(), trect.width() >> 1, trect.height());
985 else if (kStereoscopicModeTopAndBottomDiscard == stereo)
986 trect = QRect(trect.left(), trect.top() >> 1, trect.width(), trect.height() >> 1);
987
988 // bind default framebuffer
991
992 // PiP border
993 if (DrawBorder)
994 {
995 QRect piprect = m_displayVideoRect.adjusted(-10, -10, +10, +10);
996 static const QPen kNopen(Qt::NoPen);
997 static const QBrush kRedBrush(QBrush(QColor(127, 0, 0, 255)));
998 m_openglRender->DrawRect(nullptr, piprect, kRedBrush, kNopen, 255);
999 }
1000
1001 // bind correct textures
1002 std::vector<MythGLTexture*> textures;
1003 BindTextures(deinterlacing, inputtextures, textures);
1004
1005 // rotation
1006 if (Frame)
1007 m_lastRotation = Frame->m_rotation;
1008
1009 // apply scissoring
1010 if (tiled)
1011 {
1012 // N.B. It's not obvious whether this helps
1013 m_openglRender->glEnable(GL_SCISSOR_TEST);
1014 m_openglRender->glScissor(m_displayVideoRect.left() - 1, m_displayVideoRect.top() - 1,
1015 m_displayVideoRect.width() + 2, m_displayVideoRect.height() + 2);
1016 }
1017
1018 // draw
1019 m_openglRender->DrawBitmap(textures, nullptr, trect, m_displayVideoRect,
1020 m_shaders[program], m_lastRotation);
1021
1022 // disable scissoring
1023 if (tiled)
1024 m_openglRender->glDisable(GL_SCISSOR_TEST);
1025
1026 if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
1027 m_openglRender->logDebugMarker(LOC + "RENDER_FRAME_END");
1028}
1029
1032{
1033 for (auto & texture : m_inputTextures)
1034 texture->m_valid = false;
1035 for (auto & texture : m_prevTextures)
1036 texture->m_valid = false;
1037 for (auto & texture : m_nextTextures)
1038 texture->m_valid = false;
1039}
1040
1041void MythOpenGLVideo::BindTextures(bool Deinterlacing, std::vector<MythVideoTextureOpenGL*>& Current,
1042 std::vector<MythGLTexture*>& Textures)
1043{
1044 if (Deinterlacing && !MythVideoFrame::HardwareFormat(m_inputType))
1045 {
1046 if ((m_nextTextures.size() == Current.size()) && (m_prevTextures.size() == Current.size()))
1047 {
1048 // if we are using reference frames, we want the current frame in the middle
1049 // but next will be the first valid, followed by current...
1050 size_t count = Current.size();
1051 std::vector<MythVideoTextureOpenGL*>& current = Current[0]->m_valid ? Current : m_nextTextures;
1052 std::vector<MythVideoTextureOpenGL*>& prev = m_prevTextures[0]->m_valid ? m_prevTextures : current;
1053
1054 for (uint i = 0; i < count; ++i)
1055 Textures.push_back(reinterpret_cast<MythGLTexture*>(prev[i]));
1056 for (uint i = 0; i < count; ++i)
1057 Textures.push_back(reinterpret_cast<MythGLTexture*>(current[i]));
1058 for (uint i = 0; i < count; ++i)
1059 Textures.push_back(reinterpret_cast<MythGLTexture*>(m_nextTextures[i]));
1060 return;
1061 }
1062 }
1063
1064 std::transform(Current.cbegin(), Current.cend(), std::back_inserter(Textures),
1065 [](MythVideoTextureOpenGL* Tex) { return reinterpret_cast<MythGLTexture*>(Tex); });
1066}
1067
1069{
1071 return "opengl-hw";
1072
1073 switch (Type)
1074 {
1075 case FMT_YUY2: return "opengl"; // compatibility with old profiles
1076 case FMT_YV12: return "opengl-yv12";
1077 case FMT_NV12: return "opengl-nv12";
1078 default: break;
1079 }
1080 return "opengl";
1081}
1082
1083void MythOpenGLVideo::SetupBicubic(VideoResizing& Resize)
1084{
1085 if (((m_videoDispDim.width() < m_displayVideoRect.width()) ||
1086 (m_videoDispDim.height() < m_displayVideoRect.height())))
1087 {
1089 {
1091 {
1092 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create bicubic shader. Disabling");
1093 m_bicubicUpsize = false;
1094 }
1095 else
1096 {
1098 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Created bicubic sampler");
1099 }
1100 }
1101
1103 Resize |= Bicubic;
1104 }
1105 else
1106 {
1107 if (m_shaders[BicubicUpsize] != nullptr)
1108 {
1109 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Disabling bicubic sampler");
1110 delete m_shaders[BicubicUpsize];
1111 m_shaders[BicubicUpsize] = nullptr;
1112 }
1113 }
1114}
bool GetBoolSetting(const QString &key, bool defaultval=false)
static std::vector< MythVideoTextureOpenGL * > Retrieve(MythRenderOpenGL *Context, MythVideoColourSpace *ColourSpace, MythVideoFrame *Frame, FrameScanType Scan)
MythVideoTextureOpenGL * GetTexture()
MythVideoTextureOpenGL * Map(std::vector< MythVideoTextureOpenGL * > &Inputs, QSize DisplaySize)
std::vector< MythVideoTextureOpenGL * > m_prevTextures
MythVideoTextureOpenGL * m_frameBufferTexture
bool SetupFrameFormat(VideoFrameType InputType, VideoFrameType OutputType, QSize Size, GLenum TextureTarget)
QOpenGLFramebufferObject * m_frameBuffer
MythOpenGLVideo(MythRenderOpenGL *Render, MythVideoColourSpace *ColourSpace, MythVideoBounds *Bounds, const MythVideoProfilePtr &VideoProfile, const QString &Profile)
~MythOpenGLVideo() override
void BindTextures(bool Deinterlacing, std::vector< MythVideoTextureOpenGL * > &Current, std::vector< MythGLTexture * > &Textures)
bool m_chromaUpsamplingFilter
std::array< int, ShaderCount > m_shaderCost
std::array< QOpenGLShaderProgram *, ShaderCount > m_shaders
static QString TypeToProfile(VideoFrameType Type)
QOpenGLFunctions::OpenGLFeatures m_features
MythOpenGLTonemap * m_toneMap
std::vector< MythVideoTextureOpenGL * > m_inputTextures
void ResetTextures() override
Clear reference frames after a seek as they will contain old images.
std::vector< MythVideoTextureOpenGL * > m_nextTextures
void PrepareFrame(MythVideoFrame *Frame, FrameScanType Scan=kScan_Progressive) override
Update the current input texture using the data from the given video frame.
void ColourSpaceUpdate(bool PrimariesChanged) override
MythDeintType m_fallbackDeinterlacer
void SetupBicubic(VideoResizing &Resize)
void RenderFrame(MythVideoFrame *Frame, bool TopFieldFirst, FrameScanType Scan, StereoscopicMode StereoOverride, bool DrawBorder=false) override
void UpdateShaderParameters()
bool AddDeinterlacer(const MythVideoFrame *Frame, FrameScanType Scan, MythDeintType Filter=DEINT_SHADER, bool CreateReferences=true)
MythRenderOpenGL * m_openglRender
void ResetFrameFormat() override
bool CreateVideoShader(VideoShaderType Type, MythDeintType Deint=DEINT_NONE)
Create the appropriate shader for the operation Type.
QString GetProfile() const override
void SetShaderProgramParams(QOpenGLShaderProgram *Program, const QMatrix4x4 &Value, const char *Uniform)
int GetMaxTextureUnits(void) const
void DrawBitmap(MythGLTexture *Texture, QOpenGLFramebufferObject *Target, QRect Source, QRect Destination, QOpenGLShaderProgram *Program, int Alpha=255, qreal Scale=1.0)
void SetViewPort(QRect Rect, bool ViewportOnly=false) override
void DeleteShaderProgram(QOpenGLShaderProgram *Program)
void BindFramebuffer(QOpenGLFramebufferObject *Framebuffer)
void DeleteFramebuffer(QOpenGLFramebufferObject *Framebuffer)
QOpenGLFunctions::OpenGLFeatures GetFeatures(void) const
bool EnableShaderProgram(QOpenGLShaderProgram *Program)
void logDebugMarker(const QString &Message)
QOpenGLShaderProgram * CreateShaderProgram(const QString &Vertex, const QString &Fragment)
void DeleteTexture(MythGLTexture *Texture)
void DrawRect(QOpenGLFramebufferObject *Target, QRect Area, const QBrush &FillBrush, const QPen &LinePen, int Alpha)
int GetExtraFeatures(void) const
void SetTextureFilters(MythGLTexture *Texture, QOpenGLTexture::Filter Filter, QOpenGLTexture::WrapMode Wrap=QOpenGLTexture::ClampToEdge)
MythVideoColourSpace contains a QMatrix4x4 that can convert YCbCr data to RGB.
float GetDisplayGamma(void) const
QStringList GetColourMappingDefines(void)
QMatrix4x4 GetPrimaryMatrix(void)
bool UpdateColourSpace(const MythVideoFrame *Frame)
Set the current colourspace to use.
float GetColourGamma(void) const
static bool FormatIs422(VideoFrameType Type)
Definition: mythframe.h:443
static uint GetNumPlanes(VideoFrameType Type)
Definition: mythframe.h:213
static QString DeinterlacerName(MythDeintType Deint, bool DoubleRate, VideoFrameType Format=FMT_NONE)
Definition: mythframe.cpp:462
static bool FormatIsNV12(VideoFrameType Type)
Definition: mythframe.h:455
static QString FormatDescription(VideoFrameType Type)
Definition: mythframe.cpp:368
static bool FormatIs444(VideoFrameType Type)
Definition: mythframe.h:449
static bool FormatIs420(VideoFrameType Type)
Definition: mythframe.h:437
static bool FormatIsRGB(VideoFrameType Type)
Definition: mythframe.h:471
static bool HardwareFramesFormat(VideoFrameType Type)
Definition: mythframe.h:432
static bool YUVFormat(VideoFrameType Type)
Definition: mythframe.h:465
static int ColorDepth(int Format)
Definition: mythframe.h:398
static bool HardwareFormat(VideoFrameType Type)
Definition: mythframe.h:424
VideoResizing m_resizing
Definition: mythvideogpu.h:89
int m_lastRotation
Definition: mythvideogpu.h:90
uint m_lastStereo
Definition: mythvideogpu.h:95
VideoFrameType m_inputType
Definition: mythvideogpu.h:80
QSize m_inputTextureSize
Definition: mythvideogpu.h:88
QRect m_videoRect
Definition: mythvideogpu.h:86
StereoscopicMode m_stereoMode
Definition: mythvideogpu.h:96
uint64_t m_discontinuityCounter
Definition: mythvideogpu.h:78
QString m_profile
Definition: mythvideogpu.h:79
bool m_bicubicUpsize
Definition: mythvideogpu.h:97
QSize m_videoDim
Definition: mythvideogpu.h:83
QRect m_displayVideoRect
Definition: mythvideogpu.h:85
MythVideoColourSpace * m_videoColourSpace
Definition: mythvideogpu.h:87
QSize m_masterViewportSize
Definition: mythvideogpu.h:84
VideoFrameType m_outputType
Definition: mythvideogpu.h:81
void OutputChanged(QSize VideoDim, QSize VideoDispDim, float)
static QString VideoResizeToString(VideoResizing Resize)
void UpdateColourSpace(bool PrimariesChanged)
virtual void ResetFrameFormat()
QSize m_videoDispDim
Definition: mythvideogpu.h:82
bool m_deinterlacer2x
Definition: mythvideogpu.h:92
MythDeintType m_deinterlacer
Definition: mythvideogpu.h:91
static void SetTextureFilters(MythRenderOpenGL *Context, const std::vector< MythVideoTextureOpenGL * > &Textures, QOpenGLTexture::Filter Filter, QOpenGLTexture::WrapMode Wrap=QOpenGLTexture::ClampToEdge)
static std::vector< MythVideoTextureOpenGL * > CreateTextures(MythRenderOpenGL *Context, VideoFrameType Type, VideoFrameType Format, std::vector< QSize > Sizes, GLenum Target=QOpenGLTexture::Target2D)
Create a set of textures suitable for the given Type and Format.
static VideoFramebuffer CreateVideoFrameBuffer(MythRenderOpenGL *Context, VideoFrameType OutputType, QSize Size, bool HighPrecision=true)
static void DeleteTextures(MythRenderOpenGL *Context, std::vector< MythVideoTextureOpenGL * > &Textures)
static void UpdateTextures(MythRenderOpenGL *Context, const MythVideoFrame *Frame, const std::vector< MythVideoTextureOpenGL * > &Textures)
Update the contents of the given Textures for data held in Frame.
unsigned int uint
Definition: compat.h:60
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythDeintType
Definition: mythframe.h:67
@ DEINT_HIGH
Definition: mythframe.h:71
@ DEINT_DRIVER
Definition: mythframe.h:74
@ DEINT_MEDIUM
Definition: mythframe.h:70
@ DEINT_BASIC
Definition: mythframe.h:69
@ DEINT_NONE
Definition: mythframe.h:68
@ DEINT_SHADER
Definition: mythframe.h:73
@ DEINT_CPU
Definition: mythframe.h:72
std::array< int, 3 > FrameOffsets
Definition: mythframe.h:84
VideoFrameType
Definition: mythframe.h:20
@ FMT_RGBA32
Definition: mythframe.h:34
@ FMT_YV12
Definition: mythframe.h:23
@ FMT_DRMPRIME
Definition: mythframe.h:63
@ FMT_NONE
Definition: mythframe.h:21
@ FMT_NV12
Definition: mythframe.h:52
@ FMT_YUY2
Definition: mythframe.h:50
@ FMT_MEDIACODEC
Definition: mythframe.h:60
std::array< int, 3 > FramePitches
Definition: mythframe.h:83
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
Definition: mythlogging.h:29
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
#define LOC
static const QString YUVFragmentShader
static const QString DefaultVertexShader
static const QString GLSL300VertexShader
static const QString BicubicShader
static const QString YUVFragmentExtensions
static const QString GLSL300YUVFragmentShader
static const QString GLSL300YUVFragmentExtensions
static const QString RGBFragmentShader
@ kGLTiled
#define GL_TEXTURE_EXTERNAL_OES
std::shared_ptr< MythVideoProfile > MythVideoProfilePtr
Definition: mythvideogpu.h:18
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
FrameScanType
Definition: videoouttypes.h:95
@ kScan_Intr2ndField
Definition: videoouttypes.h:99
@ kScan_Interlaced
Definition: videoouttypes.h:98
bool is_interlaced(FrameScanType Scan)
StereoscopicMode
@ kStereoscopicModeAuto
@ kStereoscopicModeTopAndBottomDiscard
@ kStereoscopicModeSideBySideDiscard