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