1 | // MythTV headers |
---|
2 | #include "openglvideo.h" |
---|
3 | #include "mythcontext.h" |
---|
4 | #include "tv.h" |
---|
5 | #include "mythrender_opengl.h" |
---|
6 | #include "mythavutil.h" |
---|
7 | |
---|
8 | #define LOC QString("GLVid: ") |
---|
9 | #define COLOUR_UNIFORM "m_colourMatrix" |
---|
10 | #define MYTHTV_YV12 0x8a20 |
---|
11 | |
---|
12 | enum DisplayBuffer |
---|
13 | { |
---|
14 | kDefaultBuffer, |
---|
15 | kFrameBufferObject |
---|
16 | }; |
---|
17 | |
---|
18 | class OpenGLFilter |
---|
19 | { |
---|
20 | public: |
---|
21 | vector<GLuint> fragmentPrograms; |
---|
22 | uint numInputs; |
---|
23 | vector<GLuint> frameBuffers; |
---|
24 | vector<GLuint> frameBufferTextures; |
---|
25 | DisplayBuffer outputBuffer; |
---|
26 | }; |
---|
27 | |
---|
28 | /** |
---|
29 | * \class OpenGLVideo |
---|
30 | * A class used to display video frames and associated imagery |
---|
31 | * using the OpenGL API. |
---|
32 | * |
---|
33 | * The basic operational concept is to use a series of filter stages to |
---|
34 | * generate the desired video output, using limited software assistance |
---|
35 | * alongside OpenGL fragment programs (deinterlacing and YUV->RGB conversion) |
---|
36 | * , FrameBuffer Objects (flexible GPU storage) and PixelBuffer Objects |
---|
37 | * (faster CPU->GPU memory transfers). |
---|
38 | * |
---|
39 | * In the most basic case, for example, a YV12 frame pre-converted in software |
---|
40 | * to BGRA format is simply blitted to the frame buffer. |
---|
41 | * Currently, the most complicated example is the rendering of a standard |
---|
42 | * definition, interlaced frame to a high(er) definition display using |
---|
43 | * OpenGL (i.e. hardware based) deinterlacing, colourspace conversion and |
---|
44 | * bicubic upsampling. |
---|
45 | * |
---|
46 | * Higher level tasks such as coordination between OpenGLVideo instances, |
---|
47 | * video buffer management, audio/video synchronisation etc are handled by |
---|
48 | * the higher level classes VideoOutput and NuppelVideoPlayer. The bulk of |
---|
49 | * the lower level interface with the window system and OpenGL is handled by |
---|
50 | * MythRenderOpenGL. |
---|
51 | * |
---|
52 | * N.B. Direct use of OpenGL calls is minimised to maintain platform |
---|
53 | * independance. The only member function where this is impractical is |
---|
54 | * PrepareFrame(). |
---|
55 | * |
---|
56 | * \warning Any direct OpenGL calls must be wrapped by calls to |
---|
57 | * gl_context->MakeCurrent(). Alternatively use the convenience class |
---|
58 | * OpenGLLocker. |
---|
59 | */ |
---|
60 | |
---|
61 | /** |
---|
62 | * Create a new OpenGLVideo instance that must be initialised |
---|
63 | * with a call to OpenGLVideo::Init() |
---|
64 | */ |
---|
65 | |
---|
66 | OpenGLVideo::OpenGLVideo() : |
---|
67 | gl_context(nullptr), video_disp_dim(0,0), |
---|
68 | video_dim(0,0), viewportSize(0,0), |
---|
69 | masterViewportSize(0,0), display_visible_rect(0,0,0,0), |
---|
70 | display_video_rect(0,0,0,0), video_rect(0,0,0,0), |
---|
71 | frameBufferRect(0,0,0,0), hardwareDeinterlacing(false), |
---|
72 | colourSpace(nullptr), viewportControl(false), |
---|
73 | inputTextureSize(0,0), currentFrameNum(0), |
---|
74 | inputUpdated(false), refsNeeded(0), |
---|
75 | textureRects(false), textureType(GL_TEXTURE_2D), |
---|
76 | helperTexture(0), defaultUpsize(kGLFilterResize), |
---|
77 | gl_features(0), videoTextureType(GL_BGRA), |
---|
78 | preferYCBCR(false) |
---|
79 | { |
---|
80 | } |
---|
81 | |
---|
82 | OpenGLVideo::~OpenGLVideo() |
---|
83 | { |
---|
84 | OpenGLLocker ctx_lock(gl_context); |
---|
85 | Teardown(); |
---|
86 | } |
---|
87 | |
---|
88 | void OpenGLVideo::Teardown(void) |
---|
89 | { |
---|
90 | if (helperTexture) |
---|
91 | gl_context->DeleteTexture(helperTexture); |
---|
92 | helperTexture = 0; |
---|
93 | |
---|
94 | DeleteTextures(&inputTextures); |
---|
95 | DeleteTextures(&referenceTextures); |
---|
96 | |
---|
97 | while (!filters.empty()) |
---|
98 | { |
---|
99 | RemoveFilter(filters.begin()->first); |
---|
100 | filters.erase(filters.begin()); |
---|
101 | } |
---|
102 | } |
---|
103 | |
---|
104 | /** |
---|
105 | * \param glcontext the MythRenderOpenGL object responsible for lower |
---|
106 | * levelwindow and OpenGL context integration |
---|
107 | * \param colourspace the colourspace management object |
---|
108 | * \param videoDim the size of the video source |
---|
109 | * \param videoDispDim the size of the display |
---|
110 | * \param displayVisibleRect the bounding rectangle of the OpenGL window |
---|
111 | * \param displayVideoRect the bounding rectangle for the area to display |
---|
112 | * the video frame |
---|
113 | * \param videoRect the portion of the video frame to display in |
---|
114 | displayVideoRect |
---|
115 | * \param viewport_control if true, this instance may permanently change |
---|
116 | the OpenGL viewport |
---|
117 | * \param options a string defining OpenGL features to disable |
---|
118 | * \param hw_accel if true, a GPU decoder will copy frames directly |
---|
119 | to an RGBA texture |
---|
120 | */ |
---|
121 | |
---|
122 | bool OpenGLVideo::Init(MythRenderOpenGL *glcontext, VideoColourSpace *colourspace, |
---|
123 | QSize videoDim, QSize videoDispDim, |
---|
124 | QRect displayVisibleRect, |
---|
125 | QRect displayVideoRect, QRect videoRect, |
---|
126 | bool viewport_control, QString options, |
---|
127 | bool hw_accel) |
---|
128 | { |
---|
129 | if (!glcontext) |
---|
130 | return false; |
---|
131 | |
---|
132 | gl_context = glcontext; |
---|
133 | OpenGLLocker ctx_lock(gl_context); |
---|
134 | |
---|
135 | video_dim = videoDim; |
---|
136 | video_disp_dim = videoDispDim; |
---|
137 | display_visible_rect = displayVisibleRect; |
---|
138 | display_video_rect = displayVideoRect; |
---|
139 | video_rect = videoRect; |
---|
140 | masterViewportSize = display_visible_rect.size(); |
---|
141 | frameBufferRect = QRect(QPoint(0,0), video_disp_dim); |
---|
142 | softwareDeinterlacer = ""; |
---|
143 | hardwareDeinterlacing = false; |
---|
144 | colourSpace = colourspace; |
---|
145 | viewportControl = viewport_control; |
---|
146 | inputTextureSize = QSize(0,0); |
---|
147 | currentFrameNum = -1; |
---|
148 | inputUpdated = false; |
---|
149 | |
---|
150 | // OpenGL-Lite - use implementation specific extensions for updating frames |
---|
151 | if (options.contains("preferycbcr")) |
---|
152 | preferYCBCR = true; |
---|
153 | |
---|
154 | // Set OpenGL feature support |
---|
155 | gl_features = gl_context->GetFeatures(); |
---|
156 | |
---|
157 | if (viewportControl) |
---|
158 | gl_context->SetFence(); |
---|
159 | |
---|
160 | SetViewPort(display_visible_rect.size()); |
---|
161 | |
---|
162 | bool glsl = gl_features & kGLSL; |
---|
163 | bool shaders = glsl || (gl_features & kGLExtFragProg); |
---|
164 | bool fbos = gl_features & kGLExtFBufObj; |
---|
165 | bool pbos = gl_features & kGLExtPBufObj; |
---|
166 | |
---|
167 | #ifdef ANDROID |
---|
168 | #define YV12DEFAULT false |
---|
169 | #else |
---|
170 | #define YV12DEFAULT true |
---|
171 | #endif |
---|
172 | |
---|
173 | bool yv12 = gCoreContext->GetBoolSetting("OpenGLYV12", YV12DEFAULT) |
---|
174 | && !getenv("OPENGL_NOYV12"); |
---|
175 | bool uyvy = gCoreContext->GetBoolSetting("OpenGLUYVY", true) |
---|
176 | && !getenv("OPENGL_NOUYVY"); |
---|
177 | bool ycbcr = (gl_features & kGLMesaYCbCr) || (gl_features & kGLAppleYCbCr); |
---|
178 | |
---|
179 | // warn about the lite profile when it offers no benefit |
---|
180 | if (!ycbcr && preferYCBCR) |
---|
181 | { |
---|
182 | LOG(VB_GENERAL, LOG_WARNING, LOC + |
---|
183 | "You have selected the opengl-lite profile but no required OpenGL " |
---|
184 | "extensions are available."); |
---|
185 | } |
---|
186 | |
---|
187 | // decide on best video input texture format |
---|
188 | videoTextureType = GL_BGRA; |
---|
189 | if (hw_accel) |
---|
190 | videoTextureType = GL_RGBA; |
---|
191 | else if ((!shaders || preferYCBCR) && (gl_features & kGLMesaYCbCr)) |
---|
192 | videoTextureType = GL_YCBCR_MESA; |
---|
193 | else if ((!shaders || preferYCBCR) && (gl_features & kGLAppleYCbCr)) |
---|
194 | videoTextureType = GL_YCBCR_422_APPLE; |
---|
195 | else if (glsl && fbos && !(pbos && uyvy) && yv12) |
---|
196 | videoTextureType = MYTHTV_YV12; |
---|
197 | else if (shaders && fbos && uyvy) |
---|
198 | videoTextureType = MYTHTV_UYVY; |
---|
199 | |
---|
200 | // colourspace adjustments require shaders to operate on YUV textures |
---|
201 | if ((GL_BGRA != videoTextureType) && |
---|
202 | (MYTHTV_UYVY != videoTextureType) && |
---|
203 | (MYTHTV_YV12 != videoTextureType)) |
---|
204 | { |
---|
205 | colourSpace->SetSupportedAttributes(kPictureAttributeSupported_None); |
---|
206 | } |
---|
207 | |
---|
208 | // turn on bicubic filtering |
---|
209 | if (options.contains("openglbicubic")) |
---|
210 | { |
---|
211 | if (shaders && fbos) |
---|
212 | defaultUpsize = kGLFilterBicubic; |
---|
213 | else |
---|
214 | LOG(VB_PLAYBACK, LOG_ERR, LOC + |
---|
215 | "No OpenGL feature support for Bicubic filter."); |
---|
216 | } |
---|
217 | |
---|
218 | // decide on best input texture type |
---|
219 | if ((GL_RGBA != videoTextureType) && |
---|
220 | (MYTHTV_YV12 != videoTextureType) && |
---|
221 | (defaultUpsize != kGLFilterBicubic) && |
---|
222 | (gl_features & kGLExtRect)) |
---|
223 | { |
---|
224 | textureType = gl_context->GetTextureType(textureRects); |
---|
225 | } |
---|
226 | |
---|
227 | // Create initial input texture and associated filter stage |
---|
228 | GLuint tex = CreateVideoTexture(video_dim, inputTextureSize); |
---|
229 | bool ok = false; |
---|
230 | |
---|
231 | if ((GL_BGRA == videoTextureType) || (MYTHTV_UYVY == videoTextureType)) |
---|
232 | ok = tex && AddFilter(kGLFilterYUV2RGB); |
---|
233 | else if (MYTHTV_YV12 == videoTextureType) |
---|
234 | ok = tex && AddFilter(kGLFilterYV12RGB); |
---|
235 | else |
---|
236 | ok = tex && AddFilter(kGLFilterResize); |
---|
237 | |
---|
238 | if (ok) |
---|
239 | { |
---|
240 | if (GL_RGBA == videoTextureType) |
---|
241 | LOG(VB_GENERAL, LOG_INFO, LOC + "Using raw RGBA input textures."); |
---|
242 | else if ((GL_YCBCR_MESA == videoTextureType) || |
---|
243 | (GL_YCBCR_422_APPLE == videoTextureType)) |
---|
244 | LOG(VB_GENERAL, LOG_INFO, LOC + |
---|
245 | "Using YCbCr->BGRA input textures."); |
---|
246 | else if (MYTHTV_YV12 == videoTextureType) |
---|
247 | LOG(VB_GENERAL, LOG_INFO, LOC + |
---|
248 | "Using YV12 input textures."); |
---|
249 | else if (MYTHTV_UYVY == videoTextureType) |
---|
250 | LOG(VB_GENERAL, LOG_INFO, LOC + |
---|
251 | "Using custom UYVY input textures."); |
---|
252 | else |
---|
253 | LOG(VB_GENERAL, LOG_INFO, LOC + |
---|
254 | "Using plain BGRA input textures."); |
---|
255 | inputTextures.push_back(tex); |
---|
256 | } |
---|
257 | else |
---|
258 | Teardown(); |
---|
259 | |
---|
260 | if (filters.empty()) |
---|
261 | { |
---|
262 | LOG(VB_PLAYBACK, LOG_INFO, LOC + |
---|
263 | "Failed to setup colourspace conversion.\n\t\t\t" |
---|
264 | "Falling back to software conversion.\n\t\t\t" |
---|
265 | "Any opengl filters will also be disabled."); |
---|
266 | |
---|
267 | videoTextureType = GL_BGRA; |
---|
268 | GLuint bgra32tex = CreateVideoTexture(video_dim, inputTextureSize); |
---|
269 | |
---|
270 | if (bgra32tex && AddFilter(kGLFilterResize)) |
---|
271 | { |
---|
272 | inputTextures.push_back(bgra32tex); |
---|
273 | colourSpace->SetSupportedAttributes(kPictureAttributeSupported_None); |
---|
274 | } |
---|
275 | else |
---|
276 | { |
---|
277 | LOG(VB_GENERAL, LOG_ERR, LOC + "Fatal error"); |
---|
278 | Teardown(); |
---|
279 | return false; |
---|
280 | } |
---|
281 | } |
---|
282 | |
---|
283 | bool mmx = false; |
---|
284 | #ifdef MMX |
---|
285 | // cppcheck-suppress redundantAssignment |
---|
286 | mmx = true; |
---|
287 | #endif |
---|
288 | |
---|
289 | CheckResize(false); |
---|
290 | |
---|
291 | LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("MMX: %1 PBO: %2") |
---|
292 | .arg(mmx).arg((gl_features & kGLExtPBufObj) > 0)); |
---|
293 | |
---|
294 | return true; |
---|
295 | } |
---|
296 | |
---|
297 | /** |
---|
298 | * Determine if the output is to be scaled at all and create or destroy |
---|
299 | * the appropriate filter as necessary. |
---|
300 | */ |
---|
301 | |
---|
302 | void OpenGLVideo::CheckResize(bool deinterlacing, bool allow) |
---|
303 | { |
---|
304 | // to improve performance on slower cards |
---|
305 | bool resize_up = ((video_disp_dim.height() < display_video_rect.height()) || |
---|
306 | (video_disp_dim.width() < display_video_rect.width())) && allow; |
---|
307 | |
---|
308 | // to ensure deinterlacing works correctly |
---|
309 | bool resize_down = (video_disp_dim.height() > display_video_rect.height()) && |
---|
310 | deinterlacing && allow; |
---|
311 | |
---|
312 | // UYVY packed pixels must be sampled exactly and any overscan settings will |
---|
313 | // break sampling - so always force an extra stage |
---|
314 | resize_down |= videoTextureType == MYTHTV_UYVY; |
---|
315 | // Extra stage needed on Fire Stick 4k, maybe others, because of blank screen when playing. |
---|
316 | resize_down |= gCoreContext->GetBoolSetting("OpenGLExtraStage", false); |
---|
317 | |
---|
318 | if (resize_up && (defaultUpsize == kGLFilterBicubic)) |
---|
319 | { |
---|
320 | RemoveFilter(kGLFilterResize); |
---|
321 | filters.erase(kGLFilterResize); |
---|
322 | AddFilter(kGLFilterBicubic); |
---|
323 | OptimiseFilters(); |
---|
324 | return; |
---|
325 | } |
---|
326 | |
---|
327 | if ((resize_up && (defaultUpsize == kGLFilterResize)) || resize_down) |
---|
328 | { |
---|
329 | RemoveFilter(kGLFilterBicubic); |
---|
330 | filters.erase(kGLFilterBicubic); |
---|
331 | AddFilter(kGLFilterResize); |
---|
332 | OptimiseFilters(); |
---|
333 | return; |
---|
334 | } |
---|
335 | |
---|
336 | RemoveFilter(kGLFilterBicubic); |
---|
337 | filters.erase(kGLFilterBicubic); |
---|
338 | OptimiseFilters(); |
---|
339 | } |
---|
340 | |
---|
341 | /** |
---|
342 | * Ensure the current chain of OpenGLFilters is logically correct |
---|
343 | * and has the resources required to complete rendering. |
---|
344 | */ |
---|
345 | |
---|
346 | bool OpenGLVideo::OptimiseFilters(void) |
---|
347 | { |
---|
348 | glfilt_map_t::reverse_iterator it; |
---|
349 | |
---|
350 | // add/remove required frame buffer objects |
---|
351 | // and link filters |
---|
352 | uint buffers_needed = 1; |
---|
353 | bool last_filter = true; |
---|
354 | for (it = filters.rbegin(); it != filters.rend(); ++it) |
---|
355 | { |
---|
356 | if (!last_filter) |
---|
357 | { |
---|
358 | it->second->outputBuffer = kFrameBufferObject; |
---|
359 | uint buffers_have = it->second->frameBuffers.size(); |
---|
360 | int buffers_diff = buffers_needed - buffers_have; |
---|
361 | if (buffers_diff > 0) |
---|
362 | { |
---|
363 | uint tmp_buf, tmp_tex; |
---|
364 | for (int i = 0; i < buffers_diff; i++) |
---|
365 | { |
---|
366 | if (!AddFrameBuffer(tmp_buf, tmp_tex, video_disp_dim)) |
---|
367 | return false; |
---|
368 | else |
---|
369 | { |
---|
370 | it->second->frameBuffers.push_back(tmp_buf); |
---|
371 | it->second->frameBufferTextures.push_back(tmp_tex); |
---|
372 | } |
---|
373 | } |
---|
374 | } |
---|
375 | else if (buffers_diff < 0) |
---|
376 | { |
---|
377 | for (int i = 0; i > buffers_diff; i--) |
---|
378 | { |
---|
379 | OpenGLFilter *filt = it->second; |
---|
380 | |
---|
381 | gl_context->DeleteFrameBuffer( |
---|
382 | filt->frameBuffers.back()); |
---|
383 | gl_context->DeleteTexture( |
---|
384 | filt->frameBufferTextures.back()); |
---|
385 | |
---|
386 | filt->frameBuffers.pop_back(); |
---|
387 | filt->frameBufferTextures.pop_back(); |
---|
388 | } |
---|
389 | } |
---|
390 | } |
---|
391 | else |
---|
392 | { |
---|
393 | it->second->outputBuffer = kDefaultBuffer; |
---|
394 | last_filter = false; |
---|
395 | } |
---|
396 | buffers_needed = it->second->numInputs; |
---|
397 | } |
---|
398 | |
---|
399 | SetFiltering(); |
---|
400 | |
---|
401 | return true; |
---|
402 | } |
---|
403 | |
---|
404 | /** |
---|
405 | * Set the OpenGL texture mapping functions to optimise speed and quality. |
---|
406 | */ |
---|
407 | |
---|
408 | void OpenGLVideo::SetFiltering(void) |
---|
409 | { |
---|
410 | if (filters.size() < 2) |
---|
411 | { |
---|
412 | SetTextureFilters(&inputTextures, GL_LINEAR, GL_CLAMP_TO_EDGE); |
---|
413 | SetTextureFilters(&referenceTextures, GL_LINEAR, GL_CLAMP_TO_EDGE); |
---|
414 | return; |
---|
415 | } |
---|
416 | |
---|
417 | SetTextureFilters(&inputTextures, GL_NEAREST, GL_CLAMP_TO_EDGE); |
---|
418 | SetTextureFilters(&referenceTextures, GL_NEAREST, GL_CLAMP_TO_EDGE); |
---|
419 | |
---|
420 | glfilt_map_t::reverse_iterator rit; |
---|
421 | int last_filter = 0; |
---|
422 | |
---|
423 | for (rit = filters.rbegin(); rit != filters.rend(); ++rit) |
---|
424 | { |
---|
425 | if (last_filter == 1) |
---|
426 | { |
---|
427 | SetTextureFilters(&(rit->second->frameBufferTextures), |
---|
428 | GL_LINEAR, GL_CLAMP_TO_EDGE); |
---|
429 | } |
---|
430 | else if (last_filter > 1) |
---|
431 | { |
---|
432 | SetTextureFilters(&(rit->second->frameBufferTextures), |
---|
433 | GL_NEAREST, GL_CLAMP_TO_EDGE); |
---|
434 | } |
---|
435 | ++last_filter; |
---|
436 | } |
---|
437 | } |
---|
438 | |
---|
439 | /** |
---|
440 | * Add a new filter stage and create any additional resources needed. |
---|
441 | */ |
---|
442 | |
---|
443 | bool OpenGLVideo::AddFilter(OpenGLFilterType filter) |
---|
444 | { |
---|
445 | if (filters.count(filter)) |
---|
446 | return true; |
---|
447 | |
---|
448 | switch (filter) |
---|
449 | { |
---|
450 | case kGLFilterNone: |
---|
451 | // Nothing to do. Prevents compiler warning. |
---|
452 | break; |
---|
453 | |
---|
454 | case kGLFilterResize: |
---|
455 | if (!(gl_features & kGLExtFBufObj) && !filters.empty()) |
---|
456 | { |
---|
457 | LOG(VB_PLAYBACK, LOG_ERR, LOC + |
---|
458 | "GL_EXT_framebuffer_object not available " |
---|
459 | "for scaling/resizing filter."); |
---|
460 | return false; |
---|
461 | } |
---|
462 | break; |
---|
463 | |
---|
464 | case kGLFilterBicubic: |
---|
465 | if (!(gl_features & kGLExtFragProg) || !(gl_features & kGLExtFBufObj)) |
---|
466 | { |
---|
467 | LOG(VB_PLAYBACK, LOG_ERR, LOC + |
---|
468 | "Features not available for bicubic filter."); |
---|
469 | return false; |
---|
470 | } |
---|
471 | break; |
---|
472 | |
---|
473 | case kGLFilterYUV2RGB: |
---|
474 | if (!(gl_features & kGLExtFragProg) && !(gl_features & kGLSL)) |
---|
475 | { |
---|
476 | LOG(VB_PLAYBACK, LOG_ERR, LOC + |
---|
477 | "No shader support for OpenGL deinterlacing."); |
---|
478 | return false; |
---|
479 | } |
---|
480 | break; |
---|
481 | |
---|
482 | case kGLFilterYV12RGB: |
---|
483 | if (!(gl_features & kGLSL)) |
---|
484 | { |
---|
485 | LOG(VB_PLAYBACK, LOG_ERR, LOC + |
---|
486 | "No shader support for OpenGL deinterlacing."); |
---|
487 | return false; |
---|
488 | } |
---|
489 | break; |
---|
490 | } |
---|
491 | |
---|
492 | bool success = true; |
---|
493 | |
---|
494 | LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Creating %1 filter.") |
---|
495 | .arg(FilterToString(filter))); |
---|
496 | |
---|
497 | OpenGLFilter *temp = new OpenGLFilter(); |
---|
498 | |
---|
499 | temp->numInputs = 1; |
---|
500 | GLuint program = 0; |
---|
501 | |
---|
502 | if (filter == kGLFilterBicubic) |
---|
503 | { |
---|
504 | if (helperTexture) |
---|
505 | gl_context->DeleteTexture(helperTexture); |
---|
506 | |
---|
507 | helperTexture = gl_context->CreateHelperTexture(); |
---|
508 | if (!helperTexture) |
---|
509 | success = false; |
---|
510 | } |
---|
511 | |
---|
512 | if (success && |
---|
513 | (((filter != kGLFilterNone) && (filter != kGLFilterResize)) || |
---|
514 | ((gl_features & kGLSL) && (filter == kGLFilterResize)))) |
---|
515 | { |
---|
516 | program = AddFragmentProgram(filter); |
---|
517 | if (!program) |
---|
518 | success = false; |
---|
519 | else |
---|
520 | temp->fragmentPrograms.push_back(program); |
---|
521 | } |
---|
522 | |
---|
523 | if (success) |
---|
524 | { |
---|
525 | temp->outputBuffer = kDefaultBuffer; |
---|
526 | temp->frameBuffers.clear(); |
---|
527 | temp->frameBufferTextures.clear(); |
---|
528 | filters[filter] = temp; |
---|
529 | temp = nullptr; |
---|
530 | success &= OptimiseFilters(); |
---|
531 | } |
---|
532 | |
---|
533 | if (!success) |
---|
534 | { |
---|
535 | RemoveFilter(filter); |
---|
536 | filters.erase(filter); |
---|
537 | delete temp; // If temp wasn't added to the filter list, we need to delete |
---|
538 | return false; |
---|
539 | } |
---|
540 | |
---|
541 | return true; |
---|
542 | } |
---|
543 | |
---|
544 | bool OpenGLVideo::RemoveFilter(OpenGLFilterType filter) |
---|
545 | { |
---|
546 | if (!filters.count(filter)) |
---|
547 | return true; |
---|
548 | |
---|
549 | LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Removing %1 filter") |
---|
550 | .arg(FilterToString(filter))); |
---|
551 | |
---|
552 | vector<GLuint> temp; |
---|
553 | vector<GLuint>::iterator it; |
---|
554 | |
---|
555 | temp = filters[filter]->fragmentPrograms; |
---|
556 | for (it = temp.begin(); it != temp.end(); ++it) |
---|
557 | gl_context->DeleteShaderObject(*it); |
---|
558 | filters[filter]->fragmentPrograms.clear(); |
---|
559 | |
---|
560 | temp = filters[filter]->frameBuffers; |
---|
561 | for (it = temp.begin(); it != temp.end(); ++it) |
---|
562 | gl_context->DeleteFrameBuffer(*it); |
---|
563 | filters[filter]->frameBuffers.clear(); |
---|
564 | |
---|
565 | DeleteTextures(&(filters[filter]->frameBufferTextures)); |
---|
566 | |
---|
567 | delete filters[filter]; |
---|
568 | filters[filter] = nullptr; |
---|
569 | |
---|
570 | return true; |
---|
571 | } |
---|
572 | |
---|
573 | void OpenGLVideo::TearDownDeinterlacer(void) |
---|
574 | { |
---|
575 | glfilt_map_t::iterator it; |
---|
576 | if (filters.end() == (it = filters.find(kGLFilterYUV2RGB)) && |
---|
577 | filters.end() == (it = filters.find(kGLFilterYV12RGB)) ) |
---|
578 | { |
---|
579 | return; |
---|
580 | } |
---|
581 | |
---|
582 | OpenGLFilter *tmp = it->second; |
---|
583 | |
---|
584 | if (tmp->fragmentPrograms.size() == 3) |
---|
585 | { |
---|
586 | gl_context->DeleteShaderObject(tmp->fragmentPrograms[2]); |
---|
587 | tmp->fragmentPrograms.pop_back(); |
---|
588 | } |
---|
589 | |
---|
590 | if (tmp->fragmentPrograms.size() == 2) |
---|
591 | { |
---|
592 | gl_context->DeleteShaderObject(tmp->fragmentPrograms[1]); |
---|
593 | tmp->fragmentPrograms.pop_back(); |
---|
594 | } |
---|
595 | |
---|
596 | DeleteTextures(&referenceTextures); |
---|
597 | refsNeeded = 0; |
---|
598 | } |
---|
599 | |
---|
600 | /** |
---|
601 | * Extends the functionality of the basic YUV->RGB filter stage to include |
---|
602 | * deinterlacing (combining the stages is significantly more efficient than |
---|
603 | * 2 separate stages). Create 2 deinterlacing fragment programs, 1 for each |
---|
604 | * required field. |
---|
605 | */ |
---|
606 | |
---|
607 | bool OpenGLVideo::AddDeinterlacer(const QString &deinterlacer) |
---|
608 | { |
---|
609 | if (!(gl_features & kGLExtFragProg) && !(gl_features & kGLSL)) |
---|
610 | { |
---|
611 | LOG(VB_PLAYBACK, LOG_ERR, LOC + |
---|
612 | "No shader support for OpenGL deinterlacing."); |
---|
613 | return false; |
---|
614 | } |
---|
615 | |
---|
616 | OpenGLLocker ctx_lock(gl_context); |
---|
617 | |
---|
618 | if (filters.end() == filters.find(kGLFilterYUV2RGB) && |
---|
619 | filters.end() == filters.find(kGLFilterYV12RGB) ) |
---|
620 | { |
---|
621 | LOG(VB_PLAYBACK, LOG_ERR, LOC + |
---|
622 | "No YUV2RGB filter stage for OpenGL deinterlacing."); |
---|
623 | return false; |
---|
624 | } |
---|
625 | |
---|
626 | if (hardwareDeinterlacer == deinterlacer) |
---|
627 | return true; |
---|
628 | |
---|
629 | TearDownDeinterlacer(); |
---|
630 | |
---|
631 | bool success = true; |
---|
632 | |
---|
633 | uint ref_size = 2; |
---|
634 | |
---|
635 | if (deinterlacer == "openglbobdeint" || |
---|
636 | deinterlacer == "openglonefield" || |
---|
637 | deinterlacer == "opengllinearblend" || |
---|
638 | deinterlacer == "opengldoubleratelinearblend" || |
---|
639 | deinterlacer == "opengldoubleratefieldorder") |
---|
640 | { |
---|
641 | ref_size = 0; |
---|
642 | } |
---|
643 | |
---|
644 | refsNeeded = ref_size; |
---|
645 | if (ref_size > 0) |
---|
646 | { |
---|
647 | for (; ref_size > 0; ref_size--) |
---|
648 | { |
---|
649 | GLuint tex = CreateVideoTexture(video_dim, inputTextureSize); |
---|
650 | if (tex) |
---|
651 | { |
---|
652 | referenceTextures.push_back(tex); |
---|
653 | } |
---|
654 | else |
---|
655 | { |
---|
656 | success = false; |
---|
657 | } |
---|
658 | } |
---|
659 | } |
---|
660 | |
---|
661 | OpenGLFilterType type = (MYTHTV_YV12 == videoTextureType) ? |
---|
662 | kGLFilterYV12RGB : kGLFilterYUV2RGB; |
---|
663 | |
---|
664 | uint prog1 = AddFragmentProgram(type, deinterlacer, kScan_Interlaced); |
---|
665 | uint prog2 = AddFragmentProgram(type, deinterlacer, kScan_Intr2ndField); |
---|
666 | |
---|
667 | if (prog1 && prog2) |
---|
668 | { |
---|
669 | filters[type]->fragmentPrograms.push_back(prog1); |
---|
670 | filters[type]->fragmentPrograms.push_back(prog2); |
---|
671 | } |
---|
672 | else |
---|
673 | { |
---|
674 | success = false; |
---|
675 | } |
---|
676 | |
---|
677 | if (success) |
---|
678 | { |
---|
679 | CheckResize(hardwareDeinterlacing); |
---|
680 | hardwareDeinterlacer = deinterlacer; |
---|
681 | return true; |
---|
682 | } |
---|
683 | |
---|
684 | hardwareDeinterlacer = ""; |
---|
685 | TearDownDeinterlacer(); |
---|
686 | |
---|
687 | return false; |
---|
688 | } |
---|
689 | |
---|
690 | /** |
---|
691 | * Create the correct fragment program for the given filter type |
---|
692 | */ |
---|
693 | |
---|
694 | uint OpenGLVideo::AddFragmentProgram(OpenGLFilterType name, |
---|
695 | QString deint, FrameScanType field) |
---|
696 | { |
---|
697 | if (!gl_context) |
---|
698 | return 0; |
---|
699 | |
---|
700 | QString vertex, fragment; |
---|
701 | if (gl_features & kGLSL) |
---|
702 | { |
---|
703 | GetProgramStrings(vertex, fragment, name, deint, field); |
---|
704 | } |
---|
705 | else if (gl_features & kGLExtFragProg) |
---|
706 | { |
---|
707 | fragment = GetProgramString(name, deint, field); |
---|
708 | } |
---|
709 | else |
---|
710 | { |
---|
711 | LOG(VB_PLAYBACK, LOG_ERR, LOC + "No OpenGL shader/program support"); |
---|
712 | return 0; |
---|
713 | } |
---|
714 | |
---|
715 | return gl_context->CreateShaderObject(vertex, fragment); |
---|
716 | } |
---|
717 | |
---|
718 | /** |
---|
719 | * Add a FrameBuffer object of the correct size to the given texture. |
---|
720 | */ |
---|
721 | |
---|
722 | bool OpenGLVideo::AddFrameBuffer(uint &framebuffer, |
---|
723 | uint &texture, QSize vid_size) |
---|
724 | { |
---|
725 | if (!(gl_features & kGLExtFBufObj)) |
---|
726 | { |
---|
727 | LOG(VB_PLAYBACK, LOG_ERR, LOC + "Framebuffer binding not supported."); |
---|
728 | return false; |
---|
729 | } |
---|
730 | |
---|
731 | texture = gl_context->CreateTexture(vid_size, false, textureType); |
---|
732 | |
---|
733 | bool ok = gl_context->CreateFrameBuffer(framebuffer, texture); |
---|
734 | |
---|
735 | if (!ok) |
---|
736 | gl_context->DeleteTexture(texture); |
---|
737 | |
---|
738 | return ok; |
---|
739 | } |
---|
740 | |
---|
741 | void OpenGLVideo::SetViewPort(const QSize &viewPortSize) |
---|
742 | { |
---|
743 | uint w = max(viewPortSize.width(), video_disp_dim.width()); |
---|
744 | uint h = max(viewPortSize.height(), video_disp_dim.height()); |
---|
745 | |
---|
746 | viewportSize = QSize(w, h); |
---|
747 | |
---|
748 | if (!viewportControl) |
---|
749 | return; |
---|
750 | |
---|
751 | LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Viewport: %1x%2") .arg(w).arg(h)); |
---|
752 | gl_context->SetViewPort(QRect(QPoint(),viewportSize)); |
---|
753 | } |
---|
754 | |
---|
755 | /** |
---|
756 | * Create and initialise an OpenGL texture suitable for a YV12 video frame |
---|
757 | * of the given size. |
---|
758 | */ |
---|
759 | |
---|
760 | uint OpenGLVideo::CreateVideoTexture(QSize size, QSize &tex_size) |
---|
761 | { |
---|
762 | uint tmp_tex = 0; |
---|
763 | bool use_pbo = gl_features & kGLExtPBufObj; |
---|
764 | if (GL_YCBCR_MESA == videoTextureType) |
---|
765 | { |
---|
766 | tmp_tex = gl_context->CreateTexture(size, use_pbo, textureType, |
---|
767 | GL_UNSIGNED_SHORT_8_8_MESA, |
---|
768 | GL_YCBCR_MESA, GL_RGBA); |
---|
769 | } |
---|
770 | else if (GL_YCBCR_422_APPLE == videoTextureType) |
---|
771 | { |
---|
772 | tmp_tex = gl_context->CreateTexture(size, use_pbo, textureType, |
---|
773 | GL_UNSIGNED_SHORT_8_8_MESA, |
---|
774 | GL_YCBCR_422_APPLE, GL_RGBA); |
---|
775 | } |
---|
776 | else if (MYTHTV_UYVY == videoTextureType) |
---|
777 | { |
---|
778 | QSize fix(size.width() / 2, size.height()); |
---|
779 | tmp_tex = gl_context->CreateTexture(fix, use_pbo, textureType, |
---|
780 | GL_UNSIGNED_BYTE, GL_RGBA, GL_RGBA); |
---|
781 | } |
---|
782 | else if (MYTHTV_YV12 == videoTextureType) |
---|
783 | { |
---|
784 | // 4:1:1 YVU planar (12bpp) |
---|
785 | size.setHeight((3 * size.height() + 1) / 2); |
---|
786 | tmp_tex = gl_context->CreateTexture(size, use_pbo, textureType, |
---|
787 | GL_UNSIGNED_BYTE, // data_type |
---|
788 | GL_LUMINANCE, // data_fmt |
---|
789 | GL_LUMINANCE // internal_fmt |
---|
790 | ); |
---|
791 | } |
---|
792 | else |
---|
793 | tmp_tex = gl_context->CreateTexture(size, use_pbo, textureType); |
---|
794 | |
---|
795 | tex_size = gl_context->GetTextureSize(textureType, size); |
---|
796 | if (!tmp_tex) |
---|
797 | return 0; |
---|
798 | |
---|
799 | return tmp_tex; |
---|
800 | } |
---|
801 | |
---|
802 | QSize OpenGLVideo::GetTextureSize(const QSize &size) |
---|
803 | { |
---|
804 | if (textureRects) |
---|
805 | return size; |
---|
806 | |
---|
807 | int w = 64; |
---|
808 | int h = 64; |
---|
809 | |
---|
810 | while (w < size.width()) |
---|
811 | { |
---|
812 | w *= 2; |
---|
813 | } |
---|
814 | |
---|
815 | while (h < size.height()) |
---|
816 | { |
---|
817 | h *= 2; |
---|
818 | } |
---|
819 | |
---|
820 | return QSize(w, h); |
---|
821 | } |
---|
822 | |
---|
823 | uint OpenGLVideo::GetInputTexture(void) const |
---|
824 | { |
---|
825 | return inputTextures[0]; |
---|
826 | } |
---|
827 | |
---|
828 | uint OpenGLVideo::GetTextureType(void) const |
---|
829 | { |
---|
830 | return textureType; |
---|
831 | } |
---|
832 | |
---|
833 | void OpenGLVideo::SetInputUpdated(void) |
---|
834 | { |
---|
835 | inputUpdated = true; |
---|
836 | } |
---|
837 | |
---|
838 | /** |
---|
839 | * Update the current input texture using the data from the given YV12 video |
---|
840 | * frame. If the required hardware support is not available, fall back to |
---|
841 | * software YUV->RGB conversion. |
---|
842 | */ |
---|
843 | |
---|
844 | void OpenGLVideo::UpdateInputFrame(const VideoFrame *frame, bool soft_bob) |
---|
845 | { |
---|
846 | OpenGLLocker ctx_lock(gl_context); |
---|
847 | |
---|
848 | if (frame->width != video_dim.width() || |
---|
849 | frame->height != video_dim.height() || |
---|
850 | frame->width < 1 || frame->height < 1 || |
---|
851 | frame->codec != FMT_YV12) |
---|
852 | { |
---|
853 | return; |
---|
854 | } |
---|
855 | if (hardwareDeinterlacing) |
---|
856 | RotateTextures(); |
---|
857 | |
---|
858 | // We need to convert frames here to avoid dependencies in MythRenderOpenGL |
---|
859 | void* buf = gl_context->GetTextureBuffer(inputTextures[0]); |
---|
860 | if (!buf) |
---|
861 | return; |
---|
862 | |
---|
863 | if (MYTHTV_YV12 == videoTextureType) |
---|
864 | { |
---|
865 | |
---|
866 | if (gl_features & kGLExtPBufObj) |
---|
867 | { |
---|
868 | // Copy the frame to the pixel buffer which updates the texture |
---|
869 | copybuffer((uint8_t*)buf, frame, video_dim.width()); |
---|
870 | } |
---|
871 | else if (video_dim.width() != frame->pitches[0]) |
---|
872 | { |
---|
873 | // Re-packing is needed |
---|
874 | copybuffer((uint8_t*)buf, frame, video_dim.width()); |
---|
875 | } |
---|
876 | else |
---|
877 | { |
---|
878 | // UpdateTexture will copy the frame to the texture |
---|
879 | buf = frame->buf; |
---|
880 | } |
---|
881 | } |
---|
882 | else if (!filters.count(kGLFilterYUV2RGB) || |
---|
883 | MYTHTV_UYVY == videoTextureType) |
---|
884 | { |
---|
885 | // software conversion |
---|
886 | AVFrame img_out; |
---|
887 | AVPixelFormat out_fmt = AV_PIX_FMT_BGRA; |
---|
888 | if ((GL_YCBCR_MESA == videoTextureType) || |
---|
889 | (GL_YCBCR_422_APPLE == videoTextureType) || |
---|
890 | (MYTHTV_UYVY == videoTextureType)) |
---|
891 | { |
---|
892 | out_fmt = AV_PIX_FMT_UYVY422; |
---|
893 | } |
---|
894 | m_copyCtx.Copy(&img_out, frame, (unsigned char*)buf, out_fmt); |
---|
895 | } |
---|
896 | else if (frame->interlaced_frame && !soft_bob) |
---|
897 | { |
---|
898 | pack_yv12interlaced(frame->buf, (unsigned char*)buf, frame->offsets, |
---|
899 | frame->pitches, video_dim); |
---|
900 | } |
---|
901 | else |
---|
902 | { |
---|
903 | pack_yv12progressive(frame->buf, (unsigned char*)buf, frame->offsets, |
---|
904 | frame->pitches, video_dim); |
---|
905 | } |
---|
906 | |
---|
907 | gl_context->UpdateTexture(inputTextures[0], buf); |
---|
908 | inputUpdated = true; |
---|
909 | } |
---|
910 | |
---|
911 | void OpenGLVideo::SetDeinterlacing(bool deinterlacing) |
---|
912 | { |
---|
913 | hardwareDeinterlacing = deinterlacing; |
---|
914 | OpenGLLocker ctx_lock(gl_context); |
---|
915 | CheckResize(hardwareDeinterlacing); |
---|
916 | } |
---|
917 | |
---|
918 | void OpenGLVideo::SetSoftwareDeinterlacer(const QString &filter) |
---|
919 | { |
---|
920 | if (softwareDeinterlacer != filter) |
---|
921 | CheckResize(false, filter != "bobdeint"); |
---|
922 | softwareDeinterlacer = filter; |
---|
923 | softwareDeinterlacer.detach(); |
---|
924 | } |
---|
925 | |
---|
926 | /** |
---|
927 | * Render the contents of the current input texture to the framebuffer |
---|
928 | * using the currently enabled filters. |
---|
929 | * \param topfieldfirst the frame is interlaced and top_field_first |
---|
930 | * is set |
---|
931 | * \param scan interlaced or progressive? |
---|
932 | * \param softwareDeinterlacing the frame has been deinterlaced in software |
---|
933 | * \param frame the frame number |
---|
934 | * \param stereo Whether/how to drop stereo video information |
---|
935 | * \param draw_border if true, draw a red border around the frame |
---|
936 | * \warning This function is a finely tuned, sensitive beast. Tinker at |
---|
937 | * your own risk. |
---|
938 | */ |
---|
939 | |
---|
940 | void OpenGLVideo::PrepareFrame(bool topfieldfirst, FrameScanType scan, |
---|
941 | bool softwareDeinterlacing, |
---|
942 | long long frame, StereoscopicMode stereo, |
---|
943 | bool draw_border) |
---|
944 | { |
---|
945 | if (inputTextures.empty() || filters.empty()) |
---|
946 | return; |
---|
947 | |
---|
948 | OpenGLLocker ctx_lock(gl_context); |
---|
949 | |
---|
950 | // we need to special case software bobdeint for 1080i |
---|
951 | bool softwarebob = softwareDeinterlacer == "bobdeint" && |
---|
952 | softwareDeinterlacing; |
---|
953 | |
---|
954 | vector<GLuint> inputs = inputTextures; |
---|
955 | QSize inputsize = inputTextureSize; |
---|
956 | QSize realsize = GetTextureSize(video_disp_dim); |
---|
957 | |
---|
958 | //#define RECT_TEST 1 |
---|
959 | #define LOG_RECT(r, s) \ |
---|
960 | if (currentFrameNum <= 0) { \ |
---|
961 | LOG(VB_GENERAL, LOG_INFO, LOC + \ |
---|
962 | QString("PrepareFrame %1 %2 %3,%4,%5,%6") \ |
---|
963 | .arg(s) \ |
---|
964 | .arg(currentFrameNum) \ |
---|
965 | .arg(r.left()) \ |
---|
966 | .arg(r.top()) \ |
---|
967 | .arg(r.right()) \ |
---|
968 | .arg(r.bottom())); } |
---|
969 | #define LOG_SIZE(r, s) \ |
---|
970 | if (currentFrameNum <= 0) { \ |
---|
971 | LOG(VB_GENERAL, LOG_INFO, LOC + \ |
---|
972 | QString("PrepareFrame %1 %2 %3,%4") \ |
---|
973 | .arg(s) \ |
---|
974 | .arg(currentFrameNum) \ |
---|
975 | .arg(r.width()) \ |
---|
976 | .arg(r.height())); } |
---|
977 | #define LOG_TINFO(s) \ |
---|
978 | if (currentFrameNum <= 0) { \ |
---|
979 | LOG(VB_GENERAL, LOG_INFO, LOC + s ); } |
---|
980 | |
---|
981 | if (currentFrameNum <= 0) |
---|
982 | { |
---|
983 | LOG(VB_GENERAL, LOG_INFO, LOC + |
---|
984 | QString("PrepareFrame frame num %1").arg(frame)); |
---|
985 | } |
---|
986 | LOG_SIZE(video_dim, "A video_dim"); |
---|
987 | LOG_SIZE(video_disp_dim, "A video_disp_dim"); |
---|
988 | |
---|
989 | glfilt_map_t::iterator it; |
---|
990 | for (it = filters.begin(); it != filters.end(); ++it) |
---|
991 | { |
---|
992 | OpenGLFilterType type = it->first; |
---|
993 | OpenGLFilter *filter = it->second; |
---|
994 | |
---|
995 | bool actual = softwarebob && (filter->outputBuffer == kDefaultBuffer); |
---|
996 | |
---|
997 | if (currentFrameNum <= 0) |
---|
998 | { |
---|
999 | LOG(VB_GENERAL, LOG_INFO, LOC + |
---|
1000 | QString("PrepareFrame filter %1").arg(FilterToString(type))); |
---|
1001 | } |
---|
1002 | // texture coordinates |
---|
1003 | float trueheight = (float)(actual ? video_dim.height() : |
---|
1004 | video_disp_dim.height()); |
---|
1005 | float width = video_disp_dim.width(); |
---|
1006 | if ((type == kGLFilterYUV2RGB) && (videoTextureType == MYTHTV_UYVY)) |
---|
1007 | width /= 2.0f; |
---|
1008 | |
---|
1009 | QRectF trect(QPoint(0, 0), QSize(width, trueheight)); |
---|
1010 | |
---|
1011 | LOG_RECT(trect, "A trect"); |
---|
1012 | // only apply overscan on last filter |
---|
1013 | if (filter->outputBuffer == kDefaultBuffer) |
---|
1014 | { |
---|
1015 | trect.setRect(video_rect.left(), video_rect.top(), |
---|
1016 | video_rect.width(), video_rect.height()); |
---|
1017 | LOG_RECT(trect, "B trect default"); |
---|
1018 | } |
---|
1019 | |
---|
1020 | if (!textureRects && (inputsize.height() > 0)) |
---|
1021 | trueheight /= inputsize.height(); |
---|
1022 | |
---|
1023 | // software bobdeint |
---|
1024 | if (actual) |
---|
1025 | { |
---|
1026 | bool top = (scan == kScan_Intr2ndField && topfieldfirst) || |
---|
1027 | (scan == kScan_Interlaced && !topfieldfirst); |
---|
1028 | bool bot = (scan == kScan_Interlaced && topfieldfirst) || |
---|
1029 | (scan == kScan_Intr2ndField && !topfieldfirst); |
---|
1030 | bool first = filters.size() < 2; |
---|
1031 | float bob = (trueheight / (float)video_disp_dim.height()) / 4.0f; |
---|
1032 | if ((top && !first) || (bot && first)) |
---|
1033 | { |
---|
1034 | trect.setBottom(trect.bottom() / 2); |
---|
1035 | trect.setTop(trect.top() / 2); |
---|
1036 | LOG_RECT(trect, "C trect int a"); |
---|
1037 | trect.adjust(0, bob, 0, bob); |
---|
1038 | } |
---|
1039 | if ((bot && !first) || (top && first)) |
---|
1040 | { |
---|
1041 | trect.setTop(static_cast<qreal>(trueheight / 2) + (trect.top() / 2)); |
---|
1042 | trect.setBottom(static_cast<qreal>(trueheight / 2) + (trect.bottom() / 2)); |
---|
1043 | LOG_RECT(trect, "D trect int b"); |
---|
1044 | trect.adjust(0, -bob, 0, -bob); |
---|
1045 | } |
---|
1046 | } |
---|
1047 | |
---|
1048 | // discard stereoscopic fields |
---|
1049 | if (filter->outputBuffer == kDefaultBuffer) |
---|
1050 | { |
---|
1051 | if (kStereoscopicModeSideBySideDiscard == stereo) |
---|
1052 | trect = QRectF(trect.left() / 2.0, trect.top(), |
---|
1053 | trect.width() / 2.0, trect.height()); |
---|
1054 | if (kStereoscopicModeTopAndBottomDiscard == stereo) |
---|
1055 | trect = QRectF(trect.left(), trect.top() / 2.0, |
---|
1056 | trect.width(), trect.height() / 2.0); |
---|
1057 | } |
---|
1058 | |
---|
1059 | // vertex coordinates |
---|
1060 | QRect display = (filter->outputBuffer == kDefaultBuffer) ? |
---|
1061 | display_video_rect : frameBufferRect; |
---|
1062 | QRect visible = (filter->outputBuffer == kDefaultBuffer) ? |
---|
1063 | display_visible_rect : frameBufferRect; |
---|
1064 | #ifdef RECT_TEST |
---|
1065 | QRectF vrect(trect); |
---|
1066 | draw_border = true; |
---|
1067 | #else |
---|
1068 | QRectF vrect(display); |
---|
1069 | #endif |
---|
1070 | |
---|
1071 | #ifndef RECT_TEST |
---|
1072 | // invert if first filter |
---|
1073 | if (it == filters.begin()) |
---|
1074 | { |
---|
1075 | LOG_RECT(display, "E display"); |
---|
1076 | LOG_RECT(visible, "F visible"); |
---|
1077 | if (filters.size() > 1) |
---|
1078 | { |
---|
1079 | vrect.setTop(visible.height() - display.top()); |
---|
1080 | vrect.setBottom(vrect.top() - display.height()); |
---|
1081 | LOG_RECT(vrect, "G vrect sf"); |
---|
1082 | } |
---|
1083 | else |
---|
1084 | { |
---|
1085 | vrect.setBottom(display.top()); |
---|
1086 | vrect.setTop(display.top() + display.height()); |
---|
1087 | LOG_RECT(vrect, "H vrect bf"); |
---|
1088 | } |
---|
1089 | } |
---|
1090 | #endif |
---|
1091 | |
---|
1092 | // hardware bobdeint |
---|
1093 | if (filter->outputBuffer == kDefaultBuffer && |
---|
1094 | hardwareDeinterlacing && |
---|
1095 | hardwareDeinterlacer == "openglbobdeint") |
---|
1096 | { |
---|
1097 | float bob = ((float)display.height() / (float)video_rect.height()) |
---|
1098 | / 2.0f; |
---|
1099 | float field = kScan_Interlaced ? -1.0f : 1.0f; |
---|
1100 | bob = bob * (topfieldfirst ? field : -field); |
---|
1101 | vrect.adjust(0, bob, 0, bob); |
---|
1102 | } |
---|
1103 | |
---|
1104 | uint target = 0; |
---|
1105 | // bind correct frame buffer (default onscreen) and set viewport |
---|
1106 | switch (filter->outputBuffer) |
---|
1107 | { |
---|
1108 | case kDefaultBuffer: |
---|
1109 | gl_context->BindFramebuffer(0); |
---|
1110 | if (viewportControl) |
---|
1111 | gl_context->SetViewPort(QRect(QPoint(), display_visible_rect.size())); |
---|
1112 | else |
---|
1113 | gl_context->SetViewPort(QRect(QPoint(), masterViewportSize)); |
---|
1114 | break; |
---|
1115 | case kFrameBufferObject: |
---|
1116 | if (!filter->frameBuffers.empty()) |
---|
1117 | { |
---|
1118 | gl_context->BindFramebuffer(filter->frameBuffers[0]); |
---|
1119 | gl_context->SetViewPort(QRect(QPoint(), frameBufferRect.size())); |
---|
1120 | target = filter->frameBuffers[0]; |
---|
1121 | } |
---|
1122 | break; |
---|
1123 | |
---|
1124 | default: |
---|
1125 | continue; |
---|
1126 | } |
---|
1127 | |
---|
1128 | if (draw_border && filter->outputBuffer == kDefaultBuffer) |
---|
1129 | { |
---|
1130 | LOG_RECT(vrect, "I vrect"); |
---|
1131 | #ifdef RECT_TEST |
---|
1132 | vrect = vrect.adjusted(+10, +10, +10, +10); |
---|
1133 | QRectF piprectf = vrect.adjusted(-1, -1, +1, +1); |
---|
1134 | #else |
---|
1135 | QRectF piprectf = vrect.adjusted(-10, -10, +10, +10); |
---|
1136 | #endif |
---|
1137 | QRect piprect(piprectf.left(), piprectf.top(), |
---|
1138 | piprectf.width(), piprectf.height()); |
---|
1139 | static const QPen nopen(Qt::NoPen); |
---|
1140 | static const QBrush redbrush(QBrush(QColor(127, 0, 0, 255))); |
---|
1141 | gl_context->DrawRect(piprect, redbrush, nopen, 255); |
---|
1142 | } |
---|
1143 | |
---|
1144 | // bind correct textures |
---|
1145 | uint textures[4]; // NB |
---|
1146 | uint texture_count = 0; |
---|
1147 | for (uint i = 0; i < inputs.size(); i++) |
---|
1148 | textures[texture_count++] = inputs[i]; |
---|
1149 | |
---|
1150 | if (!referenceTextures.empty() && |
---|
1151 | hardwareDeinterlacing && |
---|
1152 | (type == kGLFilterYUV2RGB || type == kGLFilterYV12RGB)) |
---|
1153 | { |
---|
1154 | for (uint i = 0; i < referenceTextures.size(); i++) |
---|
1155 | textures[texture_count++] = referenceTextures[i]; |
---|
1156 | } |
---|
1157 | |
---|
1158 | if (helperTexture && type == kGLFilterBicubic) |
---|
1159 | textures[texture_count++] = helperTexture; |
---|
1160 | |
---|
1161 | // enable fragment program and set any environment variables |
---|
1162 | GLuint program = 0; |
---|
1163 | if (((type != kGLFilterNone) && (type != kGLFilterResize)) || |
---|
1164 | ((gl_features & kGLSL) && (type == kGLFilterResize))) |
---|
1165 | { |
---|
1166 | GLuint prog_ref = 0; |
---|
1167 | |
---|
1168 | if (type == kGLFilterYUV2RGB || type == kGLFilterYV12RGB) |
---|
1169 | { |
---|
1170 | if (hardwareDeinterlacing && |
---|
1171 | filter->fragmentPrograms.size() == 3 && |
---|
1172 | !refsNeeded) |
---|
1173 | { |
---|
1174 | if (scan == kScan_Interlaced) |
---|
1175 | prog_ref = topfieldfirst ? 1 : 2; |
---|
1176 | else if (scan == kScan_Intr2ndField) |
---|
1177 | prog_ref = topfieldfirst ? 2 : 1; |
---|
1178 | } |
---|
1179 | } |
---|
1180 | program = filter->fragmentPrograms[prog_ref]; |
---|
1181 | } |
---|
1182 | |
---|
1183 | if (type == kGLFilterYUV2RGB || type == kGLFilterYV12RGB) |
---|
1184 | { |
---|
1185 | gl_context->SetShaderParams(program, |
---|
1186 | GLMatrix4x4(reinterpret_cast<float*>(colourSpace->GetMatrix())), |
---|
1187 | COLOUR_UNIFORM); |
---|
1188 | } |
---|
1189 | |
---|
1190 | LOG_RECT(trect, "Final trect"); |
---|
1191 | LOG_RECT(vrect, "Final vrect"); |
---|
1192 | gl_context->DrawBitmap(textures, texture_count, target, &trect, &vrect, |
---|
1193 | program); |
---|
1194 | |
---|
1195 | inputs = filter->frameBufferTextures; |
---|
1196 | inputsize = realsize; |
---|
1197 | } |
---|
1198 | |
---|
1199 | currentFrameNum = frame; |
---|
1200 | inputUpdated = false; |
---|
1201 | } |
---|
1202 | |
---|
1203 | void OpenGLVideo::RotateTextures(void) |
---|
1204 | { |
---|
1205 | if (referenceTextures.size() < 2) |
---|
1206 | return; |
---|
1207 | |
---|
1208 | if (refsNeeded > 0) |
---|
1209 | refsNeeded--; |
---|
1210 | |
---|
1211 | GLuint tmp = referenceTextures[referenceTextures.size() - 1]; |
---|
1212 | |
---|
1213 | for (uint i = referenceTextures.size() - 1; i > 0; i--) |
---|
1214 | referenceTextures[i] = referenceTextures[i - 1]; |
---|
1215 | |
---|
1216 | referenceTextures[0] = inputTextures[0]; |
---|
1217 | inputTextures[0] = tmp; |
---|
1218 | } |
---|
1219 | |
---|
1220 | void OpenGLVideo::DeleteTextures(vector<GLuint> *textures) |
---|
1221 | { |
---|
1222 | if ((*textures).empty()) |
---|
1223 | return; |
---|
1224 | |
---|
1225 | for (uint i = 0; i < (*textures).size(); i++) |
---|
1226 | gl_context->DeleteTexture((*textures)[i]); |
---|
1227 | (*textures).clear(); |
---|
1228 | } |
---|
1229 | |
---|
1230 | void OpenGLVideo::SetTextureFilters(vector<GLuint> *textures, |
---|
1231 | int filt, int wrap) |
---|
1232 | { |
---|
1233 | if (textures->empty()) |
---|
1234 | return; |
---|
1235 | |
---|
1236 | for (uint i = 0; i < textures->size(); i++) |
---|
1237 | gl_context->SetTextureFilters((*textures)[i], filt, wrap); |
---|
1238 | } |
---|
1239 | |
---|
1240 | OpenGLVideo::OpenGLFilterType OpenGLVideo::StringToFilter(const QString &filter) |
---|
1241 | { |
---|
1242 | OpenGLFilterType ret = kGLFilterNone; |
---|
1243 | |
---|
1244 | if (filter.contains("master")) |
---|
1245 | ret = kGLFilterYUV2RGB; |
---|
1246 | else if (filter.contains("resize")) |
---|
1247 | ret = kGLFilterResize; |
---|
1248 | else if (filter.contains("bicubic")) |
---|
1249 | ret = kGLFilterBicubic; |
---|
1250 | else if (filter.contains("yv12rgb")) |
---|
1251 | ret = kGLFilterYV12RGB; |
---|
1252 | |
---|
1253 | return ret; |
---|
1254 | } |
---|
1255 | |
---|
1256 | QString OpenGLVideo::FilterToString(OpenGLFilterType filt) |
---|
1257 | { |
---|
1258 | switch (filt) |
---|
1259 | { |
---|
1260 | case kGLFilterNone: |
---|
1261 | break; |
---|
1262 | case kGLFilterYUV2RGB: |
---|
1263 | return "master"; |
---|
1264 | case kGLFilterResize: |
---|
1265 | return "resize"; |
---|
1266 | case kGLFilterBicubic: |
---|
1267 | return "bicubic"; |
---|
1268 | case kGLFilterYV12RGB: |
---|
1269 | return "yv12rgb"; |
---|
1270 | } |
---|
1271 | |
---|
1272 | return ""; |
---|
1273 | } |
---|
1274 | |
---|
1275 | static const QString attrib_fast = |
---|
1276 | "ATTRIB tex = fragment.texcoord[0];\n" |
---|
1277 | "PARAM yuv[3] = { program.local[0..2] };\n"; |
---|
1278 | |
---|
1279 | static const QString tex_fast = |
---|
1280 | "TEX res, tex, texture[0], %1;\n"; |
---|
1281 | |
---|
1282 | static const QString var_fast = |
---|
1283 | "TEMP tmp, res;\n"; |
---|
1284 | |
---|
1285 | static const QString var_col = |
---|
1286 | "TEMP col;\n"; |
---|
1287 | |
---|
1288 | static const QString select_col = |
---|
1289 | "MUL col, tex.xxxx, %8;\n" |
---|
1290 | "FRC col, col;\n" |
---|
1291 | "SUB col, col, 0.5;\n" |
---|
1292 | "CMP res, col, res.rabg, res.rgba;\n"; |
---|
1293 | |
---|
1294 | static const QString end_fast = |
---|
1295 | "DPH tmp.r, res.arbg, yuv[0];\n" |
---|
1296 | "DPH tmp.g, res.arbg, yuv[1];\n" |
---|
1297 | "DPH tmp.b, res.arbg, yuv[2];\n" |
---|
1298 | "MOV tmp.a, 1.0;\n" |
---|
1299 | "MOV result.color, tmp;\n"; |
---|
1300 | |
---|
1301 | static const QString var_deint = |
---|
1302 | "TEMP other, current, mov, prev;\n"; |
---|
1303 | |
---|
1304 | static const QString field_calc = |
---|
1305 | "MUL prev, tex.yyyy, %2;\n" |
---|
1306 | "FRC prev, prev;\n" |
---|
1307 | "SUB prev, prev, 0.5;\n"; |
---|
1308 | |
---|
1309 | static const QString bobdeint[2] = { |
---|
1310 | field_calc + |
---|
1311 | "ADD other, tex, {0.0, %3, 0.0, 0.0};\n" |
---|
1312 | "MIN other, other, {10000.0, %9, 10000.0, 10000.0};\n" |
---|
1313 | "TEX other, other, texture[0], %1;\n" |
---|
1314 | "CMP res, prev, res, other;\n", |
---|
1315 | field_calc + |
---|
1316 | "SUB other, tex, {0.0, %3, 0.0, 0.0};\n" |
---|
1317 | "TEX other, other, texture[0], %1;\n" |
---|
1318 | "CMP res, prev, other, res;\n" |
---|
1319 | }; |
---|
1320 | |
---|
1321 | static const QString deint_end_top = |
---|
1322 | "CMP res, prev, current, other;\n"; |
---|
1323 | |
---|
1324 | static const QString deint_end_bot = |
---|
1325 | "CMP res, prev, other, current;\n"; |
---|
1326 | |
---|
1327 | static const QString linearblend[2] = { |
---|
1328 | "TEX current, tex, texture[0], %1;\n" |
---|
1329 | "ADD other, tex, {0.0, %3, 0.0, 0.0};\n" |
---|
1330 | "MIN other, other, {10000.0, %9, 10000.0, 10000.0};\n" |
---|
1331 | "TEX other, other, texture[0], %1;\n" |
---|
1332 | "SUB mov, tex, {0.0, %3, 0.0, 0.0};\n" |
---|
1333 | "TEX mov, mov, texture[0], %1;\n" |
---|
1334 | "LRP other, 0.5, other, mov;\n" |
---|
1335 | + field_calc + deint_end_top, |
---|
1336 | |
---|
1337 | "TEX current, tex, texture[0], %1;\n" |
---|
1338 | "SUB other, tex, {0.0, %3, 0.0, 0.0};\n" |
---|
1339 | "TEX other, other, texture[0], %1;\n" |
---|
1340 | "ADD mov, tex, {0.0, %3, 0.0, 0.0};\n" |
---|
1341 | "TEX mov, mov, texture[0], %1;\n" |
---|
1342 | "LRP other, 0.5, other, mov;\n" |
---|
1343 | + field_calc + deint_end_bot |
---|
1344 | }; |
---|
1345 | |
---|
1346 | static const QString kerneldeint[2] = { |
---|
1347 | "TEX current, tex, texture[1], %1;\n" |
---|
1348 | "TEX prev, tex, texture[2], %1;\n" |
---|
1349 | "MUL other, 0.125, prev;\n" |
---|
1350 | "MAD other, 0.125, current, other;\n" |
---|
1351 | "ADD prev, tex, {0.0, %3, 0.0, 0.0};\n" |
---|
1352 | "MIN prev, prev, {10000.0, %9, 10000.0, 10000.0};\n" |
---|
1353 | "TEX prev, prev, texture[1], %1;\n" |
---|
1354 | "MAD other, 0.5, prev, other;\n" |
---|
1355 | "SUB prev, tex, {0.0, %3, 0.0, 0.0};\n" |
---|
1356 | "TEX prev, prev, texture[1], %1;\n" |
---|
1357 | "MAD other, 0.5, prev, other;\n" |
---|
1358 | "ADD prev, tex, {0.0, %4, 0.0, 0.0};\n" |
---|
1359 | "TEX tmp, prev, texture[1], %1;\n" |
---|
1360 | "MAD other, -0.0625, tmp, other;\n" |
---|
1361 | "TEX tmp, prev, texture[2], %1;\n" |
---|
1362 | "MAD other, -0.0625, tmp, other;\n" |
---|
1363 | "SUB prev, tex, {0.0, %4, 0.0, 0.0};\n" |
---|
1364 | "TEX tmp, prev, texture[1], %1;\n" |
---|
1365 | "MAD other, -0.0625, tmp, other;\n" |
---|
1366 | "TEX tmp, prev, texture[2], %1;\n" |
---|
1367 | "MAD other, -0.0625, tmp, other;\n" |
---|
1368 | + field_calc + deint_end_top, |
---|
1369 | |
---|
1370 | "TEX current, tex, texture[1], %1;\n" |
---|
1371 | "MUL other, 0.125, res;\n" |
---|
1372 | "MAD other, 0.125, current, other;\n" |
---|
1373 | "ADD prev, tex, {0.0, %3, 0.0, 0.0};\n" |
---|
1374 | "TEX prev, prev, texture[1], %1;\n" |
---|
1375 | "MAD other, 0.5, prev, other;\n" |
---|
1376 | "SUB prev, tex, {0.0, %3, 0.0, 0.0};\n" |
---|
1377 | "TEX prev, prev, texture[1], %1;\n" |
---|
1378 | "MAD other, 0.5, prev, other;\n" |
---|
1379 | "ADD prev, tex, {0.0, %4, 0.0, 0.0};\n" |
---|
1380 | "TEX tmp, prev, texture[1], %1;\n" |
---|
1381 | "MAD other, -0.0625, tmp, other;\n" |
---|
1382 | "TEX tmp, prev, texture[0], %1;\n" |
---|
1383 | "MAD other, -0.0625, tmp, other;\n" |
---|
1384 | "SUB prev, tex, {0.0, %4, 0.0, 0.0};\n" |
---|
1385 | "TEX tmp, prev, texture[1], %1;\n" |
---|
1386 | "MAD other, -0.0625, tmp, other;\n" |
---|
1387 | "TEX tmp, prev, texture[0], %1;\n" |
---|
1388 | "MAD other, -0.0625, tmp, other;\n" |
---|
1389 | + field_calc + deint_end_bot |
---|
1390 | }; |
---|
1391 | |
---|
1392 | static const QString bicubic = |
---|
1393 | "TEMP coord, coord2, cdelta, parmx, parmy, a, b, c, d;\n" |
---|
1394 | "MAD coord.xy, fragment.texcoord[0], {%6, %7}, {0.5, 0.5};\n" |
---|
1395 | "TEX parmx, coord.x, texture[1], 1D;\n" |
---|
1396 | "TEX parmy, coord.y, texture[1], 1D;\n" |
---|
1397 | "MUL cdelta.xz, parmx.rrgg, {-%5, 0, %5, 0};\n" |
---|
1398 | "MUL cdelta.yw, parmy.rrgg, {0, -%3, 0, %3};\n" |
---|
1399 | "ADD coord, fragment.texcoord[0].xyxy, cdelta.xyxw;\n" |
---|
1400 | "ADD coord2, fragment.texcoord[0].xyxy, cdelta.zyzw;\n" |
---|
1401 | "TEX a, coord.xyxy, texture[0], 2D;\n" |
---|
1402 | "TEX b, coord.zwzw, texture[0], 2D;\n" |
---|
1403 | "TEX c, coord2.xyxy, texture[0], 2D;\n" |
---|
1404 | "TEX d, coord2.zwzw, texture[0], 2D;\n" |
---|
1405 | "LRP a, parmy.b, a, b;\n" |
---|
1406 | "LRP c, parmy.b, c, d;\n" |
---|
1407 | "LRP result.color, parmx.b, a, c;\n"; |
---|
1408 | |
---|
1409 | QString OpenGLVideo::GetProgramString(OpenGLFilterType name, |
---|
1410 | QString deint, FrameScanType field) |
---|
1411 | { |
---|
1412 | QString ret = |
---|
1413 | "!!ARBfp1.0\n" |
---|
1414 | "OPTION ARB_precision_hint_fastest;\n"; |
---|
1415 | |
---|
1416 | switch (name) |
---|
1417 | { |
---|
1418 | case kGLFilterYUV2RGB: |
---|
1419 | { |
---|
1420 | bool need_tex = true; |
---|
1421 | bool packed = MYTHTV_UYVY == videoTextureType; |
---|
1422 | QString deint_bit = ""; |
---|
1423 | if (deint != "") |
---|
1424 | { |
---|
1425 | uint tmp_field = 0; |
---|
1426 | if (field == kScan_Intr2ndField) |
---|
1427 | tmp_field = 1; |
---|
1428 | if (deint == "openglbobdeint" || |
---|
1429 | deint == "openglonefield" || |
---|
1430 | deint == "opengldoubleratefieldorder") |
---|
1431 | { |
---|
1432 | deint_bit = bobdeint[tmp_field]; |
---|
1433 | } |
---|
1434 | else if (deint == "opengllinearblend" || |
---|
1435 | deint == "opengldoubleratelinearblend") |
---|
1436 | { |
---|
1437 | deint_bit = linearblend[tmp_field]; |
---|
1438 | if (!tmp_field) { need_tex = false; } |
---|
1439 | } |
---|
1440 | else if (deint == "openglkerneldeint" || |
---|
1441 | deint == "opengldoubleratekerneldeint") |
---|
1442 | { |
---|
1443 | deint_bit = kerneldeint[tmp_field]; |
---|
1444 | if (!tmp_field) { need_tex = false; } |
---|
1445 | } |
---|
1446 | else |
---|
1447 | { |
---|
1448 | LOG(VB_PLAYBACK, LOG_ERR, LOC + |
---|
1449 | "Unrecognised OpenGL deinterlacer"); |
---|
1450 | } |
---|
1451 | } |
---|
1452 | |
---|
1453 | ret += attrib_fast; |
---|
1454 | ret += (deint != "") ? var_deint : ""; |
---|
1455 | ret += packed ? var_col : ""; |
---|
1456 | ret += var_fast + (need_tex ? tex_fast : ""); |
---|
1457 | ret += deint_bit; |
---|
1458 | ret += packed ? select_col : ""; |
---|
1459 | ret += end_fast; |
---|
1460 | } |
---|
1461 | break; |
---|
1462 | |
---|
1463 | case kGLFilterNone: |
---|
1464 | case kGLFilterResize: |
---|
1465 | break; |
---|
1466 | |
---|
1467 | case kGLFilterBicubic: |
---|
1468 | |
---|
1469 | ret += bicubic; |
---|
1470 | break; |
---|
1471 | |
---|
1472 | case kGLFilterYV12RGB: // TODO: extend this for opengl1 |
---|
1473 | default: |
---|
1474 | LOG(VB_PLAYBACK, LOG_ERR, LOC + "Unknown fragment program."); |
---|
1475 | break; |
---|
1476 | } |
---|
1477 | |
---|
1478 | CustomiseProgramString(ret); |
---|
1479 | ret += "END"; |
---|
1480 | |
---|
1481 | LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Created %1 fragment program %2") |
---|
1482 | .arg(FilterToString(name)).arg(deint)); |
---|
1483 | |
---|
1484 | return ret; |
---|
1485 | } |
---|
1486 | |
---|
1487 | void OpenGLVideo::CustomiseProgramString(QString &string) |
---|
1488 | { |
---|
1489 | string.replace("%1", textureRects ? "RECT" : "2D"); |
---|
1490 | |
---|
1491 | if (!textureRects) |
---|
1492 | { |
---|
1493 | string.replace("GLSL_SAMPLER", "sampler2D"); |
---|
1494 | string.replace("GLSL_TEXTURE", "texture2D"); |
---|
1495 | } |
---|
1496 | |
---|
1497 | float lineHeight = 1.0f; |
---|
1498 | float colWidth = 1.0f; |
---|
1499 | float yselect = 1.0f; |
---|
1500 | QSize fb_size = GetTextureSize(video_disp_dim); |
---|
1501 | |
---|
1502 | LOG(VB_GENERAL, LOG_INFO, LOC + |
---|
1503 | QString("CustomiseProgramString fbsize %1,%2").arg(fb_size.width()).arg(fb_size.height())); |
---|
1504 | LOG(VB_GENERAL, LOG_INFO, LOC + |
---|
1505 | QString("CustomiseProgramString inputTextureSize %1,%2 tr %3") |
---|
1506 | .arg(inputTextureSize.width()).arg(inputTextureSize.height()).arg(textureRects)); |
---|
1507 | |
---|
1508 | if (!textureRects && |
---|
1509 | (inputTextureSize.height() > 0)) |
---|
1510 | { |
---|
1511 | lineHeight /= inputTextureSize.height(); |
---|
1512 | colWidth /= inputTextureSize.width(); |
---|
1513 | yselect /= ((float)inputTextureSize.width() / 2.0f); |
---|
1514 | LOG(VB_GENERAL, LOG_INFO, LOC + |
---|
1515 | QString("CustomiseProgramString lineHeight %1 colWidth %2 yselect %3") |
---|
1516 | .arg(QString::number(lineHeight, 'f', 16)) |
---|
1517 | .arg(QString::number(colWidth, 'f', 16)) |
---|
1518 | .arg(QString::number(yselect, 'f', 16))); |
---|
1519 | } |
---|
1520 | |
---|
1521 | float maxheight = (float)(min(inputTextureSize.height(), 2160) - 1) * |
---|
1522 | lineHeight; |
---|
1523 | float fieldSize = 1.0f / (lineHeight * 2.0f); |
---|
1524 | LOG(VB_GENERAL, LOG_INFO, LOC + |
---|
1525 | QString("CustomiseProgramString maxHeight %1 fieldSize %2 %3") |
---|
1526 | .arg(maxheight).arg(fieldSize, 0, 'f', 8).arg(fieldSize, 0, 'f', 16)); |
---|
1527 | |
---|
1528 | string.replace("%2", QString::number(fieldSize, 'f', 8)); |
---|
1529 | string.replace("%3", QString::number(lineHeight, 'f', 16)); |
---|
1530 | string.replace("%4", QString::number(lineHeight * 2.0f, 'f', 16)); |
---|
1531 | string.replace("%5", QString::number(colWidth, 'f', 16)); |
---|
1532 | string.replace("%6", QString::number((float)fb_size.width(), 'f', 1)); |
---|
1533 | string.replace("%7", QString::number((float)fb_size.height(), 'f', 1)); |
---|
1534 | string.replace("%8", QString::number(1.0f / yselect, 'f', 16)); |
---|
1535 | // make sure truncation errors dont affect clamping |
---|
1536 | string.replace("%9", QString::number(maxheight, 'f', 16)); |
---|
1537 | |
---|
1538 | float width = float(video_dim.width()) / inputTextureSize.width(); |
---|
1539 | string.replace("%WIDTH%", QString::number(width, 'f', 8)); |
---|
1540 | |
---|
1541 | float height = float(video_dim.height()) / inputTextureSize.height(); |
---|
1542 | string.replace("%HEIGHT%", QString::number(height, 'f', 8)); |
---|
1543 | |
---|
1544 | LOG(VB_GENERAL, LOG_INFO, LOC + |
---|
1545 | QString("CustomiseProgramString width %1 height %2") |
---|
1546 | .arg(height, 0, 'f', 8).arg(width, 0, 'f', 8)); |
---|
1547 | |
---|
1548 | string.replace("COLOUR_UNIFORM", COLOUR_UNIFORM); |
---|
1549 | } |
---|
1550 | |
---|
1551 | static const QString YUV2RGBVertexShader = |
---|
1552 | "GLSL_DEFINES" |
---|
1553 | "attribute vec2 a_position;\n" |
---|
1554 | "attribute vec2 a_texcoord0;\n" |
---|
1555 | "varying vec2 v_texcoord0;\n" |
---|
1556 | "uniform mat4 u_projection;\n" |
---|
1557 | "void main() {\n" |
---|
1558 | " gl_Position = u_projection * vec4(a_position, 0.0, 1.0);\n" |
---|
1559 | " v_texcoord0 = a_texcoord0;\n" |
---|
1560 | "}\n"; |
---|
1561 | |
---|
1562 | static const QString SelectColumn = |
---|
1563 | " if (fract(v_texcoord0.x * %8) < 0.5)\n" |
---|
1564 | " yuva = yuva.rabg;\n"; |
---|
1565 | |
---|
1566 | static const QString YUV2RGBFragmentShader = |
---|
1567 | "GLSL_DEFINES" |
---|
1568 | "uniform GLSL_SAMPLER s_texture0;\n" |
---|
1569 | "uniform mat4 COLOUR_UNIFORM;\n" |
---|
1570 | "varying vec2 v_texcoord0;\n" |
---|
1571 | "void main(void)\n" |
---|
1572 | "{\n" |
---|
1573 | " vec4 yuva = GLSL_TEXTURE(s_texture0, v_texcoord0);\n" |
---|
1574 | "SELECT_COLUMN" |
---|
1575 | " gl_FragColor = vec4(yuva.arb, 1.0) * COLOUR_UNIFORM;\n" |
---|
1576 | "}\n"; |
---|
1577 | |
---|
1578 | static const QString OneFieldShader[2] = { |
---|
1579 | "GLSL_DEFINES" |
---|
1580 | "uniform GLSL_SAMPLER s_texture0;\n" |
---|
1581 | "uniform mat4 COLOUR_UNIFORM;\n" |
---|
1582 | "varying vec2 v_texcoord0;\n" |
---|
1583 | "void main(void)\n" |
---|
1584 | "{\n" |
---|
1585 | " float field = v_texcoord0.y - (step(0.5, fract(v_texcoord0.y * %2)) * %3);\n" |
---|
1586 | " field = clamp(field, 0.0, %9);\n" |
---|
1587 | " vec4 yuva = GLSL_TEXTURE(s_texture0, vec2(v_texcoord0.x, field));\n" |
---|
1588 | "SELECT_COLUMN" |
---|
1589 | " gl_FragColor = vec4(yuva.arb, 1.0) * COLOUR_UNIFORM;\n" |
---|
1590 | "}\n", |
---|
1591 | |
---|
1592 | "GLSL_DEFINES" |
---|
1593 | "uniform GLSL_SAMPLER s_texture0;\n" |
---|
1594 | "uniform mat4 COLOUR_UNIFORM;\n" |
---|
1595 | "varying vec2 v_texcoord0;\n" |
---|
1596 | "void main(void)\n" |
---|
1597 | "{\n" |
---|
1598 | " vec2 field = vec2(0.0, step(0.5, 1.0 - fract(v_texcoord0.y * %2)) * %3);\n" |
---|
1599 | " vec4 yuva = GLSL_TEXTURE(s_texture0, v_texcoord0 + field);\n" |
---|
1600 | "SELECT_COLUMN" |
---|
1601 | " gl_FragColor = vec4(yuva.arb, 1.0) * COLOUR_UNIFORM;\n" |
---|
1602 | "}\n" |
---|
1603 | }; |
---|
1604 | |
---|
1605 | static const QString LinearBlendShader[2] = { |
---|
1606 | "GLSL_DEFINES" |
---|
1607 | "uniform GLSL_SAMPLER s_texture0;\n" |
---|
1608 | "uniform mat4 COLOUR_UNIFORM;\n" |
---|
1609 | "varying vec2 v_texcoord0;\n" |
---|
1610 | "void main(void)\n" |
---|
1611 | "{\n" |
---|
1612 | " vec2 line1 = vec2(v_texcoord0.x, clamp(v_texcoord0.y - %3, 0.0, %9));\n" |
---|
1613 | " vec2 line2 = vec2(v_texcoord0.x, clamp(v_texcoord0.y + %3, 0.0, %9));\n" |
---|
1614 | " vec4 yuva = GLSL_TEXTURE(s_texture0, v_texcoord0);\n" |
---|
1615 | " vec4 above = GLSL_TEXTURE(s_texture0, line2);\n" |
---|
1616 | " vec4 below = GLSL_TEXTURE(s_texture0, line1);\n" |
---|
1617 | " if (fract(v_texcoord0.y * %2) < 0.5)\n" |
---|
1618 | " yuva = mix(above, below, 0.5);\n" |
---|
1619 | "SELECT_COLUMN" |
---|
1620 | " gl_FragColor = vec4(yuva.arb, 1.0) * COLOUR_UNIFORM;\n" |
---|
1621 | "}\n", |
---|
1622 | |
---|
1623 | "GLSL_DEFINES" |
---|
1624 | "uniform GLSL_SAMPLER s_texture0;\n" |
---|
1625 | "uniform mat4 COLOUR_UNIFORM;\n" |
---|
1626 | "varying vec2 v_texcoord0;\n" |
---|
1627 | "void main(void)\n" |
---|
1628 | "{\n" |
---|
1629 | " vec2 line1 = vec2(v_texcoord0.x, clamp(v_texcoord0.y - %3, 0.0, %9));\n" |
---|
1630 | " vec2 line2 = vec2(v_texcoord0.x, clamp(v_texcoord0.y + %3, 0.0, %9));\n" |
---|
1631 | " vec4 yuva = GLSL_TEXTURE(s_texture0, v_texcoord0);\n" |
---|
1632 | " vec4 above = GLSL_TEXTURE(s_texture0, line2);\n" |
---|
1633 | " vec4 below = GLSL_TEXTURE(s_texture0, line1);\n" |
---|
1634 | " if (fract(v_texcoord0.y * %2) >= 0.5)\n" |
---|
1635 | " yuva = mix(above, below, 0.5);\n" |
---|
1636 | "SELECT_COLUMN" |
---|
1637 | " gl_FragColor = vec4(yuva.arb, 1.0) * COLOUR_UNIFORM;\n" |
---|
1638 | "}\n" |
---|
1639 | }; |
---|
1640 | |
---|
1641 | static const QString KernelShader[2] = { |
---|
1642 | "GLSL_DEFINES" |
---|
1643 | "uniform GLSL_SAMPLER s_texture0;\n" |
---|
1644 | "uniform GLSL_SAMPLER s_texture1;\n" |
---|
1645 | "uniform mat4 COLOUR_UNIFORM;\n" |
---|
1646 | "varying vec2 v_texcoord0;\n" |
---|
1647 | "void main(void)\n" |
---|
1648 | "{\n" |
---|
1649 | " vec4 yuva = GLSL_TEXTURE(s_texture0, v_texcoord0);\n" |
---|
1650 | " if (fract(v_texcoord0.y * %2) < 0.5)\n" |
---|
1651 | " {\n" |
---|
1652 | " vec2 twoup = vec2(v_texcoord0.x, clamp(v_texcoord0.y - %4, 0.0, %9));\n" |
---|
1653 | " vec2 twodown = vec2(v_texcoord0.x, clamp(v_texcoord0.y + %4, 0.0, %9));\n" |
---|
1654 | " vec2 oneup = vec2(v_texcoord0.x, clamp(v_texcoord0.y - %3, 0.0, %9));\n" |
---|
1655 | " vec2 onedown = vec2(v_texcoord0.x, clamp(v_texcoord0.y + %3, 0.0, %9));\n" |
---|
1656 | " vec4 line0 = GLSL_TEXTURE(s_texture0, twoup);\n" |
---|
1657 | " vec4 line1 = GLSL_TEXTURE(s_texture0, oneup);\n" |
---|
1658 | " vec4 line3 = GLSL_TEXTURE(s_texture0, onedown);\n" |
---|
1659 | " vec4 line4 = GLSL_TEXTURE(s_texture0, twodown);\n" |
---|
1660 | " vec4 line00 = GLSL_TEXTURE(s_texture1, twoup);\n" |
---|
1661 | " vec4 line20 = GLSL_TEXTURE(s_texture1, v_texcoord0);\n" |
---|
1662 | " vec4 line40 = GLSL_TEXTURE(s_texture1, twodown);\n" |
---|
1663 | " yuva = (yuva * 0.125);\n" |
---|
1664 | " yuva = (line20 * 0.125) + yuva;\n" |
---|
1665 | " yuva = (line1 * 0.5) + yuva;\n" |
---|
1666 | " yuva = (line3 * 0.5) + yuva;\n" |
---|
1667 | " yuva = (line0 * -0.0625) + yuva;\n" |
---|
1668 | " yuva = (line4 * -0.0625) + yuva;\n" |
---|
1669 | " yuva = (line00 * -0.0625) + yuva;\n" |
---|
1670 | " yuva = (line40 * -0.0625) + yuva;\n" |
---|
1671 | " }\n" |
---|
1672 | "SELECT_COLUMN" |
---|
1673 | " gl_FragColor = vec4(yuva.arb, 1.0) * COLOUR_UNIFORM;\n" |
---|
1674 | "}\n", |
---|
1675 | |
---|
1676 | "GLSL_DEFINES" |
---|
1677 | "uniform GLSL_SAMPLER s_texture0;\n" |
---|
1678 | "uniform GLSL_SAMPLER s_texture1;\n" |
---|
1679 | "uniform mat4 COLOUR_UNIFORM;\n" |
---|
1680 | "varying vec2 v_texcoord0;\n" |
---|
1681 | "void main(void)\n" |
---|
1682 | "{\n" |
---|
1683 | " vec4 yuva = GLSL_TEXTURE(s_texture1, v_texcoord0);\n" |
---|
1684 | " if (fract(v_texcoord0.y * %2) >= 0.5)\n" |
---|
1685 | " {\n" |
---|
1686 | " vec2 twoup = vec2(v_texcoord0.x, clamp(v_texcoord0.y - %4, 0.0, %9));\n" |
---|
1687 | " vec2 twodown = vec2(v_texcoord0.x, clamp(v_texcoord0.y + %4, 0.0, %9));\n" |
---|
1688 | " vec2 oneup = vec2(v_texcoord0.x, clamp(v_texcoord0.y - %3, 0.0, %9));\n" |
---|
1689 | " vec2 onedown = vec2(v_texcoord0.x, clamp(v_texcoord0.y + %3, 0.0, %9));\n" |
---|
1690 | " vec4 line0 = GLSL_TEXTURE(s_texture1, twoup);\n" |
---|
1691 | " vec4 line1 = GLSL_TEXTURE(s_texture1, oneup);\n" |
---|
1692 | " vec4 line3 = GLSL_TEXTURE(s_texture1, onedown);\n" |
---|
1693 | " vec4 line4 = GLSL_TEXTURE(s_texture1, twodown);\n" |
---|
1694 | " vec4 line00 = GLSL_TEXTURE(s_texture0, twoup);\n" |
---|
1695 | " vec4 line20 = GLSL_TEXTURE(s_texture0, v_texcoord0);\n" |
---|
1696 | " vec4 line40 = GLSL_TEXTURE(s_texture0, twodown);\n" |
---|
1697 | " yuva = (yuva * 0.125);\n" |
---|
1698 | " yuva = (line20 * 0.125) + yuva;\n" |
---|
1699 | " yuva = (line1 * 0.5) + yuva;\n" |
---|
1700 | " yuva = (line3 * 0.5) + yuva;\n" |
---|
1701 | " yuva = (line0 * -0.0625) + yuva;\n" |
---|
1702 | " yuva = (line4 * -0.0625) + yuva;\n" |
---|
1703 | " yuva = (line00 * -0.0625) + yuva;\n" |
---|
1704 | " yuva = (line40 * -0.0625) + yuva;\n" |
---|
1705 | " }\n" |
---|
1706 | "SELECT_COLUMN" |
---|
1707 | " gl_FragColor = vec4(yuva.arb, 1.0) * COLOUR_UNIFORM;\n" |
---|
1708 | "}\n" |
---|
1709 | }; |
---|
1710 | |
---|
1711 | static const QString BicubicShader = |
---|
1712 | "GLSL_DEFINES" |
---|
1713 | "uniform sampler2D s_texture0;\n" |
---|
1714 | "uniform sampler1D s_texture1;\n" |
---|
1715 | "varying vec2 v_texcoord0;\n" |
---|
1716 | "void main(void)\n" |
---|
1717 | "{\n" |
---|
1718 | " vec2 coord = (v_texcoord0 * vec2(%6, %7)) - vec2(0.5, 0.5);\n" |
---|
1719 | " vec4 parmx = texture1D(s_texture1, coord.x);\n" |
---|
1720 | " vec4 parmy = texture1D(s_texture1, coord.y);\n" |
---|
1721 | " vec2 e_x = vec2(%5, 0.0);\n" |
---|
1722 | " vec2 e_y = vec2(0.0, %3);\n" |
---|
1723 | " vec2 coord10 = v_texcoord0 + parmx.x * e_x;\n" |
---|
1724 | " vec2 coord00 = v_texcoord0 - parmx.y * e_x;\n" |
---|
1725 | " vec2 coord11 = coord10 + parmy.x * e_y;\n" |
---|
1726 | " vec2 coord01 = coord00 + parmy.x * e_y;\n" |
---|
1727 | " coord10 = coord10 - parmy.y * e_y;\n" |
---|
1728 | " coord00 = coord00 - parmy.y * e_y;\n" |
---|
1729 | " vec4 tex00 = texture2D(s_texture0, coord00);\n" |
---|
1730 | " vec4 tex10 = texture2D(s_texture0, coord10);\n" |
---|
1731 | " vec4 tex01 = texture2D(s_texture0, coord01);\n" |
---|
1732 | " vec4 tex11 = texture2D(s_texture0, coord11);\n" |
---|
1733 | " tex00 = mix(tex00, tex01, parmy.z);\n" |
---|
1734 | " tex10 = mix(tex10, tex11, parmy.z);\n" |
---|
1735 | " gl_FragColor = mix(tex00, tex10, parmx.z);\n" |
---|
1736 | "}\n"; |
---|
1737 | |
---|
1738 | static const QString DefaultFragmentShader = |
---|
1739 | "GLSL_DEFINES" |
---|
1740 | "uniform GLSL_SAMPLER s_texture0;\n" |
---|
1741 | "varying vec2 v_texcoord0;\n" |
---|
1742 | "void main(void)\n" |
---|
1743 | "{\n" |
---|
1744 | " vec4 color = GLSL_TEXTURE(s_texture0, v_texcoord0);\n" |
---|
1745 | " gl_FragColor = vec4(color.xyz, 1.0);\n" |
---|
1746 | "}\n"; |
---|
1747 | |
---|
1748 | static const QString YV12RGBVertexShader = |
---|
1749 | "//YV12RGBVertexShader\n" |
---|
1750 | "GLSL_DEFINES" |
---|
1751 | "attribute vec2 a_position;\n" |
---|
1752 | "attribute vec2 a_texcoord0;\n" |
---|
1753 | "varying vec2 v_texcoord0;\n" |
---|
1754 | "uniform mat4 u_projection;\n" |
---|
1755 | "void main() {\n" |
---|
1756 | " gl_Position = u_projection * vec4(a_position, 0.0, 1.0);\n" |
---|
1757 | " v_texcoord0 = a_texcoord0;\n" |
---|
1758 | "}\n"; |
---|
1759 | |
---|
1760 | #ifdef ANDROID |
---|
1761 | #define SAMPLEYVU "\ |
---|
1762 | vec3 sampleYVU(in GLSL_SAMPLER texture, vec2 texcoordY)\n\ |
---|
1763 | {\n\ |
---|
1764 | vec2 texcoordV, texcoordU;\n\ |
---|
1765 | texcoordV = vec2(texcoordY.s / 2.0, %HEIGHT% + texcoordY.t / 4.0);\n\ |
---|
1766 | texcoordU = vec2(texcoordV.s, texcoordV.t + %HEIGHT% / 4.0);\n\ |
---|
1767 | vec3 yvu;\n\ |
---|
1768 | yvu.r = GLSL_TEXTURE(texture, texcoordY).r;\n\ |
---|
1769 | yvu.g = GLSL_TEXTURE(texture, texcoordV).r;\n\ |
---|
1770 | yvu.b = GLSL_TEXTURE(texture, texcoordU).r;\n\ |
---|
1771 | return yvu;\n\ |
---|
1772 | }\n" |
---|
1773 | #else |
---|
1774 | |
---|
1775 | #define SAMPLEYVU "\ |
---|
1776 | vec3 sampleYVU(in GLSL_SAMPLER texture, vec2 texcoordY)\n\ |
---|
1777 | {\n\ |
---|
1778 | vec2 texcoordV, texcoordU;\n\ |
---|
1779 | texcoordV = vec2(texcoordY.s / 2.0, %HEIGHT% + texcoordY.t / 4.0);\n\ |
---|
1780 | texcoordU = vec2(texcoordV.s, texcoordV.t + %HEIGHT% / 4.0);\n\ |
---|
1781 | if (fract(texcoordY.t * %2) >= 0.5)\n\ |
---|
1782 | {\n\ |
---|
1783 | texcoordV.s += %WIDTH% / 2.0;\n\ |
---|
1784 | texcoordU.s += %WIDTH% / 2.0;\n\ |
---|
1785 | }\n\ |
---|
1786 | vec3 yvu;\n\ |
---|
1787 | yvu.r = GLSL_TEXTURE(texture, texcoordY).r;\n\ |
---|
1788 | yvu.g = GLSL_TEXTURE(texture, texcoordV).r;\n\ |
---|
1789 | yvu.b = GLSL_TEXTURE(texture, texcoordU).r;\n\ |
---|
1790 | return yvu;\n\ |
---|
1791 | }\n" |
---|
1792 | #endif |
---|
1793 | |
---|
1794 | static const QString YV12RGBFragmentShader = |
---|
1795 | "//YV12RGBFragmentShader\n" |
---|
1796 | "GLSL_DEFINES" |
---|
1797 | "uniform GLSL_SAMPLER s_texture0; // 4:1:1 YVU planar\n" |
---|
1798 | "uniform mat4 COLOUR_UNIFORM;\n" |
---|
1799 | "varying vec2 v_texcoord0;\n" |
---|
1800 | SAMPLEYVU |
---|
1801 | "void main(void)\n" |
---|
1802 | "{\n" |
---|
1803 | " vec3 yvu = sampleYVU(s_texture0, v_texcoord0);\n" |
---|
1804 | " gl_FragColor = vec4(yvu, 1.0) * COLOUR_UNIFORM;\n" |
---|
1805 | "}\n"; |
---|
1806 | |
---|
1807 | static const QString YV12RGBOneFieldVertexShader[2] = { |
---|
1808 | "//YV12RGBOneFieldVertexShader 1\n" |
---|
1809 | "GLSL_DEFINES" |
---|
1810 | "attribute vec2 a_position;\n" |
---|
1811 | "attribute vec2 a_texcoord0;\n" |
---|
1812 | "varying vec2 v_texcoord0;\n" |
---|
1813 | "uniform mat4 u_projection;\n" |
---|
1814 | "void main() {\n" |
---|
1815 | " gl_Position = u_projection * vec4(a_position, 0.0, 1.0);\n" |
---|
1816 | " v_texcoord0 = a_texcoord0;\n" |
---|
1817 | " if (fract(v_texcoord0.t * %2) >= 0.5)\n" |
---|
1818 | " v_texcoord0.t -= %3;\n" |
---|
1819 | "}\n", |
---|
1820 | |
---|
1821 | "//YV12RGBOneFieldVertexShader 2\n" |
---|
1822 | "GLSL_DEFINES" |
---|
1823 | "attribute vec2 a_position;\n" |
---|
1824 | "attribute vec2 a_texcoord0;\n" |
---|
1825 | "varying vec2 v_texcoord0;\n" |
---|
1826 | "uniform mat4 u_projection;\n" |
---|
1827 | "void main() {\n" |
---|
1828 | " gl_Position = u_projection * vec4(a_position, 0.0, 1.0);\n" |
---|
1829 | " v_texcoord0 = a_texcoord0;\n" |
---|
1830 | " if (fract(v_texcoord0.t * %2) < 0.5)\n" |
---|
1831 | " {\n" |
---|
1832 | " v_texcoord0.t += %3;\n" |
---|
1833 | " v_texcoord0.t = min(v_texcoord0.t, %HEIGHT% - %3);\n" |
---|
1834 | " }\n" |
---|
1835 | "}\n" |
---|
1836 | }; |
---|
1837 | |
---|
1838 | static const QString YV12RGBLinearBlendFragmentShader = |
---|
1839 | "//YV12RGBLinearBlendFragmentShader\n" |
---|
1840 | "GLSL_DEFINES" |
---|
1841 | "uniform GLSL_SAMPLER s_texture0; // 4:1:1 YVU planar\n" |
---|
1842 | "uniform mat4 COLOUR_UNIFORM;\n" |
---|
1843 | "varying vec2 v_texcoord0;\n" |
---|
1844 | SAMPLEYVU |
---|
1845 | "void main(void)\n" |
---|
1846 | "{\n" |
---|
1847 | " vec2 texcoord;\n" |
---|
1848 | " texcoord = v_texcoord0 - vec2(0.0, %3);\n" |
---|
1849 | " vec3 yvu1 = sampleYVU(s_texture0, texcoord);\n" |
---|
1850 | " vec3 yvu2 = sampleYVU(s_texture0, v_texcoord0);\n" |
---|
1851 | " texcoord = v_texcoord0 + vec2(0.0, %3);\n" |
---|
1852 | " texcoord.t = min(texcoord.t, %HEIGHT% - %3);\n" |
---|
1853 | " vec3 yvu3 = sampleYVU(s_texture0, texcoord);\n" |
---|
1854 | " vec3 yvu = (yvu1 + 2.0 * yvu2 + yvu3) / 4.0;\n" |
---|
1855 | " gl_FragColor = vec4(yvu, 1.0) * COLOUR_UNIFORM;\n" |
---|
1856 | "}\n"; |
---|
1857 | |
---|
1858 | #define KERNELYVU "\ |
---|
1859 | vec3 kernelYVU(in vec3 yvu)\n\ |
---|
1860 | {\n\ |
---|
1861 | vec2 twoup = v_texcoord0 - vec2(0.0, %4);\n\ |
---|
1862 | vec2 twodown = v_texcoord0 + vec2(0.0, %4);\n\ |
---|
1863 | twodown.t = min(twodown.t, %HEIGHT% - %3);\n\ |
---|
1864 | vec2 onedown = v_texcoord0 + vec2(0.0, %3);\n\ |
---|
1865 | onedown.t = min(onedown.t, %HEIGHT% - %3);\n\ |
---|
1866 | vec3 line0 = sampleYVU(s_texture0, twoup);\n\ |
---|
1867 | vec3 line1 = sampleYVU(s_texture0, v_texcoord0 - vec2(0.0, %3));\n\ |
---|
1868 | vec3 line3 = sampleYVU(s_texture0, onedown);\n\ |
---|
1869 | vec3 line4 = sampleYVU(s_texture0, twodown);\n\ |
---|
1870 | vec3 line00 = sampleYVU(s_texture1, twoup);\n\ |
---|
1871 | vec3 line20 = sampleYVU(s_texture1, v_texcoord0);\n\ |
---|
1872 | vec3 line40 = sampleYVU(s_texture1, twodown);\n\ |
---|
1873 | yvu *= 0.125;\n\ |
---|
1874 | yvu += line20 * 0.125;\n\ |
---|
1875 | yvu += line1 * 0.5;\n\ |
---|
1876 | yvu += line3 * 0.5;\n\ |
---|
1877 | yvu += line0 * -0.0625;\n\ |
---|
1878 | yvu += line4 * -0.0625;\n\ |
---|
1879 | yvu += line00 * -0.0625;\n\ |
---|
1880 | yvu += line40 * -0.0625;\n\ |
---|
1881 | return yvu;\n\ |
---|
1882 | }\n" |
---|
1883 | |
---|
1884 | static const QString YV12RGBKernelShader[2] = { |
---|
1885 | "//YV12RGBKernelShader 1\n" |
---|
1886 | "GLSL_DEFINES" |
---|
1887 | "uniform GLSL_SAMPLER s_texture0, s_texture1; // 4:1:1 YVU planar\n" |
---|
1888 | "uniform mat4 COLOUR_UNIFORM;\n" |
---|
1889 | "varying vec2 v_texcoord0;\n" |
---|
1890 | SAMPLEYVU |
---|
1891 | KERNELYVU |
---|
1892 | "void main(void)\n" |
---|
1893 | "{\n" |
---|
1894 | " vec3 yvu = sampleYVU(s_texture0, v_texcoord0);\n" |
---|
1895 | " if (fract(v_texcoord0.t * %2) >= 0.5)\n" |
---|
1896 | " yvu = kernelYVU(yvu);\n" |
---|
1897 | " gl_FragColor = vec4(yvu, 1.0) * COLOUR_UNIFORM;\n" |
---|
1898 | "}\n", |
---|
1899 | |
---|
1900 | "//YV12RGBKernelShader 2\n" |
---|
1901 | "GLSL_DEFINES" |
---|
1902 | "uniform GLSL_SAMPLER s_texture0, s_texture1; // 4:1:1 YVU planar\n" |
---|
1903 | "uniform mat4 COLOUR_UNIFORM;\n" |
---|
1904 | "varying vec2 v_texcoord0;\n" |
---|
1905 | SAMPLEYVU |
---|
1906 | KERNELYVU |
---|
1907 | "void main(void)\n" |
---|
1908 | "{\n" |
---|
1909 | " vec3 yvu = sampleYVU(s_texture0, v_texcoord0);\n" |
---|
1910 | " if (fract(v_texcoord0.t * %2) < 0.5)\n" |
---|
1911 | " yvu = kernelYVU(yvu);\n" |
---|
1912 | " gl_FragColor = vec4(yvu, 1.0) * COLOUR_UNIFORM;\n" |
---|
1913 | "}\n" |
---|
1914 | }; |
---|
1915 | |
---|
1916 | void OpenGLVideo::GetProgramStrings(QString &vertex, QString &fragment, |
---|
1917 | OpenGLFilterType filter, |
---|
1918 | QString deint, FrameScanType field) |
---|
1919 | { |
---|
1920 | uint bottom = field == kScan_Intr2ndField; |
---|
1921 | vertex = YUV2RGBVertexShader; |
---|
1922 | switch (filter) |
---|
1923 | { |
---|
1924 | case kGLFilterYUV2RGB: |
---|
1925 | { |
---|
1926 | if (deint == "openglonefield" || deint == "openglbobdeint") |
---|
1927 | fragment = OneFieldShader[bottom]; |
---|
1928 | else if (deint == "opengllinearblend" || |
---|
1929 | deint == "opengldoubleratelinearblend") |
---|
1930 | fragment = LinearBlendShader[bottom]; |
---|
1931 | else if (deint == "openglkerneldeint" || |
---|
1932 | deint == "opengldoubleratekerneldeint") |
---|
1933 | fragment = KernelShader[bottom]; |
---|
1934 | else |
---|
1935 | fragment = YUV2RGBFragmentShader; |
---|
1936 | |
---|
1937 | fragment.replace("SELECT_COLUMN", MYTHTV_UYVY == videoTextureType ? |
---|
1938 | SelectColumn : ""); |
---|
1939 | break; |
---|
1940 | } |
---|
1941 | case kGLFilterYV12RGB: |
---|
1942 | if (deint == "openglonefield" || deint == "openglbobdeint") |
---|
1943 | { |
---|
1944 | vertex = YV12RGBOneFieldVertexShader[bottom]; |
---|
1945 | fragment = YV12RGBFragmentShader; |
---|
1946 | } |
---|
1947 | else if (deint == "opengllinearblend" || |
---|
1948 | deint == "opengldoubleratelinearblend") |
---|
1949 | { |
---|
1950 | vertex = YV12RGBVertexShader; |
---|
1951 | fragment = YV12RGBLinearBlendFragmentShader; |
---|
1952 | } |
---|
1953 | else if (deint == "openglkerneldeint" || |
---|
1954 | deint == "opengldoubleratekerneldeint") |
---|
1955 | { |
---|
1956 | vertex = YV12RGBVertexShader; |
---|
1957 | fragment = YV12RGBKernelShader[bottom]; |
---|
1958 | } |
---|
1959 | else |
---|
1960 | { |
---|
1961 | vertex = YV12RGBVertexShader; |
---|
1962 | fragment = YV12RGBFragmentShader; |
---|
1963 | } |
---|
1964 | break; |
---|
1965 | case kGLFilterNone: |
---|
1966 | break; |
---|
1967 | case kGLFilterResize: |
---|
1968 | fragment = DefaultFragmentShader; |
---|
1969 | break; |
---|
1970 | case kGLFilterBicubic: |
---|
1971 | fragment = BicubicShader; |
---|
1972 | break; |
---|
1973 | default: |
---|
1974 | LOG(VB_PLAYBACK, LOG_ERR, LOC + "Unknown filter"); |
---|
1975 | break; |
---|
1976 | } |
---|
1977 | CustomiseProgramString(vertex); |
---|
1978 | CustomiseProgramString(fragment); |
---|
1979 | } |
---|