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 "GLLibraryEGL.h"
33 # include "GLContextCGL.h"
34 # include "MacIOSurfaceImage.h"
38 # include "mozilla/layers/D3D11ShareHandleImage.h"
39 # include "mozilla/layers/D3D11TextureIMFSampleImage.h"
40 # include "mozilla/layers/D3D11YCbCrImage.h"
44 # include "mozilla/layers/DMABUFSurfaceImage.h"
45 # include "mozilla/widget/DMABufSurface.h"
48 using mozilla::layers::PlanarYCbCrData
;
49 using mozilla::layers::PlanarYCbCrImage
;
56 static const char kFragPreprocHeader
[] = R
"(
58 #ifdef GL_FRAGMENT_PRECISION_HIGH
68 #if __VERSION__ >= 130
71 #define VARYING varying
73 #if __VERSION__ >= 120
82 const char* const kFragHeader_Tex2D
= R
"(
83 #define SAMPLER sampler2D
84 #if __VERSION__ >= 130
85 #define TEXTURE texture
87 #define TEXTURE texture2D
90 const char* const kFragHeader_Tex2DRect
= R
"(
91 #define SAMPLER sampler2DRect
92 #if __VERSION__ >= 130
93 #define TEXTURE texture
95 #define TEXTURE texture2DRect
98 const char* const kFragHeader_TexExt
= R
"(
99 #extension GL_OES_EGL_image_external : enable
100 #extension GL_OES_EGL_image_external_essl3 : enable
101 #if __VERSION__ >= 130
102 #define TEXTURE texture
104 #define TEXTURE texture2D
106 #define SAMPLER samplerExternalOES
111 static const char kFragDeclHeader
[] = R
"(
112 precision PRECISION float;
113 #if __VERSION__ >= 130
114 #define FRAG_COLOR oFragColor
117 #define FRAG_COLOR gl_FragColor
123 const char* const kFragSample_OnePlane
= R
"(
124 VARYING mediump vec2 vTexCoord0;
125 uniform PRECISION SAMPLER uTex0;
128 vec4 src = TEXTURE(uTex0, vTexCoord0);
132 // Ideally this would just change the color-matrix it uses, but this is
133 // acceptable debt for now.
134 // `extern` so that we don't get ifdef-dependent const-var-unused Werrors.
135 extern const char* const kFragSample_OnePlane_YUV_via_GBR
= R
"(
136 VARYING mediump vec2 vTexCoord0;
137 uniform PRECISION SAMPLER uTex0;
140 vec4 yuva = TEXTURE(uTex0, vTexCoord0).gbra;
144 const char* const kFragSample_TwoPlane
= R
"(
145 VARYING mediump vec2 vTexCoord0;
146 VARYING mediump vec2 vTexCoord1;
147 uniform PRECISION SAMPLER uTex0;
148 uniform PRECISION SAMPLER uTex1;
151 vec4 src = TEXTURE(uTex0, vTexCoord0); // Keep r and a.
152 src.gb = TEXTURE(uTex1, vTexCoord1).rg;
156 const char* const kFragSample_ThreePlane
= R
"(
157 VARYING mediump vec2 vTexCoord0;
158 VARYING mediump vec2 vTexCoord1;
159 uniform PRECISION SAMPLER uTex0;
160 uniform PRECISION SAMPLER uTex1;
161 uniform PRECISION SAMPLER uTex2;
164 vec4 src = TEXTURE(uTex0, vTexCoord0); // Keep r and a.
165 src.g = TEXTURE(uTex1, vTexCoord1).r;
166 src.b = TEXTURE(uTex2, vTexCoord1).r;
173 const char* const kFragConvert_None
= R
"(
174 vec3 metaConvert(vec3 src) {
178 const char* const kFragConvert_BGR
= R
"(
179 vec3 metaConvert(vec3 src) {
183 const char* const kFragConvert_ColorMatrix
= R
"(
184 uniform mediump MAT4X3 uColorMatrix;
186 vec3 metaConvert(vec3 src) {
187 return (uColorMatrix * vec4(src, 1)).rgb;
190 const char* const kFragConvert_ColorLut
= R
"(
191 uniform PRECISION sampler3D uColorLut;
193 vec3 metaConvert(vec3 src) {
194 // Half-texel filtering hazard!
195 // E.g. For texture size of 2,
196 // E.g. 0.5/2=0.25 is still sampling 100% of texel 0, 0% of texel 1.
197 // For the LUT, we need 0.5/2=0.25 to filter 25/75 texel 0 and 1.
198 // That is, we need to adjust our sampling point such that it's 0.25 of the
199 // way from texel 0's center to texel 1's center.
201 // v=0.0|N=2 => v'=0.5/2
202 // v=1.0|N=2 => v'=1.5/2
204 // v=0.0|N=3 => v'=0.5/3
205 // v=1.0|N=3 => v'=2.5/3
206 // => v' = ( 0.5 + v * (3 - 1) )/3
207 vec3 size = vec3(textureSize(uColorLut, 0));
208 src = (0.5 + src * (size - 1.0)) / size;
209 return texture(uColorLut, src).rgb;
215 const char* const kFragMixin_AlphaMultColors
= R
"(
216 #define MIXIN_ALPHA_MULT_COLORS
218 const char* const kFragMixin_AlphaClampColors
= R
"(
219 #define MIXIN_ALPHA_CLAMP_COLORS
221 const char* const kFragMixin_AlphaOne
= R
"(
222 #define MIXIN_ALPHA_ONE
227 static const char kFragBody
[] = R
"(
229 vec4 src = metaSample();
230 vec4 dst = vec4(metaConvert(src.rgb), src.a);
232 #ifdef MIXIN_ALPHA_MULT_COLORS
235 #ifdef MIXIN_ALPHA_CLAMP_COLORS
236 dst.rgb = min(dst.rgb, vec3(dst.a)); // Ensure valid premult-alpha colors.
238 #ifdef MIXIN_ALPHA_ONE
248 Mat3
SubRectMat3(const float x
, const float y
, const float w
, const float h
) {
258 Mat3
SubRectMat3(const gfx::IntRect
& subrect
, const gfx::IntSize
& size
) {
259 return SubRectMat3(float(subrect
.X()) / size
.width
,
260 float(subrect
.Y()) / size
.height
,
261 float(subrect
.Width()) / size
.width
,
262 float(subrect
.Height()) / size
.height
);
265 Mat3
SubRectMat3(const gfx::IntRect
& bigSubrect
, const gfx::IntSize
& smallSize
,
266 const gfx::IntSize
& divisors
) {
267 const float x
= float(bigSubrect
.X()) / divisors
.width
;
268 const float y
= float(bigSubrect
.Y()) / divisors
.height
;
269 const float w
= float(bigSubrect
.Width()) / divisors
.width
;
270 const float h
= float(bigSubrect
.Height()) / divisors
.height
;
271 return SubRectMat3(x
/ smallSize
.width
, y
/ smallSize
.height
,
272 w
/ smallSize
.width
, h
/ smallSize
.height
);
277 ScopedSaveMultiTex::ScopedSaveMultiTex(GLContext
* const gl
,
278 const std::vector
<uint8_t>& texUnits
,
279 const GLenum texTarget
)
282 mTexTarget(texTarget
),
283 mOldTexUnit(mGL
.GetIntAs
<GLenum
>(LOCAL_GL_ACTIVE_TEXTURE
)) {
284 MOZ_RELEASE_ASSERT(texUnits
.size() >= 1);
287 switch (mTexTarget
) {
288 case LOCAL_GL_TEXTURE_2D
:
289 texBinding
= LOCAL_GL_TEXTURE_BINDING_2D
;
291 case LOCAL_GL_TEXTURE_3D
:
292 texBinding
= LOCAL_GL_TEXTURE_BINDING_3D
;
294 case LOCAL_GL_TEXTURE_RECTANGLE
:
295 texBinding
= LOCAL_GL_TEXTURE_BINDING_RECTANGLE
;
297 case LOCAL_GL_TEXTURE_EXTERNAL
:
298 texBinding
= LOCAL_GL_TEXTURE_BINDING_EXTERNAL
;
301 gfxCriticalError() << "Unhandled texTarget: " << texTarget
;
305 for (const auto i
: IntegerRange(mTexUnits
.size())) {
306 const auto& unit
= mTexUnits
[i
];
307 mGL
.fActiveTexture(LOCAL_GL_TEXTURE0
+ unit
);
308 if (mGL
.IsSupported(GLFeature::sampler_objects
)) {
309 mOldTexSampler
[i
] = mGL
.GetIntAs
<GLuint
>(LOCAL_GL_SAMPLER_BINDING
);
310 mGL
.fBindSampler(unit
, 0);
312 mOldTex
[i
] = mGL
.GetIntAs
<GLuint
>(texBinding
);
316 ScopedSaveMultiTex::~ScopedSaveMultiTex() {
317 // Unbind in reverse order, in case we have repeats.
318 // Order matters because we unbound samplers during ctor, so now we have to
319 // make sure we rebind them in the right order.
320 for (const auto i
: Reversed(IntegerRange(mTexUnits
.size()))) {
321 const auto& unit
= mTexUnits
[i
];
322 mGL
.fActiveTexture(LOCAL_GL_TEXTURE0
+ unit
);
323 if (mGL
.IsSupported(GLFeature::sampler_objects
)) {
324 mGL
.fBindSampler(unit
, mOldTexSampler
[i
]);
326 mGL
.fBindTexture(mTexTarget
, mOldTex
[i
]);
328 mGL
.fActiveTexture(mOldTexUnit
);
333 class ScopedBindArrayBuffer final
{
336 const GLuint mOldVBO
;
338 ScopedBindArrayBuffer(GLContext
* const gl
, const GLuint vbo
)
339 : mGL(*gl
), mOldVBO(mGL
.GetIntAs
<GLuint
>(LOCAL_GL_ARRAY_BUFFER_BINDING
)) {
340 mGL
.fBindBuffer(LOCAL_GL_ARRAY_BUFFER
, vbo
);
343 ~ScopedBindArrayBuffer() { mGL
.fBindBuffer(LOCAL_GL_ARRAY_BUFFER
, mOldVBO
); }
348 class ScopedShader final
{
353 ScopedShader(GLContext
* const gl
, const GLenum shaderType
)
354 : mGL(*gl
), mName(mGL
.fCreateShader(shaderType
)) {}
356 ~ScopedShader() { mGL
.fDeleteShader(mName
); }
358 operator GLuint() const { return mName
; }
363 class SaveRestoreCurrentProgram final
{
368 explicit SaveRestoreCurrentProgram(GLContext
* const gl
)
369 : mGL(*gl
), mOld(mGL
.GetIntAs
<GLuint
>(LOCAL_GL_CURRENT_PROGRAM
)) {}
371 ~SaveRestoreCurrentProgram() { mGL
.fUseProgram(mOld
); }
376 class ScopedDrawBlitState final
{
381 const bool depthTest
;
383 const bool polyOffsFill
;
384 const bool sampleAToC
;
385 const bool sampleCover
;
388 Maybe
<bool> rasterizerDiscard
;
390 realGLboolean colorMask
[4];
394 ScopedDrawBlitState(GLContext
* const gl
, const gfx::IntSize
& destSize
)
396 blend(mGL
.PushEnabled(LOCAL_GL_BLEND
, false)),
397 cullFace(mGL
.PushEnabled(LOCAL_GL_CULL_FACE
, false)),
398 depthTest(mGL
.PushEnabled(LOCAL_GL_DEPTH_TEST
, false)),
399 dither(mGL
.PushEnabled(LOCAL_GL_DITHER
, true)),
400 polyOffsFill(mGL
.PushEnabled(LOCAL_GL_POLYGON_OFFSET_FILL
, false)),
401 sampleAToC(mGL
.PushEnabled(LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE
, false)),
402 sampleCover(mGL
.PushEnabled(LOCAL_GL_SAMPLE_COVERAGE
, false)),
403 scissor(mGL
.PushEnabled(LOCAL_GL_SCISSOR_TEST
, false)),
404 stencil(mGL
.PushEnabled(LOCAL_GL_STENCIL_TEST
, false)) {
405 if (mGL
.IsSupported(GLFeature::transform_feedback2
)) {
406 // Technically transform_feedback2 requires transform_feedback, which
407 // actually adds RASTERIZER_DISCARD.
409 Some(mGL
.PushEnabled(LOCAL_GL_RASTERIZER_DISCARD
, false));
412 mGL
.fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK
, colorMask
);
413 if (mGL
.IsSupported(GLFeature::draw_buffers_indexed
)) {
414 mGL
.fColorMaski(0, true, true, true, true);
416 mGL
.fColorMask(true, true, true, true);
419 mGL
.fGetIntegerv(LOCAL_GL_VIEWPORT
, viewport
);
420 MOZ_ASSERT(destSize
.width
&& destSize
.height
);
421 mGL
.fViewport(0, 0, destSize
.width
, destSize
.height
);
424 ~ScopedDrawBlitState() {
425 mGL
.SetEnabled(LOCAL_GL_BLEND
, blend
);
426 mGL
.SetEnabled(LOCAL_GL_CULL_FACE
, cullFace
);
427 mGL
.SetEnabled(LOCAL_GL_DEPTH_TEST
, depthTest
);
428 mGL
.SetEnabled(LOCAL_GL_DITHER
, dither
);
429 mGL
.SetEnabled(LOCAL_GL_POLYGON_OFFSET_FILL
, polyOffsFill
);
430 mGL
.SetEnabled(LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE
, sampleAToC
);
431 mGL
.SetEnabled(LOCAL_GL_SAMPLE_COVERAGE
, sampleCover
);
432 mGL
.SetEnabled(LOCAL_GL_SCISSOR_TEST
, scissor
);
433 mGL
.SetEnabled(LOCAL_GL_STENCIL_TEST
, stencil
);
434 if (rasterizerDiscard
) {
435 mGL
.SetEnabled(LOCAL_GL_RASTERIZER_DISCARD
, rasterizerDiscard
.value());
438 if (mGL
.IsSupported(GLFeature::draw_buffers_indexed
)) {
439 mGL
.fColorMaski(0, colorMask
[0], colorMask
[1], colorMask
[2],
442 mGL
.fColorMask(colorMask
[0], colorMask
[1], colorMask
[2], colorMask
[3]);
444 mGL
.fViewport(viewport
[0], viewport
[1], viewport
[2], viewport
[3]);
450 DrawBlitProg::DrawBlitProg(const GLBlitHelper
* const parent
, const GLuint prog
)
453 mLoc_uDestMatrix(mParent
.mGL
->fGetUniformLocation(mProg
, "uDestMatrix")),
454 mLoc_uTexMatrix0(mParent
.mGL
->fGetUniformLocation(mProg
, "uTexMatrix0")),
455 mLoc_uTexMatrix1(mParent
.mGL
->fGetUniformLocation(mProg
, "uTexMatrix1")),
456 mLoc_uColorLut(mParent
.mGL
->fGetUniformLocation(mProg
, "uColorLut")),
458 mParent
.mGL
->fGetUniformLocation(mProg
, "uColorMatrix")) {
459 const auto& gl
= mParent
.mGL
;
460 MOZ_GL_ASSERT(gl
, mLoc_uDestMatrix
!= -1); // Required
461 MOZ_GL_ASSERT(gl
, mLoc_uTexMatrix0
!= -1); // Required
462 if (mLoc_uColorMatrix
!= -1) {
463 MOZ_GL_ASSERT(gl
, mLoc_uTexMatrix1
!= -1);
465 int32_t numActiveUniforms
= 0;
466 gl
->fGetProgramiv(mProg
, LOCAL_GL_ACTIVE_UNIFORMS
, &numActiveUniforms
);
468 const size_t kMaxNameSize
= 32;
469 char name
[kMaxNameSize
] = {0};
472 for (int32_t i
= 0; i
< numActiveUniforms
; i
++) {
473 gl
->fGetActiveUniform(mProg
, i
, kMaxNameSize
, nullptr, &size
, &type
,
475 if (strcmp("uColorMatrix", name
) == 0) {
476 mType_uColorMatrix
= type
;
480 MOZ_GL_ASSERT(gl
, mType_uColorMatrix
);
484 DrawBlitProg::~DrawBlitProg() {
485 const auto& gl
= mParent
.mGL
;
486 if (!gl
->MakeCurrent()) return;
488 gl
->fDeleteProgram(mProg
);
491 void DrawBlitProg::Draw(const BaseArgs
& args
,
492 const YUVArgs
* const argsYUV
) const {
493 const auto& gl
= mParent
.mGL
;
495 const SaveRestoreCurrentProgram
oldProg(gl
);
496 gl
->fUseProgram(mProg
);
502 const auto& destRect
= args
.destRect
.value();
503 destMatrix
= SubRectMat3(destRect
.X() / args
.destSize
.width
,
504 destRect
.Y() / args
.destSize
.height
,
505 destRect
.Width() / args
.destSize
.width
,
506 destRect
.Height() / args
.destSize
.height
);
508 destMatrix
= Mat3::I();
512 // Apply the y-flip matrix before the destMatrix.
513 // That is, flip y=[0-1] to y=[1-0] before we restrict to the destRect.
514 destMatrix
.at(2, 1) += destMatrix
.at(1, 1);
515 destMatrix
.at(1, 1) *= -1.0f
;
518 gl
->fUniformMatrix3fv(mLoc_uDestMatrix
, 1, false, destMatrix
.m
);
519 gl
->fUniformMatrix3fv(mLoc_uTexMatrix0
, 1, false, args
.texMatrix0
.m
);
521 if (args
.texUnitForColorLut
) {
522 gl
->fUniform1i(mLoc_uColorLut
,
523 AssertedCast
<GLint
>(*args
.texUnitForColorLut
));
526 MOZ_ASSERT(bool(argsYUV
) == (mLoc_uColorMatrix
!= -1));
528 gl
->fUniformMatrix3fv(mLoc_uTexMatrix1
, 1, false, argsYUV
->texMatrix1
.m
);
530 if (mLoc_uColorMatrix
!= -1) {
531 const auto& colorMatrix
=
532 gfxUtils::YuvToRgbMatrix4x4ColumnMajor(*argsYUV
->colorSpaceForMatrix
);
534 switch (mType_uColorMatrix
) {
535 case LOCAL_GL_FLOAT_MAT4
:
536 gl
->fUniformMatrix4fv(mLoc_uColorMatrix
, 1, false, colorMatrix
);
538 case LOCAL_GL_FLOAT_MAT4x3
:
539 for (int x
= 0; x
< 4; x
++) {
540 for (int y
= 0; y
< 3; y
++) {
541 mat4x3
[3 * x
+ y
] = colorMatrix
[4 * x
+ y
];
544 gl
->fUniformMatrix4x3fv(mLoc_uColorMatrix
, 1, false, mat4x3
);
548 << "Bad mType_uColorMatrix: " << gfx::hexa(mType_uColorMatrix
);
555 const ScopedDrawBlitState
drawState(gl
, args
.destSize
);
561 GLint vaa0Normalized
;
565 if (mParent
.mQuadVAO
) {
566 oldVAO
= gl
->GetIntAs
<GLuint
>(LOCAL_GL_VERTEX_ARRAY_BINDING
);
567 gl
->fBindVertexArray(mParent
.mQuadVAO
);
570 gl
->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING
, (GLint
*)&vaa0Buffer
);
571 gl
->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED
, &vaa0Enabled
);
572 gl
->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE
, &vaa0Size
);
573 gl
->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE
, (GLint
*)&vaa0Type
);
574 gl
->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED
, &vaa0Normalized
);
575 gl
->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE
, (GLint
*)&vaa0Stride
);
576 gl
->fGetVertexAttribPointerv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER
, &vaa0Pointer
);
579 gl
->fEnableVertexAttribArray(0);
580 const ScopedBindArrayBuffer
bindVBO(gl
, mParent
.mQuadVBO
);
581 gl
->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT
, false, 0, 0);
584 gl
->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP
, 0, 4);
586 if (mParent
.mQuadVAO
) {
587 gl
->fBindVertexArray(oldVAO
);
590 gl
->fEnableVertexAttribArray(0);
592 gl
->fDisableVertexAttribArray(0);
594 // The current VERTEX_ARRAY_BINDING is not necessarily the same as the
595 // buffer set for vaa0Buffer.
596 const ScopedBindArrayBuffer
bindVBO(gl
, vaa0Buffer
);
597 gl
->fVertexAttribPointer(0, vaa0Size
, vaa0Type
, bool(vaa0Normalized
),
598 vaa0Stride
, vaa0Pointer
);
604 GLBlitHelper::GLBlitHelper(GLContext
* const gl
)
606 mDrawBlitProg_VertShader(mGL
->fCreateShader(LOCAL_GL_VERTEX_SHADER
))
607 //, mYuvUploads_YSize(0, 0)
608 //, mYuvUploads_UVSize(0, 0)
610 mGL
->fGenBuffers(1, &mQuadVBO
);
612 const ScopedBindArrayBuffer
bindVBO(mGL
, mQuadVBO
);
614 const float quadData
[] = {0, 0, 1, 0, 0, 1, 1, 1};
615 const HeapCopyOfStackArray
<float> heapQuadData(quadData
);
616 mGL
->fBufferData(LOCAL_GL_ARRAY_BUFFER
, heapQuadData
.ByteLength(),
617 heapQuadData
.Data(), LOCAL_GL_STATIC_DRAW
);
619 if (mGL
->IsSupported(GLFeature::vertex_array_object
)) {
620 const auto prev
= mGL
->GetIntAs
<GLuint
>(LOCAL_GL_VERTEX_ARRAY_BINDING
);
622 mGL
->fGenVertexArrays(1, &mQuadVAO
);
623 mGL
->fBindVertexArray(mQuadVAO
);
624 mGL
->fEnableVertexAttribArray(0);
625 mGL
->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT
, false, 0, 0);
627 mGL
->fBindVertexArray(prev
);
633 const auto glslVersion
= mGL
->ShadingLanguageVersion();
636 // If you run into problems on old android devices, it might be because some
637 // devices have OES_EGL_image_external but not OES_EGL_image_external_essl3.
638 // We could just use 100 in that particular case, but then we lose out on
639 // e.g. sampler3D. Let's just try 300 for now, and if we get regressions
640 // we'll add an essl100 fallback.
641 if (glslVersion
>= 300) {
642 mDrawBlitProg_VersionLine
= nsCString("#version 300 es\n");
644 mDrawBlitProg_VersionLine
= nsCString("#version 100\n");
646 } else if (glslVersion
>= 130) {
647 mDrawBlitProg_VersionLine
= nsPrintfCString("#version %u\n", glslVersion
);
650 const char kVertSource
[] =
652 #if __VERSION__ >= 130 \n\
653 #define ATTRIBUTE in \n\
654 #define VARYING out \n\
656 #define ATTRIBUTE attribute \n\
657 #define VARYING varying \n\
660 ATTRIBUTE vec2 aVert; // [0.0-1.0] \n\
662 uniform mat3 uDestMatrix; \n\
663 uniform mat3 uTexMatrix0; \n\
664 uniform mat3 uTexMatrix1; \n\
666 VARYING vec2 vTexCoord0; \n\
667 VARYING vec2 vTexCoord1; \n\
671 vec2 destPos = (uDestMatrix * vec3(aVert, 1.0)).xy; \n\
672 gl_Position = vec4(destPos * 2.0 - 1.0, 0.0, 1.0); \n\
674 vTexCoord0 = (uTexMatrix0 * vec3(aVert, 1.0)).xy; \n\
675 vTexCoord1 = (uTexMatrix1 * vec3(aVert, 1.0)).xy; \n\
678 const char* const parts
[] = {mDrawBlitProg_VersionLine
.get(), kVertSource
};
679 mGL
->fShaderSource(mDrawBlitProg_VertShader
, ArrayLength(parts
), parts
,
681 mGL
->fCompileShader(mDrawBlitProg_VertShader
);
684 GLBlitHelper::~GLBlitHelper() {
685 for (const auto& pair
: mDrawBlitProgs
) {
686 const auto& ptr
= pair
.second
;
689 mDrawBlitProgs
.clear();
691 if (!mGL
->MakeCurrent()) return;
693 mGL
->fDeleteShader(mDrawBlitProg_VertShader
);
694 mGL
->fDeleteBuffers(1, &mQuadVBO
);
697 mGL
->fDeleteVertexArrays(1, &mQuadVAO
);
703 const DrawBlitProg
* GLBlitHelper::GetDrawBlitProg(
704 const DrawBlitProg::Key
& key
) const {
705 const auto& res
= mDrawBlitProgs
.insert({key
, nullptr});
706 auto& pair
= *(res
.first
);
707 const auto& didInsert
= res
.second
;
709 pair
.second
= CreateDrawBlitProg(pair
.first
);
714 const DrawBlitProg
* GLBlitHelper::CreateDrawBlitProg(
715 const DrawBlitProg::Key
& key
) const {
716 const auto precisionPref
= StaticPrefs::gfx_blithelper_precision();
717 const char* precision
;
718 switch (precisionPref
) {
723 precision
= "mediump";
726 if (precisionPref
!= 2) {
727 NS_WARNING("gfx.blithelper.precision clamped to 2.");
733 nsPrintfCString
precisionLine("\n#define PRECISION %s\n", precision
);
737 const ScopedShader
fs(mGL
, LOCAL_GL_FRAGMENT_SHADER
);
739 std::vector
<const char*> parts
;
741 parts
.push_back(mDrawBlitProg_VersionLine
.get());
742 parts
.push_back(kFragPreprocHeader
);
743 if (key
.fragHeader
) {
744 parts
.push_back(key
.fragHeader
);
746 parts
.push_back(precisionLine
.BeginReading());
747 parts
.push_back(kFragDeclHeader
);
748 for (const auto& part
: key
.fragParts
) {
750 parts
.push_back(part
);
753 parts
.push_back(kFragBody
);
756 const auto PrintFragSource
= [&]() {
757 printf_stderr("Frag source:\n");
759 for (const auto& part
: parts
) {
760 printf_stderr("// parts[%i]:\n%s\n", i
, part
);
764 if (gfxEnv::MOZ_DUMP_GLBLITHELPER()) {
768 mGL
->fShaderSource(fs
, AssertedCast
<GLint
>(parts
.size()), parts
.data(),
770 mGL
->fCompileShader(fs
);
772 const auto prog
= mGL
->fCreateProgram();
773 mGL
->fAttachShader(prog
, mDrawBlitProg_VertShader
);
774 mGL
->fAttachShader(prog
, fs
);
776 mGL
->fBindAttribLocation(prog
, 0, "aPosition");
777 mGL
->fLinkProgram(prog
);
780 mGL
->fGetProgramiv(prog
, LOCAL_GL_LINK_STATUS
, (GLint
*)&status
);
781 if (status
== LOCAL_GL_TRUE
|| mGL
->CheckContextLost()) {
782 const SaveRestoreCurrentProgram
oldProg(mGL
);
783 mGL
->fUseProgram(prog
);
784 const char* samplerNames
[] = {"uTex0", "uTex1", "uTex2"};
785 for (int i
= 0; i
< 3; i
++) {
786 const auto loc
= mGL
->fGetUniformLocation(prog
, samplerNames
[i
]);
787 if (loc
== -1) continue;
788 mGL
->fUniform1i(loc
, i
);
791 return new DrawBlitProg(this, prog
);
794 GLuint progLogLen
= 0;
795 mGL
->fGetProgramiv(prog
, LOCAL_GL_INFO_LOG_LENGTH
, (GLint
*)&progLogLen
);
796 const UniquePtr
<char[]> progLog(new char[progLogLen
+ 1]);
797 mGL
->fGetProgramInfoLog(prog
, progLogLen
, nullptr, progLog
.get());
798 progLog
[progLogLen
] = 0;
800 const auto& vs
= mDrawBlitProg_VertShader
;
802 mGL
->fGetShaderiv(vs
, LOCAL_GL_INFO_LOG_LENGTH
, (GLint
*)&vsLogLen
);
803 const UniquePtr
<char[]> vsLog(new char[vsLogLen
+ 1]);
804 mGL
->fGetShaderInfoLog(vs
, vsLogLen
, nullptr, vsLog
.get());
808 mGL
->fGetShaderiv(fs
, LOCAL_GL_INFO_LOG_LENGTH
, (GLint
*)&fsLogLen
);
809 const UniquePtr
<char[]> fsLog(new char[fsLogLen
+ 1]);
810 mGL
->fGetShaderInfoLog(fs
, fsLogLen
, nullptr, fsLog
.get());
814 std::string("DrawBlitProg link failed:\n") + "progLog: " + progLog
.get() +
815 "\n" + "vsLog: " + vsLog
.get() + "\n" + "fsLog: " + fsLog
.get() + "\n";
816 gfxCriticalError() << logs
;
820 MOZ_CRASH("DrawBlitProg link failed");
823 // -----------------------------------------------------------------------------
826 static RefPtr
<MacIOSurface
> LookupSurface(
827 const layers::SurfaceDescriptorMacIOSurface
& sd
) {
828 return MacIOSurface::LookupSurface(sd
.surfaceId(), !sd
.isOpaque(),
833 bool GLBlitHelper::BlitSdToFramebuffer(const layers::SurfaceDescriptor
& asd
,
834 const gfx::IntSize
& destSize
,
835 const OriginPos destOrigin
) {
836 const auto sdType
= asd
.type();
838 case layers::SurfaceDescriptor::TSurfaceDescriptorBuffer
: {
839 const auto& sd
= asd
.get_SurfaceDescriptorBuffer();
840 const auto yuvData
= PlanarYCbCrData::From(sd
);
842 gfxCriticalNote
<< "[GLBlitHelper::BlitSdToFramebuffer] "
843 "PlanarYCbCrData::From failed";
846 return BlitPlanarYCbCr(*yuvData
, destSize
, destOrigin
);
849 case layers::SurfaceDescriptor::TSurfaceDescriptorD3D10
: {
850 const auto& sd
= asd
.get_SurfaceDescriptorD3D10();
851 return BlitDescriptor(sd
, destSize
, destOrigin
);
853 case layers::SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr
: {
854 const auto& sd
= asd
.get_SurfaceDescriptorDXGIYCbCr();
855 return BlitDescriptor(sd
, destSize
, destOrigin
);
859 case layers::SurfaceDescriptor::TSurfaceDescriptorMacIOSurface
: {
860 const auto& sd
= asd
.get_SurfaceDescriptorMacIOSurface();
861 const auto surf
= LookupSurface(sd
);
863 NS_WARNING("LookupSurface(MacIOSurface) failed");
864 // Sometimes that frame for our handle gone already. That's life, for
868 return BlitImage(surf
, destSize
, destOrigin
);
871 #ifdef MOZ_WIDGET_ANDROID
872 case layers::SurfaceDescriptor::TSurfaceTextureDescriptor
: {
873 const auto& sd
= asd
.get_SurfaceTextureDescriptor();
874 auto surfaceTexture
= java::GeckoSurfaceTexture::Lookup(sd
.handle());
875 return Blit(surfaceTexture
, destSize
, destOrigin
);
883 bool GLBlitHelper::BlitImageToFramebuffer(layers::Image
* const srcImage
,
884 const gfx::IntSize
& destSize
,
885 const OriginPos destOrigin
) {
886 switch (srcImage
->GetFormat()) {
887 case ImageFormat::PLANAR_YCBCR
: {
888 const auto srcImage2
= static_cast<PlanarYCbCrImage
*>(srcImage
);
889 const auto data
= srcImage2
->GetData();
890 return BlitPlanarYCbCr(*data
, destSize
, destOrigin
);
893 case ImageFormat::SURFACE_TEXTURE
: {
894 #ifdef MOZ_WIDGET_ANDROID
895 auto* image
= srcImage
->AsSurfaceTextureImage();
897 auto surfaceTexture
=
898 java::GeckoSurfaceTexture::Lookup(image
->GetHandle());
899 return Blit(surfaceTexture
, destSize
, destOrigin
);
905 case ImageFormat::MAC_IOSURFACE
:
907 return BlitImage(srcImage
->AsMacIOSurfaceImage(), destSize
, destOrigin
);
913 case ImageFormat::GPU_VIDEO
:
914 return BlitImage(static_cast<layers::GPUVideoImage
*>(srcImage
), destSize
,
917 case ImageFormat::D3D11_SHARE_HANDLE_TEXTURE
:
918 return BlitImage(static_cast<layers::D3D11ShareHandleImage
*>(srcImage
),
919 destSize
, destOrigin
);
920 case ImageFormat::D3D11_TEXTURE_IMF_SAMPLE
:
922 static_cast<layers::D3D11TextureIMFSampleImage
*>(srcImage
), destSize
,
924 case ImageFormat::D3D11_YCBCR_IMAGE
:
925 return BlitImage(static_cast<layers::D3D11YCbCrImage
*>(srcImage
),
926 destSize
, destOrigin
);
927 case ImageFormat::D3D9_RGB32_TEXTURE
:
928 return false; // todo
929 case ImageFormat::DCOMP_SURFACE
:
932 case ImageFormat::D3D11_SHARE_HANDLE_TEXTURE
:
933 case ImageFormat::D3D11_TEXTURE_IMF_SAMPLE
:
934 case ImageFormat::D3D11_YCBCR_IMAGE
:
935 case ImageFormat::D3D9_RGB32_TEXTURE
:
936 case ImageFormat::DCOMP_SURFACE
:
940 case ImageFormat::DMABUF
:
942 return BlitImage(static_cast<layers::DMABUFSurfaceImage
*>(srcImage
),
943 destSize
, destOrigin
);
947 case ImageFormat::MOZ2D_SURFACE
:
948 case ImageFormat::NV_IMAGE
:
949 case ImageFormat::OVERLAY_IMAGE
:
950 case ImageFormat::SHARED_RGB
:
951 case ImageFormat::TEXTURE_WRAPPER
:
952 return false; // todo
957 // -------------------------------------
959 #ifdef MOZ_WIDGET_ANDROID
960 bool GLBlitHelper::Blit(const java::GeckoSurfaceTexture::Ref
& surfaceTexture
,
961 const gfx::IntSize
& destSize
,
962 const OriginPos destOrigin
) const {
963 if (!surfaceTexture
) {
967 const ScopedBindTextureUnit
boundTU(mGL
, LOCAL_GL_TEXTURE0
);
969 if (!surfaceTexture
->IsAttachedToGLContext((int64_t)mGL
)) {
972 mGL
->fGenTextures(1, &tex
);
974 if (NS_FAILED(surfaceTexture
->AttachToGLContext((int64_t)mGL
, tex
))) {
975 mGL
->fDeleteTextures(1, &tex
);
980 const ScopedBindTexture
savedTex(mGL
, surfaceTexture
->GetTexName(),
981 LOCAL_GL_TEXTURE_EXTERNAL
);
982 surfaceTexture
->UpdateTexImage();
983 const auto transform3
= Mat3::I();
984 const auto srcOrigin
= OriginPos::TopLeft
;
985 const bool yFlip
= (srcOrigin
!= destOrigin
);
986 const auto& prog
= GetDrawBlitProg(
987 {kFragHeader_TexExt
, {kFragSample_OnePlane
, kFragConvert_None
}});
988 const DrawBlitProg::BaseArgs baseArgs
= {transform3
, yFlip
, destSize
,
990 prog
->Draw(baseArgs
, nullptr);
992 if (surfaceTexture
->IsSingleBuffer()) {
993 surfaceTexture
->ReleaseTexImage();
1000 // -------------------------------------
1002 bool GuessDivisors(const gfx::IntSize
& ySize
, const gfx::IntSize
& uvSize
,
1003 gfx::IntSize
* const out_divisors
) {
1004 const gfx::IntSize
divisors((ySize
.width
== uvSize
.width
) ? 1 : 2,
1005 (ySize
.height
== uvSize
.height
) ? 1 : 2);
1006 if (uvSize
.width
* divisors
.width
!= ySize
.width
||
1007 uvSize
.height
* divisors
.height
!= ySize
.height
) {
1010 *out_divisors
= divisors
;
1014 bool GLBlitHelper::BlitPlanarYCbCr(const PlanarYCbCrData
& yuvData
,
1015 const gfx::IntSize
& destSize
,
1016 const OriginPos destOrigin
) {
1017 const auto& prog
= GetDrawBlitProg(
1018 {kFragHeader_Tex2D
, {kFragSample_ThreePlane
, kFragConvert_ColorMatrix
}});
1020 if (!mYuvUploads
[0]) {
1021 mGL
->fGenTextures(3, mYuvUploads
);
1022 const ScopedBindTexture
bindTex(mGL
, mYuvUploads
[0]);
1023 mGL
->TexParams_SetClampNoMips();
1024 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[1]);
1025 mGL
->TexParams_SetClampNoMips();
1026 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[2]);
1027 mGL
->TexParams_SetClampNoMips();
1032 auto ySize
= yuvData
.YDataSize();
1033 auto cbcrSize
= yuvData
.CbCrDataSize();
1034 if (yuvData
.mYSkip
|| yuvData
.mCbSkip
|| yuvData
.mCrSkip
|| ySize
.width
< 0 ||
1035 ySize
.height
< 0 || cbcrSize
.width
< 0 || cbcrSize
.height
< 0 ||
1036 yuvData
.mYStride
< 0 || yuvData
.mCbCrStride
< 0) {
1037 gfxCriticalError() << "Unusual PlanarYCbCrData: " << yuvData
.mYSkip
<< ","
1038 << yuvData
.mCbSkip
<< "," << yuvData
.mCrSkip
<< ", "
1039 << ySize
.width
<< "," << ySize
.height
<< ", "
1040 << cbcrSize
.width
<< "," << cbcrSize
.height
<< ", "
1041 << yuvData
.mYStride
<< "," << yuvData
.mCbCrStride
;
1045 gfx::IntSize divisors
;
1046 switch (yuvData
.mChromaSubsampling
) {
1047 case gfx::ChromaSubsampling::FULL
:
1048 divisors
= gfx::IntSize(1, 1);
1050 case gfx::ChromaSubsampling::HALF_WIDTH
:
1051 divisors
= gfx::IntSize(2, 1);
1053 case gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT
:
1054 divisors
= gfx::IntSize(2, 2);
1057 gfxCriticalError() << "Unknown chroma subsampling:"
1058 << int(yuvData
.mChromaSubsampling
);
1064 // RED textures aren't valid in GLES2, and ALPHA textures are not valid in
1065 // desktop GL Core Profiles. So use R8 textures on GL3.0+ and GLES3.0+, but
1066 // LUMINANCE/LUMINANCE/UNSIGNED_BYTE otherwise.
1067 GLenum internalFormat
;
1068 GLenum unpackFormat
;
1069 if (mGL
->IsAtLeast(gl::ContextProfile::OpenGLCore
, 300) ||
1070 mGL
->IsAtLeast(gl::ContextProfile::OpenGLES
, 300)) {
1071 internalFormat
= LOCAL_GL_R8
;
1072 unpackFormat
= LOCAL_GL_RED
;
1074 internalFormat
= LOCAL_GL_LUMINANCE
;
1075 unpackFormat
= LOCAL_GL_LUMINANCE
;
1080 const ScopedSaveMultiTex
saveTex(mGL
, {0, 1, 2}, LOCAL_GL_TEXTURE_2D
);
1081 const ResetUnpackState
reset(mGL
);
1082 const gfx::IntSize
yTexSize(yuvData
.mYStride
, yuvData
.YDataSize().height
);
1083 const gfx::IntSize
uvTexSize(yuvData
.mCbCrStride
,
1084 yuvData
.CbCrDataSize().height
);
1086 if (yTexSize
!= mYuvUploads_YSize
|| uvTexSize
!= mYuvUploads_UVSize
) {
1087 mYuvUploads_YSize
= yTexSize
;
1088 mYuvUploads_UVSize
= uvTexSize
;
1090 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
);
1091 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[0]);
1092 mGL
->fTexImage2D(LOCAL_GL_TEXTURE_2D
, 0, internalFormat
, yTexSize
.width
,
1093 yTexSize
.height
, 0, unpackFormat
, LOCAL_GL_UNSIGNED_BYTE
,
1095 for (int i
= 1; i
< 3; i
++) {
1096 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
+ i
);
1097 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[i
]);
1098 mGL
->fTexImage2D(LOCAL_GL_TEXTURE_2D
, 0, internalFormat
, uvTexSize
.width
,
1099 uvTexSize
.height
, 0, unpackFormat
,
1100 LOCAL_GL_UNSIGNED_BYTE
, nullptr);
1106 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
);
1107 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[0]);
1108 mGL
->fTexSubImage2D(LOCAL_GL_TEXTURE_2D
, 0, 0, 0, yTexSize
.width
,
1109 yTexSize
.height
, unpackFormat
, LOCAL_GL_UNSIGNED_BYTE
,
1111 mGL
->fActiveTexture(LOCAL_GL_TEXTURE1
);
1112 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[1]);
1113 mGL
->fTexSubImage2D(LOCAL_GL_TEXTURE_2D
, 0, 0, 0, uvTexSize
.width
,
1114 uvTexSize
.height
, unpackFormat
, LOCAL_GL_UNSIGNED_BYTE
,
1115 yuvData
.mCbChannel
);
1116 mGL
->fActiveTexture(LOCAL_GL_TEXTURE2
);
1117 mGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mYuvUploads
[2]);
1118 mGL
->fTexSubImage2D(LOCAL_GL_TEXTURE_2D
, 0, 0, 0, uvTexSize
.width
,
1119 uvTexSize
.height
, unpackFormat
, LOCAL_GL_UNSIGNED_BYTE
,
1120 yuvData
.mCrChannel
);
1124 const auto& clipRect
= yuvData
.mPictureRect
;
1125 const auto srcOrigin
= OriginPos::BottomLeft
;
1126 const bool yFlip
= (destOrigin
!= srcOrigin
);
1128 const DrawBlitProg::BaseArgs baseArgs
= {SubRectMat3(clipRect
, yTexSize
),
1129 yFlip
, destSize
, Nothing()};
1130 const DrawBlitProg::YUVArgs yuvArgs
= {
1131 SubRectMat3(clipRect
, uvTexSize
, divisors
), Some(yuvData
.mYUVColorSpace
)};
1132 prog
->Draw(baseArgs
, &yuvArgs
);
1136 // -------------------------------------
1139 bool GLBlitHelper::BlitImage(layers::MacIOSurfaceImage
* const srcImage
,
1140 const gfx::IntSize
& destSize
,
1141 const OriginPos destOrigin
) const {
1142 return BlitImage(srcImage
->GetSurface(), destSize
, destOrigin
);
1145 static std::string
IntAsAscii(const int x
) {
1148 auto u
= static_cast<unsigned int>(x
);
1150 str
.insert(str
.begin(), u
& 0xff);
1153 str
.insert(str
.begin(), '\'');
1154 str
.push_back('\'');
1158 bool GLBlitHelper::BlitImage(MacIOSurface
* const iosurf
,
1159 const gfx::IntSize
& destSize
,
1160 const OriginPos destOrigin
) const {
1162 gfxCriticalError() << "Null MacIOSurface for GLBlitHelper::BlitImage";
1165 if (mGL
->GetContextType() != GLContextType::CGL
) {
1169 const auto glCGL
= static_cast<GLContextCGL
*>(mGL
);
1170 const auto cglContext
= glCGL
->GetCGLContext();
1172 const auto& srcOrigin
= OriginPos::BottomLeft
;
1174 DrawBlitProg::BaseArgs baseArgs
;
1175 baseArgs
.yFlip
= (destOrigin
!= srcOrigin
);
1176 baseArgs
.destSize
= destSize
;
1178 // TODO: The colorspace is known by the IOSurface, why override it?
1179 // See GetYUVColorSpace/GetFullRange()
1180 DrawBlitProg::YUVArgs yuvArgs
;
1181 yuvArgs
.colorSpaceForMatrix
= Some(iosurf
->GetYUVColorSpace());
1183 const DrawBlitProg::YUVArgs
* pYuvArgs
= nullptr;
1185 auto planes
= iosurf
->GetPlaneCount();
1187 planes
= 1; // Bad API. No cookie.
1190 const GLenum texTarget
= LOCAL_GL_TEXTURE_RECTANGLE
;
1192 std::vector
<uint8_t> texUnits
;
1193 for (uint8_t i
= 0; i
< planes
; i
++) {
1194 texUnits
.push_back(i
);
1196 const ScopedSaveMultiTex
saveTex(mGL
, texUnits
, texTarget
);
1197 const ScopedTexture
tex0(mGL
);
1198 const ScopedTexture
tex1(mGL
);
1199 const ScopedTexture
tex2(mGL
);
1200 const GLuint texs
[3] = {tex0
, tex1
, tex2
};
1202 const auto pixelFormat
= iosurf
->GetPixelFormat();
1203 if (mGL
->ShouldSpew()) {
1204 const auto formatStr
= IntAsAscii(pixelFormat
);
1205 printf_stderr("iosurf format: %s (0x%08x)\n", formatStr
.c_str(),
1209 const char* fragSample
;
1212 switch (pixelFormat
) {
1213 case kCVPixelFormatType_24RGB
:
1214 case kCVPixelFormatType_24BGR
:
1215 case kCVPixelFormatType_32ARGB
:
1216 case kCVPixelFormatType_32BGRA
:
1217 case kCVPixelFormatType_32ABGR
:
1218 case kCVPixelFormatType_32RGBA
:
1219 case kCVPixelFormatType_64ARGB
:
1220 case kCVPixelFormatType_48RGB
:
1221 fragSample
= kFragSample_OnePlane
;
1223 case kCVPixelFormatType_422YpCbCr8
:
1224 case kCVPixelFormatType_422YpCbCr8_yuvs
:
1225 fragSample
= kFragSample_OnePlane_YUV_via_GBR
;
1226 pYuvArgs
= &yuvArgs
;
1230 if (pixelFormat
<= 0xff) {
1231 str
= std::to_string(pixelFormat
);
1233 str
= IntAsAscii(pixelFormat
);
1235 gfxCriticalError() << "Unhandled kCVPixelFormatType_*: " << str
;
1236 // Probably YUV though
1237 fragSample
= kFragSample_OnePlane_YUV_via_GBR
;
1238 pYuvArgs
= &yuvArgs
;
1244 fragSample
= kFragSample_TwoPlane
;
1245 pYuvArgs
= &yuvArgs
;
1248 fragSample
= kFragSample_ThreePlane
;
1249 pYuvArgs
= &yuvArgs
;
1252 gfxCriticalError() << "Unexpected plane count: " << planes
;
1256 for (uint32_t p
= 0; p
< planes
; p
++) {
1257 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
+ p
);
1258 mGL
->fBindTexture(texTarget
, texs
[p
]);
1259 mGL
->TexParams_SetClampNoMips(texTarget
);
1261 auto err
= iosurf
->CGLTexImageIOSurface2D(mGL
, cglContext
, p
);
1267 const auto width
= iosurf
->GetDevicePixelWidth(p
);
1268 const auto height
= iosurf
->GetDevicePixelHeight(p
);
1269 baseArgs
.texMatrix0
= SubRectMat3(0, 0, width
, height
);
1270 yuvArgs
.texMatrix1
= SubRectMat3(0, 0, width
/ 2.0, height
/ 2.0);
1274 const auto& prog
= GetDrawBlitProg({
1275 kFragHeader_Tex2DRect
,
1276 {fragSample
, kFragConvert_ColorMatrix
},
1278 prog
->Draw(baseArgs
, pYuvArgs
);
1283 // -----------------------------------------------------------------------------
1285 void GLBlitHelper::DrawBlitTextureToFramebuffer(const GLuint srcTex
,
1286 const gfx::IntSize
& srcSize
,
1287 const gfx::IntSize
& destSize
,
1288 const GLenum srcTarget
,
1289 const bool srcIsBGRA
) const {
1290 const char* fragHeader
= nullptr;
1292 switch (srcTarget
) {
1293 case LOCAL_GL_TEXTURE_2D
:
1294 fragHeader
= kFragHeader_Tex2D
;
1295 texMatrix0
= Mat3::I();
1297 case LOCAL_GL_TEXTURE_RECTANGLE_ARB
:
1298 fragHeader
= kFragHeader_Tex2DRect
;
1299 texMatrix0
= SubRectMat3(0, 0, srcSize
.width
, srcSize
.height
);
1302 gfxCriticalError() << "Unexpected srcTarget: " << srcTarget
;
1305 const auto fragConvert
= srcIsBGRA
? kFragConvert_BGR
: kFragConvert_None
;
1306 const auto& prog
= GetDrawBlitProg({
1308 {kFragSample_OnePlane
, fragConvert
},
1311 const ScopedSaveMultiTex
saveTex(mGL
, {0}, srcTarget
);
1312 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
);
1313 mGL
->fBindTexture(srcTarget
, srcTex
);
1315 const bool yFlip
= false;
1316 const DrawBlitProg::BaseArgs baseArgs
= {texMatrix0
, yFlip
, destSize
,
1318 prog
->Draw(baseArgs
);
1321 // -----------------------------------------------------------------------------
1323 void GLBlitHelper::BlitFramebuffer(const gfx::IntRect
& srcRect
,
1324 const gfx::IntRect
& destRect
,
1325 GLuint filter
) const {
1326 MOZ_ASSERT(mGL
->IsSupported(GLFeature::framebuffer_blit
));
1328 const ScopedGLState
scissor(mGL
, LOCAL_GL_SCISSOR_TEST
, false);
1329 mGL
->fBlitFramebuffer(srcRect
.x
, srcRect
.y
, srcRect
.XMost(), srcRect
.YMost(),
1330 destRect
.x
, destRect
.y
, destRect
.XMost(),
1331 destRect
.YMost(), LOCAL_GL_COLOR_BUFFER_BIT
, filter
);
1336 void GLBlitHelper::BlitFramebufferToFramebuffer(const GLuint srcFB
,
1337 const GLuint destFB
,
1338 const gfx::IntRect
& srcRect
,
1339 const gfx::IntRect
& destRect
,
1340 GLuint filter
) const {
1341 MOZ_ASSERT(mGL
->IsSupported(GLFeature::framebuffer_blit
));
1342 MOZ_GL_ASSERT(mGL
, !srcFB
|| mGL
->fIsFramebuffer(srcFB
));
1343 MOZ_GL_ASSERT(mGL
, !destFB
|| mGL
->fIsFramebuffer(destFB
));
1345 const ScopedBindFramebuffer
boundFB(mGL
);
1346 mGL
->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
, srcFB
);
1347 mGL
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, destFB
);
1349 BlitFramebuffer(srcRect
, destRect
, filter
);
1352 void GLBlitHelper::BlitTextureToFramebuffer(GLuint srcTex
,
1353 const gfx::IntSize
& srcSize
,
1354 const gfx::IntSize
& destSize
,
1355 GLenum srcTarget
) const {
1356 MOZ_GL_ASSERT(mGL
, mGL
->fIsTexture(srcTex
));
1358 if (mGL
->IsSupported(GLFeature::framebuffer_blit
)) {
1359 const ScopedFramebufferForTexture
srcWrapper(mGL
, srcTex
, srcTarget
);
1360 const ScopedBindFramebuffer
bindFB(mGL
);
1361 mGL
->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
, srcWrapper
.FB());
1362 BlitFramebuffer(gfx::IntRect({}, srcSize
), gfx::IntRect({}, destSize
));
1366 DrawBlitTextureToFramebuffer(srcTex
, srcSize
, destSize
, srcTarget
);
1369 void GLBlitHelper::BlitFramebufferToTexture(GLuint destTex
,
1370 const gfx::IntSize
& srcSize
,
1371 const gfx::IntSize
& destSize
,
1372 GLenum destTarget
) const {
1373 MOZ_GL_ASSERT(mGL
, mGL
->fIsTexture(destTex
));
1375 if (mGL
->IsSupported(GLFeature::framebuffer_blit
)) {
1376 const ScopedFramebufferForTexture
destWrapper(mGL
, destTex
, destTarget
);
1377 const ScopedBindFramebuffer
bindFB(mGL
);
1378 mGL
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, destWrapper
.FB());
1379 BlitFramebuffer(gfx::IntRect({}, srcSize
), gfx::IntRect({}, destSize
));
1383 ScopedBindTexture
autoTex(mGL
, destTex
, destTarget
);
1384 ScopedGLState
scissor(mGL
, LOCAL_GL_SCISSOR_TEST
, false);
1385 mGL
->fCopyTexSubImage2D(destTarget
, 0, 0, 0, 0, 0, srcSize
.width
,
1389 void GLBlitHelper::BlitTextureToTexture(GLuint srcTex
, GLuint destTex
,
1390 const gfx::IntSize
& srcSize
,
1391 const gfx::IntSize
& destSize
,
1393 GLenum destTarget
) const {
1394 MOZ_GL_ASSERT(mGL
, mGL
->fIsTexture(srcTex
));
1395 MOZ_GL_ASSERT(mGL
, mGL
->fIsTexture(destTex
));
1397 // Start down the CopyTexSubImage path, not the DrawBlit path.
1398 const ScopedFramebufferForTexture
srcWrapper(mGL
, srcTex
, srcTarget
);
1399 const ScopedBindFramebuffer
bindFB(mGL
, srcWrapper
.FB());
1400 BlitFramebufferToTexture(destTex
, srcSize
, destSize
, destTarget
);
1403 // -------------------------------------
1405 bool GLBlitHelper::BlitImage(layers::GPUVideoImage
* const srcImage
,
1406 const gfx::IntSize
& destSize
,
1407 const OriginPos destOrigin
) const {
1408 const auto& data
= srcImage
->GetData();
1409 if (!data
) return false;
1411 const auto& desc
= data
->SD();
1415 layers::SurfaceDescriptorGPUVideo::TSurfaceDescriptorRemoteDecoder
);
1416 const auto& subdescUnion
=
1417 desc
.get_SurfaceDescriptorRemoteDecoder().subdesc();
1418 switch (subdescUnion
.type()) {
1419 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorDMABuf
: {
1424 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorD3D10
: {
1425 const auto& subdesc
= subdescUnion
.get_SurfaceDescriptorD3D10();
1426 return BlitDescriptor(subdesc
, destSize
, destOrigin
);
1428 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorDXGIYCbCr
: {
1429 const auto& subdesc
= subdescUnion
.get_SurfaceDescriptorDXGIYCbCr();
1430 return BlitDescriptor(subdesc
, destSize
, destOrigin
);
1434 case layers::RemoteDecoderVideoSubDescriptor::
1435 TSurfaceDescriptorMacIOSurface
: {
1436 const auto& subdesc
= subdescUnion
.get_SurfaceDescriptorMacIOSurface();
1437 RefPtr
<MacIOSurface
> surface
= MacIOSurface::LookupSurface(
1438 subdesc
.surfaceId(), !subdesc
.isOpaque(), subdesc
.yUVColorSpace());
1439 MOZ_ASSERT(surface
);
1443 return BlitImage(surface
, destSize
, destOrigin
);
1446 case layers::RemoteDecoderVideoSubDescriptor::Tnull_t
:
1447 // This GPUVideoImage isn't directly readable outside the GPU process.
1451 gfxCriticalError() << "Unhandled subdesc type: "
1452 << uint32_t(subdescUnion
.type());
1457 // -------------------------------------
1459 bool GLBlitHelper::Blit(DMABufSurface
* surface
, const gfx::IntSize
& destSize
,
1460 OriginPos destOrigin
) const {
1461 const auto& srcOrigin
= OriginPos::BottomLeft
;
1463 DrawBlitProg::BaseArgs baseArgs
;
1464 baseArgs
.yFlip
= (destOrigin
!= srcOrigin
);
1465 baseArgs
.destSize
= destSize
;
1467 // TODO: The colorspace is known by the DMABUFSurface, why override it?
1468 // See GetYUVColorSpace/GetFullRange()
1469 DrawBlitProg::YUVArgs yuvArgs
;
1470 yuvArgs
.colorSpaceForMatrix
= Some(surface
->GetYUVColorSpace());
1472 const DrawBlitProg::YUVArgs
* pYuvArgs
= nullptr;
1474 const auto planes
= surface
->GetTextureCount();
1475 const GLenum texTarget
= LOCAL_GL_TEXTURE_2D
;
1477 std::vector
<uint8_t> texUnits
;
1478 for (uint8_t i
= 0; i
< planes
; i
++) {
1479 texUnits
.push_back(i
);
1481 const ScopedSaveMultiTex
saveTex(mGL
, texUnits
, texTarget
);
1482 const auto pixelFormat
= surface
->GetSurfaceType();
1484 const char* fragSample
;
1485 auto fragConvert
= kFragConvert_None
;
1486 switch (pixelFormat
) {
1487 case DMABufSurface::SURFACE_RGBA
:
1488 fragSample
= kFragSample_OnePlane
;
1490 case DMABufSurface::SURFACE_NV12
:
1491 fragSample
= kFragSample_TwoPlane
;
1492 pYuvArgs
= &yuvArgs
;
1493 fragConvert
= kFragConvert_ColorMatrix
;
1495 case DMABufSurface::SURFACE_YUV420
:
1496 fragSample
= kFragSample_ThreePlane
;
1497 pYuvArgs
= &yuvArgs
;
1498 fragConvert
= kFragConvert_ColorMatrix
;
1501 gfxCriticalError() << "Unexpected pixel format: " << pixelFormat
;
1505 for (const auto p
: IntegerRange(planes
)) {
1506 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
+ p
);
1507 mGL
->fBindTexture(texTarget
, surface
->GetTexture(p
));
1508 mGL
->TexParams_SetClampNoMips(texTarget
);
1511 // We support only NV12/YUV420 formats only with 1/2 texture scale.
1512 // We don't set cliprect as DMABus textures are created without padding.
1513 baseArgs
.texMatrix0
= SubRectMat3(0, 0, 1, 1);
1514 yuvArgs
.texMatrix1
= SubRectMat3(0, 0, 1, 1);
1517 GetDrawBlitProg({kFragHeader_Tex2D
, {fragSample
, fragConvert
}});
1518 prog
->Draw(baseArgs
, pYuvArgs
);
1523 bool GLBlitHelper::BlitImage(layers::DMABUFSurfaceImage
* srcImage
,
1524 const gfx::IntSize
& destSize
,
1525 OriginPos destOrigin
) const {
1526 DMABufSurface
* surface
= srcImage
->GetSurface();
1528 gfxCriticalError() << "Null DMABUFSurface for GLBlitHelper::BlitImage";
1531 return Blit(surface
, destSize
, destOrigin
);
1538 static void PushUnorm(uint32_t* const out
, const float inVal
) {
1539 const uint32_t mask
= (1 << N
) - 1;
1541 fval
= std::max(0.0f
, std::min(fval
, 1.0f
));
1543 fval
= roundf(fval
);
1544 auto ival
= static_cast<uint32_t>(fval
);
1551 static uint32_t toRgb10A2(const color::vec4
& val
) {
1554 PushUnorm
<2>(&ret
, val
.w());
1555 PushUnorm
<10>(&ret
, val
.z());
1556 PushUnorm
<10>(&ret
, val
.y());
1557 PushUnorm
<10>(&ret
, val
.x());
1561 std::shared_ptr
<gl::Texture
> GLBlitHelper::GetColorLutTex(
1562 const ColorLutKey
& key
) const {
1563 auto& weak
= mColorLutTexMap
[key
];
1564 auto strong
= weak
.lock();
1567 strong
= std::make_shared
<gl::Texture
>(gl
);
1570 const auto ct
= color::ColorspaceTransform::Create(key
.src
, key
.dst
);
1574 const auto minLutSize
= color::ivec3
{2};
1575 const auto maxLutSize
= color::ivec3
{256};
1576 auto lutSize
= minLutSize
;
1577 if (ct
.srcSpace
.yuv
) {
1578 lutSize
.x(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_y()));
1579 lutSize
.y(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_cb()));
1580 lutSize
.z(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_cr()));
1582 lutSize
.x(int(StaticPrefs::gfx_blithelper_lut_size_rgb_r()));
1583 lutSize
.y(int(StaticPrefs::gfx_blithelper_lut_size_rgb_g()));
1584 lutSize
.z(int(StaticPrefs::gfx_blithelper_lut_size_rgb_b()));
1586 lutSize
= max(minLutSize
, min(lutSize
, maxLutSize
)); // Clamp
1588 const auto lut
= ct
.ToLut3(lutSize
);
1589 const auto& size
= lut
.size
;
1593 constexpr GLenum target
= LOCAL_GL_TEXTURE_3D
;
1594 const auto bind
= gl::ScopedBindTexture(&gl
, strong
->name
, target
);
1595 gl
.fTexParameteri(target
, LOCAL_GL_TEXTURE_WRAP_S
, LOCAL_GL_CLAMP_TO_EDGE
);
1596 gl
.fTexParameteri(target
, LOCAL_GL_TEXTURE_WRAP_T
, LOCAL_GL_CLAMP_TO_EDGE
);
1597 gl
.fTexParameteri(target
, LOCAL_GL_TEXTURE_WRAP_R
, LOCAL_GL_CLAMP_TO_EDGE
);
1598 gl
.fTexParameteri(target
, LOCAL_GL_TEXTURE_MAG_FILTER
, LOCAL_GL_LINEAR
);
1599 gl
.fTexParameteri(target
, LOCAL_GL_TEXTURE_MIN_FILTER
, LOCAL_GL_LINEAR
);
1601 bool useFloat16
= true;
1603 // Use rgba16f, which we can thankfully upload as rgba32f
1604 static_assert(sizeof(color::vec4
) == sizeof(float) * 4);
1605 std::vector
<color::vec4
> uploadData
;
1606 uploadData
.reserve(lut
.data
.size());
1607 for (const auto& src
: lut
.data
) {
1608 const auto dst
= color::vec4
{src
, 1};
1609 uploadData
.push_back(dst
);
1612 gl
.fTexStorage3D(target
, 1, LOCAL_GL_RGBA16F
, size
.x(), size
.y(),
1614 gl
.fTexSubImage3D(target
, 0, 0, 0, 0, size
.x(), size
.y(), size
.z(),
1615 LOCAL_GL_RGBA
, LOCAL_GL_FLOAT
, uploadData
.data());
1618 std::vector
<uint32_t> uploadData
;
1619 uploadData
.reserve(lut
.data
.size());
1620 for (const auto& src
: lut
.data
) {
1621 const auto dst
= toRgb10A2({src
, 1});
1622 uploadData
.push_back(dst
);
1625 gl
.fTexStorage3D(target
, 1, LOCAL_GL_RGB10_A2
, size
.x(), size
.y(),
1627 gl
.fTexSubImage3D(target
, 0, 0, 0, 0, size
.x(), size
.y(), size
.z(),
1628 LOCAL_GL_RGBA
, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV
,
1636 } // namespace mozilla