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