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"
49 using mozilla::layers::PlanarYCbCrData
;
50 using mozilla::layers::PlanarYCbCrImage
;
57 static const char kFragPreprocHeader
[] = R
"(
59 #ifdef GL_FRAGMENT_PRECISION_HIGH
69 #if __VERSION__ >= 130
72 #define VARYING varying
74 #if __VERSION__ >= 120
83 const char* const kFragHeader_Tex2D
= R
"(
84 #define SAMPLER sampler2D
85 #if __VERSION__ >= 130
86 #define TEXTURE texture
88 #define TEXTURE texture2D
91 const char* const kFragHeader_Tex2DRect
= R
"(
92 #define SAMPLER sampler2DRect
93 #if __VERSION__ >= 130
94 #define TEXTURE texture
96 #define TEXTURE texture2DRect
99 const char* const kFragHeader_TexExt
= R
"(
100 #extension GL_OES_EGL_image_external : enable
101 #extension GL_OES_EGL_image_external_essl3 : enable
102 #if __VERSION__ >= 130
103 #define TEXTURE texture
105 #define TEXTURE texture2D
107 #define SAMPLER samplerExternalOES
112 static const char kFragDeclHeader
[] = R
"(
113 precision PRECISION float;
114 #if __VERSION__ >= 130
115 #define FRAG_COLOR oFragColor
118 #define FRAG_COLOR gl_FragColor
124 const char* const kFragSample_OnePlane
= R
"(
125 VARYING mediump vec2 vTexCoord0;
126 uniform PRECISION SAMPLER uTex0;
129 vec4 src = TEXTURE(uTex0, vTexCoord0);
133 // Ideally this would just change the color-matrix it uses, but this is
134 // acceptable debt for now.
135 // `extern` so that we don't get ifdef-dependent const-var-unused Werrors.
136 extern const char* const kFragSample_OnePlane_YUV_via_GBR
= R
"(
137 VARYING mediump vec2 vTexCoord0;
138 uniform PRECISION SAMPLER uTex0;
141 vec4 yuva = TEXTURE(uTex0, vTexCoord0).gbra;
145 const char* const kFragSample_TwoPlane
= R
"(
146 VARYING mediump vec2 vTexCoord0;
147 VARYING mediump vec2 vTexCoord1;
148 uniform PRECISION SAMPLER uTex0;
149 uniform PRECISION SAMPLER uTex1;
152 vec4 src = TEXTURE(uTex0, vTexCoord0); // Keep r and a.
153 src.gb = TEXTURE(uTex1, vTexCoord1).rg;
157 const char* const kFragSample_ThreePlane
= R
"(
158 VARYING mediump vec2 vTexCoord0;
159 VARYING mediump vec2 vTexCoord1;
160 uniform PRECISION SAMPLER uTex0;
161 uniform PRECISION SAMPLER uTex1;
162 uniform PRECISION SAMPLER uTex2;
165 vec4 src = TEXTURE(uTex0, vTexCoord0); // Keep r and a.
166 src.g = TEXTURE(uTex1, vTexCoord1).r;
167 src.b = TEXTURE(uTex2, vTexCoord1).r;
174 const char* const kFragConvert_None
= R
"(
175 vec3 metaConvert(vec3 src) {
179 const char* const kFragConvert_BGR
= R
"(
180 vec3 metaConvert(vec3 src) {
184 const char* const kFragConvert_ColorMatrix
= R
"(
185 uniform mediump MAT4X3 uColorMatrix;
187 vec3 metaConvert(vec3 src) {
188 return (uColorMatrix * vec4(src, 1)).rgb;
191 const char* const kFragConvert_ColorLut
= R
"(
192 uniform PRECISION sampler3D uColorLut;
194 vec3 metaConvert(vec3 src) {
195 // Half-texel filtering hazard!
196 // E.g. For texture size of 2,
197 // E.g. 0.5/2=0.25 is still sampling 100% of texel 0, 0% of texel 1.
198 // For the LUT, we need 0.5/2=0.25 to filter 25/75 texel 0 and 1.
199 // That is, we need to adjust our sampling point such that it's 0.25 of the
200 // way from texel 0's center to texel 1's center.
202 // v=0.0|N=2 => v'=0.5/2
203 // v=1.0|N=2 => v'=1.5/2
205 // v=0.0|N=3 => v'=0.5/3
206 // v=1.0|N=3 => v'=2.5/3
207 // => v' = ( 0.5 + v * (3 - 1) )/3
208 vec3 size = vec3(textureSize(uColorLut, 0));
209 src = (0.5 + src * (size - 1.0)) / size;
210 return texture(uColorLut, src).rgb;
216 const char* const kFragMixin_AlphaMultColors
= R
"(
217 #define MIXIN_ALPHA_MULT_COLORS
219 const char* const kFragMixin_AlphaClampColors
= R
"(
220 #define MIXIN_ALPHA_CLAMP_COLORS
222 const char* const kFragMixin_AlphaOne
= R
"(
223 #define MIXIN_ALPHA_ONE
228 static const char kFragBody
[] = R
"(
230 vec4 src = metaSample();
231 vec4 dst = vec4(metaConvert(src.rgb), src.a);
233 #ifdef MIXIN_ALPHA_MULT_COLORS
236 #ifdef MIXIN_ALPHA_CLAMP_COLORS
237 dst.rgb = min(dst.rgb, vec3(dst.a)); // Ensure valid premult-alpha colors.
239 #ifdef MIXIN_ALPHA_ONE
249 Mat3
SubRectMat3(const float x
, const float y
, const float w
, const float h
) {
259 Mat3
SubRectMat3(const gfx::IntRect
& subrect
, const gfx::IntSize
& size
) {
260 return SubRectMat3(float(subrect
.X()) / size
.width
,
261 float(subrect
.Y()) / size
.height
,
262 float(subrect
.Width()) / size
.width
,
263 float(subrect
.Height()) / size
.height
);
266 Mat3
SubRectMat3(const gfx::IntRect
& bigSubrect
, const gfx::IntSize
& smallSize
,
267 const gfx::IntSize
& divisors
) {
268 const float x
= float(bigSubrect
.X()) / divisors
.width
;
269 const float y
= float(bigSubrect
.Y()) / divisors
.height
;
270 const float w
= float(bigSubrect
.Width()) / divisors
.width
;
271 const float h
= float(bigSubrect
.Height()) / divisors
.height
;
272 return SubRectMat3(x
/ smallSize
.width
, y
/ smallSize
.height
,
273 w
/ smallSize
.width
, h
/ smallSize
.height
);
278 ScopedSaveMultiTex::ScopedSaveMultiTex(GLContext
* const gl
,
279 const std::vector
<uint8_t>& texUnits
,
280 const GLenum texTarget
)
283 mTexTarget(texTarget
),
284 mOldTexUnit(mGL
.GetIntAs
<GLenum
>(LOCAL_GL_ACTIVE_TEXTURE
)) {
285 MOZ_RELEASE_ASSERT(texUnits
.size() >= 1);
288 switch (mTexTarget
) {
289 case LOCAL_GL_TEXTURE_2D
:
290 texBinding
= LOCAL_GL_TEXTURE_BINDING_2D
;
292 case LOCAL_GL_TEXTURE_3D
:
293 texBinding
= LOCAL_GL_TEXTURE_BINDING_3D
;
295 case LOCAL_GL_TEXTURE_RECTANGLE
:
296 texBinding
= LOCAL_GL_TEXTURE_BINDING_RECTANGLE
;
298 case LOCAL_GL_TEXTURE_EXTERNAL
:
299 texBinding
= LOCAL_GL_TEXTURE_BINDING_EXTERNAL
;
302 gfxCriticalError() << "Unhandled texTarget: " << texTarget
;
306 for (const auto i
: IntegerRange(mTexUnits
.size())) {
307 const auto& unit
= mTexUnits
[i
];
308 mGL
.fActiveTexture(LOCAL_GL_TEXTURE0
+ unit
);
309 if (mGL
.IsSupported(GLFeature::sampler_objects
)) {
310 mOldTexSampler
[i
] = mGL
.GetIntAs
<GLuint
>(LOCAL_GL_SAMPLER_BINDING
);
311 mGL
.fBindSampler(unit
, 0);
313 mOldTex
[i
] = mGL
.GetIntAs
<GLuint
>(texBinding
);
317 ScopedSaveMultiTex::~ScopedSaveMultiTex() {
318 // Unbind in reverse order, in case we have repeats.
319 // Order matters because we unbound samplers during ctor, so now we have to
320 // make sure we rebind them in the right order.
321 for (const auto i
: Reversed(IntegerRange(mTexUnits
.size()))) {
322 const auto& unit
= mTexUnits
[i
];
323 mGL
.fActiveTexture(LOCAL_GL_TEXTURE0
+ unit
);
324 if (mGL
.IsSupported(GLFeature::sampler_objects
)) {
325 mGL
.fBindSampler(unit
, mOldTexSampler
[i
]);
327 mGL
.fBindTexture(mTexTarget
, mOldTex
[i
]);
329 mGL
.fActiveTexture(mOldTexUnit
);
334 class ScopedBindArrayBuffer final
{
337 const GLuint mOldVBO
;
339 ScopedBindArrayBuffer(GLContext
* const gl
, const GLuint vbo
)
340 : mGL(*gl
), mOldVBO(mGL
.GetIntAs
<GLuint
>(LOCAL_GL_ARRAY_BUFFER_BINDING
)) {
341 mGL
.fBindBuffer(LOCAL_GL_ARRAY_BUFFER
, vbo
);
344 ~ScopedBindArrayBuffer() { mGL
.fBindBuffer(LOCAL_GL_ARRAY_BUFFER
, mOldVBO
); }
349 class ScopedShader final
{
354 ScopedShader(GLContext
* const gl
, const GLenum shaderType
)
355 : mGL(*gl
), mName(mGL
.fCreateShader(shaderType
)) {}
357 ~ScopedShader() { mGL
.fDeleteShader(mName
); }
359 operator GLuint() const { return mName
; }
364 class SaveRestoreCurrentProgram final
{
369 explicit SaveRestoreCurrentProgram(GLContext
* const gl
)
370 : mGL(*gl
), mOld(mGL
.GetIntAs
<GLuint
>(LOCAL_GL_CURRENT_PROGRAM
)) {}
372 ~SaveRestoreCurrentProgram() { mGL
.fUseProgram(mOld
); }
377 class ScopedDrawBlitState final
{
382 const bool depthTest
;
384 const bool polyOffsFill
;
385 const bool sampleAToC
;
386 const bool sampleCover
;
389 Maybe
<bool> rasterizerDiscard
;
391 realGLboolean colorMask
[4];
395 ScopedDrawBlitState(GLContext
* const gl
, const gfx::IntSize
& destSize
)
397 blend(mGL
.PushEnabled(LOCAL_GL_BLEND
, false)),
398 cullFace(mGL
.PushEnabled(LOCAL_GL_CULL_FACE
, false)),
399 depthTest(mGL
.PushEnabled(LOCAL_GL_DEPTH_TEST
, false)),
400 dither(mGL
.PushEnabled(LOCAL_GL_DITHER
, true)),
401 polyOffsFill(mGL
.PushEnabled(LOCAL_GL_POLYGON_OFFSET_FILL
, false)),
402 sampleAToC(mGL
.PushEnabled(LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE
, false)),
403 sampleCover(mGL
.PushEnabled(LOCAL_GL_SAMPLE_COVERAGE
, false)),
404 scissor(mGL
.PushEnabled(LOCAL_GL_SCISSOR_TEST
, false)),
405 stencil(mGL
.PushEnabled(LOCAL_GL_STENCIL_TEST
, false)) {
406 if (mGL
.IsSupported(GLFeature::transform_feedback2
)) {
407 // Technically transform_feedback2 requires transform_feedback, which
408 // actually adds RASTERIZER_DISCARD.
410 Some(mGL
.PushEnabled(LOCAL_GL_RASTERIZER_DISCARD
, false));
413 mGL
.fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK
, colorMask
);
414 if (mGL
.IsSupported(GLFeature::draw_buffers_indexed
)) {
415 mGL
.fColorMaski(0, true, true, true, true);
417 mGL
.fColorMask(true, true, true, true);
420 mGL
.fGetIntegerv(LOCAL_GL_VIEWPORT
, viewport
);
421 MOZ_ASSERT(destSize
.width
&& destSize
.height
);
422 mGL
.fViewport(0, 0, destSize
.width
, destSize
.height
);
425 ~ScopedDrawBlitState() {
426 mGL
.SetEnabled(LOCAL_GL_BLEND
, blend
);
427 mGL
.SetEnabled(LOCAL_GL_CULL_FACE
, cullFace
);
428 mGL
.SetEnabled(LOCAL_GL_DEPTH_TEST
, depthTest
);
429 mGL
.SetEnabled(LOCAL_GL_DITHER
, dither
);
430 mGL
.SetEnabled(LOCAL_GL_POLYGON_OFFSET_FILL
, polyOffsFill
);
431 mGL
.SetEnabled(LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE
, sampleAToC
);
432 mGL
.SetEnabled(LOCAL_GL_SAMPLE_COVERAGE
, sampleCover
);
433 mGL
.SetEnabled(LOCAL_GL_SCISSOR_TEST
, scissor
);
434 mGL
.SetEnabled(LOCAL_GL_STENCIL_TEST
, stencil
);
435 if (rasterizerDiscard
) {
436 mGL
.SetEnabled(LOCAL_GL_RASTERIZER_DISCARD
, rasterizerDiscard
.value());
439 if (mGL
.IsSupported(GLFeature::draw_buffers_indexed
)) {
440 mGL
.fColorMaski(0, colorMask
[0], colorMask
[1], colorMask
[2],
443 mGL
.fColorMask(colorMask
[0], colorMask
[1], colorMask
[2], colorMask
[3]);
445 mGL
.fViewport(viewport
[0], viewport
[1], viewport
[2], viewport
[3]);
451 DrawBlitProg::DrawBlitProg(const GLBlitHelper
* const parent
, const GLuint prog
)
454 mLoc_uDestMatrix(mParent
.mGL
->fGetUniformLocation(mProg
, "uDestMatrix")),
455 mLoc_uTexMatrix0(mParent
.mGL
->fGetUniformLocation(mProg
, "uTexMatrix0")),
456 mLoc_uTexMatrix1(mParent
.mGL
->fGetUniformLocation(mProg
, "uTexMatrix1")),
457 mLoc_uColorLut(mParent
.mGL
->fGetUniformLocation(mProg
, "uColorLut")),
459 mParent
.mGL
->fGetUniformLocation(mProg
, "uColorMatrix")) {
460 const auto& gl
= mParent
.mGL
;
461 MOZ_GL_ASSERT(gl
, mLoc_uDestMatrix
!= -1); // Required
462 MOZ_GL_ASSERT(gl
, mLoc_uTexMatrix0
!= -1); // Required
463 if (mLoc_uColorMatrix
!= -1) {
464 MOZ_GL_ASSERT(gl
, mLoc_uTexMatrix1
!= -1);
466 int32_t numActiveUniforms
= 0;
467 gl
->fGetProgramiv(mProg
, LOCAL_GL_ACTIVE_UNIFORMS
, &numActiveUniforms
);
469 const size_t kMaxNameSize
= 32;
470 char name
[kMaxNameSize
] = {0};
473 for (int32_t i
= 0; i
< numActiveUniforms
; i
++) {
474 gl
->fGetActiveUniform(mProg
, i
, kMaxNameSize
, nullptr, &size
, &type
,
476 if (strcmp("uColorMatrix", name
) == 0) {
477 mType_uColorMatrix
= type
;
481 MOZ_GL_ASSERT(gl
, mType_uColorMatrix
);
485 DrawBlitProg::~DrawBlitProg() {
486 const auto& gl
= mParent
.mGL
;
487 if (!gl
->MakeCurrent()) return;
489 gl
->fDeleteProgram(mProg
);
492 void DrawBlitProg::Draw(const BaseArgs
& args
,
493 const YUVArgs
* const argsYUV
) const {
494 const auto& gl
= mParent
.mGL
;
496 const SaveRestoreCurrentProgram
oldProg(gl
);
497 gl
->fUseProgram(mProg
);
503 const auto& destRect
= args
.destRect
.value();
504 destMatrix
= SubRectMat3(destRect
.X() / args
.destSize
.width
,
505 destRect
.Y() / args
.destSize
.height
,
506 destRect
.Width() / args
.destSize
.width
,
507 destRect
.Height() / args
.destSize
.height
);
509 destMatrix
= Mat3::I();
513 // Apply the y-flip matrix before the destMatrix.
514 // That is, flip y=[0-1] to y=[1-0] before we restrict to the destRect.
515 destMatrix
.at(2, 1) += destMatrix
.at(1, 1);
516 destMatrix
.at(1, 1) *= -1.0f
;
519 gl
->fUniformMatrix3fv(mLoc_uDestMatrix
, 1, false, destMatrix
.m
);
520 gl
->fUniformMatrix3fv(mLoc_uTexMatrix0
, 1, false, args
.texMatrix0
.m
);
522 if (args
.texUnitForColorLut
) {
523 gl
->fUniform1i(mLoc_uColorLut
,
524 AssertedCast
<GLint
>(*args
.texUnitForColorLut
));
527 MOZ_ASSERT(bool(argsYUV
) == (mLoc_uColorMatrix
!= -1));
529 gl
->fUniformMatrix3fv(mLoc_uTexMatrix1
, 1, false, argsYUV
->texMatrix1
.m
);
531 if (mLoc_uColorMatrix
!= -1) {
532 const auto& colorMatrix
=
533 gfxUtils::YuvToRgbMatrix4x4ColumnMajor(*argsYUV
->colorSpaceForMatrix
);
535 switch (mType_uColorMatrix
) {
536 case LOCAL_GL_FLOAT_MAT4
:
537 gl
->fUniformMatrix4fv(mLoc_uColorMatrix
, 1, false, colorMatrix
);
539 case LOCAL_GL_FLOAT_MAT4x3
:
540 for (int x
= 0; x
< 4; x
++) {
541 for (int y
= 0; y
< 3; y
++) {
542 mat4x3
[3 * x
+ y
] = colorMatrix
[4 * x
+ y
];
545 gl
->fUniformMatrix4x3fv(mLoc_uColorMatrix
, 1, false, mat4x3
);
549 << "Bad mType_uColorMatrix: " << gfx::hexa(mType_uColorMatrix
);
556 const ScopedDrawBlitState
drawState(gl
, args
.destSize
);
562 GLint vaa0Normalized
;
566 if (mParent
.mQuadVAO
) {
567 oldVAO
= gl
->GetIntAs
<GLuint
>(LOCAL_GL_VERTEX_ARRAY_BINDING
);
568 gl
->fBindVertexArray(mParent
.mQuadVAO
);
571 gl
->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING
, (GLint
*)&vaa0Buffer
);
572 gl
->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED
, &vaa0Enabled
);
573 gl
->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE
, &vaa0Size
);
574 gl
->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE
, (GLint
*)&vaa0Type
);
575 gl
->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED
, &vaa0Normalized
);
576 gl
->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE
, (GLint
*)&vaa0Stride
);
577 gl
->fGetVertexAttribPointerv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER
, &vaa0Pointer
);
580 gl
->fEnableVertexAttribArray(0);
581 const ScopedBindArrayBuffer
bindVBO(gl
, mParent
.mQuadVBO
);
582 gl
->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT
, false, 0, 0);
585 gl
->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP
, 0, 4);
587 if (mParent
.mQuadVAO
) {
588 gl
->fBindVertexArray(oldVAO
);
591 gl
->fEnableVertexAttribArray(0);
593 gl
->fDisableVertexAttribArray(0);
595 // The current VERTEX_ARRAY_BINDING is not necessarily the same as the
596 // buffer set for vaa0Buffer.
597 const ScopedBindArrayBuffer
bindVBO(gl
, vaa0Buffer
);
598 gl
->fVertexAttribPointer(0, vaa0Size
, vaa0Type
, bool(vaa0Normalized
),
599 vaa0Stride
, vaa0Pointer
);
605 GLBlitHelper::GLBlitHelper(GLContext
* const gl
)
607 mDrawBlitProg_VertShader(mGL
->fCreateShader(LOCAL_GL_VERTEX_SHADER
))
608 //, mYuvUploads_YSize(0, 0)
609 //, mYuvUploads_UVSize(0, 0)
611 mGL
->fGenBuffers(1, &mQuadVBO
);
613 const ScopedBindArrayBuffer
bindVBO(mGL
, mQuadVBO
);
615 const float quadData
[] = {0, 0, 1, 0, 0, 1, 1, 1};
616 const HeapCopyOfStackArray
<float> heapQuadData(quadData
);
617 mGL
->fBufferData(LOCAL_GL_ARRAY_BUFFER
, heapQuadData
.ByteLength(),
618 heapQuadData
.Data(), LOCAL_GL_STATIC_DRAW
);
620 if (mGL
->IsSupported(GLFeature::vertex_array_object
)) {
621 const auto prev
= mGL
->GetIntAs
<GLuint
>(LOCAL_GL_VERTEX_ARRAY_BINDING
);
623 mGL
->fGenVertexArrays(1, &mQuadVAO
);
624 mGL
->fBindVertexArray(mQuadVAO
);
625 mGL
->fEnableVertexAttribArray(0);
626 mGL
->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT
, false, 0, 0);
628 mGL
->fBindVertexArray(prev
);
634 const auto glslVersion
= mGL
->ShadingLanguageVersion();
637 // If you run into problems on old android devices, it might be because some
638 // devices have OES_EGL_image_external but not OES_EGL_image_external_essl3.
639 // We could just use 100 in that particular case, but then we lose out on
640 // e.g. sampler3D. Let's just try 300 for now, and if we get regressions
641 // we'll add an essl100 fallback.
642 if (glslVersion
>= 300) {
643 mDrawBlitProg_VersionLine
= nsCString("#version 300 es\n");
645 mDrawBlitProg_VersionLine
= nsCString("#version 100\n");
647 } else if (glslVersion
>= 130) {
648 mDrawBlitProg_VersionLine
= nsPrintfCString("#version %u\n", glslVersion
);
651 const char kVertSource
[] =
653 #if __VERSION__ >= 130 \n\
654 #define ATTRIBUTE in \n\
655 #define VARYING out \n\
657 #define ATTRIBUTE attribute \n\
658 #define VARYING varying \n\
661 ATTRIBUTE vec2 aVert; // [0.0-1.0] \n\
663 uniform mat3 uDestMatrix; \n\
664 uniform mat3 uTexMatrix0; \n\
665 uniform mat3 uTexMatrix1; \n\
667 VARYING vec2 vTexCoord0; \n\
668 VARYING vec2 vTexCoord1; \n\
672 vec2 destPos = (uDestMatrix * vec3(aVert, 1.0)).xy; \n\
673 gl_Position = vec4(destPos * 2.0 - 1.0, 0.0, 1.0); \n\
675 vTexCoord0 = (uTexMatrix0 * vec3(aVert, 1.0)).xy; \n\
676 vTexCoord1 = (uTexMatrix1 * vec3(aVert, 1.0)).xy; \n\
679 const char* const parts
[] = {mDrawBlitProg_VersionLine
.get(), kVertSource
};
680 mGL
->fShaderSource(mDrawBlitProg_VertShader
, ArrayLength(parts
), parts
,
682 mGL
->fCompileShader(mDrawBlitProg_VertShader
);
685 GLBlitHelper::~GLBlitHelper() {
686 for (const auto& pair
: mDrawBlitProgs
) {
687 const auto& ptr
= pair
.second
;
690 mDrawBlitProgs
.clear();
692 if (!mGL
->MakeCurrent()) return;
694 mGL
->fDeleteShader(mDrawBlitProg_VertShader
);
695 mGL
->fDeleteBuffers(1, &mQuadVBO
);
698 mGL
->fDeleteVertexArrays(1, &mQuadVAO
);
704 const DrawBlitProg
* GLBlitHelper::GetDrawBlitProg(
705 const DrawBlitProg::Key
& key
) const {
706 const auto& res
= mDrawBlitProgs
.insert({key
, nullptr});
707 auto& pair
= *(res
.first
);
708 const auto& didInsert
= res
.second
;
710 pair
.second
= CreateDrawBlitProg(pair
.first
);
715 const DrawBlitProg
* GLBlitHelper::CreateDrawBlitProg(
716 const DrawBlitProg::Key
& key
) const {
717 const auto precisionPref
= StaticPrefs::gfx_blithelper_precision();
718 const char* precision
;
719 switch (precisionPref
) {
724 precision
= "mediump";
727 if (precisionPref
!= 2) {
728 NS_WARNING("gfx.blithelper.precision clamped to 2.");
734 nsPrintfCString
precisionLine("\n#define PRECISION %s\n", precision
);
738 const ScopedShader
fs(mGL
, LOCAL_GL_FRAGMENT_SHADER
);
740 std::vector
<const char*> parts
;
742 parts
.push_back(mDrawBlitProg_VersionLine
.get());
743 parts
.push_back(kFragPreprocHeader
);
744 if (key
.fragHeader
) {
745 parts
.push_back(key
.fragHeader
);
747 parts
.push_back(precisionLine
.BeginReading());
748 parts
.push_back(kFragDeclHeader
);
749 for (const auto& part
: key
.fragParts
) {
751 parts
.push_back(part
);
754 parts
.push_back(kFragBody
);
757 const auto PrintFragSource
= [&]() {
758 printf_stderr("Frag source:\n");
760 for (const auto& part
: parts
) {
761 printf_stderr("// parts[%i]:\n%s\n", i
, part
);
765 if (gfxEnv::MOZ_DUMP_GLBLITHELPER()) {
769 mGL
->fShaderSource(fs
, AssertedCast
<GLint
>(parts
.size()), parts
.data(),
771 mGL
->fCompileShader(fs
);
773 const auto prog
= mGL
->fCreateProgram();
774 mGL
->fAttachShader(prog
, mDrawBlitProg_VertShader
);
775 mGL
->fAttachShader(prog
, fs
);
777 mGL
->fBindAttribLocation(prog
, 0, "aVert");
778 mGL
->fLinkProgram(prog
);
781 mGL
->fGetProgramiv(prog
, LOCAL_GL_LINK_STATUS
, (GLint
*)&status
);
782 if (status
== LOCAL_GL_TRUE
|| mGL
->CheckContextLost()) {
783 const SaveRestoreCurrentProgram
oldProg(mGL
);
784 mGL
->fUseProgram(prog
);
785 const char* samplerNames
[] = {"uTex0", "uTex1", "uTex2"};
786 for (int i
= 0; i
< 3; i
++) {
787 const auto loc
= mGL
->fGetUniformLocation(prog
, samplerNames
[i
]);
788 if (loc
== -1) continue;
789 mGL
->fUniform1i(loc
, i
);
792 return new DrawBlitProg(this, prog
);
795 GLuint progLogLen
= 0;
796 mGL
->fGetProgramiv(prog
, LOCAL_GL_INFO_LOG_LENGTH
, (GLint
*)&progLogLen
);
797 const UniquePtr
<char[]> progLog(new char[progLogLen
+ 1]);
798 mGL
->fGetProgramInfoLog(prog
, progLogLen
, nullptr, progLog
.get());
799 progLog
[progLogLen
] = 0;
801 const auto& vs
= mDrawBlitProg_VertShader
;
803 mGL
->fGetShaderiv(vs
, LOCAL_GL_INFO_LOG_LENGTH
, (GLint
*)&vsLogLen
);
804 const UniquePtr
<char[]> vsLog(new char[vsLogLen
+ 1]);
805 mGL
->fGetShaderInfoLog(vs
, vsLogLen
, nullptr, vsLog
.get());
809 mGL
->fGetShaderiv(fs
, LOCAL_GL_INFO_LOG_LENGTH
, (GLint
*)&fsLogLen
);
810 const UniquePtr
<char[]> fsLog(new char[fsLogLen
+ 1]);
811 mGL
->fGetShaderInfoLog(fs
, fsLogLen
, nullptr, fsLog
.get());
815 std::string("DrawBlitProg link failed:\n") + "progLog: " + progLog
.get() +
816 "\n" + "vsLog: " + vsLog
.get() + "\n" + "fsLog: " + fsLog
.get() + "\n";
817 gfxCriticalError() << logs
;
821 MOZ_CRASH("DrawBlitProg link failed");
824 // -----------------------------------------------------------------------------
827 static RefPtr
<MacIOSurface
> LookupSurface(
828 const layers::SurfaceDescriptorMacIOSurface
& sd
) {
829 return MacIOSurface::LookupSurface(sd
.surfaceId(), !sd
.isOpaque(),
834 bool GLBlitHelper::BlitSdToFramebuffer(const layers::SurfaceDescriptor
& asd
,
835 const gfx::IntSize
& destSize
,
836 const OriginPos destOrigin
) {
837 const auto sdType
= asd
.type();
839 case layers::SurfaceDescriptor::TSurfaceDescriptorBuffer
: {
840 const auto& sd
= asd
.get_SurfaceDescriptorBuffer();
841 const auto yuvData
= PlanarYCbCrData::From(sd
);
843 gfxCriticalNote
<< "[GLBlitHelper::BlitSdToFramebuffer] "
844 "PlanarYCbCrData::From failed";
847 return BlitPlanarYCbCr(*yuvData
, destSize
, destOrigin
);
850 case layers::SurfaceDescriptor::TSurfaceDescriptorD3D10
: {
851 const auto& sd
= asd
.get_SurfaceDescriptorD3D10();
852 return BlitDescriptor(sd
, destSize
, destOrigin
);
854 case layers::SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr
: {
855 const auto& sd
= asd
.get_SurfaceDescriptorDXGIYCbCr();
856 return BlitDescriptor(sd
, destSize
, destOrigin
);
860 case layers::SurfaceDescriptor::TSurfaceDescriptorMacIOSurface
: {
861 const auto& sd
= asd
.get_SurfaceDescriptorMacIOSurface();
862 const auto surf
= LookupSurface(sd
);
864 NS_WARNING("LookupSurface(MacIOSurface) failed");
865 // Sometimes that frame for our handle gone already. That's life, for
869 return BlitImage(surf
, destSize
, destOrigin
);
872 #ifdef MOZ_WIDGET_ANDROID
873 case layers::SurfaceDescriptor::TSurfaceTextureDescriptor
: {
874 const auto& sd
= asd
.get_SurfaceTextureDescriptor();
875 auto surfaceTexture
= java::GeckoSurfaceTexture::Lookup(sd
.handle());
876 return Blit(surfaceTexture
, destSize
, destOrigin
);
880 case layers::SurfaceDescriptor::TSurfaceDescriptorDMABuf
: {
881 const auto& sd
= asd
.get_SurfaceDescriptorDMABuf();
882 RefPtr
<DMABufSurface
> surface
= DMABufSurface::CreateDMABufSurface(sd
);
883 return Blit(surface
, destSize
, destOrigin
);
891 bool GLBlitHelper::BlitImageToFramebuffer(layers::Image
* const srcImage
,
892 const gfx::IntSize
& destSize
,
893 const OriginPos destOrigin
) {
894 switch (srcImage
->GetFormat()) {
895 case ImageFormat::PLANAR_YCBCR
: {
896 const auto srcImage2
= static_cast<PlanarYCbCrImage
*>(srcImage
);
897 const auto data
= srcImage2
->GetData();
898 return BlitPlanarYCbCr(*data
, destSize
, destOrigin
);
901 case ImageFormat::SURFACE_TEXTURE
: {
902 #ifdef MOZ_WIDGET_ANDROID
903 auto* image
= srcImage
->AsSurfaceTextureImage();
905 auto surfaceTexture
=
906 java::GeckoSurfaceTexture::Lookup(image
->GetHandle());
907 return Blit(surfaceTexture
, destSize
, destOrigin
);
913 case ImageFormat::MAC_IOSURFACE
:
915 return BlitImage(srcImage
->AsMacIOSurfaceImage(), destSize
, destOrigin
);
921 case ImageFormat::GPU_VIDEO
:
922 return BlitImage(static_cast<layers::GPUVideoImage
*>(srcImage
), destSize
,
925 case ImageFormat::D3D11_SHARE_HANDLE_TEXTURE
:
926 return BlitImage(static_cast<layers::D3D11ShareHandleImage
*>(srcImage
),
927 destSize
, destOrigin
);
928 case ImageFormat::D3D11_TEXTURE_IMF_SAMPLE
:
930 static_cast<layers::D3D11TextureIMFSampleImage
*>(srcImage
), destSize
,
932 case ImageFormat::D3D11_YCBCR_IMAGE
:
933 return BlitImage(static_cast<layers::D3D11YCbCrImage
*>(srcImage
),
934 destSize
, destOrigin
);
935 case ImageFormat::D3D9_RGB32_TEXTURE
:
936 return false; // todo
937 case ImageFormat::DCOMP_SURFACE
:
940 case ImageFormat::D3D11_SHARE_HANDLE_TEXTURE
:
941 case ImageFormat::D3D11_TEXTURE_IMF_SAMPLE
:
942 case ImageFormat::D3D11_YCBCR_IMAGE
:
943 case ImageFormat::D3D9_RGB32_TEXTURE
:
944 case ImageFormat::DCOMP_SURFACE
:
948 case ImageFormat::DMABUF
:
950 return BlitImage(static_cast<layers::DMABUFSurfaceImage
*>(srcImage
),
951 destSize
, destOrigin
);
955 case ImageFormat::MOZ2D_SURFACE
:
956 case ImageFormat::NV_IMAGE
:
957 case ImageFormat::OVERLAY_IMAGE
:
958 case ImageFormat::SHARED_RGB
:
959 case ImageFormat::TEXTURE_WRAPPER
:
960 return false; // todo
965 // -------------------------------------
967 #ifdef MOZ_WIDGET_ANDROID
968 bool GLBlitHelper::Blit(const java::GeckoSurfaceTexture::Ref
& surfaceTexture
,
969 const gfx::IntSize
& destSize
,
970 const OriginPos destOrigin
) const {
971 if (!surfaceTexture
) {
975 const ScopedBindTextureUnit
boundTU(mGL
, LOCAL_GL_TEXTURE0
);
977 if (!surfaceTexture
->IsAttachedToGLContext((int64_t)mGL
)) {
980 mGL
->fGenTextures(1, &tex
);
982 if (NS_FAILED(surfaceTexture
->AttachToGLContext((int64_t)mGL
, tex
))) {
983 mGL
->fDeleteTextures(1, &tex
);
988 const ScopedBindTexture
savedTex(mGL
, surfaceTexture
->GetTexName(),
989 LOCAL_GL_TEXTURE_EXTERNAL
);
990 surfaceTexture
->UpdateTexImage();
991 const auto transform3
= Mat3::I();
992 // const auto srcOrigin = OriginPos::TopLeft;
993 const auto srcOrigin
= OriginPos::BottomLeft
;
994 const bool yFlip
= (srcOrigin
!= destOrigin
);
995 const auto& prog
= GetDrawBlitProg(
996 {kFragHeader_TexExt
, {kFragSample_OnePlane
, kFragConvert_None
}});
997 const DrawBlitProg::BaseArgs baseArgs
= {transform3
, yFlip
, destSize
,
999 prog
->Draw(baseArgs
, nullptr);
1001 if (surfaceTexture
->IsSingleBuffer()) {
1002 surfaceTexture
->ReleaseTexImage();
1009 // -------------------------------------
1011 bool GuessDivisors(const gfx::IntSize
& ySize
, const gfx::IntSize
& uvSize
,
1012 gfx::IntSize
* const out_divisors
) {
1013 const gfx::IntSize
divisors((ySize
.width
== uvSize
.width
) ? 1 : 2,
1014 (ySize
.height
== uvSize
.height
) ? 1 : 2);
1015 if (uvSize
.width
* divisors
.width
!= ySize
.width
||
1016 uvSize
.height
* divisors
.height
!= ySize
.height
) {
1019 *out_divisors
= divisors
;
1023 bool GLBlitHelper::BlitPlanarYCbCr(const PlanarYCbCrData
& yuvData
,
1024 const gfx::IntSize
& destSize
,
1025 const OriginPos destOrigin
) {
1026 const auto& prog
= GetDrawBlitProg(
1027 {kFragHeader_Tex2D
, {kFragSample_ThreePlane
, kFragConvert_ColorMatrix
}});
1029 if (!mYuvUploads
[0]) {
1030 mGL
->fGenTextures(3, mYuvUploads
);
1031 const ScopedBindTexture
bindTex(mGL
, mYuvUploads
[0]);
1032 mGL
->TexParams_SetClampNoMips();
1033 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[1]);
1034 mGL
->TexParams_SetClampNoMips();
1035 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[2]);
1036 mGL
->TexParams_SetClampNoMips();
1041 auto ySize
= yuvData
.YDataSize();
1042 auto cbcrSize
= yuvData
.CbCrDataSize();
1043 if (yuvData
.mYSkip
|| yuvData
.mCbSkip
|| yuvData
.mCrSkip
|| ySize
.width
< 0 ||
1044 ySize
.height
< 0 || cbcrSize
.width
< 0 || cbcrSize
.height
< 0 ||
1045 yuvData
.mYStride
< 0 || yuvData
.mCbCrStride
< 0) {
1046 gfxCriticalError() << "Unusual PlanarYCbCrData: " << yuvData
.mYSkip
<< ","
1047 << yuvData
.mCbSkip
<< "," << yuvData
.mCrSkip
<< ", "
1048 << ySize
.width
<< "," << ySize
.height
<< ", "
1049 << cbcrSize
.width
<< "," << cbcrSize
.height
<< ", "
1050 << yuvData
.mYStride
<< "," << yuvData
.mCbCrStride
;
1054 gfx::IntSize divisors
;
1055 switch (yuvData
.mChromaSubsampling
) {
1056 case gfx::ChromaSubsampling::FULL
:
1057 divisors
= gfx::IntSize(1, 1);
1059 case gfx::ChromaSubsampling::HALF_WIDTH
:
1060 divisors
= gfx::IntSize(2, 1);
1062 case gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT
:
1063 divisors
= gfx::IntSize(2, 2);
1066 gfxCriticalError() << "Unknown chroma subsampling:"
1067 << int(yuvData
.mChromaSubsampling
);
1073 // RED textures aren't valid in GLES2, and ALPHA textures are not valid in
1074 // desktop GL Core Profiles. So use R8 textures on GL3.0+ and GLES3.0+, but
1075 // LUMINANCE/LUMINANCE/UNSIGNED_BYTE otherwise.
1076 GLenum internalFormat
;
1077 GLenum unpackFormat
;
1078 if (mGL
->IsAtLeast(gl::ContextProfile::OpenGLCore
, 300) ||
1079 mGL
->IsAtLeast(gl::ContextProfile::OpenGLES
, 300)) {
1080 internalFormat
= LOCAL_GL_R8
;
1081 unpackFormat
= LOCAL_GL_RED
;
1083 internalFormat
= LOCAL_GL_LUMINANCE
;
1084 unpackFormat
= LOCAL_GL_LUMINANCE
;
1089 const ScopedSaveMultiTex
saveTex(mGL
, {0, 1, 2}, LOCAL_GL_TEXTURE_2D
);
1090 const ResetUnpackState
reset(mGL
);
1091 const gfx::IntSize
yTexSize(yuvData
.mYStride
, yuvData
.YDataSize().height
);
1092 const gfx::IntSize
uvTexSize(yuvData
.mCbCrStride
,
1093 yuvData
.CbCrDataSize().height
);
1095 if (yTexSize
!= mYuvUploads_YSize
|| uvTexSize
!= mYuvUploads_UVSize
) {
1096 mYuvUploads_YSize
= yTexSize
;
1097 mYuvUploads_UVSize
= uvTexSize
;
1099 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
);
1100 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[0]);
1101 mGL
->fTexImage2D(LOCAL_GL_TEXTURE_2D
, 0, internalFormat
, yTexSize
.width
,
1102 yTexSize
.height
, 0, unpackFormat
, LOCAL_GL_UNSIGNED_BYTE
,
1104 for (int i
= 1; i
< 3; i
++) {
1105 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
+ i
);
1106 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[i
]);
1107 mGL
->fTexImage2D(LOCAL_GL_TEXTURE_2D
, 0, internalFormat
, uvTexSize
.width
,
1108 uvTexSize
.height
, 0, unpackFormat
,
1109 LOCAL_GL_UNSIGNED_BYTE
, nullptr);
1115 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
);
1116 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[0]);
1117 mGL
->fTexSubImage2D(LOCAL_GL_TEXTURE_2D
, 0, 0, 0, yTexSize
.width
,
1118 yTexSize
.height
, unpackFormat
, LOCAL_GL_UNSIGNED_BYTE
,
1120 mGL
->fActiveTexture(LOCAL_GL_TEXTURE1
);
1121 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[1]);
1122 mGL
->fTexSubImage2D(LOCAL_GL_TEXTURE_2D
, 0, 0, 0, uvTexSize
.width
,
1123 uvTexSize
.height
, unpackFormat
, LOCAL_GL_UNSIGNED_BYTE
,
1124 yuvData
.mCbChannel
);
1125 mGL
->fActiveTexture(LOCAL_GL_TEXTURE2
);
1126 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[2]);
1127 mGL
->fTexSubImage2D(LOCAL_GL_TEXTURE_2D
, 0, 0, 0, uvTexSize
.width
,
1128 uvTexSize
.height
, unpackFormat
, LOCAL_GL_UNSIGNED_BYTE
,
1129 yuvData
.mCrChannel
);
1133 const auto& clipRect
= yuvData
.mPictureRect
;
1134 const auto srcOrigin
= OriginPos::BottomLeft
;
1135 const bool yFlip
= (destOrigin
!= srcOrigin
);
1137 const DrawBlitProg::BaseArgs baseArgs
= {SubRectMat3(clipRect
, yTexSize
),
1138 yFlip
, destSize
, Nothing()};
1139 const DrawBlitProg::YUVArgs yuvArgs
= {
1140 SubRectMat3(clipRect
, uvTexSize
, divisors
), Some(yuvData
.mYUVColorSpace
)};
1141 prog
->Draw(baseArgs
, &yuvArgs
);
1145 // -------------------------------------
1148 bool GLBlitHelper::BlitImage(layers::MacIOSurfaceImage
* const srcImage
,
1149 const gfx::IntSize
& destSize
,
1150 const OriginPos destOrigin
) const {
1151 return BlitImage(srcImage
->GetSurface(), destSize
, destOrigin
);
1154 static std::string
IntAsAscii(const int x
) {
1157 auto u
= static_cast<unsigned int>(x
);
1159 str
.insert(str
.begin(), u
& 0xff);
1162 str
.insert(str
.begin(), '\'');
1163 str
.push_back('\'');
1167 bool GLBlitHelper::BlitImage(MacIOSurface
* const iosurf
,
1168 const gfx::IntSize
& destSize
,
1169 const OriginPos destOrigin
) const {
1171 gfxCriticalError() << "Null MacIOSurface for GLBlitHelper::BlitImage";
1174 if (mGL
->GetContextType() != GLContextType::CGL
) {
1178 const auto glCGL
= static_cast<GLContextCGL
*>(mGL
);
1179 const auto cglContext
= glCGL
->GetCGLContext();
1181 const auto& srcOrigin
= OriginPos::BottomLeft
;
1183 DrawBlitProg::BaseArgs baseArgs
;
1184 baseArgs
.yFlip
= (destOrigin
!= srcOrigin
);
1185 baseArgs
.destSize
= destSize
;
1187 // TODO: The colorspace is known by the IOSurface, why override it?
1188 // See GetYUVColorSpace/GetFullRange()
1189 DrawBlitProg::YUVArgs yuvArgs
;
1190 yuvArgs
.colorSpaceForMatrix
= Some(iosurf
->GetYUVColorSpace());
1192 const DrawBlitProg::YUVArgs
* pYuvArgs
= nullptr;
1194 auto planes
= iosurf
->GetPlaneCount();
1196 planes
= 1; // Bad API. No cookie.
1199 const GLenum texTarget
= LOCAL_GL_TEXTURE_RECTANGLE
;
1201 std::vector
<uint8_t> texUnits
;
1202 for (uint8_t i
= 0; i
< planes
; i
++) {
1203 texUnits
.push_back(i
);
1205 const ScopedSaveMultiTex
saveTex(mGL
, texUnits
, texTarget
);
1206 const ScopedTexture
tex0(mGL
);
1207 const ScopedTexture
tex1(mGL
);
1208 const ScopedTexture
tex2(mGL
);
1209 const GLuint texs
[3] = {tex0
, tex1
, tex2
};
1211 const auto pixelFormat
= iosurf
->GetPixelFormat();
1212 if (mGL
->ShouldSpew()) {
1213 const auto formatStr
= IntAsAscii(pixelFormat
);
1214 printf_stderr("iosurf format: %s (0x%08x)\n", formatStr
.c_str(),
1218 const char* fragSample
;
1221 switch (pixelFormat
) {
1222 case kCVPixelFormatType_24RGB
:
1223 case kCVPixelFormatType_24BGR
:
1224 case kCVPixelFormatType_32ARGB
:
1225 case kCVPixelFormatType_32BGRA
:
1226 case kCVPixelFormatType_32ABGR
:
1227 case kCVPixelFormatType_32RGBA
:
1228 case kCVPixelFormatType_64ARGB
:
1229 case kCVPixelFormatType_48RGB
:
1230 fragSample
= kFragSample_OnePlane
;
1232 case kCVPixelFormatType_422YpCbCr8
:
1233 case kCVPixelFormatType_422YpCbCr8_yuvs
:
1234 fragSample
= kFragSample_OnePlane_YUV_via_GBR
;
1235 pYuvArgs
= &yuvArgs
;
1239 if (pixelFormat
<= 0xff) {
1240 str
= std::to_string(pixelFormat
);
1242 str
= IntAsAscii(pixelFormat
);
1244 gfxCriticalError() << "Unhandled kCVPixelFormatType_*: " << str
;
1245 // Probably YUV though
1246 fragSample
= kFragSample_OnePlane_YUV_via_GBR
;
1247 pYuvArgs
= &yuvArgs
;
1253 fragSample
= kFragSample_TwoPlane
;
1254 pYuvArgs
= &yuvArgs
;
1257 fragSample
= kFragSample_ThreePlane
;
1258 pYuvArgs
= &yuvArgs
;
1261 gfxCriticalError() << "Unexpected plane count: " << planes
;
1265 for (uint32_t p
= 0; p
< planes
; p
++) {
1266 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
+ p
);
1267 mGL
->fBindTexture(texTarget
, texs
[p
]);
1268 mGL
->TexParams_SetClampNoMips(texTarget
);
1270 auto err
= iosurf
->CGLTexImageIOSurface2D(mGL
, cglContext
, p
);
1276 const auto width
= iosurf
->GetDevicePixelWidth(p
);
1277 const auto height
= iosurf
->GetDevicePixelHeight(p
);
1278 baseArgs
.texMatrix0
= SubRectMat3(0, 0, width
, height
);
1279 yuvArgs
.texMatrix1
= SubRectMat3(0, 0, width
/ 2.0, height
/ 2.0);
1283 const auto& prog
= GetDrawBlitProg({
1284 kFragHeader_Tex2DRect
,
1285 {fragSample
, kFragConvert_ColorMatrix
},
1287 prog
->Draw(baseArgs
, pYuvArgs
);
1292 // -----------------------------------------------------------------------------
1294 void GLBlitHelper::DrawBlitTextureToFramebuffer(const GLuint srcTex
,
1295 const gfx::IntSize
& srcSize
,
1296 const gfx::IntSize
& destSize
,
1297 const GLenum srcTarget
,
1298 const bool srcIsBGRA
) const {
1299 const char* fragHeader
= nullptr;
1301 switch (srcTarget
) {
1302 case LOCAL_GL_TEXTURE_2D
:
1303 fragHeader
= kFragHeader_Tex2D
;
1304 texMatrix0
= Mat3::I();
1306 case LOCAL_GL_TEXTURE_RECTANGLE_ARB
:
1307 fragHeader
= kFragHeader_Tex2DRect
;
1308 texMatrix0
= SubRectMat3(0, 0, srcSize
.width
, srcSize
.height
);
1311 gfxCriticalError() << "Unexpected srcTarget: " << srcTarget
;
1314 const auto fragConvert
= srcIsBGRA
? kFragConvert_BGR
: kFragConvert_None
;
1315 const auto& prog
= GetDrawBlitProg({
1317 {kFragSample_OnePlane
, fragConvert
},
1320 const ScopedSaveMultiTex
saveTex(mGL
, {0}, srcTarget
);
1321 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
);
1322 mGL
->fBindTexture(srcTarget
, srcTex
);
1324 const bool yFlip
= false;
1325 const DrawBlitProg::BaseArgs baseArgs
= {texMatrix0
, yFlip
, destSize
,
1327 prog
->Draw(baseArgs
);
1330 // -----------------------------------------------------------------------------
1332 void GLBlitHelper::BlitFramebuffer(const gfx::IntRect
& srcRect
,
1333 const gfx::IntRect
& destRect
,
1334 GLuint filter
) const {
1335 MOZ_ASSERT(mGL
->IsSupported(GLFeature::framebuffer_blit
));
1337 const ScopedGLState
scissor(mGL
, LOCAL_GL_SCISSOR_TEST
, false);
1338 mGL
->fBlitFramebuffer(srcRect
.x
, srcRect
.y
, srcRect
.XMost(), srcRect
.YMost(),
1339 destRect
.x
, destRect
.y
, destRect
.XMost(),
1340 destRect
.YMost(), LOCAL_GL_COLOR_BUFFER_BIT
, filter
);
1345 void GLBlitHelper::BlitFramebufferToFramebuffer(const GLuint srcFB
,
1346 const GLuint destFB
,
1347 const gfx::IntRect
& srcRect
,
1348 const gfx::IntRect
& destRect
,
1349 GLuint filter
) const {
1350 MOZ_ASSERT(mGL
->IsSupported(GLFeature::framebuffer_blit
));
1351 MOZ_GL_ASSERT(mGL
, !srcFB
|| mGL
->fIsFramebuffer(srcFB
));
1352 MOZ_GL_ASSERT(mGL
, !destFB
|| mGL
->fIsFramebuffer(destFB
));
1354 const ScopedBindFramebuffer
boundFB(mGL
);
1355 mGL
->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
, srcFB
);
1356 mGL
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, destFB
);
1358 BlitFramebuffer(srcRect
, destRect
, filter
);
1361 void GLBlitHelper::BlitTextureToFramebuffer(GLuint srcTex
,
1362 const gfx::IntSize
& srcSize
,
1363 const gfx::IntSize
& destSize
,
1364 GLenum srcTarget
) const {
1365 MOZ_GL_ASSERT(mGL
, mGL
->fIsTexture(srcTex
));
1367 if (mGL
->IsSupported(GLFeature::framebuffer_blit
)) {
1368 const ScopedFramebufferForTexture
srcWrapper(mGL
, srcTex
, srcTarget
);
1369 const ScopedBindFramebuffer
bindFB(mGL
);
1370 mGL
->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
, srcWrapper
.FB());
1371 BlitFramebuffer(gfx::IntRect({}, srcSize
), gfx::IntRect({}, destSize
));
1375 DrawBlitTextureToFramebuffer(srcTex
, srcSize
, destSize
, srcTarget
);
1378 void GLBlitHelper::BlitFramebufferToTexture(GLuint destTex
,
1379 const gfx::IntSize
& srcSize
,
1380 const gfx::IntSize
& destSize
,
1381 GLenum destTarget
) const {
1382 MOZ_GL_ASSERT(mGL
, mGL
->fIsTexture(destTex
));
1384 if (mGL
->IsSupported(GLFeature::framebuffer_blit
)) {
1385 const ScopedFramebufferForTexture
destWrapper(mGL
, destTex
, destTarget
);
1386 const ScopedBindFramebuffer
bindFB(mGL
);
1387 mGL
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, destWrapper
.FB());
1388 BlitFramebuffer(gfx::IntRect({}, srcSize
), gfx::IntRect({}, destSize
));
1392 ScopedBindTexture
autoTex(mGL
, destTex
, destTarget
);
1393 ScopedGLState
scissor(mGL
, LOCAL_GL_SCISSOR_TEST
, false);
1394 mGL
->fCopyTexSubImage2D(destTarget
, 0, 0, 0, 0, 0, srcSize
.width
,
1398 void GLBlitHelper::BlitTextureToTexture(GLuint srcTex
, GLuint destTex
,
1399 const gfx::IntSize
& srcSize
,
1400 const gfx::IntSize
& destSize
,
1402 GLenum destTarget
) const {
1403 MOZ_GL_ASSERT(mGL
, mGL
->fIsTexture(srcTex
));
1404 MOZ_GL_ASSERT(mGL
, mGL
->fIsTexture(destTex
));
1406 // Start down the CopyTexSubImage path, not the DrawBlit path.
1407 const ScopedFramebufferForTexture
srcWrapper(mGL
, srcTex
, srcTarget
);
1408 const ScopedBindFramebuffer
bindFB(mGL
, srcWrapper
.FB());
1409 BlitFramebufferToTexture(destTex
, srcSize
, destSize
, destTarget
);
1412 // -------------------------------------
1414 bool GLBlitHelper::BlitImage(layers::GPUVideoImage
* const srcImage
,
1415 const gfx::IntSize
& destSize
,
1416 const OriginPos destOrigin
) const {
1417 const auto& data
= srcImage
->GetData();
1418 if (!data
) return false;
1420 const auto& desc
= data
->SD();
1424 layers::SurfaceDescriptorGPUVideo::TSurfaceDescriptorRemoteDecoder
);
1425 const auto& subdescUnion
=
1426 desc
.get_SurfaceDescriptorRemoteDecoder().subdesc();
1427 switch (subdescUnion
.type()) {
1429 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorDMABuf
: {
1430 const auto& subdesc
= subdescUnion
.get_SurfaceDescriptorDMABuf();
1431 RefPtr
<DMABufSurface
> surface
=
1432 DMABufSurface::CreateDMABufSurface(subdesc
);
1433 return Blit(surface
, destSize
, destOrigin
);
1437 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorD3D10
: {
1438 const auto& subdesc
= subdescUnion
.get_SurfaceDescriptorD3D10();
1439 return BlitDescriptor(subdesc
, destSize
, destOrigin
);
1441 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorDXGIYCbCr
: {
1442 const auto& subdesc
= subdescUnion
.get_SurfaceDescriptorDXGIYCbCr();
1443 return BlitDescriptor(subdesc
, destSize
, destOrigin
);
1447 case layers::RemoteDecoderVideoSubDescriptor::
1448 TSurfaceDescriptorMacIOSurface
: {
1449 const auto& subdesc
= subdescUnion
.get_SurfaceDescriptorMacIOSurface();
1450 RefPtr
<MacIOSurface
> surface
= MacIOSurface::LookupSurface(
1451 subdesc
.surfaceId(), !subdesc
.isOpaque(), subdesc
.yUVColorSpace());
1452 MOZ_ASSERT(surface
);
1456 return BlitImage(surface
, destSize
, destOrigin
);
1459 case layers::RemoteDecoderVideoSubDescriptor::Tnull_t
:
1460 // This GPUVideoImage isn't directly readable outside the GPU process.
1464 gfxCriticalError() << "Unhandled subdesc type: "
1465 << uint32_t(subdescUnion
.type());
1470 // -------------------------------------
1472 bool GLBlitHelper::Blit(DMABufSurface
* surface
, const gfx::IntSize
& destSize
,
1473 OriginPos destOrigin
) const {
1474 const auto& srcOrigin
= OriginPos::BottomLeft
;
1476 DrawBlitProg::BaseArgs baseArgs
;
1477 baseArgs
.yFlip
= (destOrigin
!= srcOrigin
);
1478 baseArgs
.destSize
= destSize
;
1480 // TODO: The colorspace is known by the DMABUFSurface, why override it?
1481 // See GetYUVColorSpace/GetFullRange()
1482 DrawBlitProg::YUVArgs yuvArgs
;
1483 yuvArgs
.colorSpaceForMatrix
= Some(surface
->GetYUVColorSpace());
1485 const DrawBlitProg::YUVArgs
* pYuvArgs
= nullptr;
1486 const auto planes
= surface
->GetTextureCount();
1489 // Ensure textures for all planes have been created.
1491 const bool createTextures
= [&]() {
1492 for (int i
= 0; i
< planes
; i
++) {
1493 if (!surface
->GetTexture(i
)) {
1500 bool didCreateTexture
= false;
1501 auto releaseTextures
= mozilla::MakeScopeExit([&] {
1502 if (didCreateTexture
) {
1503 surface
->ReleaseTextures();
1507 if (createTextures
) {
1508 for (int i
= 0; i
< planes
; i
++) {
1509 if (surface
->GetTexture(i
)) {
1512 if (!surface
->CreateTexture(mGL
, i
)) {
1513 LOGDMABUF(("GLBlitHelper::Blit(): Failed to create DMABuf textures."));
1516 didCreateTexture
= true;
1522 const GLenum texTarget
= LOCAL_GL_TEXTURE_2D
;
1524 std::vector
<uint8_t> texUnits
;
1525 for (uint8_t i
= 0; i
< planes
; i
++) {
1526 texUnits
.push_back(i
);
1528 const ScopedSaveMultiTex
saveTex(mGL
, texUnits
, texTarget
);
1529 const auto pixelFormat
= surface
->GetSurfaceType();
1531 const char* fragSample
;
1532 auto fragConvert
= kFragConvert_None
;
1533 switch (pixelFormat
) {
1534 case DMABufSurface::SURFACE_RGBA
:
1535 fragSample
= kFragSample_OnePlane
;
1537 case DMABufSurface::SURFACE_NV12
:
1538 fragSample
= kFragSample_TwoPlane
;
1539 pYuvArgs
= &yuvArgs
;
1540 fragConvert
= kFragConvert_ColorMatrix
;
1542 case DMABufSurface::SURFACE_YUV420
:
1543 fragSample
= kFragSample_ThreePlane
;
1544 pYuvArgs
= &yuvArgs
;
1545 fragConvert
= kFragConvert_ColorMatrix
;
1548 gfxCriticalError() << "Unexpected pixel format: " << pixelFormat
;
1552 for (const auto p
: IntegerRange(planes
)) {
1553 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
+ p
);
1554 mGL
->fBindTexture(texTarget
, surface
->GetTexture(p
));
1555 mGL
->TexParams_SetClampNoMips(texTarget
);
1558 // We support only NV12/YUV420 formats only with 1/2 texture scale.
1559 // We don't set cliprect as DMABus textures are created without padding.
1560 baseArgs
.texMatrix0
= SubRectMat3(0, 0, 1, 1);
1561 yuvArgs
.texMatrix1
= SubRectMat3(0, 0, 1, 1);
1564 GetDrawBlitProg({kFragHeader_Tex2D
, {fragSample
, fragConvert
}});
1565 prog
->Draw(baseArgs
, pYuvArgs
);
1570 bool GLBlitHelper::BlitImage(layers::DMABUFSurfaceImage
* srcImage
,
1571 const gfx::IntSize
& destSize
,
1572 OriginPos destOrigin
) const {
1573 DMABufSurface
* surface
= srcImage
->GetSurface();
1575 gfxCriticalError() << "Null DMABUFSurface for GLBlitHelper::BlitImage";
1578 return Blit(surface
, destSize
, destOrigin
);
1585 static void PushUnorm(uint32_t* const out
, const float inVal
) {
1586 const uint32_t mask
= (1 << N
) - 1;
1588 fval
= std::max(0.0f
, std::min(fval
, 1.0f
));
1590 fval
= roundf(fval
);
1591 auto ival
= static_cast<uint32_t>(fval
);
1598 static uint32_t toRgb10A2(const color::vec4
& val
) {
1601 PushUnorm
<2>(&ret
, val
.w());
1602 PushUnorm
<10>(&ret
, val
.z());
1603 PushUnorm
<10>(&ret
, val
.y());
1604 PushUnorm
<10>(&ret
, val
.x());
1608 std::shared_ptr
<gl::Texture
> GLBlitHelper::GetColorLutTex(
1609 const ColorLutKey
& key
) const {
1610 auto& weak
= mColorLutTexMap
[key
];
1611 auto strong
= weak
.lock();
1614 strong
= std::make_shared
<gl::Texture
>(gl
);
1617 const auto ct
= color::ColorspaceTransform::Create(key
.src
, key
.dst
);
1621 const auto minLutSize
= color::ivec3
{2};
1622 const auto maxLutSize
= color::ivec3
{256};
1623 auto lutSize
= minLutSize
;
1624 if (ct
.srcSpace
.yuv
) {
1625 lutSize
.x(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_y()));
1626 lutSize
.y(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_cb()));
1627 lutSize
.z(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_cr()));
1629 lutSize
.x(int(StaticPrefs::gfx_blithelper_lut_size_rgb_r()));
1630 lutSize
.y(int(StaticPrefs::gfx_blithelper_lut_size_rgb_g()));
1631 lutSize
.z(int(StaticPrefs::gfx_blithelper_lut_size_rgb_b()));
1633 lutSize
= max(minLutSize
, min(lutSize
, maxLutSize
)); // Clamp
1635 const auto lut
= ct
.ToLut3(lutSize
);
1636 const auto& size
= lut
.size
;
1640 constexpr GLenum target
= LOCAL_GL_TEXTURE_3D
;
1641 const auto bind
= gl::ScopedBindTexture(&gl
, strong
->name
, target
);
1642 gl
.fTexParameteri(target
, LOCAL_GL_TEXTURE_WRAP_S
, LOCAL_GL_CLAMP_TO_EDGE
);
1643 gl
.fTexParameteri(target
, LOCAL_GL_TEXTURE_WRAP_T
, LOCAL_GL_CLAMP_TO_EDGE
);
1644 gl
.fTexParameteri(target
, LOCAL_GL_TEXTURE_WRAP_R
, LOCAL_GL_CLAMP_TO_EDGE
);
1645 gl
.fTexParameteri(target
, LOCAL_GL_TEXTURE_MAG_FILTER
, LOCAL_GL_LINEAR
);
1646 gl
.fTexParameteri(target
, LOCAL_GL_TEXTURE_MIN_FILTER
, LOCAL_GL_LINEAR
);
1648 bool useFloat16
= true;
1650 // Use rgba16f, which we can thankfully upload as rgba32f
1651 static_assert(sizeof(color::vec4
) == sizeof(float) * 4);
1652 std::vector
<color::vec4
> uploadData
;
1653 uploadData
.reserve(lut
.data
.size());
1654 for (const auto& src
: lut
.data
) {
1655 const auto dst
= color::vec4
{src
, 1};
1656 uploadData
.push_back(dst
);
1659 gl
.fTexStorage3D(target
, 1, LOCAL_GL_RGBA16F
, size
.x(), size
.y(),
1661 gl
.fTexSubImage3D(target
, 0, 0, 0, 0, size
.x(), size
.y(), size
.z(),
1662 LOCAL_GL_RGBA
, LOCAL_GL_FLOAT
, uploadData
.data());
1665 std::vector
<uint32_t> uploadData
;
1666 uploadData
.reserve(lut
.data
.size());
1667 for (const auto& src
: lut
.data
) {
1668 const auto dst
= toRgb10A2({src
, 1});
1669 uploadData
.push_back(dst
);
1672 gl
.fTexStorage3D(target
, 1, LOCAL_GL_RGB10_A2
, size
.x(), size
.y(),
1674 gl
.fTexSubImage3D(target
, 0, 0, 0, 0, size
.x(), size
.y(), size
.z(),
1675 LOCAL_GL_RGBA
, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV
,
1683 } // namespace mozilla