1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=8 sts=4 et sw=4 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "gfxUtils.h"
8 #include "GLBlitHelper.h"
9 #include "GLContext.h"
10 #include "GLScreenBuffer.h"
11 #include "ScopedGLHelpers.h"
12 #include "mozilla/Preferences.h"
13 #include "ImageContainer.h"
14 #include "HeapCopyOfStackArray.h"
15 #include "mozilla/gfx/Matrix.h"
16 #include "mozilla/UniquePtr.h"
17 
18 #ifdef MOZ_WIDGET_ANDROID
19 #include "AndroidSurfaceTexture.h"
20 #include "GLImages.h"
21 #include "GLLibraryEGL.h"
22 #endif
23 
24 #ifdef XP_MACOSX
25 #include "MacIOSurfaceImage.h"
26 #include "GLContextCGL.h"
27 #endif
28 
29 using mozilla::layers::PlanarYCbCrImage;
30 using mozilla::layers::PlanarYCbCrData;
31 
32 namespace mozilla {
33 namespace gl {
34 
GLBlitHelper(GLContext * gl)35 GLBlitHelper::GLBlitHelper(GLContext* gl)
36     : mGL(gl)
37     , mTexBlit_Buffer(0)
38     , mTexBlit_VertShader(0)
39     , mTex2DBlit_FragShader(0)
40     , mTex2DRectBlit_FragShader(0)
41     , mTex2DBlit_Program(0)
42     , mTex2DRectBlit_Program(0)
43     , mYFlipLoc(-1)
44     , mTextureTransformLoc(-1)
45     , mTexExternalBlit_FragShader(0)
46     , mTexYUVPlanarBlit_FragShader(0)
47     , mTexNV12PlanarBlit_FragShader(0)
48     , mTexExternalBlit_Program(0)
49     , mTexYUVPlanarBlit_Program(0)
50     , mTexNV12PlanarBlit_Program(0)
51     , mFBO(0)
52     , mSrcTexY(0)
53     , mSrcTexCb(0)
54     , mSrcTexCr(0)
55     , mSrcTexEGL(0)
56     , mYTexScaleLoc(-1)
57     , mCbCrTexScaleLoc(-1)
58     , mYuvColorMatrixLoc(-1)
59     , mTexWidth(0)
60     , mTexHeight(0)
61     , mCurYScale(1.0f)
62     , mCurCbCrScale(1.0f)
63 {
64 }
65 
~GLBlitHelper()66 GLBlitHelper::~GLBlitHelper()
67 {
68     if (!mGL->MakeCurrent())
69         return;
70 
71     DeleteTexBlitProgram();
72 
73     GLuint tex[] = {
74         mSrcTexY,
75         mSrcTexCb,
76         mSrcTexCr,
77         mSrcTexEGL,
78     };
79 
80     mSrcTexY = mSrcTexCb = mSrcTexCr = mSrcTexEGL = 0;
81     mGL->fDeleteTextures(ArrayLength(tex), tex);
82 
83     if (mFBO) {
84         mGL->fDeleteFramebuffers(1, &mFBO);
85     }
86     mFBO = 0;
87 }
88 
89 // Allowed to be destructive of state we restore in functions below.
90 bool
InitTexQuadProgram(BlitType target)91 GLBlitHelper::InitTexQuadProgram(BlitType target)
92 {
93     const char kTexBlit_VertShaderSource[] = "\
94         #version 100                                  \n\
95         #ifdef GL_ES                                  \n\
96         precision mediump float;                      \n\
97         #endif                                        \n\
98         attribute vec2 aPosition;                     \n\
99                                                       \n\
100         uniform float uYflip;                         \n\
101         varying vec2 vTexCoord;                       \n\
102                                                       \n\
103         void main(void)                               \n\
104         {                                             \n\
105             vTexCoord = aPosition;                    \n\
106             vTexCoord.y = abs(vTexCoord.y - uYflip);  \n\
107             vec2 vertPos = aPosition * 2.0 - 1.0;     \n\
108             gl_Position = vec4(vertPos, 0.0, 1.0);    \n\
109         }                                             \n\
110     ";
111 
112     const char kTex2DBlit_FragShaderSource[] = "\
113         #version 100                                        \n\
114         #ifdef GL_ES                                        \n\
115         #ifdef GL_FRAGMENT_PRECISION_HIGH                   \n\
116             precision highp float;                          \n\
117         #else                                               \n\
118             precision mediump float;                        \n\
119         #endif                                              \n\
120         #endif                                              \n\
121         uniform sampler2D uTexUnit;                         \n\
122                                                             \n\
123         varying vec2 vTexCoord;                             \n\
124                                                             \n\
125         void main(void)                                     \n\
126         {                                                   \n\
127             gl_FragColor = texture2D(uTexUnit, vTexCoord);  \n\
128         }                                                   \n\
129     ";
130 
131     const char kTex2DRectBlit_FragShaderSource[] = "\
132         #version 100                                                  \n\
133         #ifdef GL_FRAGMENT_PRECISION_HIGH                             \n\
134             precision highp float;                                    \n\
135         #else                                                         \n\
136             precision mediump float;                                  \n\
137         #endif                                                        \n\
138                                                                       \n\
139         uniform sampler2D uTexUnit;                                   \n\
140         uniform vec2 uTexCoordMult;                                   \n\
141                                                                       \n\
142         varying vec2 vTexCoord;                                       \n\
143                                                                       \n\
144         void main(void)                                               \n\
145         {                                                             \n\
146             gl_FragColor = texture2DRect(uTexUnit,                    \n\
147                                          vTexCoord * uTexCoordMult);  \n\
148         }                                                             \n\
149     ";
150 #ifdef ANDROID /* MOZ_WIDGET_ANDROID */
151     const char kTexExternalBlit_FragShaderSource[] = "\
152         #version 100                                                    \n\
153         #extension GL_OES_EGL_image_external : require                  \n\
154         #ifdef GL_FRAGMENT_PRECISION_HIGH                               \n\
155             precision highp float;                                      \n\
156         #else                                                           \n\
157             precision mediump float;                                    \n\
158         #endif                                                          \n\
159         varying vec2 vTexCoord;                                         \n\
160         uniform mat4 uTextureTransform;                                 \n\
161         uniform samplerExternalOES uTexUnit;                            \n\
162                                                                         \n\
163         void main()                                                     \n\
164         {                                                               \n\
165             gl_FragColor = texture2D(uTexUnit,                          \n\
166                 (uTextureTransform * vec4(vTexCoord, 0.0, 1.0)).xy);    \n\
167         }                                                               \n\
168     ";
169 #endif
170     /* From Rec601:
171     [R]   [1.1643835616438356,  0.0,                 1.5960267857142858]      [ Y -  16]
172     [G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708]    x [Cb - 128]
173     [B]   [1.1643835616438356,  2.017232142857143,   8.862867620416422e-17]   [Cr - 128]
174 
175     For [0,1] instead of [0,255], and to 5 places:
176     [R]   [1.16438,  0.00000,  1.59603]   [ Y - 0.06275]
177     [G] = [1.16438, -0.39176, -0.81297] x [Cb - 0.50196]
178     [B]   [1.16438,  2.01723,  0.00000]   [Cr - 0.50196]
179 
180     From Rec709:
181     [R]   [1.1643835616438356,  4.2781193979771426e-17, 1.7927410714285714]     [ Y -  16]
182     [G] = [1.1643835616438358, -0.21324861427372963,   -0.532909328559444]    x [Cb - 128]
183     [B]   [1.1643835616438356,  2.1124017857142854,     0.0]                    [Cr - 128]
184 
185     For [0,1] instead of [0,255], and to 5 places:
186     [R]   [1.16438,  0.00000,  1.79274]   [ Y - 0.06275]
187     [G] = [1.16438, -0.21325, -0.53291] x [Cb - 0.50196]
188     [B]   [1.16438,  2.11240,  0.00000]   [Cr - 0.50196]
189     */
190     const char kTexYUVPlanarBlit_FragShaderSource[] = "\
191         #version 100                                                        \n\
192         #ifdef GL_ES                                                        \n\
193         precision mediump float;                                            \n\
194         #endif                                                              \n\
195         varying vec2 vTexCoord;                                             \n\
196         uniform sampler2D uYTexture;                                        \n\
197         uniform sampler2D uCbTexture;                                       \n\
198         uniform sampler2D uCrTexture;                                       \n\
199         uniform vec2 uYTexScale;                                            \n\
200         uniform vec2 uCbCrTexScale;                                         \n\
201         uniform mat3 uYuvColorMatrix;                                       \n\
202         void main()                                                         \n\
203         {                                                                   \n\
204             float y = texture2D(uYTexture, vTexCoord * uYTexScale).r;       \n\
205             float cb = texture2D(uCbTexture, vTexCoord * uCbCrTexScale).r;  \n\
206             float cr = texture2D(uCrTexture, vTexCoord * uCbCrTexScale).r;  \n\
207             y = y - 0.06275;                                                \n\
208             cb = cb - 0.50196;                                              \n\
209             cr = cr - 0.50196;                                              \n\
210             vec3 yuv = vec3(y, cb, cr);                                     \n\
211             gl_FragColor.rgb = uYuvColorMatrix * yuv;                       \n\
212             gl_FragColor.a = 1.0;                                           \n\
213         }                                                                   \n\
214     ";
215 
216 #ifdef XP_MACOSX
217     const char kTexNV12PlanarBlit_FragShaderSource[] = "\
218         #version 100                                                             \n\
219         #extension GL_ARB_texture_rectangle : require                            \n\
220         #ifdef GL_ES                                                             \n\
221         precision mediump float                                                  \n\
222         #endif                                                                   \n\
223         varying vec2 vTexCoord;                                                  \n\
224         uniform sampler2DRect uYTexture;                                         \n\
225         uniform sampler2DRect uCbCrTexture;                                      \n\
226         uniform vec2 uYTexScale;                                                 \n\
227         uniform vec2 uCbCrTexScale;                                              \n\
228         void main()                                                              \n\
229         {                                                                        \n\
230             float y = texture2DRect(uYTexture, vTexCoord * uYTexScale).r;        \n\
231             float cb = texture2DRect(uCbCrTexture, vTexCoord * uCbCrTexScale).r; \n\
232             float cr = texture2DRect(uCbCrTexture, vTexCoord * uCbCrTexScale).a; \n\
233             y = (y - 0.06275) * 1.16438;                                         \n\
234             cb = cb - 0.50196;                                                   \n\
235             cr = cr - 0.50196;                                                   \n\
236             gl_FragColor.r = y + cr * 1.59603;                                   \n\
237             gl_FragColor.g = y - 0.81297 * cr - 0.39176 * cb;                    \n\
238             gl_FragColor.b = y + cb * 2.01723;                                   \n\
239             gl_FragColor.a = 1.0;                                                \n\
240         }                                                                        \n\
241     ";
242 #endif
243 
244     bool success = false;
245 
246     GLuint* programPtr;
247     GLuint* fragShaderPtr;
248     const char* fragShaderSource;
249     switch (target) {
250     case ConvertEGLImage:
251     case BlitTex2D:
252         programPtr = &mTex2DBlit_Program;
253         fragShaderPtr = &mTex2DBlit_FragShader;
254         fragShaderSource = kTex2DBlit_FragShaderSource;
255         break;
256     case BlitTexRect:
257         programPtr = &mTex2DRectBlit_Program;
258         fragShaderPtr = &mTex2DRectBlit_FragShader;
259         fragShaderSource = kTex2DRectBlit_FragShaderSource;
260         break;
261 #ifdef ANDROID
262     case ConvertSurfaceTexture:
263     case ConvertGralloc:
264         programPtr = &mTexExternalBlit_Program;
265         fragShaderPtr = &mTexExternalBlit_FragShader;
266         fragShaderSource = kTexExternalBlit_FragShaderSource;
267         break;
268 #endif
269     case ConvertPlanarYCbCr:
270         programPtr = &mTexYUVPlanarBlit_Program;
271         fragShaderPtr = &mTexYUVPlanarBlit_FragShader;
272         fragShaderSource = kTexYUVPlanarBlit_FragShaderSource;
273         break;
274 #ifdef XP_MACOSX
275     case ConvertMacIOSurfaceImage:
276         programPtr = &mTexNV12PlanarBlit_Program;
277         fragShaderPtr = &mTexNV12PlanarBlit_FragShader;
278         fragShaderSource = kTexNV12PlanarBlit_FragShaderSource;
279         break;
280 #endif
281     default:
282         return false;
283     }
284 
285     GLuint& program = *programPtr;
286     GLuint& fragShader = *fragShaderPtr;
287 
288     // Use do-while(false) to let us break on failure
289     do {
290         if (program) {
291             // Already have it...
292             success = true;
293             break;
294         }
295 
296         if (!mTexBlit_Buffer) {
297 
298             /* CCW tri-strip:
299              * 2---3
300              * | \ |
301              * 0---1
302              */
303             GLfloat verts[] = {
304                 0.0f, 0.0f,
305                 1.0f, 0.0f,
306                 0.0f, 1.0f,
307                 1.0f, 1.0f
308             };
309             HeapCopyOfStackArray<GLfloat> vertsOnHeap(verts);
310 
311             MOZ_ASSERT(!mTexBlit_Buffer);
312             mGL->fGenBuffers(1, &mTexBlit_Buffer);
313             mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mTexBlit_Buffer);
314 
315             // Make sure we have a sane size.
316             mGL->fBufferData(LOCAL_GL_ARRAY_BUFFER, vertsOnHeap.ByteLength(), vertsOnHeap.Data(), LOCAL_GL_STATIC_DRAW);
317         }
318 
319         if (!mTexBlit_VertShader) {
320 
321             const char* vertShaderSource = kTexBlit_VertShaderSource;
322 
323             mTexBlit_VertShader = mGL->fCreateShader(LOCAL_GL_VERTEX_SHADER);
324             mGL->fShaderSource(mTexBlit_VertShader, 1, &vertShaderSource, nullptr);
325             mGL->fCompileShader(mTexBlit_VertShader);
326         }
327 
328         MOZ_ASSERT(!fragShader);
329         fragShader = mGL->fCreateShader(LOCAL_GL_FRAGMENT_SHADER);
330         mGL->fShaderSource(fragShader, 1, &fragShaderSource, nullptr);
331         mGL->fCompileShader(fragShader);
332 
333         program = mGL->fCreateProgram();
334         mGL->fAttachShader(program, mTexBlit_VertShader);
335         mGL->fAttachShader(program, fragShader);
336         mGL->fBindAttribLocation(program, 0, "aPosition");
337         mGL->fLinkProgram(program);
338 
339         if (GLContext::ShouldSpew()) {
340             GLint status = 0;
341             mGL->fGetShaderiv(mTexBlit_VertShader, LOCAL_GL_COMPILE_STATUS, &status);
342             if (status != LOCAL_GL_TRUE) {
343                 NS_ERROR("Vert shader compilation failed.");
344 
345                 GLint length = 0;
346                 mGL->fGetShaderiv(mTexBlit_VertShader, LOCAL_GL_INFO_LOG_LENGTH, &length);
347                 if (!length) {
348                     printf_stderr("No shader info log available.\n");
349                     break;
350                 }
351 
352                 auto buffer = MakeUnique<char[]>(length);
353                 mGL->fGetShaderInfoLog(mTexBlit_VertShader, length, nullptr, buffer.get());
354 
355                 printf_stderr("Shader info log (%d bytes): %s\n", length, buffer.get());
356                 break;
357             }
358 
359             status = 0;
360             mGL->fGetShaderiv(fragShader, LOCAL_GL_COMPILE_STATUS, &status);
361             if (status != LOCAL_GL_TRUE) {
362                 NS_ERROR("Frag shader compilation failed.");
363 
364                 GLint length = 0;
365                 mGL->fGetShaderiv(fragShader, LOCAL_GL_INFO_LOG_LENGTH, &length);
366                 if (!length) {
367                     printf_stderr("No shader info log available.\n");
368                     break;
369                 }
370 
371                 auto buffer = MakeUnique<char[]>(length);
372                 mGL->fGetShaderInfoLog(fragShader, length, nullptr, buffer.get());
373 
374                 printf_stderr("Shader info log (%d bytes): %s\n", length, buffer.get());
375                 break;
376             }
377         }
378 
379         GLint status = 0;
380         mGL->fGetProgramiv(program, LOCAL_GL_LINK_STATUS, &status);
381         if (status != LOCAL_GL_TRUE) {
382             if (GLContext::ShouldSpew()) {
383                 NS_ERROR("Linking blit program failed.");
384                 GLint length = 0;
385                 mGL->fGetProgramiv(program, LOCAL_GL_INFO_LOG_LENGTH, &length);
386                 if (!length) {
387                     printf_stderr("No program info log available.\n");
388                     break;
389                 }
390 
391                 auto buffer = MakeUnique<char[]>(length);
392                 mGL->fGetProgramInfoLog(program, length, nullptr, buffer.get());
393 
394                 printf_stderr("Program info log (%d bytes): %s\n", length, buffer.get());
395             }
396             break;
397         }
398 
399         // Cache and set attribute and uniform
400         mGL->fUseProgram(program);
401         switch (target) {
402 #ifdef ANDROID
403             case ConvertSurfaceTexture:
404             case ConvertGralloc:
405 #endif
406             case BlitTex2D:
407             case BlitTexRect:
408             case ConvertEGLImage: {
409                 GLint texUnitLoc = mGL->fGetUniformLocation(program, "uTexUnit");
410                 MOZ_ASSERT(texUnitLoc != -1, "uniform uTexUnit not found");
411                 mGL->fUniform1i(texUnitLoc, 0);
412                 break;
413             }
414             case ConvertPlanarYCbCr: {
415                 GLint texY = mGL->fGetUniformLocation(program, "uYTexture");
416                 GLint texCb = mGL->fGetUniformLocation(program, "uCbTexture");
417                 GLint texCr = mGL->fGetUniformLocation(program, "uCrTexture");
418                 mYTexScaleLoc = mGL->fGetUniformLocation(program, "uYTexScale");
419                 mCbCrTexScaleLoc = mGL->fGetUniformLocation(program, "uCbCrTexScale");
420                 mYuvColorMatrixLoc = mGL->fGetUniformLocation(program, "uYuvColorMatrix");
421 
422                 DebugOnly<bool> hasUniformLocations = texY != -1 &&
423                                                       texCb != -1 &&
424                                                       texCr != -1 &&
425                                                       mYTexScaleLoc != -1 &&
426                                                       mCbCrTexScaleLoc != -1 &&
427                                                       mYuvColorMatrixLoc != -1;
428                 MOZ_ASSERT(hasUniformLocations, "uniforms not found");
429 
430                 mGL->fUniform1i(texY, Channel_Y);
431                 mGL->fUniform1i(texCb, Channel_Cb);
432                 mGL->fUniform1i(texCr, Channel_Cr);
433                 break;
434             }
435             case ConvertMacIOSurfaceImage: {
436 #ifdef XP_MACOSX
437                 GLint texY = mGL->fGetUniformLocation(program, "uYTexture");
438                 GLint texCbCr = mGL->fGetUniformLocation(program, "uCbCrTexture");
439                 mYTexScaleLoc = mGL->fGetUniformLocation(program, "uYTexScale");
440                 mCbCrTexScaleLoc= mGL->fGetUniformLocation(program, "uCbCrTexScale");
441 
442                 DebugOnly<bool> hasUniformLocations = texY != -1 &&
443                                                       texCbCr != -1 &&
444                                                       mYTexScaleLoc != -1 &&
445                                                       mCbCrTexScaleLoc != -1;
446                 MOZ_ASSERT(hasUniformLocations, "uniforms not found");
447 
448                 mGL->fUniform1i(texY, Channel_Y);
449                 mGL->fUniform1i(texCbCr, Channel_Cb);
450 #endif
451                 break;
452             }
453             default:
454                 return false;
455         }
456         MOZ_ASSERT(mGL->fGetAttribLocation(program, "aPosition") == 0);
457         mYFlipLoc = mGL->fGetUniformLocation(program, "uYflip");
458         MOZ_ASSERT(mYFlipLoc != -1, "uniform: uYflip not found");
459         mTextureTransformLoc = mGL->fGetUniformLocation(program, "uTextureTransform");
460         if (mTextureTransformLoc >= 0) {
461             // Set identity matrix as default
462             gfx::Matrix4x4 identity;
463             mGL->fUniformMatrix4fv(mTextureTransformLoc, 1, false, &identity._11);
464         }
465         success = true;
466     } while (false);
467 
468     if (!success) {
469         // Clean up:
470         DeleteTexBlitProgram();
471         return false;
472     }
473 
474     mGL->fUseProgram(program);
475     mGL->fEnableVertexAttribArray(0);
476     mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mTexBlit_Buffer);
477     mGL->fVertexAttribPointer(0,
478                               2,
479                               LOCAL_GL_FLOAT,
480                               false,
481                               0,
482                               nullptr);
483     return true;
484 }
485 
486 bool
UseTexQuadProgram(BlitType target,const gfx::IntSize & srcSize)487 GLBlitHelper::UseTexQuadProgram(BlitType target, const gfx::IntSize& srcSize)
488 {
489     if (!InitTexQuadProgram(target)) {
490         return false;
491     }
492 
493     if (target == BlitTexRect) {
494         GLint texCoordMultLoc = mGL->fGetUniformLocation(mTex2DRectBlit_Program, "uTexCoordMult");
495         MOZ_ASSERT(texCoordMultLoc != -1, "uniform not found");
496         mGL->fUniform2f(texCoordMultLoc, srcSize.width, srcSize.height);
497     }
498 
499     return true;
500 }
501 
502 void
DeleteTexBlitProgram()503 GLBlitHelper::DeleteTexBlitProgram()
504 {
505     if (mTexBlit_Buffer) {
506         mGL->fDeleteBuffers(1, &mTexBlit_Buffer);
507         mTexBlit_Buffer = 0;
508     }
509     if (mTexBlit_VertShader) {
510         mGL->fDeleteShader(mTexBlit_VertShader);
511         mTexBlit_VertShader = 0;
512     }
513     if (mTex2DBlit_FragShader) {
514         mGL->fDeleteShader(mTex2DBlit_FragShader);
515         mTex2DBlit_FragShader = 0;
516     }
517     if (mTex2DRectBlit_FragShader) {
518         mGL->fDeleteShader(mTex2DRectBlit_FragShader);
519         mTex2DRectBlit_FragShader = 0;
520     }
521     if (mTex2DBlit_Program) {
522         mGL->fDeleteProgram(mTex2DBlit_Program);
523         mTex2DBlit_Program = 0;
524     }
525     if (mTex2DRectBlit_Program) {
526         mGL->fDeleteProgram(mTex2DRectBlit_Program);
527         mTex2DRectBlit_Program = 0;
528     }
529     if (mTexExternalBlit_FragShader) {
530         mGL->fDeleteShader(mTexExternalBlit_FragShader);
531         mTexExternalBlit_FragShader = 0;
532     }
533     if (mTexYUVPlanarBlit_FragShader) {
534         mGL->fDeleteShader(mTexYUVPlanarBlit_FragShader);
535         mTexYUVPlanarBlit_FragShader = 0;
536     }
537     if (mTexNV12PlanarBlit_FragShader) {
538         mGL->fDeleteShader(mTexNV12PlanarBlit_FragShader);
539         mTexNV12PlanarBlit_FragShader = 0;
540     }
541     if (mTexExternalBlit_Program) {
542         mGL->fDeleteProgram(mTexExternalBlit_Program);
543         mTexExternalBlit_Program = 0;
544     }
545     if (mTexYUVPlanarBlit_Program) {
546         mGL->fDeleteProgram(mTexYUVPlanarBlit_Program);
547         mTexYUVPlanarBlit_Program = 0;
548     }
549     if (mTexNV12PlanarBlit_Program) {
550         mGL->fDeleteProgram(mTexNV12PlanarBlit_Program);
551         mTexNV12PlanarBlit_Program = 0;
552     }
553 }
554 
555 void
BlitFramebufferToFramebuffer(GLuint srcFB,GLuint destFB,const gfx::IntSize & srcSize,const gfx::IntSize & destSize,bool internalFBs)556 GLBlitHelper::BlitFramebufferToFramebuffer(GLuint srcFB, GLuint destFB,
557                                            const gfx::IntSize& srcSize,
558                                            const gfx::IntSize& destSize,
559                                            bool internalFBs)
560 {
561     MOZ_ASSERT(!srcFB || mGL->fIsFramebuffer(srcFB));
562     MOZ_ASSERT(!destFB || mGL->fIsFramebuffer(destFB));
563 
564     MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit));
565 
566     ScopedBindFramebuffer boundFB(mGL);
567     ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false);
568 
569     if (internalFBs) {
570         mGL->Screen()->BindReadFB_Internal(srcFB);
571         mGL->Screen()->BindDrawFB_Internal(destFB);
572     } else {
573         mGL->BindReadFB(srcFB);
574         mGL->BindDrawFB(destFB);
575     }
576 
577     mGL->fBlitFramebuffer(0, 0,  srcSize.width,  srcSize.height,
578                           0, 0, destSize.width, destSize.height,
579                           LOCAL_GL_COLOR_BUFFER_BIT,
580                           LOCAL_GL_NEAREST);
581 }
582 
583 void
BlitFramebufferToFramebuffer(GLuint srcFB,GLuint destFB,const gfx::IntSize & srcSize,const gfx::IntSize & destSize,const GLFormats & srcFormats,bool internalFBs)584 GLBlitHelper::BlitFramebufferToFramebuffer(GLuint srcFB, GLuint destFB,
585                                            const gfx::IntSize& srcSize,
586                                            const gfx::IntSize& destSize,
587                                            const GLFormats& srcFormats,
588                                            bool internalFBs)
589 {
590     MOZ_ASSERT(!srcFB || mGL->fIsFramebuffer(srcFB));
591     MOZ_ASSERT(!destFB || mGL->fIsFramebuffer(destFB));
592 
593     if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
594         BlitFramebufferToFramebuffer(srcFB, destFB,
595                                      srcSize, destSize,
596                                      internalFBs);
597         return;
598     }
599 
600     GLuint tex = CreateTextureForOffscreen(mGL, srcFormats, srcSize);
601     MOZ_ASSERT(tex);
602 
603     BlitFramebufferToTexture(srcFB, tex, srcSize, srcSize, internalFBs);
604     BlitTextureToFramebuffer(tex, destFB, srcSize, destSize, internalFBs);
605 
606     mGL->fDeleteTextures(1, &tex);
607 }
608 
609 void
BindAndUploadYUVTexture(Channel which,uint32_t width,uint32_t height,void * data,bool needsAllocation)610 GLBlitHelper::BindAndUploadYUVTexture(Channel which,
611                                       uint32_t width,
612                                       uint32_t height,
613                                       void* data,
614                                       bool needsAllocation)
615 {
616     MOZ_ASSERT(which < Channel_Max, "Invalid channel!");
617     GLuint* srcTexArr[3] = {&mSrcTexY, &mSrcTexCb, &mSrcTexCr};
618     GLuint& tex = *srcTexArr[which];
619 
620     // RED textures aren't valid in GLES2, and ALPHA textures are not valid in desktop GL Core Profiles.
621     // So use R8 textures on GL3.0+ and GLES3.0+, but LUMINANCE/LUMINANCE/UNSIGNED_BYTE otherwise.
622     GLenum format;
623     GLenum internalFormat;
624     if (mGL->IsAtLeast(gl::ContextProfile::OpenGLCore, 300) ||
625         mGL->IsAtLeast(gl::ContextProfile::OpenGLES, 300)) {
626         format = LOCAL_GL_RED;
627         internalFormat = LOCAL_GL_R8;
628     } else {
629         format = LOCAL_GL_LUMINANCE;
630         internalFormat = LOCAL_GL_LUMINANCE;
631     }
632 
633     if (!tex) {
634         MOZ_ASSERT(needsAllocation);
635         tex = CreateTexture(mGL, internalFormat, format, LOCAL_GL_UNSIGNED_BYTE,
636                             gfx::IntSize(width, height), false);
637     }
638     mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + which);
639 
640     mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, tex);
641     if (!needsAllocation) {
642         mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D,
643                             0,
644                             0,
645                             0,
646                             width,
647                             height,
648                             format,
649                             LOCAL_GL_UNSIGNED_BYTE,
650                             data);
651     } else {
652         mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D,
653                          0,
654                          internalFormat,
655                          width,
656                          height,
657                          0,
658                          format,
659                          LOCAL_GL_UNSIGNED_BYTE,
660                          data);
661     }
662 }
663 
664 void
BindAndUploadEGLImage(EGLImage image,GLuint target)665 GLBlitHelper::BindAndUploadEGLImage(EGLImage image, GLuint target)
666 {
667     MOZ_ASSERT(image != EGL_NO_IMAGE, "Bad EGLImage");
668 
669     if (!mSrcTexEGL) {
670         mGL->fGenTextures(1, &mSrcTexEGL);
671         mGL->fBindTexture(target, mSrcTexEGL);
672         mGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
673         mGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
674         mGL->fTexParameteri(target, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);
675         mGL->fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST);
676     } else {
677         mGL->fBindTexture(target, mSrcTexEGL);
678     }
679     mGL->fEGLImageTargetTexture2D(target, image);
680 }
681 
682 #ifdef MOZ_WIDGET_ANDROID
683 
684 #define ATTACH_WAIT_MS 50
685 
686 bool
BlitSurfaceTextureImage(layers::SurfaceTextureImage * stImage)687 GLBlitHelper::BlitSurfaceTextureImage(layers::SurfaceTextureImage* stImage)
688 {
689     AndroidSurfaceTexture* surfaceTexture = stImage->GetSurfaceTexture();
690 
691     ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0);
692 
693     if (NS_FAILED(surfaceTexture->Attach(mGL, PR_MillisecondsToInterval(ATTACH_WAIT_MS))))
694         return false;
695 
696     // UpdateTexImage() changes the EXTERNAL binding, so save it here
697     // so we can restore it after.
698     int oldBinding = 0;
699     mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &oldBinding);
700 
701     surfaceTexture->UpdateTexImage();
702 
703     gfx::Matrix4x4 transform;
704     surfaceTexture->GetTransformMatrix(transform);
705 
706     mGL->fUniformMatrix4fv(mTextureTransformLoc, 1, false, &transform._11);
707     mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
708 
709     surfaceTexture->Detach();
710 
711     mGL->fBindTexture(LOCAL_GL_TEXTURE_EXTERNAL, oldBinding);
712     return true;
713 }
714 
715 bool
BlitEGLImageImage(layers::EGLImageImage * image)716 GLBlitHelper::BlitEGLImageImage(layers::EGLImageImage* image)
717 {
718     EGLImage eglImage = image->GetImage();
719     EGLSync eglSync = image->GetSync();
720 
721     if (eglSync) {
722         EGLint status = sEGLLibrary.fClientWaitSync(EGL_DISPLAY(), eglSync, 0, LOCAL_EGL_FOREVER);
723         if (status != LOCAL_EGL_CONDITION_SATISFIED) {
724             return false;
725         }
726     }
727 
728     ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0);
729 
730     int oldBinding = 0;
731     mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldBinding);
732 
733     BindAndUploadEGLImage(eglImage, LOCAL_GL_TEXTURE_2D);
734 
735     mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
736 
737     mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, oldBinding);
738     return true;
739 }
740 
741 #endif
742 
743 bool
BlitPlanarYCbCrImage(layers::PlanarYCbCrImage * yuvImage)744 GLBlitHelper::BlitPlanarYCbCrImage(layers::PlanarYCbCrImage* yuvImage)
745 {
746     ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0);
747     const PlanarYCbCrData* yuvData = yuvImage->GetData();
748 
749     bool needsAllocation = false;
750     if (mTexWidth != yuvData->mYStride || mTexHeight != yuvData->mYSize.height) {
751         mTexWidth = yuvData->mYStride;
752         mTexHeight = yuvData->mYSize.height;
753         needsAllocation = true;
754     }
755 
756     GLint oldTex[3];
757     for (int i = 0; i < 3; i++) {
758         mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
759         mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldTex[i]);
760     }
761     BindAndUploadYUVTexture(Channel_Y, yuvData->mYStride, yuvData->mYSize.height, yuvData->mYChannel, needsAllocation);
762     BindAndUploadYUVTexture(Channel_Cb, yuvData->mCbCrStride, yuvData->mCbCrSize.height, yuvData->mCbChannel, needsAllocation);
763     BindAndUploadYUVTexture(Channel_Cr, yuvData->mCbCrStride, yuvData->mCbCrSize.height, yuvData->mCrChannel, needsAllocation);
764 
765     if (needsAllocation) {
766         mGL->fUniform2f(mYTexScaleLoc, (float)yuvData->mYSize.width/yuvData->mYStride, 1.0f);
767         mGL->fUniform2f(mCbCrTexScaleLoc, (float)yuvData->mCbCrSize.width/yuvData->mCbCrStride, 1.0f);
768     }
769 
770     float* yuvToRgb = gfxUtils::Get3x3YuvColorMatrix(yuvData->mYUVColorSpace);
771     mGL->fUniformMatrix3fv(mYuvColorMatrixLoc, 1, 0, yuvToRgb);
772 
773     mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
774     for (int i = 0; i < 3; i++) {
775         mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
776         mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, oldTex[i]);
777     }
778     return true;
779 }
780 
781 #ifdef XP_MACOSX
782 bool
BlitMacIOSurfaceImage(layers::MacIOSurfaceImage * ioImage)783 GLBlitHelper::BlitMacIOSurfaceImage(layers::MacIOSurfaceImage* ioImage)
784 {
785     ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0);
786     MacIOSurface* surf = ioImage->GetSurface();
787 
788     GLint oldTex[2];
789     for (int i = 0; i < 2; i++) {
790         mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
791         mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldTex[i]);
792     }
793 
794     GLuint textures[2];
795     mGL->fGenTextures(2, textures);
796 
797     mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
798     mGL->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, textures[0]);
799     mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
800     mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
801     surf->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(mGL)->GetCGLContext(), 0);
802     mGL->fUniform2f(mYTexScaleLoc, surf->GetWidth(0), surf->GetHeight(0));
803 
804     mGL->fActiveTexture(LOCAL_GL_TEXTURE1);
805     mGL->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, textures[1]);
806     mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
807     mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
808     surf->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(mGL)->GetCGLContext(), 1);
809     mGL->fUniform2f(mCbCrTexScaleLoc, surf->GetWidth(1), surf->GetHeight(1));
810 
811     mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
812     for (int i = 0; i < 2; i++) {
813         mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
814         mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, oldTex[i]);
815     }
816 
817     mGL->fDeleteTextures(2, textures);
818     return true;
819 }
820 #endif
821 
822 bool
BlitImageToFramebuffer(layers::Image * srcImage,const gfx::IntSize & destSize,GLuint destFB,OriginPos destOrigin)823 GLBlitHelper::BlitImageToFramebuffer(layers::Image* srcImage,
824                                      const gfx::IntSize& destSize,
825                                      GLuint destFB,
826                                      OriginPos destOrigin)
827 {
828     ScopedGLDrawState autoStates(mGL);
829 
830     BlitType type;
831     OriginPos srcOrigin;
832 
833     switch (srcImage->GetFormat()) {
834     case ImageFormat::PLANAR_YCBCR:
835         type = ConvertPlanarYCbCr;
836         srcOrigin = OriginPos::BottomLeft;
837         break;
838 
839 #ifdef MOZ_WIDGET_ANDROID
840     case ImageFormat::SURFACE_TEXTURE:
841         type = ConvertSurfaceTexture;
842         srcOrigin = srcImage->AsSurfaceTextureImage()->GetOriginPos();
843         break;
844     case ImageFormat::EGLIMAGE:
845         type = ConvertEGLImage;
846         srcOrigin = srcImage->AsEGLImageImage()->GetOriginPos();
847         break;
848 #endif
849 #ifdef XP_MACOSX
850     case ImageFormat::MAC_IOSURFACE:
851         type = ConvertMacIOSurfaceImage;
852         srcOrigin = OriginPos::TopLeft;
853         break;
854 #endif
855 
856     default:
857         return false;
858     }
859 
860     bool init = InitTexQuadProgram(type);
861     if (!init) {
862         return false;
863     }
864 
865     const bool needsYFlip = (srcOrigin != destOrigin);
866     mGL->fUniform1f(mYFlipLoc, needsYFlip ? (float)1.0 : (float)0.0);
867 
868     ScopedBindFramebuffer boundFB(mGL, destFB);
869     mGL->fColorMask(LOCAL_GL_TRUE, LOCAL_GL_TRUE, LOCAL_GL_TRUE, LOCAL_GL_TRUE);
870     mGL->fViewport(0, 0, destSize.width, destSize.height);
871 
872     switch (type) {
873     case ConvertPlanarYCbCr: {
874             const auto saved = mGL->GetIntAs<GLint>(LOCAL_GL_UNPACK_ALIGNMENT);
875             mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1);
876             const auto ret = BlitPlanarYCbCrImage(static_cast<PlanarYCbCrImage*>(srcImage));
877             mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, saved);
878             return ret;
879         }
880 
881 #ifdef MOZ_WIDGET_ANDROID
882     case ConvertSurfaceTexture:
883         return BlitSurfaceTextureImage(static_cast<layers::SurfaceTextureImage*>(srcImage));
884 
885     case ConvertEGLImage:
886         return BlitEGLImageImage(static_cast<layers::EGLImageImage*>(srcImage));
887 #endif
888 
889 #ifdef XP_MACOSX
890     case ConvertMacIOSurfaceImage:
891         return BlitMacIOSurfaceImage(srcImage->AsMacIOSurfaceImage());
892 #endif
893 
894     default:
895         return false;
896     }
897 }
898 
899 bool
BlitImageToTexture(layers::Image * srcImage,const gfx::IntSize & destSize,GLuint destTex,GLenum destTarget,OriginPos destOrigin)900 GLBlitHelper::BlitImageToTexture(layers::Image* srcImage,
901                                  const gfx::IntSize& destSize,
902                                  GLuint destTex,
903                                  GLenum destTarget,
904                                  OriginPos destOrigin)
905 {
906     ScopedFramebufferForTexture autoFBForTex(mGL, destTex, destTarget);
907     if (!autoFBForTex.IsComplete())
908         return false;
909 
910     return BlitImageToFramebuffer(srcImage, destSize, autoFBForTex.FB(), destOrigin);
911 }
912 
913 void
BlitTextureToFramebuffer(GLuint srcTex,GLuint destFB,const gfx::IntSize & srcSize,const gfx::IntSize & destSize,GLenum srcTarget,bool internalFBs)914 GLBlitHelper::BlitTextureToFramebuffer(GLuint srcTex, GLuint destFB,
915                                        const gfx::IntSize& srcSize,
916                                        const gfx::IntSize& destSize,
917                                        GLenum srcTarget,
918                                        bool internalFBs)
919 {
920     MOZ_ASSERT(mGL->fIsTexture(srcTex));
921     MOZ_ASSERT(!destFB || mGL->fIsFramebuffer(destFB));
922 
923     if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
924         ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget);
925         MOZ_DIAGNOSTIC_ASSERT(srcWrapper.IsComplete());
926 
927         BlitFramebufferToFramebuffer(srcWrapper.FB(), destFB,
928                                      srcSize, destSize,
929                                      internalFBs);
930         return;
931     }
932 
933     DrawBlitTextureToFramebuffer(srcTex, destFB, srcSize, destSize, srcTarget,
934                                  internalFBs);
935 }
936 
937 
938 void
DrawBlitTextureToFramebuffer(GLuint srcTex,GLuint destFB,const gfx::IntSize & srcSize,const gfx::IntSize & destSize,GLenum srcTarget,bool internalFBs)939 GLBlitHelper::DrawBlitTextureToFramebuffer(GLuint srcTex, GLuint destFB,
940                                            const gfx::IntSize& srcSize,
941                                            const gfx::IntSize& destSize,
942                                            GLenum srcTarget,
943                                            bool internalFBs)
944 {
945     BlitType type;
946     switch (srcTarget) {
947     case LOCAL_GL_TEXTURE_2D:
948         type = BlitTex2D;
949         break;
950     case LOCAL_GL_TEXTURE_RECTANGLE_ARB:
951         type = BlitTexRect;
952         break;
953     default:
954         MOZ_CRASH("GFX: Fatal Error: Bad `srcTarget`.");
955         break;
956     }
957 
958     ScopedGLDrawState autoStates(mGL);
959     if (internalFBs) {
960         mGL->Screen()->BindFB_Internal(destFB);
961     } else {
962         mGL->BindFB(destFB);
963     }
964 
965     // Does destructive things to (only!) what we just saved above.
966     bool good = UseTexQuadProgram(type, srcSize);
967     if (!good) {
968         // We're up against the wall, so bail.
969         MOZ_DIAGNOSTIC_ASSERT(false,
970                               "Error: Failed to prepare to blit texture->framebuffer.\n");
971         mGL->fScissor(0, 0, destSize.width, destSize.height);
972         mGL->fColorMask(1, 1, 1, 1);
973         mGL->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
974         return;
975     }
976 
977     mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
978 }
979 
980 void
BlitFramebufferToTexture(GLuint srcFB,GLuint destTex,const gfx::IntSize & srcSize,const gfx::IntSize & destSize,GLenum destTarget,bool internalFBs)981 GLBlitHelper::BlitFramebufferToTexture(GLuint srcFB, GLuint destTex,
982                                        const gfx::IntSize& srcSize,
983                                        const gfx::IntSize& destSize,
984                                        GLenum destTarget,
985                                        bool internalFBs)
986 {
987     // On the Android 4.3 emulator, IsFramebuffer may return false incorrectly.
988     MOZ_ASSERT_IF(mGL->Renderer() != GLRenderer::AndroidEmulator, !srcFB || mGL->fIsFramebuffer(srcFB));
989     MOZ_ASSERT(mGL->fIsTexture(destTex));
990 
991     if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
992         ScopedFramebufferForTexture destWrapper(mGL, destTex, destTarget);
993 
994         BlitFramebufferToFramebuffer(srcFB, destWrapper.FB(),
995                                      srcSize, destSize,
996                                      internalFBs);
997         return;
998     }
999 
1000     ScopedBindTexture autoTex(mGL, destTex, destTarget);
1001 
1002     ScopedBindFramebuffer boundFB(mGL);
1003     if (internalFBs) {
1004         mGL->Screen()->BindFB_Internal(srcFB);
1005     } else {
1006         mGL->BindFB(srcFB);
1007     }
1008 
1009     ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false);
1010     mGL->fCopyTexSubImage2D(destTarget, 0,
1011                        0, 0,
1012                        0, 0,
1013                        srcSize.width, srcSize.height);
1014 }
1015 
1016 void
BlitTextureToTexture(GLuint srcTex,GLuint destTex,const gfx::IntSize & srcSize,const gfx::IntSize & destSize,GLenum srcTarget,GLenum destTarget)1017 GLBlitHelper::BlitTextureToTexture(GLuint srcTex, GLuint destTex,
1018                                    const gfx::IntSize& srcSize,
1019                                    const gfx::IntSize& destSize,
1020                                    GLenum srcTarget, GLenum destTarget)
1021 {
1022     MOZ_ASSERT(mGL->fIsTexture(srcTex));
1023     MOZ_ASSERT(mGL->fIsTexture(destTex));
1024 
1025     // Generally, just use the CopyTexSubImage path
1026     ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget);
1027 
1028     BlitFramebufferToTexture(srcWrapper.FB(), destTex,
1029                              srcSize, destSize, destTarget);
1030 }
1031 
1032 } // namespace gl
1033 } // namespace mozilla
1034