Bug 1839336 - Fix handling of testdriver failures in marionette, r=webdriver-reviewers
[gecko.git] / gfx / gl / GLBlitHelper.cpp
blobb3798633584de069700dd9a125ceafa2d5be52a0
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"
9 #include "gfxEnv.h"
10 #include "gfxUtils.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"
31 #endif
33 #ifdef XP_MACOSX
34 # include "GLContextCGL.h"
35 # include "MacIOSurfaceImage.h"
36 #endif
38 #ifdef XP_WIN
39 # include "mozilla/layers/D3D11ShareHandleImage.h"
40 # include "mozilla/layers/D3D11TextureIMFSampleImage.h"
41 # include "mozilla/layers/D3D11YCbCrImage.h"
42 #endif
44 #ifdef MOZ_WAYLAND
45 # include "mozilla/layers/DMABUFSurfaceImage.h"
46 # include "mozilla/widget/DMABufSurface.h"
47 #endif
49 using mozilla::layers::PlanarYCbCrData;
50 using mozilla::layers::PlanarYCbCrImage;
52 namespace mozilla {
53 namespace gl {
55 // --
57 static const char kFragPreprocHeader[] = R"(
58 #ifdef GL_ES
59 #ifdef GL_FRAGMENT_PRECISION_HIGH
60 #define MAXP highp
61 #endif
62 #else
63 #define MAXP highp
64 #endif
65 #ifndef MAXP
66 #define MAXP mediump
67 #endif
69 #if __VERSION__ >= 130
70 #define VARYING in
71 #else
72 #define VARYING varying
73 #endif
74 #if __VERSION__ >= 120
75 #define MAT4X3 mat4x3
76 #else
77 #define MAT4X3 mat4
78 #endif
79 )";
81 // -
83 const char* const kFragHeader_Tex2D = R"(
84 #define SAMPLER sampler2D
85 #if __VERSION__ >= 130
86 #define TEXTURE texture
87 #else
88 #define TEXTURE texture2D
89 #endif
90 )";
91 const char* const kFragHeader_Tex2DRect = R"(
92 #define SAMPLER sampler2DRect
93 #if __VERSION__ >= 130
94 #define TEXTURE texture
95 #else
96 #define TEXTURE texture2DRect
97 #endif
98 )";
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
104 #else
105 #define TEXTURE texture2D
106 #endif
107 #define SAMPLER samplerExternalOES
110 // -
112 static const char kFragDeclHeader[] = R"(
113 precision PRECISION float;
114 #if __VERSION__ >= 130
115 #define FRAG_COLOR oFragColor
116 out vec4 FRAG_COLOR;
117 #else
118 #define FRAG_COLOR gl_FragColor
119 #endif
122 // -
124 const char* const kFragSample_OnePlane = R"(
125 VARYING mediump vec2 vTexCoord0;
126 uniform PRECISION SAMPLER uTex0;
128 vec4 metaSample() {
129 vec4 src = TEXTURE(uTex0, vTexCoord0);
130 return src;
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;
140 vec4 metaSample() {
141 vec4 yuva = TEXTURE(uTex0, vTexCoord0).gbra;
142 return yuva;
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;
151 vec4 metaSample() {
152 vec4 src = TEXTURE(uTex0, vTexCoord0); // Keep r and a.
153 src.gb = TEXTURE(uTex1, vTexCoord1).rg;
154 return src;
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;
164 vec4 metaSample() {
165 vec4 src = TEXTURE(uTex0, vTexCoord0); // Keep r and a.
166 src.g = TEXTURE(uTex1, vTexCoord1).r;
167 src.b = TEXTURE(uTex2, vTexCoord1).r;
168 return src;
172 // -
174 const char* const kFragConvert_None = R"(
175 vec3 metaConvert(vec3 src) {
176 return src;
179 const char* const kFragConvert_BGR = R"(
180 vec3 metaConvert(vec3 src) {
181 return src.bgr;
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.
201 // We need, for N=2:
202 // v=0.0|N=2 => v'=0.5/2
203 // v=1.0|N=2 => v'=1.5/2
204 // For N=3:
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;
214 // -
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
226 // -
228 static const char kFragBody[] = R"(
229 void main(void) {
230 vec4 src = metaSample();
231 vec4 dst = vec4(metaConvert(src.rgb), src.a);
233 #ifdef MIXIN_ALPHA_MULT_COLORS
234 dst.rgb *= dst.a;
235 #endif
236 #ifdef MIXIN_ALPHA_CLAMP_COLORS
237 dst.rgb = min(dst.rgb, vec3(dst.a)); // Ensure valid premult-alpha colors.
238 #endif
239 #ifdef MIXIN_ALPHA_ONE
240 dst.a = 1.0;
241 #endif
243 FRAG_COLOR = dst;
247 // --
249 Mat3 SubRectMat3(const float x, const float y, const float w, const float h) {
250 auto ret = Mat3{};
251 ret.at(0, 0) = w;
252 ret.at(1, 1) = h;
253 ret.at(2, 0) = x;
254 ret.at(2, 1) = y;
255 ret.at(2, 2) = 1.0f;
256 return ret;
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);
276 // --
278 ScopedSaveMultiTex::ScopedSaveMultiTex(GLContext* const gl,
279 const std::vector<uint8_t>& texUnits,
280 const GLenum texTarget)
281 : mGL(*gl),
282 mTexUnits(texUnits),
283 mTexTarget(texTarget),
284 mOldTexUnit(mGL.GetIntAs<GLenum>(LOCAL_GL_ACTIVE_TEXTURE)) {
285 MOZ_RELEASE_ASSERT(texUnits.size() >= 1);
287 GLenum texBinding;
288 switch (mTexTarget) {
289 case LOCAL_GL_TEXTURE_2D:
290 texBinding = LOCAL_GL_TEXTURE_BINDING_2D;
291 break;
292 case LOCAL_GL_TEXTURE_3D:
293 texBinding = LOCAL_GL_TEXTURE_BINDING_3D;
294 break;
295 case LOCAL_GL_TEXTURE_RECTANGLE:
296 texBinding = LOCAL_GL_TEXTURE_BINDING_RECTANGLE;
297 break;
298 case LOCAL_GL_TEXTURE_EXTERNAL:
299 texBinding = LOCAL_GL_TEXTURE_BINDING_EXTERNAL;
300 break;
301 default:
302 gfxCriticalError() << "Unhandled texTarget: " << texTarget;
303 MOZ_CRASH();
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);
332 // --
334 class ScopedBindArrayBuffer final {
335 public:
336 GLContext& mGL;
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); }
347 // --
349 class ScopedShader final {
350 GLContext& mGL;
351 const GLuint mName;
353 public:
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; }
362 // --
364 class SaveRestoreCurrentProgram final {
365 GLContext& mGL;
366 const GLuint mOld;
368 public:
369 explicit SaveRestoreCurrentProgram(GLContext* const gl)
370 : mGL(*gl), mOld(mGL.GetIntAs<GLuint>(LOCAL_GL_CURRENT_PROGRAM)) {}
372 ~SaveRestoreCurrentProgram() { mGL.fUseProgram(mOld); }
375 // --
377 class ScopedDrawBlitState final {
378 GLContext& mGL;
380 const bool blend;
381 const bool cullFace;
382 const bool depthTest;
383 const bool dither;
384 const bool polyOffsFill;
385 const bool sampleAToC;
386 const bool sampleCover;
387 const bool scissor;
388 const bool stencil;
389 Maybe<bool> rasterizerDiscard;
391 realGLboolean colorMask[4];
392 GLint viewport[4];
394 public:
395 ScopedDrawBlitState(GLContext* const gl, const gfx::IntSize& destSize)
396 : mGL(*gl),
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.
409 rasterizerDiscard =
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);
416 } else {
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],
441 colorMask[3]);
442 } else {
443 mGL.fColorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]);
445 mGL.fViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
449 // --
451 DrawBlitProg::DrawBlitProg(const GLBlitHelper* const parent, const GLuint prog)
452 : mParent(*parent),
453 mProg(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")),
458 mLoc_uColorMatrix(
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};
471 GLint size = 0;
472 GLenum type = 0;
473 for (int32_t i = 0; i < numActiveUniforms; i++) {
474 gl->fGetActiveUniform(mProg, i, kMaxNameSize, nullptr, &size, &type,
475 name);
476 if (strcmp("uColorMatrix", name) == 0) {
477 mType_uColorMatrix = type;
478 break;
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);
499 // --
501 Mat3 destMatrix;
502 if (args.destRect) {
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);
508 } else {
509 destMatrix = Mat3::I();
512 if (args.yFlip) {
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));
528 if (argsYUV) {
529 gl->fUniformMatrix3fv(mLoc_uTexMatrix1, 1, false, argsYUV->texMatrix1.m);
531 if (mLoc_uColorMatrix != -1) {
532 const auto& colorMatrix =
533 gfxUtils::YuvToRgbMatrix4x4ColumnMajor(*argsYUV->colorSpaceForMatrix);
534 float mat4x3[4 * 3];
535 switch (mType_uColorMatrix) {
536 case LOCAL_GL_FLOAT_MAT4:
537 gl->fUniformMatrix4fv(mLoc_uColorMatrix, 1, false, colorMatrix);
538 break;
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);
546 break;
547 default:
548 gfxCriticalError()
549 << "Bad mType_uColorMatrix: " << gfx::hexa(mType_uColorMatrix);
554 // --
556 const ScopedDrawBlitState drawState(gl, args.destSize);
558 GLuint oldVAO;
559 GLint vaa0Enabled;
560 GLint vaa0Size;
561 GLenum vaa0Type;
562 GLint vaa0Normalized;
563 GLsizei vaa0Stride;
564 GLvoid* vaa0Pointer;
565 GLuint vaa0Buffer;
566 if (mParent.mQuadVAO) {
567 oldVAO = gl->GetIntAs<GLuint>(LOCAL_GL_VERTEX_ARRAY_BINDING);
568 gl->fBindVertexArray(mParent.mQuadVAO);
569 } else {
570 // clang-format off
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);
578 // clang-format on
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);
589 } else {
590 if (vaa0Enabled) {
591 gl->fEnableVertexAttribArray(0);
592 } else {
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);
603 // --
605 GLBlitHelper::GLBlitHelper(GLContext* const gl)
606 : mGL(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);
632 // --
634 const auto glslVersion = mGL->ShadingLanguageVersion();
636 if (mGL->IsGLES()) {
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");
644 } else {
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\
656 #else \n\
657 #define ATTRIBUTE attribute \n\
658 #define VARYING varying \n\
659 #endif \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\
670 void main(void) \n\
671 { \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\
677 } \n\
679 const char* const parts[] = {mDrawBlitProg_VersionLine.get(), kVertSource};
680 mGL->fShaderSource(mDrawBlitProg_VertShader, ArrayLength(parts), parts,
681 nullptr);
682 mGL->fCompileShader(mDrawBlitProg_VertShader);
685 GLBlitHelper::~GLBlitHelper() {
686 for (const auto& pair : mDrawBlitProgs) {
687 const auto& ptr = pair.second;
688 delete ptr;
690 mDrawBlitProgs.clear();
692 if (!mGL->MakeCurrent()) return;
694 mGL->fDeleteShader(mDrawBlitProg_VertShader);
695 mGL->fDeleteBuffers(1, &mQuadVBO);
697 if (mQuadVAO) {
698 mGL->fDeleteVertexArrays(1, &mQuadVAO);
702 // --
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;
709 if (didInsert) {
710 pair.second = CreateDrawBlitProg(pair.first);
712 return pair.second;
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) {
720 case 0:
721 precision = "lowp";
722 break;
723 case 1:
724 precision = "mediump";
725 break;
726 default:
727 if (precisionPref != 2) {
728 NS_WARNING("gfx.blithelper.precision clamped to 2.");
730 precision = "MAXP";
731 break;
734 nsPrintfCString precisionLine("\n#define PRECISION %s\n", precision);
736 // -
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) {
750 if (part) {
751 parts.push_back(part);
754 parts.push_back(kFragBody);
757 const auto PrintFragSource = [&]() {
758 printf_stderr("Frag source:\n");
759 int i = 0;
760 for (const auto& part : parts) {
761 printf_stderr("// parts[%i]:\n%s\n", i, part);
762 i += 1;
765 if (gfxEnv::MOZ_DUMP_GLBLITHELPER()) {
766 PrintFragSource();
769 mGL->fShaderSource(fs, AssertedCast<GLint>(parts.size()), parts.data(),
770 nullptr);
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);
780 GLenum status = 0;
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;
802 GLuint vsLogLen = 0;
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());
806 vsLog[vsLogLen] = 0;
808 GLuint fsLogLen = 0;
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());
812 fsLog[fsLogLen] = 0;
814 const auto logs =
815 std::string("DrawBlitProg link failed:\n") + "progLog: " + progLog.get() +
816 "\n" + "vsLog: " + vsLog.get() + "\n" + "fsLog: " + fsLog.get() + "\n";
817 gfxCriticalError() << logs;
819 PrintFragSource();
821 MOZ_CRASH("DrawBlitProg link failed");
824 // -----------------------------------------------------------------------------
826 #ifdef XP_MACOSX
827 static RefPtr<MacIOSurface> LookupSurface(
828 const layers::SurfaceDescriptorMacIOSurface& sd) {
829 return MacIOSurface::LookupSurface(sd.surfaceId(), !sd.isOpaque(),
830 sd.yUVColorSpace());
832 #endif
834 bool GLBlitHelper::BlitSdToFramebuffer(const layers::SurfaceDescriptor& asd,
835 const gfx::IntSize& destSize,
836 const OriginPos destOrigin) {
837 const auto sdType = asd.type();
838 switch (sdType) {
839 case layers::SurfaceDescriptor::TSurfaceDescriptorBuffer: {
840 const auto& sd = asd.get_SurfaceDescriptorBuffer();
841 const auto yuvData = PlanarYCbCrData::From(sd);
842 if (!yuvData) {
843 gfxCriticalNote << "[GLBlitHelper::BlitSdToFramebuffer] "
844 "PlanarYCbCrData::From failed";
845 return false;
847 return BlitPlanarYCbCr(*yuvData, destSize, destOrigin);
849 #ifdef XP_WIN
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);
858 #endif
859 #ifdef XP_MACOSX
860 case layers::SurfaceDescriptor::TSurfaceDescriptorMacIOSurface: {
861 const auto& sd = asd.get_SurfaceDescriptorMacIOSurface();
862 const auto surf = LookupSurface(sd);
863 if (!surf) {
864 NS_WARNING("LookupSurface(MacIOSurface) failed");
865 // Sometimes that frame for our handle gone already. That's life, for
866 // now.
867 return false;
869 return BlitImage(surf, destSize, destOrigin);
871 #endif
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);
878 #endif
879 #ifdef MOZ_WAYLAND
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);
885 #endif
886 default:
887 return false;
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();
904 MOZ_ASSERT(image);
905 auto surfaceTexture =
906 java::GeckoSurfaceTexture::Lookup(image->GetHandle());
907 return Blit(surfaceTexture, destSize, destOrigin);
908 #else
909 MOZ_ASSERT(false);
910 return false;
911 #endif
913 case ImageFormat::MAC_IOSURFACE:
914 #ifdef XP_MACOSX
915 return BlitImage(srcImage->AsMacIOSurfaceImage(), destSize, destOrigin);
916 #else
917 MOZ_ASSERT(false);
918 return false;
919 #endif
921 case ImageFormat::GPU_VIDEO:
922 return BlitImage(static_cast<layers::GPUVideoImage*>(srcImage), destSize,
923 destOrigin);
924 #ifdef XP_WIN
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:
929 return BlitImage(
930 static_cast<layers::D3D11TextureIMFSampleImage*>(srcImage), destSize,
931 destOrigin);
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:
938 return false;
939 #else
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:
945 MOZ_ASSERT(false);
946 return false;
947 #endif
948 case ImageFormat::DMABUF:
949 #ifdef MOZ_WAYLAND
950 return BlitImage(static_cast<layers::DMABUFSurfaceImage*>(srcImage),
951 destSize, destOrigin);
952 #else
953 return false;
954 #endif
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
962 return false;
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) {
972 return false;
975 const ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0);
977 if (!surfaceTexture->IsAttachedToGLContext((int64_t)mGL)) {
978 GLuint tex;
979 mGL->MakeCurrent();
980 mGL->fGenTextures(1, &tex);
982 if (NS_FAILED(surfaceTexture->AttachToGLContext((int64_t)mGL, tex))) {
983 mGL->fDeleteTextures(1, &tex);
984 return false;
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,
998 Nothing()};
999 prog->Draw(baseArgs, nullptr);
1001 if (surfaceTexture->IsSingleBuffer()) {
1002 surfaceTexture->ReleaseTexImage();
1005 return true;
1007 #endif
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) {
1017 return false;
1019 *out_divisors = divisors;
1020 return true;
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();
1039 // --
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;
1051 return false;
1054 gfx::IntSize divisors;
1055 switch (yuvData.mChromaSubsampling) {
1056 case gfx::ChromaSubsampling::FULL:
1057 divisors = gfx::IntSize(1, 1);
1058 break;
1059 case gfx::ChromaSubsampling::HALF_WIDTH:
1060 divisors = gfx::IntSize(2, 1);
1061 break;
1062 case gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT:
1063 divisors = gfx::IntSize(2, 2);
1064 break;
1065 default:
1066 gfxCriticalError() << "Unknown chroma subsampling:"
1067 << int(yuvData.mChromaSubsampling);
1068 return false;
1071 // --
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;
1082 } else {
1083 internalFormat = LOCAL_GL_LUMINANCE;
1084 unpackFormat = LOCAL_GL_LUMINANCE;
1087 // --
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,
1103 nullptr);
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);
1113 // --
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,
1119 yuvData.mYChannel);
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);
1131 // --
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);
1142 return true;
1145 // -------------------------------------
1147 #ifdef XP_MACOSX
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) {
1155 std::string str;
1156 str.reserve(6);
1157 auto u = static_cast<unsigned int>(x);
1158 while (u) {
1159 str.insert(str.begin(), u & 0xff);
1160 u >>= 8;
1162 str.insert(str.begin(), '\'');
1163 str.push_back('\'');
1164 return str;
1167 bool GLBlitHelper::BlitImage(MacIOSurface* const iosurf,
1168 const gfx::IntSize& destSize,
1169 const OriginPos destOrigin) const {
1170 if (!iosurf) {
1171 gfxCriticalError() << "Null MacIOSurface for GLBlitHelper::BlitImage";
1172 return false;
1174 if (mGL->GetContextType() != GLContextType::CGL) {
1175 MOZ_ASSERT(false);
1176 return false;
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();
1195 if (!planes) {
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(),
1215 pixelFormat);
1218 const char* fragSample;
1219 switch (planes) {
1220 case 1:
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;
1231 break;
1232 case kCVPixelFormatType_422YpCbCr8:
1233 case kCVPixelFormatType_422YpCbCr8_yuvs:
1234 fragSample = kFragSample_OnePlane_YUV_via_GBR;
1235 pYuvArgs = &yuvArgs;
1236 break;
1237 default: {
1238 std::string str;
1239 if (pixelFormat <= 0xff) {
1240 str = std::to_string(pixelFormat);
1241 } else {
1242 str = IntAsAscii(pixelFormat);
1244 gfxCriticalError() << "Unhandled kCVPixelFormatType_*: " << str;
1245 // Probably YUV though
1246 fragSample = kFragSample_OnePlane_YUV_via_GBR;
1247 pYuvArgs = &yuvArgs;
1248 break;
1251 break;
1252 case 2:
1253 fragSample = kFragSample_TwoPlane;
1254 pYuvArgs = &yuvArgs;
1255 break;
1256 case 3:
1257 fragSample = kFragSample_ThreePlane;
1258 pYuvArgs = &yuvArgs;
1259 break;
1260 default:
1261 gfxCriticalError() << "Unexpected plane count: " << planes;
1262 return false;
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);
1271 if (err) {
1272 return false;
1275 if (p == 0) {
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);
1288 return true;
1290 #endif
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;
1300 Mat3 texMatrix0;
1301 switch (srcTarget) {
1302 case LOCAL_GL_TEXTURE_2D:
1303 fragHeader = kFragHeader_Tex2D;
1304 texMatrix0 = Mat3::I();
1305 break;
1306 case LOCAL_GL_TEXTURE_RECTANGLE_ARB:
1307 fragHeader = kFragHeader_Tex2DRect;
1308 texMatrix0 = SubRectMat3(0, 0, srcSize.width, srcSize.height);
1309 break;
1310 default:
1311 gfxCriticalError() << "Unexpected srcTarget: " << srcTarget;
1312 return;
1314 const auto fragConvert = srcIsBGRA ? kFragConvert_BGR : kFragConvert_None;
1315 const auto& prog = GetDrawBlitProg({
1316 fragHeader,
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,
1326 Nothing()};
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);
1343 // --
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));
1372 return;
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));
1389 return;
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,
1395 srcSize.height);
1398 void GLBlitHelper::BlitTextureToTexture(GLuint srcTex, GLuint destTex,
1399 const gfx::IntSize& srcSize,
1400 const gfx::IntSize& destSize,
1401 GLenum srcTarget,
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();
1422 MOZ_ASSERT(
1423 desc.type() ==
1424 layers::SurfaceDescriptorGPUVideo::TSurfaceDescriptorRemoteDecoder);
1425 const auto& subdescUnion =
1426 desc.get_SurfaceDescriptorRemoteDecoder().subdesc();
1427 switch (subdescUnion.type()) {
1428 #ifdef MOZ_WAYLAND
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);
1435 #endif
1436 #ifdef XP_WIN
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);
1445 #endif
1446 #ifdef XP_MACOSX
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);
1453 if (!surface) {
1454 return false;
1456 return BlitImage(surface, destSize, destOrigin);
1458 #endif
1459 case layers::RemoteDecoderVideoSubDescriptor::Tnull_t:
1460 // This GPUVideoImage isn't directly readable outside the GPU process.
1461 // Abort.
1462 return false;
1463 default:
1464 gfxCriticalError() << "Unhandled subdesc type: "
1465 << uint32_t(subdescUnion.type());
1466 return false;
1470 // -------------------------------------
1471 #ifdef MOZ_WAYLAND
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();
1488 // -
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)) {
1494 return true;
1497 return false;
1498 }();
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)) {
1510 continue;
1512 if (!surface->CreateTexture(mGL, i)) {
1513 LOGDMABUF(("GLBlitHelper::Blit(): Failed to create DMABuf textures."));
1514 return false;
1516 didCreateTexture = true;
1520 // -
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;
1536 break;
1537 case DMABufSurface::SURFACE_NV12:
1538 fragSample = kFragSample_TwoPlane;
1539 pYuvArgs = &yuvArgs;
1540 fragConvert = kFragConvert_ColorMatrix;
1541 break;
1542 case DMABufSurface::SURFACE_YUV420:
1543 fragSample = kFragSample_ThreePlane;
1544 pYuvArgs = &yuvArgs;
1545 fragConvert = kFragConvert_ColorMatrix;
1546 break;
1547 default:
1548 gfxCriticalError() << "Unexpected pixel format: " << pixelFormat;
1549 return false;
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);
1563 const auto& prog =
1564 GetDrawBlitProg({kFragHeader_Tex2D, {fragSample, fragConvert}});
1565 prog->Draw(baseArgs, pYuvArgs);
1567 return true;
1570 bool GLBlitHelper::BlitImage(layers::DMABUFSurfaceImage* srcImage,
1571 const gfx::IntSize& destSize,
1572 OriginPos destOrigin) const {
1573 DMABufSurface* surface = srcImage->GetSurface();
1574 if (!surface) {
1575 gfxCriticalError() << "Null DMABUFSurface for GLBlitHelper::BlitImage";
1576 return false;
1578 return Blit(surface, destSize, destOrigin);
1580 #endif
1582 // -
1584 template <size_t N>
1585 static void PushUnorm(uint32_t* const out, const float inVal) {
1586 const uint32_t mask = (1 << N) - 1;
1587 auto fval = inVal;
1588 fval = std::max(0.0f, std::min(fval, 1.0f));
1589 fval *= mask;
1590 fval = roundf(fval);
1591 auto ival = static_cast<uint32_t>(fval);
1592 ival &= mask;
1594 *out <<= N;
1595 *out |= ival;
1598 static uint32_t toRgb10A2(const color::vec4& val) {
1599 // R in LSB
1600 uint32_t ret = 0;
1601 PushUnorm<2>(&ret, val.w());
1602 PushUnorm<10>(&ret, val.z());
1603 PushUnorm<10>(&ret, val.y());
1604 PushUnorm<10>(&ret, val.x());
1605 return ret;
1608 std::shared_ptr<gl::Texture> GLBlitHelper::GetColorLutTex(
1609 const ColorLutKey& key) const {
1610 auto& weak = mColorLutTexMap[key];
1611 auto strong = weak.lock();
1612 if (!strong) {
1613 auto& gl = *mGL;
1614 strong = std::make_shared<gl::Texture>(gl);
1615 weak = strong;
1617 const auto ct = color::ColorspaceTransform::Create(key.src, key.dst);
1619 // -
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()));
1628 } else {
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;
1638 // -
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;
1649 if (useFloat16) {
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(),
1660 size.z());
1661 gl.fTexSubImage3D(target, 0, 0, 0, 0, size.x(), size.y(), size.z(),
1662 LOCAL_GL_RGBA, LOCAL_GL_FLOAT, uploadData.data());
1663 } else {
1664 // Use Rgb10A2
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(),
1673 size.z());
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,
1676 uploadData.data());
1679 return strong;
1682 } // namespace gl
1683 } // namespace mozilla