1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 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/. */
7 #include "GLBlitHelper.h"
11 #include "GLContext.h"
12 #include "GLScreenBuffer.h"
13 #include "GPUVideoImage.h"
14 #include "HeapCopyOfStackArray.h"
15 #include "ImageContainer.h"
16 #include "ScopedGLHelpers.h"
17 #include "mozilla/ArrayUtils.h"
18 #include "mozilla/Casting.h"
19 #include "mozilla/Preferences.h"
20 #include "mozilla/StaticPrefs_gfx.h"
21 #include "mozilla/UniquePtr.h"
22 #include "mozilla/gfx/Logging.h"
23 #include "mozilla/gfx/Matrix.h"
24 #include "mozilla/layers/ImageDataSerializer.h"
25 #include "mozilla/layers/LayersSurfaces.h"
27 #ifdef MOZ_WIDGET_ANDROID
28 # include "AndroidSurfaceTexture.h"
29 # include "GLImages.h"
30 # include "GLLibraryEGL.h"
34 # include "GLContextCGL.h"
35 # include "MacIOSurfaceImage.h"
39 # include "mozilla/layers/D3D11ShareHandleImage.h"
40 # include "mozilla/layers/D3D11TextureIMFSampleImage.h"
41 # include "mozilla/layers/D3D11YCbCrImage.h"
45 # include "mozilla/layers/DMABUFSurfaceImage.h"
46 # include "mozilla/widget/DMABufSurface.h"
47 # include "mozilla/widget/DMABufLibWrapper.h"
50 using mozilla::layers::PlanarYCbCrData
;
51 using mozilla::layers::PlanarYCbCrImage
;
58 static const char kFragPreprocHeader
[] = R
"(
60 #ifdef GL_FRAGMENT_PRECISION_HIGH
70 #if __VERSION__ >= 130
73 #define VARYING varying
75 #if __VERSION__ >= 120
84 const char* const kFragHeader_Tex2D
= R
"(
85 #define SAMPLER sampler2D
86 #if __VERSION__ >= 130
87 #define TEXTURE texture
89 #define TEXTURE texture2D
92 const char* const kFragHeader_Tex2DRect
= R
"(
93 #define SAMPLER sampler2DRect
94 #if __VERSION__ >= 130
95 #define TEXTURE texture
97 #define TEXTURE texture2DRect
100 const char* const kFragHeader_TexExt
= R
"(
101 #extension GL_OES_EGL_image_external : enable
102 #extension GL_OES_EGL_image_external_essl3 : enable
103 #if __VERSION__ >= 130
104 #define TEXTURE texture
106 #define TEXTURE texture2D
108 #define SAMPLER samplerExternalOES
113 static const char kFragDeclHeader
[] = R
"(
114 precision PRECISION float;
115 #if __VERSION__ >= 130
116 #define FRAG_COLOR oFragColor
119 #define FRAG_COLOR gl_FragColor
125 const char* const kFragSample_OnePlane
= R
"(
126 VARYING mediump vec2 vTexCoord0;
127 uniform PRECISION SAMPLER uTex0;
130 vec4 src = TEXTURE(uTex0, vTexCoord0);
134 // Ideally this would just change the color-matrix it uses, but this is
135 // acceptable debt for now.
136 // `extern` so that we don't get ifdef-dependent const-var-unused Werrors.
137 extern const char* const kFragSample_OnePlane_YUV_via_GBR
= R
"(
138 VARYING mediump vec2 vTexCoord0;
139 uniform PRECISION SAMPLER uTex0;
142 vec4 yuva = TEXTURE(uTex0, vTexCoord0).gbra;
146 const char* const kFragSample_TwoPlane
= R
"(
147 VARYING mediump vec2 vTexCoord0;
148 VARYING mediump vec2 vTexCoord1;
149 uniform PRECISION SAMPLER uTex0;
150 uniform PRECISION SAMPLER uTex1;
153 vec4 src = TEXTURE(uTex0, vTexCoord0); // Keep r and a.
154 src.gb = TEXTURE(uTex1, vTexCoord1).rg;
158 const char* const kFragSample_ThreePlane
= R
"(
159 VARYING mediump vec2 vTexCoord0;
160 VARYING mediump vec2 vTexCoord1;
161 uniform PRECISION SAMPLER uTex0;
162 uniform PRECISION SAMPLER uTex1;
163 uniform PRECISION SAMPLER uTex2;
166 vec4 src = TEXTURE(uTex0, vTexCoord0); // Keep r and a.
167 src.g = TEXTURE(uTex1, vTexCoord1).r;
168 src.b = TEXTURE(uTex2, vTexCoord1).r;
175 const char* const kFragConvert_None
= R
"(
176 vec3 metaConvert(vec3 src) {
180 const char* const kFragConvert_BGR
= R
"(
181 vec3 metaConvert(vec3 src) {
185 const char* const kFragConvert_ColorMatrix
= R
"(
186 uniform mediump MAT4X3 uColorMatrix;
188 vec3 metaConvert(vec3 src) {
189 return (uColorMatrix * vec4(src, 1)).rgb;
192 const char* const kFragConvert_ColorLut
= R
"(
193 uniform PRECISION sampler3D uColorLut;
195 vec3 metaConvert(vec3 src) {
196 // Half-texel filtering hazard!
197 // E.g. For texture size of 2,
198 // E.g. 0.5/2=0.25 is still sampling 100% of texel 0, 0% of texel 1.
199 // For the LUT, we need 0.5/2=0.25 to filter 25/75 texel 0 and 1.
200 // That is, we need to adjust our sampling point such that it's 0.25 of the
201 // way from texel 0's center to texel 1's center.
203 // v=0.0|N=2 => v'=0.5/2
204 // v=1.0|N=2 => v'=1.5/2
206 // v=0.0|N=3 => v'=0.5/3
207 // v=1.0|N=3 => v'=2.5/3
208 // => v' = ( 0.5 + v * (3 - 1) )/3
209 vec3 size = vec3(textureSize(uColorLut, 0));
210 src = (0.5 + src * (size - 1.0)) / size;
211 return texture(uColorLut, src).rgb;
217 const char* const kFragMixin_AlphaMultColors
= R
"(
218 #define MIXIN_ALPHA_MULT_COLORS
220 const char* const kFragMixin_AlphaClampColors
= R
"(
221 #define MIXIN_ALPHA_CLAMP_COLORS
223 const char* const kFragMixin_AlphaOne
= R
"(
224 #define MIXIN_ALPHA_ONE
229 static const char kFragBody
[] = R
"(
231 vec4 src = metaSample();
232 vec4 dst = vec4(metaConvert(src.rgb), src.a);
234 #ifdef MIXIN_ALPHA_MULT_COLORS
237 #ifdef MIXIN_ALPHA_CLAMP_COLORS
238 dst.rgb = min(dst.rgb, vec3(dst.a)); // Ensure valid premult-alpha colors.
240 #ifdef MIXIN_ALPHA_ONE
250 Mat3
SubRectMat3(const float x
, const float y
, const float w
, const float h
) {
260 Mat3
SubRectMat3(const gfx::IntRect
& subrect
, const gfx::IntSize
& size
) {
261 return SubRectMat3(float(subrect
.X()) / size
.width
,
262 float(subrect
.Y()) / size
.height
,
263 float(subrect
.Width()) / size
.width
,
264 float(subrect
.Height()) / size
.height
);
267 Mat3
SubRectMat3(const gfx::IntRect
& bigSubrect
, const gfx::IntSize
& smallSize
,
268 const gfx::IntSize
& divisors
) {
269 const float x
= float(bigSubrect
.X()) / divisors
.width
;
270 const float y
= float(bigSubrect
.Y()) / divisors
.height
;
271 const float w
= float(bigSubrect
.Width()) / divisors
.width
;
272 const float h
= float(bigSubrect
.Height()) / divisors
.height
;
273 return SubRectMat3(x
/ smallSize
.width
, y
/ smallSize
.height
,
274 w
/ smallSize
.width
, h
/ smallSize
.height
);
279 ScopedSaveMultiTex::ScopedSaveMultiTex(GLContext
* const gl
,
280 const std::vector
<uint8_t>& texUnits
,
281 const GLenum texTarget
)
284 mTexTarget(texTarget
),
285 mOldTexUnit(mGL
.GetIntAs
<GLenum
>(LOCAL_GL_ACTIVE_TEXTURE
)) {
286 MOZ_RELEASE_ASSERT(texUnits
.size() >= 1);
289 switch (mTexTarget
) {
290 case LOCAL_GL_TEXTURE_2D
:
291 texBinding
= LOCAL_GL_TEXTURE_BINDING_2D
;
293 case LOCAL_GL_TEXTURE_3D
:
294 texBinding
= LOCAL_GL_TEXTURE_BINDING_3D
;
296 case LOCAL_GL_TEXTURE_RECTANGLE
:
297 texBinding
= LOCAL_GL_TEXTURE_BINDING_RECTANGLE
;
299 case LOCAL_GL_TEXTURE_EXTERNAL
:
300 texBinding
= LOCAL_GL_TEXTURE_BINDING_EXTERNAL
;
303 gfxCriticalError() << "Unhandled texTarget: " << texTarget
;
307 for (const auto i
: IntegerRange(mTexUnits
.size())) {
308 const auto& unit
= mTexUnits
[i
];
309 mGL
.fActiveTexture(LOCAL_GL_TEXTURE0
+ unit
);
310 if (mGL
.IsSupported(GLFeature::sampler_objects
)) {
311 mOldTexSampler
[i
] = mGL
.GetIntAs
<GLuint
>(LOCAL_GL_SAMPLER_BINDING
);
312 mGL
.fBindSampler(unit
, 0);
314 mOldTex
[i
] = mGL
.GetIntAs
<GLuint
>(texBinding
);
318 ScopedSaveMultiTex::~ScopedSaveMultiTex() {
319 // Unbind in reverse order, in case we have repeats.
320 // Order matters because we unbound samplers during ctor, so now we have to
321 // make sure we rebind them in the right order.
322 for (const auto i
: Reversed(IntegerRange(mTexUnits
.size()))) {
323 const auto& unit
= mTexUnits
[i
];
324 mGL
.fActiveTexture(LOCAL_GL_TEXTURE0
+ unit
);
325 if (mGL
.IsSupported(GLFeature::sampler_objects
)) {
326 mGL
.fBindSampler(unit
, mOldTexSampler
[i
]);
328 mGL
.fBindTexture(mTexTarget
, mOldTex
[i
]);
330 mGL
.fActiveTexture(mOldTexUnit
);
335 class ScopedBindArrayBuffer final
{
338 const GLuint mOldVBO
;
340 ScopedBindArrayBuffer(GLContext
* const gl
, const GLuint vbo
)
341 : mGL(*gl
), mOldVBO(mGL
.GetIntAs
<GLuint
>(LOCAL_GL_ARRAY_BUFFER_BINDING
)) {
342 mGL
.fBindBuffer(LOCAL_GL_ARRAY_BUFFER
, vbo
);
345 ~ScopedBindArrayBuffer() { mGL
.fBindBuffer(LOCAL_GL_ARRAY_BUFFER
, mOldVBO
); }
350 class ScopedShader final
{
355 ScopedShader(GLContext
* const gl
, const GLenum shaderType
)
356 : mGL(*gl
), mName(mGL
.fCreateShader(shaderType
)) {}
358 ~ScopedShader() { mGL
.fDeleteShader(mName
); }
360 operator GLuint() const { return mName
; }
365 class SaveRestoreCurrentProgram final
{
370 explicit SaveRestoreCurrentProgram(GLContext
* const gl
)
371 : mGL(*gl
), mOld(mGL
.GetIntAs
<GLuint
>(LOCAL_GL_CURRENT_PROGRAM
)) {}
373 ~SaveRestoreCurrentProgram() { mGL
.fUseProgram(mOld
); }
378 class ScopedDrawBlitState final
{
383 const bool depthTest
;
385 const bool polyOffsFill
;
386 const bool sampleAToC
;
387 const bool sampleCover
;
390 Maybe
<bool> rasterizerDiscard
;
392 realGLboolean colorMask
[4];
396 ScopedDrawBlitState(GLContext
* const gl
, const gfx::IntSize
& destSize
)
398 blend(mGL
.PushEnabled(LOCAL_GL_BLEND
, false)),
399 cullFace(mGL
.PushEnabled(LOCAL_GL_CULL_FACE
, false)),
400 depthTest(mGL
.PushEnabled(LOCAL_GL_DEPTH_TEST
, false)),
401 dither(mGL
.PushEnabled(LOCAL_GL_DITHER
, true)),
402 polyOffsFill(mGL
.PushEnabled(LOCAL_GL_POLYGON_OFFSET_FILL
, false)),
403 sampleAToC(mGL
.PushEnabled(LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE
, false)),
404 sampleCover(mGL
.PushEnabled(LOCAL_GL_SAMPLE_COVERAGE
, false)),
405 scissor(mGL
.PushEnabled(LOCAL_GL_SCISSOR_TEST
, false)),
406 stencil(mGL
.PushEnabled(LOCAL_GL_STENCIL_TEST
, false)) {
407 if (mGL
.IsSupported(GLFeature::transform_feedback2
)) {
408 // Technically transform_feedback2 requires transform_feedback, which
409 // actually adds RASTERIZER_DISCARD.
411 Some(mGL
.PushEnabled(LOCAL_GL_RASTERIZER_DISCARD
, false));
414 mGL
.fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK
, colorMask
);
415 if (mGL
.IsSupported(GLFeature::draw_buffers_indexed
)) {
416 mGL
.fColorMaski(0, true, true, true, true);
418 mGL
.fColorMask(true, true, true, true);
421 mGL
.fGetIntegerv(LOCAL_GL_VIEWPORT
, viewport
);
422 MOZ_ASSERT(destSize
.width
&& destSize
.height
);
423 mGL
.fViewport(0, 0, destSize
.width
, destSize
.height
);
426 ~ScopedDrawBlitState() {
427 mGL
.SetEnabled(LOCAL_GL_BLEND
, blend
);
428 mGL
.SetEnabled(LOCAL_GL_CULL_FACE
, cullFace
);
429 mGL
.SetEnabled(LOCAL_GL_DEPTH_TEST
, depthTest
);
430 mGL
.SetEnabled(LOCAL_GL_DITHER
, dither
);
431 mGL
.SetEnabled(LOCAL_GL_POLYGON_OFFSET_FILL
, polyOffsFill
);
432 mGL
.SetEnabled(LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE
, sampleAToC
);
433 mGL
.SetEnabled(LOCAL_GL_SAMPLE_COVERAGE
, sampleCover
);
434 mGL
.SetEnabled(LOCAL_GL_SCISSOR_TEST
, scissor
);
435 mGL
.SetEnabled(LOCAL_GL_STENCIL_TEST
, stencil
);
436 if (rasterizerDiscard
) {
437 mGL
.SetEnabled(LOCAL_GL_RASTERIZER_DISCARD
, rasterizerDiscard
.value());
440 if (mGL
.IsSupported(GLFeature::draw_buffers_indexed
)) {
441 mGL
.fColorMaski(0, colorMask
[0], colorMask
[1], colorMask
[2],
444 mGL
.fColorMask(colorMask
[0], colorMask
[1], colorMask
[2], colorMask
[3]);
446 mGL
.fViewport(viewport
[0], viewport
[1], viewport
[2], viewport
[3]);
452 DrawBlitProg::DrawBlitProg(const GLBlitHelper
* const parent
, const GLuint prog
)
455 mLoc_uDestMatrix(mParent
.mGL
->fGetUniformLocation(mProg
, "uDestMatrix")),
456 mLoc_uTexMatrix0(mParent
.mGL
->fGetUniformLocation(mProg
, "uTexMatrix0")),
457 mLoc_uTexMatrix1(mParent
.mGL
->fGetUniformLocation(mProg
, "uTexMatrix1")),
458 mLoc_uColorLut(mParent
.mGL
->fGetUniformLocation(mProg
, "uColorLut")),
460 mParent
.mGL
->fGetUniformLocation(mProg
, "uColorMatrix")) {
461 const auto& gl
= mParent
.mGL
;
462 MOZ_GL_ASSERT(gl
, mLoc_uDestMatrix
!= -1); // Required
463 MOZ_GL_ASSERT(gl
, mLoc_uTexMatrix0
!= -1); // Required
464 if (mLoc_uColorMatrix
!= -1) {
465 MOZ_GL_ASSERT(gl
, mLoc_uTexMatrix1
!= -1);
467 int32_t numActiveUniforms
= 0;
468 gl
->fGetProgramiv(mProg
, LOCAL_GL_ACTIVE_UNIFORMS
, &numActiveUniforms
);
470 const size_t kMaxNameSize
= 32;
471 char name
[kMaxNameSize
] = {0};
474 for (int32_t i
= 0; i
< numActiveUniforms
; i
++) {
475 gl
->fGetActiveUniform(mProg
, i
, kMaxNameSize
, nullptr, &size
, &type
,
477 if (strcmp("uColorMatrix", name
) == 0) {
478 mType_uColorMatrix
= type
;
482 MOZ_GL_ASSERT(gl
, mType_uColorMatrix
);
486 DrawBlitProg::~DrawBlitProg() {
487 const auto& gl
= mParent
.mGL
;
488 if (!gl
->MakeCurrent()) return;
490 gl
->fDeleteProgram(mProg
);
493 void DrawBlitProg::Draw(const BaseArgs
& args
,
494 const YUVArgs
* const argsYUV
) const {
495 const auto& gl
= mParent
.mGL
;
497 const SaveRestoreCurrentProgram
oldProg(gl
);
498 gl
->fUseProgram(mProg
);
504 const auto& destRect
= args
.destRect
.value();
505 destMatrix
= SubRectMat3(destRect
.X() / args
.destSize
.width
,
506 destRect
.Y() / args
.destSize
.height
,
507 destRect
.Width() / args
.destSize
.width
,
508 destRect
.Height() / args
.destSize
.height
);
510 destMatrix
= Mat3::I();
514 // Apply the y-flip matrix before the destMatrix.
515 // That is, flip y=[0-1] to y=[1-0] before we restrict to the destRect.
516 destMatrix
.at(2, 1) += destMatrix
.at(1, 1);
517 destMatrix
.at(1, 1) *= -1.0f
;
520 gl
->fUniformMatrix3fv(mLoc_uDestMatrix
, 1, false, destMatrix
.m
);
521 gl
->fUniformMatrix3fv(mLoc_uTexMatrix0
, 1, false, args
.texMatrix0
.m
);
523 if (args
.texUnitForColorLut
) {
524 gl
->fUniform1i(mLoc_uColorLut
,
525 AssertedCast
<GLint
>(*args
.texUnitForColorLut
));
528 MOZ_ASSERT(bool(argsYUV
) == (mLoc_uColorMatrix
!= -1));
530 gl
->fUniformMatrix3fv(mLoc_uTexMatrix1
, 1, false, argsYUV
->texMatrix1
.m
);
532 if (mLoc_uColorMatrix
!= -1) {
533 const auto& colorMatrix
=
534 gfxUtils::YuvToRgbMatrix4x4ColumnMajor(*argsYUV
->colorSpaceForMatrix
);
536 switch (mType_uColorMatrix
) {
537 case LOCAL_GL_FLOAT_MAT4
:
538 gl
->fUniformMatrix4fv(mLoc_uColorMatrix
, 1, false, colorMatrix
);
540 case LOCAL_GL_FLOAT_MAT4x3
:
541 for (int x
= 0; x
< 4; x
++) {
542 for (int y
= 0; y
< 3; y
++) {
543 mat4x3
[3 * x
+ y
] = colorMatrix
[4 * x
+ y
];
546 gl
->fUniformMatrix4x3fv(mLoc_uColorMatrix
, 1, false, mat4x3
);
550 << "Bad mType_uColorMatrix: " << gfx::hexa(mType_uColorMatrix
);
557 const ScopedDrawBlitState
drawState(gl
, args
.destSize
);
563 GLint vaa0Normalized
;
567 if (mParent
.mQuadVAO
) {
568 oldVAO
= gl
->GetIntAs
<GLuint
>(LOCAL_GL_VERTEX_ARRAY_BINDING
);
569 gl
->fBindVertexArray(mParent
.mQuadVAO
);
572 gl
->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING
, (GLint
*)&vaa0Buffer
);
573 gl
->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED
, &vaa0Enabled
);
574 gl
->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE
, &vaa0Size
);
575 gl
->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE
, (GLint
*)&vaa0Type
);
576 gl
->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED
, &vaa0Normalized
);
577 gl
->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE
, (GLint
*)&vaa0Stride
);
578 gl
->fGetVertexAttribPointerv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER
, &vaa0Pointer
);
581 gl
->fEnableVertexAttribArray(0);
582 const ScopedBindArrayBuffer
bindVBO(gl
, mParent
.mQuadVBO
);
583 gl
->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT
, false, 0, 0);
586 gl
->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP
, 0, 4);
588 if (mParent
.mQuadVAO
) {
589 gl
->fBindVertexArray(oldVAO
);
592 gl
->fEnableVertexAttribArray(0);
594 gl
->fDisableVertexAttribArray(0);
596 // The current VERTEX_ARRAY_BINDING is not necessarily the same as the
597 // buffer set for vaa0Buffer.
598 const ScopedBindArrayBuffer
bindVBO(gl
, vaa0Buffer
);
599 gl
->fVertexAttribPointer(0, vaa0Size
, vaa0Type
, bool(vaa0Normalized
),
600 vaa0Stride
, vaa0Pointer
);
606 GLBlitHelper::GLBlitHelper(GLContext
* const gl
)
608 mDrawBlitProg_VertShader(mGL
->fCreateShader(LOCAL_GL_VERTEX_SHADER
))
609 //, mYuvUploads_YSize(0, 0)
610 //, mYuvUploads_UVSize(0, 0)
612 mGL
->fGenBuffers(1, &mQuadVBO
);
614 const ScopedBindArrayBuffer
bindVBO(mGL
, mQuadVBO
);
616 const float quadData
[] = {0, 0, 1, 0, 0, 1, 1, 1};
617 const HeapCopyOfStackArray
<float> heapQuadData(quadData
);
618 mGL
->fBufferData(LOCAL_GL_ARRAY_BUFFER
, heapQuadData
.ByteLength(),
619 heapQuadData
.Data(), LOCAL_GL_STATIC_DRAW
);
621 if (mGL
->IsSupported(GLFeature::vertex_array_object
)) {
622 const auto prev
= mGL
->GetIntAs
<GLuint
>(LOCAL_GL_VERTEX_ARRAY_BINDING
);
624 mGL
->fGenVertexArrays(1, &mQuadVAO
);
625 mGL
->fBindVertexArray(mQuadVAO
);
626 mGL
->fEnableVertexAttribArray(0);
627 mGL
->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT
, false, 0, 0);
629 mGL
->fBindVertexArray(prev
);
635 const auto glslVersion
= mGL
->ShadingLanguageVersion();
638 // If you run into problems on old android devices, it might be because some
639 // devices have OES_EGL_image_external but not OES_EGL_image_external_essl3.
640 // We could just use 100 in that particular case, but then we lose out on
641 // e.g. sampler3D. Let's just try 300 for now, and if we get regressions
642 // we'll add an essl100 fallback.
643 if (glslVersion
>= 300) {
644 mDrawBlitProg_VersionLine
= nsCString("#version 300 es\n");
646 mDrawBlitProg_VersionLine
= nsCString("#version 100\n");
648 } else if (glslVersion
>= 130) {
649 mDrawBlitProg_VersionLine
= nsPrintfCString("#version %u\n", glslVersion
);
652 const char kVertSource
[] =
654 #if __VERSION__ >= 130 \n\
655 #define ATTRIBUTE in \n\
656 #define VARYING out \n\
658 #define ATTRIBUTE attribute \n\
659 #define VARYING varying \n\
662 ATTRIBUTE vec2 aVert; // [0.0-1.0] \n\
664 uniform mat3 uDestMatrix; \n\
665 uniform mat3 uTexMatrix0; \n\
666 uniform mat3 uTexMatrix1; \n\
668 VARYING vec2 vTexCoord0; \n\
669 VARYING vec2 vTexCoord1; \n\
673 vec2 destPos = (uDestMatrix * vec3(aVert, 1.0)).xy; \n\
674 gl_Position = vec4(destPos * 2.0 - 1.0, 0.0, 1.0); \n\
676 vTexCoord0 = (uTexMatrix0 * vec3(aVert, 1.0)).xy; \n\
677 vTexCoord1 = (uTexMatrix1 * vec3(aVert, 1.0)).xy; \n\
680 const char* const parts
[] = {mDrawBlitProg_VersionLine
.get(), kVertSource
};
681 mGL
->fShaderSource(mDrawBlitProg_VertShader
, ArrayLength(parts
), parts
,
683 mGL
->fCompileShader(mDrawBlitProg_VertShader
);
686 GLBlitHelper::~GLBlitHelper() {
687 for (const auto& pair
: mDrawBlitProgs
) {
688 const auto& ptr
= pair
.second
;
691 mDrawBlitProgs
.clear();
693 if (!mGL
->MakeCurrent()) return;
695 mGL
->fDeleteShader(mDrawBlitProg_VertShader
);
696 mGL
->fDeleteBuffers(1, &mQuadVBO
);
699 mGL
->fDeleteVertexArrays(1, &mQuadVAO
);
705 const DrawBlitProg
* GLBlitHelper::GetDrawBlitProg(
706 const DrawBlitProg::Key
& key
) const {
707 const auto& res
= mDrawBlitProgs
.insert({key
, nullptr});
708 auto& pair
= *(res
.first
);
709 const auto& didInsert
= res
.second
;
711 pair
.second
= CreateDrawBlitProg(pair
.first
);
716 const DrawBlitProg
* GLBlitHelper::CreateDrawBlitProg(
717 const DrawBlitProg::Key
& key
) const {
718 const auto precisionPref
= StaticPrefs::gfx_blithelper_precision();
719 const char* precision
;
720 switch (precisionPref
) {
725 precision
= "mediump";
728 if (precisionPref
!= 2) {
729 NS_WARNING("gfx.blithelper.precision clamped to 2.");
735 nsPrintfCString
precisionLine("\n#define PRECISION %s\n", precision
);
739 const ScopedShader
fs(mGL
, LOCAL_GL_FRAGMENT_SHADER
);
741 std::vector
<const char*> parts
;
743 parts
.push_back(mDrawBlitProg_VersionLine
.get());
744 parts
.push_back(kFragPreprocHeader
);
745 if (key
.fragHeader
) {
746 parts
.push_back(key
.fragHeader
);
748 parts
.push_back(precisionLine
.BeginReading());
749 parts
.push_back(kFragDeclHeader
);
750 for (const auto& part
: key
.fragParts
) {
752 parts
.push_back(part
);
755 parts
.push_back(kFragBody
);
758 const auto PrintFragSource
= [&]() {
759 printf_stderr("Frag source:\n");
761 for (const auto& part
: parts
) {
762 printf_stderr("// parts[%i]:\n%s\n", i
, part
);
766 if (gfxEnv::MOZ_DUMP_GLBLITHELPER()) {
770 mGL
->fShaderSource(fs
, AssertedCast
<GLint
>(parts
.size()), parts
.data(),
772 mGL
->fCompileShader(fs
);
774 const auto prog
= mGL
->fCreateProgram();
775 mGL
->fAttachShader(prog
, mDrawBlitProg_VertShader
);
776 mGL
->fAttachShader(prog
, fs
);
778 mGL
->fBindAttribLocation(prog
, 0, "aVert");
779 mGL
->fLinkProgram(prog
);
782 mGL
->fGetProgramiv(prog
, LOCAL_GL_LINK_STATUS
, (GLint
*)&status
);
783 if (status
== LOCAL_GL_TRUE
|| mGL
->CheckContextLost()) {
784 const SaveRestoreCurrentProgram
oldProg(mGL
);
785 mGL
->fUseProgram(prog
);
786 const char* samplerNames
[] = {"uTex0", "uTex1", "uTex2"};
787 for (int i
= 0; i
< 3; i
++) {
788 const auto loc
= mGL
->fGetUniformLocation(prog
, samplerNames
[i
]);
789 if (loc
== -1) continue;
790 mGL
->fUniform1i(loc
, i
);
793 return new DrawBlitProg(this, prog
);
796 GLuint progLogLen
= 0;
797 mGL
->fGetProgramiv(prog
, LOCAL_GL_INFO_LOG_LENGTH
, (GLint
*)&progLogLen
);
798 const UniquePtr
<char[]> progLog(new char[progLogLen
+ 1]);
799 mGL
->fGetProgramInfoLog(prog
, progLogLen
, nullptr, progLog
.get());
800 progLog
[progLogLen
] = 0;
802 const auto& vs
= mDrawBlitProg_VertShader
;
804 mGL
->fGetShaderiv(vs
, LOCAL_GL_INFO_LOG_LENGTH
, (GLint
*)&vsLogLen
);
805 const UniquePtr
<char[]> vsLog(new char[vsLogLen
+ 1]);
806 mGL
->fGetShaderInfoLog(vs
, vsLogLen
, nullptr, vsLog
.get());
810 mGL
->fGetShaderiv(fs
, LOCAL_GL_INFO_LOG_LENGTH
, (GLint
*)&fsLogLen
);
811 const UniquePtr
<char[]> fsLog(new char[fsLogLen
+ 1]);
812 mGL
->fGetShaderInfoLog(fs
, fsLogLen
, nullptr, fsLog
.get());
816 std::string("DrawBlitProg link failed:\n") + "progLog: " + progLog
.get() +
817 "\n" + "vsLog: " + vsLog
.get() + "\n" + "fsLog: " + fsLog
.get() + "\n";
818 gfxCriticalError() << logs
;
822 MOZ_CRASH("DrawBlitProg link failed");
825 // -----------------------------------------------------------------------------
828 static RefPtr
<MacIOSurface
> LookupSurface(
829 const layers::SurfaceDescriptorMacIOSurface
& sd
) {
830 return MacIOSurface::LookupSurface(sd
.surfaceId(), !sd
.isOpaque(),
835 bool GLBlitHelper::BlitSdToFramebuffer(const layers::SurfaceDescriptor
& asd
,
836 const gfx::IntSize
& destSize
,
837 const OriginPos destOrigin
) {
838 const auto sdType
= asd
.type();
840 case layers::SurfaceDescriptor::TSurfaceDescriptorBuffer
: {
841 const auto& sd
= asd
.get_SurfaceDescriptorBuffer();
842 const auto yuvData
= PlanarYCbCrData::From(sd
);
844 gfxCriticalNote
<< "[GLBlitHelper::BlitSdToFramebuffer] "
845 "PlanarYCbCrData::From failed";
848 return BlitPlanarYCbCr(*yuvData
, destSize
, destOrigin
);
851 case layers::SurfaceDescriptor::TSurfaceDescriptorD3D10
: {
852 const auto& sd
= asd
.get_SurfaceDescriptorD3D10();
853 return BlitDescriptor(sd
, destSize
, destOrigin
);
855 case layers::SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr
: {
856 const auto& sd
= asd
.get_SurfaceDescriptorDXGIYCbCr();
857 return BlitDescriptor(sd
, destSize
, destOrigin
);
861 case layers::SurfaceDescriptor::TSurfaceDescriptorMacIOSurface
: {
862 const auto& sd
= asd
.get_SurfaceDescriptorMacIOSurface();
863 const auto surf
= LookupSurface(sd
);
865 NS_WARNING("LookupSurface(MacIOSurface) failed");
866 // Sometimes that frame for our handle gone already. That's life, for
870 return BlitImage(surf
, destSize
, destOrigin
);
873 #ifdef MOZ_WIDGET_ANDROID
874 case layers::SurfaceDescriptor::TSurfaceTextureDescriptor
: {
875 const auto& sd
= asd
.get_SurfaceTextureDescriptor();
876 auto surfaceTexture
= java::GeckoSurfaceTexture::Lookup(sd
.handle());
877 return Blit(surfaceTexture
, destSize
, destOrigin
);
880 #ifdef MOZ_WIDGET_GTK
881 case layers::SurfaceDescriptor::TSurfaceDescriptorDMABuf
: {
882 const auto& sd
= asd
.get_SurfaceDescriptorDMABuf();
883 RefPtr
<DMABufSurface
> surface
= DMABufSurface::CreateDMABufSurface(sd
);
884 return Blit(surface
, destSize
, destOrigin
);
892 bool GLBlitHelper::BlitImageToFramebuffer(layers::Image
* const srcImage
,
893 const gfx::IntSize
& destSize
,
894 const OriginPos destOrigin
) {
895 switch (srcImage
->GetFormat()) {
896 case ImageFormat::PLANAR_YCBCR
: {
897 const auto srcImage2
= static_cast<PlanarYCbCrImage
*>(srcImage
);
898 const auto data
= srcImage2
->GetData();
899 return BlitPlanarYCbCr(*data
, destSize
, destOrigin
);
902 case ImageFormat::SURFACE_TEXTURE
: {
903 #ifdef MOZ_WIDGET_ANDROID
904 auto* image
= srcImage
->AsSurfaceTextureImage();
906 auto surfaceTexture
=
907 java::GeckoSurfaceTexture::Lookup(image
->GetHandle());
908 return Blit(surfaceTexture
, destSize
, destOrigin
);
914 case ImageFormat::MAC_IOSURFACE
:
916 return BlitImage(srcImage
->AsMacIOSurfaceImage(), destSize
, destOrigin
);
922 case ImageFormat::GPU_VIDEO
:
923 return BlitImage(static_cast<layers::GPUVideoImage
*>(srcImage
), destSize
,
926 case ImageFormat::D3D11_SHARE_HANDLE_TEXTURE
:
927 return BlitImage(static_cast<layers::D3D11ShareHandleImage
*>(srcImage
),
928 destSize
, destOrigin
);
929 case ImageFormat::D3D11_TEXTURE_IMF_SAMPLE
:
931 static_cast<layers::D3D11TextureIMFSampleImage
*>(srcImage
), destSize
,
933 case ImageFormat::D3D11_YCBCR_IMAGE
:
934 return BlitImage(static_cast<layers::D3D11YCbCrImage
*>(srcImage
),
935 destSize
, destOrigin
);
936 case ImageFormat::D3D9_RGB32_TEXTURE
:
937 return false; // todo
938 case ImageFormat::DCOMP_SURFACE
:
941 case ImageFormat::D3D11_SHARE_HANDLE_TEXTURE
:
942 case ImageFormat::D3D11_TEXTURE_IMF_SAMPLE
:
943 case ImageFormat::D3D11_YCBCR_IMAGE
:
944 case ImageFormat::D3D9_RGB32_TEXTURE
:
945 case ImageFormat::DCOMP_SURFACE
:
949 case ImageFormat::DMABUF
:
950 #ifdef MOZ_WIDGET_GTK
951 return BlitImage(static_cast<layers::DMABUFSurfaceImage
*>(srcImage
),
952 destSize
, destOrigin
);
956 case ImageFormat::MOZ2D_SURFACE
:
957 case ImageFormat::NV_IMAGE
:
958 case ImageFormat::OVERLAY_IMAGE
:
959 case ImageFormat::SHARED_RGB
:
960 case ImageFormat::TEXTURE_WRAPPER
:
961 return false; // todo
966 // -------------------------------------
968 #ifdef MOZ_WIDGET_ANDROID
969 bool GLBlitHelper::Blit(const java::GeckoSurfaceTexture::Ref
& surfaceTexture
,
970 const gfx::IntSize
& destSize
,
971 const OriginPos destOrigin
) const {
972 if (!surfaceTexture
) {
976 const ScopedBindTextureUnit
boundTU(mGL
, LOCAL_GL_TEXTURE0
);
978 if (!surfaceTexture
->IsAttachedToGLContext((int64_t)mGL
)) {
981 mGL
->fGenTextures(1, &tex
);
983 if (NS_FAILED(surfaceTexture
->AttachToGLContext((int64_t)mGL
, tex
))) {
984 mGL
->fDeleteTextures(1, &tex
);
989 const ScopedBindTexture
savedTex(mGL
, surfaceTexture
->GetTexName(),
990 LOCAL_GL_TEXTURE_EXTERNAL
);
991 surfaceTexture
->UpdateTexImage();
992 const auto transform3
= Mat3::I();
993 // const auto srcOrigin = OriginPos::TopLeft;
994 const auto srcOrigin
= OriginPos::BottomLeft
;
995 const bool yFlip
= (srcOrigin
!= destOrigin
);
996 const auto& prog
= GetDrawBlitProg(
997 {kFragHeader_TexExt
, {kFragSample_OnePlane
, kFragConvert_None
}});
998 const DrawBlitProg::BaseArgs baseArgs
= {transform3
, yFlip
, destSize
,
1000 prog
->Draw(baseArgs
, nullptr);
1002 if (surfaceTexture
->IsSingleBuffer()) {
1003 surfaceTexture
->ReleaseTexImage();
1010 // -------------------------------------
1012 bool GuessDivisors(const gfx::IntSize
& ySize
, const gfx::IntSize
& uvSize
,
1013 gfx::IntSize
* const out_divisors
) {
1014 const gfx::IntSize
divisors((ySize
.width
== uvSize
.width
) ? 1 : 2,
1015 (ySize
.height
== uvSize
.height
) ? 1 : 2);
1016 if (uvSize
.width
* divisors
.width
!= ySize
.width
||
1017 uvSize
.height
* divisors
.height
!= ySize
.height
) {
1020 *out_divisors
= divisors
;
1024 bool GLBlitHelper::BlitPlanarYCbCr(const PlanarYCbCrData
& yuvData
,
1025 const gfx::IntSize
& destSize
,
1026 const OriginPos destOrigin
) {
1027 const auto& prog
= GetDrawBlitProg(
1028 {kFragHeader_Tex2D
, {kFragSample_ThreePlane
, kFragConvert_ColorMatrix
}});
1030 if (!mYuvUploads
[0]) {
1031 mGL
->fGenTextures(3, mYuvUploads
);
1032 const ScopedBindTexture
bindTex(mGL
, mYuvUploads
[0]);
1033 mGL
->TexParams_SetClampNoMips();
1034 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[1]);
1035 mGL
->TexParams_SetClampNoMips();
1036 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[2]);
1037 mGL
->TexParams_SetClampNoMips();
1042 auto ySize
= yuvData
.YDataSize();
1043 auto cbcrSize
= yuvData
.CbCrDataSize();
1044 if (yuvData
.mYSkip
|| yuvData
.mCbSkip
|| yuvData
.mCrSkip
|| ySize
.width
< 0 ||
1045 ySize
.height
< 0 || cbcrSize
.width
< 0 || cbcrSize
.height
< 0 ||
1046 yuvData
.mYStride
< 0 || yuvData
.mCbCrStride
< 0) {
1047 gfxCriticalError() << "Unusual PlanarYCbCrData: " << yuvData
.mYSkip
<< ","
1048 << yuvData
.mCbSkip
<< "," << yuvData
.mCrSkip
<< ", "
1049 << ySize
.width
<< "," << ySize
.height
<< ", "
1050 << cbcrSize
.width
<< "," << cbcrSize
.height
<< ", "
1051 << yuvData
.mYStride
<< "," << yuvData
.mCbCrStride
;
1055 gfx::IntSize divisors
;
1056 switch (yuvData
.mChromaSubsampling
) {
1057 case gfx::ChromaSubsampling::FULL
:
1058 divisors
= gfx::IntSize(1, 1);
1060 case gfx::ChromaSubsampling::HALF_WIDTH
:
1061 divisors
= gfx::IntSize(2, 1);
1063 case gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT
:
1064 divisors
= gfx::IntSize(2, 2);
1067 gfxCriticalError() << "Unknown chroma subsampling:"
1068 << int(yuvData
.mChromaSubsampling
);
1074 // RED textures aren't valid in GLES2, and ALPHA textures are not valid in
1075 // desktop GL Core Profiles. So use R8 textures on GL3.0+ and GLES3.0+, but
1076 // LUMINANCE/LUMINANCE/UNSIGNED_BYTE otherwise.
1077 GLenum internalFormat
;
1078 GLenum unpackFormat
;
1079 if (mGL
->IsAtLeast(gl::ContextProfile::OpenGLCore
, 300) ||
1080 mGL
->IsAtLeast(gl::ContextProfile::OpenGLES
, 300)) {
1081 internalFormat
= LOCAL_GL_R8
;
1082 unpackFormat
= LOCAL_GL_RED
;
1084 internalFormat
= LOCAL_GL_LUMINANCE
;
1085 unpackFormat
= LOCAL_GL_LUMINANCE
;
1090 const ScopedSaveMultiTex
saveTex(mGL
, {0, 1, 2}, LOCAL_GL_TEXTURE_2D
);
1091 const ResetUnpackState
reset(mGL
);
1092 const gfx::IntSize
yTexSize(yuvData
.mYStride
, yuvData
.YDataSize().height
);
1093 const gfx::IntSize
uvTexSize(yuvData
.mCbCrStride
,
1094 yuvData
.CbCrDataSize().height
);
1096 if (yTexSize
!= mYuvUploads_YSize
|| uvTexSize
!= mYuvUploads_UVSize
) {
1097 mYuvUploads_YSize
= yTexSize
;
1098 mYuvUploads_UVSize
= uvTexSize
;
1100 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
);
1101 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[0]);
1102 mGL
->fTexImage2D(LOCAL_GL_TEXTURE_2D
, 0, internalFormat
, yTexSize
.width
,
1103 yTexSize
.height
, 0, unpackFormat
, LOCAL_GL_UNSIGNED_BYTE
,
1105 for (int i
= 1; i
< 3; i
++) {
1106 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
+ i
);
1107 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[i
]);
1108 mGL
->fTexImage2D(LOCAL_GL_TEXTURE_2D
, 0, internalFormat
, uvTexSize
.width
,
1109 uvTexSize
.height
, 0, unpackFormat
,
1110 LOCAL_GL_UNSIGNED_BYTE
, nullptr);
1116 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
);
1117 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[0]);
1118 mGL
->fTexSubImage2D(LOCAL_GL_TEXTURE_2D
, 0, 0, 0, yTexSize
.width
,
1119 yTexSize
.height
, unpackFormat
, LOCAL_GL_UNSIGNED_BYTE
,
1121 mGL
->fActiveTexture(LOCAL_GL_TEXTURE1
);
1122 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[1]);
1123 mGL
->fTexSubImage2D(LOCAL_GL_TEXTURE_2D
, 0, 0, 0, uvTexSize
.width
,
1124 uvTexSize
.height
, unpackFormat
, LOCAL_GL_UNSIGNED_BYTE
,
1125 yuvData
.mCbChannel
);
1126 mGL
->fActiveTexture(LOCAL_GL_TEXTURE2
);
1127 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[2]);
1128 mGL
->fTexSubImage2D(LOCAL_GL_TEXTURE_2D
, 0, 0, 0, uvTexSize
.width
,
1129 uvTexSize
.height
, unpackFormat
, LOCAL_GL_UNSIGNED_BYTE
,
1130 yuvData
.mCrChannel
);
1134 const auto& clipRect
= yuvData
.mPictureRect
;
1135 const auto srcOrigin
= OriginPos::BottomLeft
;
1136 const bool yFlip
= (destOrigin
!= srcOrigin
);
1138 const DrawBlitProg::BaseArgs baseArgs
= {SubRectMat3(clipRect
, yTexSize
),
1139 yFlip
, destSize
, Nothing()};
1140 const DrawBlitProg::YUVArgs yuvArgs
= {
1141 SubRectMat3(clipRect
, uvTexSize
, divisors
), Some(yuvData
.mYUVColorSpace
)};
1142 prog
->Draw(baseArgs
, &yuvArgs
);
1146 // -------------------------------------
1149 bool GLBlitHelper::BlitImage(layers::MacIOSurfaceImage
* const srcImage
,
1150 const gfx::IntSize
& destSize
,
1151 const OriginPos destOrigin
) const {
1152 return BlitImage(srcImage
->GetSurface(), destSize
, destOrigin
);
1155 static std::string
IntAsAscii(const int x
) {
1158 auto u
= static_cast<unsigned int>(x
);
1160 str
.insert(str
.begin(), u
& 0xff);
1163 str
.insert(str
.begin(), '\'');
1164 str
.push_back('\'');
1168 bool GLBlitHelper::BlitImage(MacIOSurface
* const iosurf
,
1169 const gfx::IntSize
& destSize
,
1170 const OriginPos destOrigin
) const {
1172 gfxCriticalError() << "Null MacIOSurface for GLBlitHelper::BlitImage";
1175 if (mGL
->GetContextType() != GLContextType::CGL
) {
1180 const auto& srcOrigin
= OriginPos::BottomLeft
;
1182 DrawBlitProg::BaseArgs baseArgs
;
1183 baseArgs
.yFlip
= (destOrigin
!= srcOrigin
);
1184 baseArgs
.destSize
= destSize
;
1186 // TODO: The colorspace is known by the IOSurface, why override it?
1187 // See GetYUVColorSpace/GetFullRange()
1188 DrawBlitProg::YUVArgs yuvArgs
;
1189 yuvArgs
.colorSpaceForMatrix
= Some(iosurf
->GetYUVColorSpace());
1191 const DrawBlitProg::YUVArgs
* pYuvArgs
= nullptr;
1193 auto planes
= iosurf
->GetPlaneCount();
1195 planes
= 1; // Bad API. No cookie.
1198 const GLenum texTarget
= LOCAL_GL_TEXTURE_RECTANGLE
;
1200 std::vector
<uint8_t> texUnits
;
1201 for (uint8_t i
= 0; i
< planes
; i
++) {
1202 texUnits
.push_back(i
);
1204 const ScopedSaveMultiTex
saveTex(mGL
, texUnits
, texTarget
);
1205 const ScopedTexture
tex0(mGL
);
1206 const ScopedTexture
tex1(mGL
);
1207 const ScopedTexture
tex2(mGL
);
1208 const GLuint texs
[3] = {tex0
, tex1
, tex2
};
1210 const auto pixelFormat
= iosurf
->GetPixelFormat();
1211 if (mGL
->ShouldSpew()) {
1212 const auto formatStr
= IntAsAscii(pixelFormat
);
1213 printf_stderr("iosurf format: %s (0x%08x)\n", formatStr
.c_str(),
1217 const char* fragSample
;
1220 switch (pixelFormat
) {
1221 case kCVPixelFormatType_24RGB
:
1222 case kCVPixelFormatType_24BGR
:
1223 case kCVPixelFormatType_32ARGB
:
1224 case kCVPixelFormatType_32BGRA
:
1225 case kCVPixelFormatType_32ABGR
:
1226 case kCVPixelFormatType_32RGBA
:
1227 case kCVPixelFormatType_64ARGB
:
1228 case kCVPixelFormatType_48RGB
:
1229 fragSample
= kFragSample_OnePlane
;
1231 case kCVPixelFormatType_422YpCbCr8
:
1232 case kCVPixelFormatType_422YpCbCr8_yuvs
:
1233 fragSample
= kFragSample_OnePlane_YUV_via_GBR
;
1234 pYuvArgs
= &yuvArgs
;
1238 if (pixelFormat
<= 0xff) {
1239 str
= std::to_string(pixelFormat
);
1241 str
= IntAsAscii(pixelFormat
);
1243 gfxCriticalError() << "Unhandled kCVPixelFormatType_*: " << str
;
1244 // Probably YUV though
1245 fragSample
= kFragSample_OnePlane_YUV_via_GBR
;
1246 pYuvArgs
= &yuvArgs
;
1252 fragSample
= kFragSample_TwoPlane
;
1253 pYuvArgs
= &yuvArgs
;
1256 fragSample
= kFragSample_ThreePlane
;
1257 pYuvArgs
= &yuvArgs
;
1260 gfxCriticalError() << "Unexpected plane count: " << planes
;
1264 for (uint32_t p
= 0; p
< planes
; p
++) {
1265 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
+ p
);
1266 mGL
->fBindTexture(texTarget
, texs
[p
]);
1267 mGL
->TexParams_SetClampNoMips(texTarget
);
1269 if (!iosurf
->BindTexImage(mGL
, p
)) {
1274 const auto width
= iosurf
->GetDevicePixelWidth(p
);
1275 const auto height
= iosurf
->GetDevicePixelHeight(p
);
1276 baseArgs
.texMatrix0
= SubRectMat3(0, 0, width
, height
);
1277 yuvArgs
.texMatrix1
= SubRectMat3(0, 0, width
/ 2.0, height
/ 2.0);
1281 const auto& prog
= GetDrawBlitProg({
1282 kFragHeader_Tex2DRect
,
1283 {fragSample
, kFragConvert_ColorMatrix
},
1285 prog
->Draw(baseArgs
, pYuvArgs
);
1290 // -----------------------------------------------------------------------------
1292 void GLBlitHelper::DrawBlitTextureToFramebuffer(const GLuint srcTex
,
1293 const gfx::IntSize
& srcSize
,
1294 const gfx::IntSize
& destSize
,
1295 const GLenum srcTarget
,
1296 const bool srcIsBGRA
) const {
1297 const char* fragHeader
= nullptr;
1299 switch (srcTarget
) {
1300 case LOCAL_GL_TEXTURE_2D
:
1301 fragHeader
= kFragHeader_Tex2D
;
1302 texMatrix0
= Mat3::I();
1304 case LOCAL_GL_TEXTURE_RECTANGLE_ARB
:
1305 fragHeader
= kFragHeader_Tex2DRect
;
1306 texMatrix0
= SubRectMat3(0, 0, srcSize
.width
, srcSize
.height
);
1309 gfxCriticalError() << "Unexpected srcTarget: " << srcTarget
;
1312 const auto fragConvert
= srcIsBGRA
? kFragConvert_BGR
: kFragConvert_None
;
1313 const auto& prog
= GetDrawBlitProg({
1315 {kFragSample_OnePlane
, fragConvert
},
1318 const ScopedSaveMultiTex
saveTex(mGL
, {0}, srcTarget
);
1319 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
);
1320 mGL
->fBindTexture(srcTarget
, srcTex
);
1322 const bool yFlip
= false;
1323 const DrawBlitProg::BaseArgs baseArgs
= {texMatrix0
, yFlip
, destSize
,
1325 prog
->Draw(baseArgs
);
1328 // -----------------------------------------------------------------------------
1330 void GLBlitHelper::BlitFramebuffer(const gfx::IntRect
& srcRect
,
1331 const gfx::IntRect
& destRect
,
1332 GLuint filter
) const {
1333 MOZ_ASSERT(mGL
->IsSupported(GLFeature::framebuffer_blit
));
1335 const ScopedGLState
scissor(mGL
, LOCAL_GL_SCISSOR_TEST
, false);
1336 mGL
->fBlitFramebuffer(srcRect
.x
, srcRect
.y
, srcRect
.XMost(), srcRect
.YMost(),
1337 destRect
.x
, destRect
.y
, destRect
.XMost(),
1338 destRect
.YMost(), LOCAL_GL_COLOR_BUFFER_BIT
, filter
);
1343 void GLBlitHelper::BlitFramebufferToFramebuffer(const GLuint srcFB
,
1344 const GLuint destFB
,
1345 const gfx::IntRect
& srcRect
,
1346 const gfx::IntRect
& destRect
,
1347 GLuint filter
) const {
1348 MOZ_ASSERT(mGL
->IsSupported(GLFeature::framebuffer_blit
));
1349 MOZ_GL_ASSERT(mGL
, !srcFB
|| mGL
->fIsFramebuffer(srcFB
));
1350 MOZ_GL_ASSERT(mGL
, !destFB
|| mGL
->fIsFramebuffer(destFB
));
1352 const ScopedBindFramebuffer
boundFB(mGL
);
1353 mGL
->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
, srcFB
);
1354 mGL
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, destFB
);
1356 BlitFramebuffer(srcRect
, destRect
, filter
);
1359 void GLBlitHelper::BlitTextureToFramebuffer(GLuint srcTex
,
1360 const gfx::IntSize
& srcSize
,
1361 const gfx::IntSize
& destSize
,
1362 GLenum srcTarget
) const {
1363 MOZ_GL_ASSERT(mGL
, mGL
->fIsTexture(srcTex
));
1365 if (mGL
->IsSupported(GLFeature::framebuffer_blit
)) {
1366 const ScopedFramebufferForTexture
srcWrapper(mGL
, srcTex
, srcTarget
);
1367 const ScopedBindFramebuffer
bindFB(mGL
);
1368 mGL
->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
, srcWrapper
.FB());
1369 BlitFramebuffer(gfx::IntRect({}, srcSize
), gfx::IntRect({}, destSize
));
1373 DrawBlitTextureToFramebuffer(srcTex
, srcSize
, destSize
, srcTarget
);
1376 void GLBlitHelper::BlitFramebufferToTexture(GLuint destTex
,
1377 const gfx::IntSize
& srcSize
,
1378 const gfx::IntSize
& destSize
,
1379 GLenum destTarget
) const {
1380 MOZ_GL_ASSERT(mGL
, mGL
->fIsTexture(destTex
));
1382 if (mGL
->IsSupported(GLFeature::framebuffer_blit
)) {
1383 const ScopedFramebufferForTexture
destWrapper(mGL
, destTex
, destTarget
);
1384 const ScopedBindFramebuffer
bindFB(mGL
);
1385 mGL
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, destWrapper
.FB());
1386 BlitFramebuffer(gfx::IntRect({}, srcSize
), gfx::IntRect({}, destSize
));
1390 ScopedBindTexture
autoTex(mGL
, destTex
, destTarget
);
1391 ScopedGLState
scissor(mGL
, LOCAL_GL_SCISSOR_TEST
, false);
1392 mGL
->fCopyTexSubImage2D(destTarget
, 0, 0, 0, 0, 0, srcSize
.width
,
1396 void GLBlitHelper::BlitTextureToTexture(GLuint srcTex
, GLuint destTex
,
1397 const gfx::IntSize
& srcSize
,
1398 const gfx::IntSize
& destSize
,
1400 GLenum destTarget
) const {
1401 MOZ_GL_ASSERT(mGL
, mGL
->fIsTexture(srcTex
));
1402 MOZ_GL_ASSERT(mGL
, mGL
->fIsTexture(destTex
));
1404 // Start down the CopyTexSubImage path, not the DrawBlit path.
1405 const ScopedFramebufferForTexture
srcWrapper(mGL
, srcTex
, srcTarget
);
1406 const ScopedBindFramebuffer
bindFB(mGL
, srcWrapper
.FB());
1407 BlitFramebufferToTexture(destTex
, srcSize
, destSize
, destTarget
);
1410 // -------------------------------------
1412 bool GLBlitHelper::BlitImage(layers::GPUVideoImage
* const srcImage
,
1413 const gfx::IntSize
& destSize
,
1414 const OriginPos destOrigin
) const {
1415 const auto& data
= srcImage
->GetData();
1416 if (!data
) return false;
1418 const auto& desc
= data
->SD();
1422 layers::SurfaceDescriptorGPUVideo::TSurfaceDescriptorRemoteDecoder
);
1423 const auto& subdescUnion
=
1424 desc
.get_SurfaceDescriptorRemoteDecoder().subdesc();
1425 switch (subdescUnion
.type()) {
1426 #ifdef MOZ_WIDGET_GTK
1427 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorDMABuf
: {
1428 const auto& subdesc
= subdescUnion
.get_SurfaceDescriptorDMABuf();
1429 RefPtr
<DMABufSurface
> surface
=
1430 DMABufSurface::CreateDMABufSurface(subdesc
);
1431 return Blit(surface
, destSize
, destOrigin
);
1435 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorD3D10
: {
1436 const auto& subdesc
= subdescUnion
.get_SurfaceDescriptorD3D10();
1437 return BlitDescriptor(subdesc
, destSize
, destOrigin
);
1439 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorDXGIYCbCr
: {
1440 const auto& subdesc
= subdescUnion
.get_SurfaceDescriptorDXGIYCbCr();
1441 return BlitDescriptor(subdesc
, destSize
, destOrigin
);
1445 case layers::RemoteDecoderVideoSubDescriptor::
1446 TSurfaceDescriptorMacIOSurface
: {
1447 const auto& subdesc
= subdescUnion
.get_SurfaceDescriptorMacIOSurface();
1448 RefPtr
<MacIOSurface
> surface
= MacIOSurface::LookupSurface(
1449 subdesc
.surfaceId(), !subdesc
.isOpaque(), subdesc
.yUVColorSpace());
1450 MOZ_ASSERT(surface
);
1454 return BlitImage(surface
, destSize
, destOrigin
);
1457 case layers::RemoteDecoderVideoSubDescriptor::Tnull_t
:
1458 // This GPUVideoImage isn't directly readable outside the GPU process.
1462 gfxCriticalError() << "Unhandled subdesc type: "
1463 << uint32_t(subdescUnion
.type());
1468 // -------------------------------------
1469 #ifdef MOZ_WIDGET_GTK
1470 bool GLBlitHelper::Blit(DMABufSurface
* surface
, const gfx::IntSize
& destSize
,
1471 OriginPos destOrigin
) const {
1472 const auto& srcOrigin
= OriginPos::BottomLeft
;
1474 DrawBlitProg::BaseArgs baseArgs
;
1475 baseArgs
.yFlip
= (destOrigin
!= srcOrigin
);
1476 baseArgs
.destSize
= destSize
;
1478 // TODO: The colorspace is known by the DMABUFSurface, why override it?
1479 // See GetYUVColorSpace/GetFullRange()
1480 DrawBlitProg::YUVArgs yuvArgs
;
1481 yuvArgs
.colorSpaceForMatrix
= Some(surface
->GetYUVColorSpace());
1483 const DrawBlitProg::YUVArgs
* pYuvArgs
= nullptr;
1484 const auto planes
= surface
->GetTextureCount();
1487 // Ensure textures for all planes have been created.
1489 const bool createTextures
= [&]() {
1490 for (int i
= 0; i
< planes
; i
++) {
1491 if (!surface
->GetTexture(i
)) {
1498 bool didCreateTexture
= false;
1499 auto releaseTextures
= mozilla::MakeScopeExit([&] {
1500 if (didCreateTexture
) {
1501 surface
->ReleaseTextures();
1505 if (createTextures
) {
1506 for (int i
= 0; i
< planes
; i
++) {
1507 if (surface
->GetTexture(i
)) {
1510 if (!surface
->CreateTexture(mGL
, i
)) {
1511 LOGDMABUF(("GLBlitHelper::Blit(): Failed to create DMABuf textures."));
1514 didCreateTexture
= true;
1520 const GLenum texTarget
= LOCAL_GL_TEXTURE_2D
;
1522 std::vector
<uint8_t> texUnits
;
1523 for (uint8_t i
= 0; i
< planes
; i
++) {
1524 texUnits
.push_back(i
);
1526 const ScopedSaveMultiTex
saveTex(mGL
, texUnits
, texTarget
);
1527 const auto pixelFormat
= surface
->GetSurfaceType();
1529 const char* fragSample
;
1530 auto fragConvert
= kFragConvert_None
;
1531 switch (pixelFormat
) {
1532 case DMABufSurface::SURFACE_RGBA
:
1533 fragSample
= kFragSample_OnePlane
;
1535 case DMABufSurface::SURFACE_NV12
:
1536 fragSample
= kFragSample_TwoPlane
;
1537 pYuvArgs
= &yuvArgs
;
1538 fragConvert
= kFragConvert_ColorMatrix
;
1540 case DMABufSurface::SURFACE_YUV420
:
1541 fragSample
= kFragSample_ThreePlane
;
1542 pYuvArgs
= &yuvArgs
;
1543 fragConvert
= kFragConvert_ColorMatrix
;
1546 gfxCriticalError() << "Unexpected pixel format: " << pixelFormat
;
1550 for (const auto p
: IntegerRange(planes
)) {
1551 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
+ p
);
1552 mGL
->fBindTexture(texTarget
, surface
->GetTexture(p
));
1553 mGL
->TexParams_SetClampNoMips(texTarget
);
1556 // We support only NV12/YUV420 formats only with 1/2 texture scale.
1557 // We don't set cliprect as DMABus textures are created without padding.
1558 baseArgs
.texMatrix0
= SubRectMat3(0, 0, 1, 1);
1559 yuvArgs
.texMatrix1
= SubRectMat3(0, 0, 1, 1);
1562 GetDrawBlitProg({kFragHeader_Tex2D
, {fragSample
, fragConvert
}});
1563 prog
->Draw(baseArgs
, pYuvArgs
);
1568 bool GLBlitHelper::BlitImage(layers::DMABUFSurfaceImage
* srcImage
,
1569 const gfx::IntSize
& destSize
,
1570 OriginPos destOrigin
) const {
1571 DMABufSurface
* surface
= srcImage
->GetSurface();
1573 gfxCriticalError() << "Null DMABUFSurface for GLBlitHelper::BlitImage";
1576 return Blit(surface
, destSize
, destOrigin
);
1583 static void PushUnorm(uint32_t* const out
, const float inVal
) {
1584 const uint32_t mask
= (1 << N
) - 1;
1586 fval
= std::max(0.0f
, std::min(fval
, 1.0f
));
1588 fval
= roundf(fval
);
1589 auto ival
= static_cast<uint32_t>(fval
);
1596 static uint32_t toRgb10A2(const color::vec4
& val
) {
1599 PushUnorm
<2>(&ret
, val
.w());
1600 PushUnorm
<10>(&ret
, val
.z());
1601 PushUnorm
<10>(&ret
, val
.y());
1602 PushUnorm
<10>(&ret
, val
.x());
1606 std::shared_ptr
<gl::Texture
> GLBlitHelper::GetColorLutTex(
1607 const ColorLutKey
& key
) const {
1608 auto& weak
= mColorLutTexMap
[key
];
1609 auto strong
= weak
.lock();
1612 strong
= std::make_shared
<gl::Texture
>(gl
);
1615 const auto ct
= color::ColorspaceTransform::Create(key
.src
, key
.dst
);
1619 const auto minLutSize
= color::ivec3
{2};
1620 const auto maxLutSize
= color::ivec3
{256};
1621 auto lutSize
= minLutSize
;
1622 if (ct
.srcSpace
.yuv
) {
1623 lutSize
.x(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_y()));
1624 lutSize
.y(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_cb()));
1625 lutSize
.z(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_cr()));
1627 lutSize
.x(int(StaticPrefs::gfx_blithelper_lut_size_rgb_r()));
1628 lutSize
.y(int(StaticPrefs::gfx_blithelper_lut_size_rgb_g()));
1629 lutSize
.z(int(StaticPrefs::gfx_blithelper_lut_size_rgb_b()));
1631 lutSize
= max(minLutSize
, min(lutSize
, maxLutSize
)); // Clamp
1633 const auto lut
= ct
.ToLut3(lutSize
);
1634 const auto& size
= lut
.size
;
1638 constexpr GLenum target
= LOCAL_GL_TEXTURE_3D
;
1639 const auto bind
= gl::ScopedBindTexture(&gl
, strong
->name
, target
);
1640 gl
.fTexParameteri(target
, LOCAL_GL_TEXTURE_WRAP_S
, LOCAL_GL_CLAMP_TO_EDGE
);
1641 gl
.fTexParameteri(target
, LOCAL_GL_TEXTURE_WRAP_T
, LOCAL_GL_CLAMP_TO_EDGE
);
1642 gl
.fTexParameteri(target
, LOCAL_GL_TEXTURE_WRAP_R
, LOCAL_GL_CLAMP_TO_EDGE
);
1643 gl
.fTexParameteri(target
, LOCAL_GL_TEXTURE_MAG_FILTER
, LOCAL_GL_LINEAR
);
1644 gl
.fTexParameteri(target
, LOCAL_GL_TEXTURE_MIN_FILTER
, LOCAL_GL_LINEAR
);
1646 bool useFloat16
= true;
1648 // Use rgba16f, which we can thankfully upload as rgba32f
1649 static_assert(sizeof(color::vec4
) == sizeof(float) * 4);
1650 std::vector
<color::vec4
> uploadData
;
1651 uploadData
.reserve(lut
.data
.size());
1652 for (const auto& src
: lut
.data
) {
1653 const auto dst
= color::vec4
{src
, 1};
1654 uploadData
.push_back(dst
);
1657 gl
.fTexStorage3D(target
, 1, LOCAL_GL_RGBA16F
, size
.x(), size
.y(),
1659 gl
.fTexSubImage3D(target
, 0, 0, 0, 0, size
.x(), size
.y(), size
.z(),
1660 LOCAL_GL_RGBA
, LOCAL_GL_FLOAT
, uploadData
.data());
1663 std::vector
<uint32_t> uploadData
;
1664 uploadData
.reserve(lut
.data
.size());
1665 for (const auto& src
: lut
.data
) {
1666 const auto dst
= toRgb10A2({src
, 1});
1667 uploadData
.push_back(dst
);
1670 gl
.fTexStorage3D(target
, 1, LOCAL_GL_RGB10_A2
, size
.x(), size
.y(),
1672 gl
.fTexSubImage3D(target
, 0, 0, 0, 0, size
.x(), size
.y(), size
.z(),
1673 LOCAL_GL_RGBA
, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV
,
1681 } // namespace mozilla