Bug 1770235 - DrawBlitProg::Draw non-vao path must restore vaa0's buffer. r=gfx-revie...
[gecko.git] / gfx / gl / GLBlitHelper.cpp
blob403413dec406f08ab7320150a96c1ab16b105153
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 "GLContext.h"
10 #include "GLScreenBuffer.h"
11 #include "GPUVideoImage.h"
12 #include "HeapCopyOfStackArray.h"
13 #include "ImageContainer.h"
14 #include "ScopedGLHelpers.h"
15 #include "gfxUtils.h"
16 #include "mozilla/ArrayUtils.h"
17 #include "mozilla/Preferences.h"
18 #include "mozilla/UniquePtr.h"
19 #include "mozilla/gfx/Logging.h"
20 #include "mozilla/gfx/Matrix.h"
21 #include "mozilla/layers/ImageDataSerializer.h"
22 #include "mozilla/layers/LayersSurfaces.h"
24 #ifdef MOZ_WIDGET_ANDROID
25 # include "AndroidSurfaceTexture.h"
26 # include "GLLibraryEGL.h"
27 #endif
29 #ifdef XP_MACOSX
30 # include "GLContextCGL.h"
31 # include "MacIOSurfaceImage.h"
32 #endif
34 #ifdef XP_WIN
35 # include "mozilla/layers/D3D11ShareHandleImage.h"
36 # include "mozilla/layers/D3D11TextureIMFSampleImage.h"
37 # include "mozilla/layers/D3D11YCbCrImage.h"
38 #endif
40 #ifdef MOZ_WAYLAND
41 # include "mozilla/layers/DMABUFSurfaceImage.h"
42 # include "mozilla/widget/DMABufSurface.h"
43 #endif
45 using mozilla::layers::PlanarYCbCrData;
46 using mozilla::layers::PlanarYCbCrImage;
48 namespace mozilla {
49 namespace gl {
51 // --
53 const char* const kFragHeader_Tex2D =
55 #define SAMPLER sampler2D \n\
56 #if __VERSION__ >= 130 \n\
57 #define TEXTURE texture \n\
58 #else \n\
59 #define TEXTURE texture2D \n\
60 #endif \n\
62 const char* const kFragHeader_Tex2DRect =
64 #define SAMPLER sampler2DRect \n\
65 #if __VERSION__ >= 130 \n\
66 #define TEXTURE texture \n\
67 #else \n\
68 #define TEXTURE texture2DRect \n\
69 #endif \n\
71 const char* const kFragHeader_TexExt =
73 #extension GL_OES_EGL_image_external : require \n\
74 #if __VERSION__ >= 130 \n\
75 #define TEXTURE texture \n\
76 #else \n\
77 #define TEXTURE texture2D \n\
78 #endif \n\
79 #define SAMPLER samplerExternalOES \n\
82 const char* const kFragBody_RGBA =
84 VARYING vec2 vTexCoord0; \n\
85 uniform SAMPLER uTex0; \n\
86 \n\
87 void main(void) \n\
88 { \n\
89 FRAG_COLOR = TEXTURE(uTex0, vTexCoord0); \n\
90 } \n\
92 const char* const kFragBody_BGRA =
94 VARYING vec2 vTexCoord0; \n\
95 uniform SAMPLER uTex0; \n\
96 \n\
97 void main(void) \n\
98 { \n\
99 FRAG_COLOR = TEXTURE(uTex0, vTexCoord0).bgra; \n\
100 } \n\
102 const char* const kFragBody_CrYCb =
104 VARYING vec2 vTexCoord0; \n\
105 uniform SAMPLER uTex0; \n\
106 uniform MAT4X3 uColorMatrix; \n\
108 void main(void) \n\
109 { \n\
110 vec4 yuv = vec4(TEXTURE(uTex0, vTexCoord0).gbr, \n\
111 1.0); \n\
112 FRAG_COLOR = vec4((uColorMatrix * yuv).rgb, 1.0); \n\
113 } \n\
115 const char* const kFragBody_NV12 =
117 VARYING vec2 vTexCoord0; \n\
118 VARYING vec2 vTexCoord1; \n\
119 uniform SAMPLER uTex0; \n\
120 uniform SAMPLER uTex1; \n\
121 uniform MAT4X3 uColorMatrix; \n\
123 void main(void) \n\
124 { \n\
125 vec4 yuv = vec4(TEXTURE(uTex0, vTexCoord0).x, \n\
126 TEXTURE(uTex1, vTexCoord1).xy, \n\
127 1.0); \n\
128 FRAG_COLOR = vec4((uColorMatrix * yuv).rgb, 1.0); \n\
129 } \n\
131 const char* const kFragBody_PlanarYUV =
133 VARYING vec2 vTexCoord0; \n\
134 VARYING vec2 vTexCoord1; \n\
135 uniform SAMPLER uTex0; \n\
136 uniform SAMPLER uTex1; \n\
137 uniform SAMPLER uTex2; \n\
138 uniform MAT4X3 uColorMatrix; \n\
140 void main(void) \n\
141 { \n\
142 vec4 yuv = vec4(TEXTURE(uTex0, vTexCoord0).x, \n\
143 TEXTURE(uTex1, vTexCoord1).x, \n\
144 TEXTURE(uTex2, vTexCoord1).x, \n\
145 1.0); \n\
146 FRAG_COLOR = vec4((uColorMatrix * yuv).rgb, 1.0); \n\
147 } \n\
150 // --
152 template <uint8_t N>
153 /*static*/ Mat<N> Mat<N>::Zero() {
154 Mat<N> ret;
155 for (auto& x : ret.m) {
156 x = 0.0f;
158 return ret;
161 template <uint8_t N>
162 /*static*/ Mat<N> Mat<N>::I() {
163 auto ret = Mat<N>::Zero();
164 for (uint8_t i = 0; i < N; i++) {
165 ret.at(i, i) = 1.0f;
167 return ret;
170 template <uint8_t N>
171 Mat<N> Mat<N>::operator*(const Mat<N>& r) const {
172 Mat<N> ret;
173 for (uint8_t x = 0; x < N; x++) {
174 for (uint8_t y = 0; y < N; y++) {
175 float sum = 0.0f;
176 for (uint8_t i = 0; i < N; i++) {
177 sum += at(i, y) * r.at(x, i);
179 ret.at(x, y) = sum;
182 return ret;
185 Mat3 SubRectMat3(const float x, const float y, const float w, const float h) {
186 auto ret = Mat3::Zero();
187 ret.at(0, 0) = w;
188 ret.at(1, 1) = h;
189 ret.at(2, 0) = x;
190 ret.at(2, 1) = y;
191 ret.at(2, 2) = 1.0f;
192 return ret;
195 Mat3 SubRectMat3(const gfx::IntRect& subrect, const gfx::IntSize& size) {
196 return SubRectMat3(float(subrect.X()) / size.width,
197 float(subrect.Y()) / size.height,
198 float(subrect.Width()) / size.width,
199 float(subrect.Height()) / size.height);
202 Mat3 SubRectMat3(const gfx::IntRect& bigSubrect, const gfx::IntSize& smallSize,
203 const gfx::IntSize& divisors) {
204 const float x = float(bigSubrect.X()) / divisors.width;
205 const float y = float(bigSubrect.Y()) / divisors.height;
206 const float w = float(bigSubrect.Width()) / divisors.width;
207 const float h = float(bigSubrect.Height()) / divisors.height;
208 return SubRectMat3(x / smallSize.width, y / smallSize.height,
209 w / smallSize.width, h / smallSize.height);
212 // --
214 ScopedSaveMultiTex::ScopedSaveMultiTex(GLContext* const gl,
215 const uint8_t texCount,
216 const GLenum texTarget)
217 : mGL(*gl),
218 mTexCount(texCount),
219 mTexTarget(texTarget),
220 mOldTexUnit(mGL.GetIntAs<GLenum>(LOCAL_GL_ACTIVE_TEXTURE)) {
221 GLenum texBinding;
222 switch (mTexTarget) {
223 case LOCAL_GL_TEXTURE_2D:
224 texBinding = LOCAL_GL_TEXTURE_BINDING_2D;
225 break;
226 case LOCAL_GL_TEXTURE_RECTANGLE:
227 texBinding = LOCAL_GL_TEXTURE_BINDING_RECTANGLE;
228 break;
229 case LOCAL_GL_TEXTURE_EXTERNAL:
230 texBinding = LOCAL_GL_TEXTURE_BINDING_EXTERNAL;
231 break;
232 default:
233 gfxCriticalError() << "Unhandled texTarget: " << texTarget;
236 for (uint8_t i = 0; i < mTexCount; i++) {
237 mGL.fActiveTexture(LOCAL_GL_TEXTURE0 + i);
238 if (mGL.IsSupported(GLFeature::sampler_objects)) {
239 mOldTexSampler[i] = mGL.GetIntAs<GLuint>(LOCAL_GL_SAMPLER_BINDING);
240 mGL.fBindSampler(i, 0);
242 mOldTex[i] = mGL.GetIntAs<GLuint>(texBinding);
246 ScopedSaveMultiTex::~ScopedSaveMultiTex() {
247 for (uint8_t i = 0; i < mTexCount; i++) {
248 mGL.fActiveTexture(LOCAL_GL_TEXTURE0 + i);
249 if (mGL.IsSupported(GLFeature::sampler_objects)) {
250 mGL.fBindSampler(i, mOldTexSampler[i]);
252 mGL.fBindTexture(mTexTarget, mOldTex[i]);
254 mGL.fActiveTexture(mOldTexUnit);
257 // --
259 class ScopedBindArrayBuffer final {
260 public:
261 GLContext& mGL;
262 const GLuint mOldVBO;
264 ScopedBindArrayBuffer(GLContext* const gl, const GLuint vbo)
265 : mGL(*gl), mOldVBO(mGL.GetIntAs<GLuint>(LOCAL_GL_ARRAY_BUFFER_BINDING)) {
266 mGL.fBindBuffer(LOCAL_GL_ARRAY_BUFFER, vbo);
269 ~ScopedBindArrayBuffer() { mGL.fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mOldVBO); }
272 // --
274 class ScopedShader final {
275 GLContext& mGL;
276 const GLuint mName;
278 public:
279 ScopedShader(GLContext* const gl, const GLenum shaderType)
280 : mGL(*gl), mName(mGL.fCreateShader(shaderType)) {}
282 ~ScopedShader() { mGL.fDeleteShader(mName); }
284 operator GLuint() const { return mName; }
287 // --
289 class SaveRestoreCurrentProgram final {
290 GLContext& mGL;
291 const GLuint mOld;
293 public:
294 explicit SaveRestoreCurrentProgram(GLContext* const gl)
295 : mGL(*gl), mOld(mGL.GetIntAs<GLuint>(LOCAL_GL_CURRENT_PROGRAM)) {}
297 ~SaveRestoreCurrentProgram() { mGL.fUseProgram(mOld); }
300 // --
302 class ScopedDrawBlitState final {
303 GLContext& mGL;
305 const bool blend;
306 const bool cullFace;
307 const bool depthTest;
308 const bool dither;
309 const bool polyOffsFill;
310 const bool sampleAToC;
311 const bool sampleCover;
312 const bool scissor;
313 const bool stencil;
314 Maybe<bool> rasterizerDiscard;
316 realGLboolean colorMask[4];
317 GLint viewport[4];
319 public:
320 ScopedDrawBlitState(GLContext* const gl, const gfx::IntSize& destSize)
321 : mGL(*gl),
322 blend(mGL.PushEnabled(LOCAL_GL_BLEND, false)),
323 cullFace(mGL.PushEnabled(LOCAL_GL_CULL_FACE, false)),
324 depthTest(mGL.PushEnabled(LOCAL_GL_DEPTH_TEST, false)),
325 dither(mGL.PushEnabled(LOCAL_GL_DITHER, true)),
326 polyOffsFill(mGL.PushEnabled(LOCAL_GL_POLYGON_OFFSET_FILL, false)),
327 sampleAToC(mGL.PushEnabled(LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE, false)),
328 sampleCover(mGL.PushEnabled(LOCAL_GL_SAMPLE_COVERAGE, false)),
329 scissor(mGL.PushEnabled(LOCAL_GL_SCISSOR_TEST, false)),
330 stencil(mGL.PushEnabled(LOCAL_GL_STENCIL_TEST, false)) {
331 if (mGL.IsSupported(GLFeature::transform_feedback2)) {
332 // Technically transform_feedback2 requires transform_feedback, which
333 // actually adds RASTERIZER_DISCARD.
334 rasterizerDiscard =
335 Some(mGL.PushEnabled(LOCAL_GL_RASTERIZER_DISCARD, false));
338 mGL.fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK, colorMask);
339 if (mGL.IsSupported(GLFeature::draw_buffers_indexed)) {
340 mGL.fColorMaski(0, true, true, true, true);
341 } else {
342 mGL.fColorMask(true, true, true, true);
345 mGL.fGetIntegerv(LOCAL_GL_VIEWPORT, viewport);
346 MOZ_ASSERT(destSize.width && destSize.height);
347 mGL.fViewport(0, 0, destSize.width, destSize.height);
350 ~ScopedDrawBlitState() {
351 mGL.SetEnabled(LOCAL_GL_BLEND, blend);
352 mGL.SetEnabled(LOCAL_GL_CULL_FACE, cullFace);
353 mGL.SetEnabled(LOCAL_GL_DEPTH_TEST, depthTest);
354 mGL.SetEnabled(LOCAL_GL_DITHER, dither);
355 mGL.SetEnabled(LOCAL_GL_POLYGON_OFFSET_FILL, polyOffsFill);
356 mGL.SetEnabled(LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE, sampleAToC);
357 mGL.SetEnabled(LOCAL_GL_SAMPLE_COVERAGE, sampleCover);
358 mGL.SetEnabled(LOCAL_GL_SCISSOR_TEST, scissor);
359 mGL.SetEnabled(LOCAL_GL_STENCIL_TEST, stencil);
360 if (rasterizerDiscard) {
361 mGL.SetEnabled(LOCAL_GL_RASTERIZER_DISCARD, rasterizerDiscard.value());
364 if (mGL.IsSupported(GLFeature::draw_buffers_indexed)) {
365 mGL.fColorMaski(0, colorMask[0], colorMask[1], colorMask[2],
366 colorMask[3]);
367 } else {
368 mGL.fColorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]);
370 mGL.fViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
374 // --
376 DrawBlitProg::DrawBlitProg(const GLBlitHelper* const parent, const GLuint prog)
377 : mParent(*parent),
378 mProg(prog),
379 mLoc_uDestMatrix(mParent.mGL->fGetUniformLocation(mProg, "uDestMatrix")),
380 mLoc_uTexMatrix0(mParent.mGL->fGetUniformLocation(mProg, "uTexMatrix0")),
381 mLoc_uTexMatrix1(mParent.mGL->fGetUniformLocation(mProg, "uTexMatrix1")),
382 mLoc_uColorMatrix(
383 mParent.mGL->fGetUniformLocation(mProg, "uColorMatrix")) {
384 const auto& gl = mParent.mGL;
385 MOZ_GL_ASSERT(gl, mLoc_uDestMatrix != -1);
386 MOZ_GL_ASSERT(gl, mLoc_uTexMatrix0 != -1);
387 if (mLoc_uColorMatrix != -1) {
388 MOZ_GL_ASSERT(gl, mLoc_uTexMatrix1 != -1);
390 int32_t numActiveUniforms = 0;
391 gl->fGetProgramiv(mProg, LOCAL_GL_ACTIVE_UNIFORMS, &numActiveUniforms);
393 const size_t kMaxNameSize = 32;
394 char name[kMaxNameSize] = {0};
395 GLint size = 0;
396 GLenum type = 0;
397 for (int32_t i = 0; i < numActiveUniforms; i++) {
398 gl->fGetActiveUniform(mProg, i, kMaxNameSize, nullptr, &size, &type,
399 name);
400 if (strcmp("uColorMatrix", name) == 0) {
401 mType_uColorMatrix = type;
402 break;
405 MOZ_GL_ASSERT(gl, mType_uColorMatrix);
409 DrawBlitProg::~DrawBlitProg() {
410 const auto& gl = mParent.mGL;
411 if (!gl->MakeCurrent()) return;
413 gl->fDeleteProgram(mProg);
416 void DrawBlitProg::Draw(const BaseArgs& args,
417 const YUVArgs* const argsYUV) const {
418 const auto& gl = mParent.mGL;
420 const SaveRestoreCurrentProgram oldProg(gl);
421 gl->fUseProgram(mProg);
423 // --
425 Mat3 destMatrix;
426 if (args.destRect) {
427 const auto& destRect = args.destRect.value();
428 destMatrix = SubRectMat3(destRect.X() / args.destSize.width,
429 destRect.Y() / args.destSize.height,
430 destRect.Width() / args.destSize.width,
431 destRect.Height() / args.destSize.height);
432 } else {
433 destMatrix = Mat3::I();
436 if (args.yFlip) {
437 // Apply the y-flip matrix before the destMatrix.
438 // That is, flip y=[0-1] to y=[1-0] before we restrict to the destRect.
439 destMatrix.at(2, 1) += destMatrix.at(1, 1);
440 destMatrix.at(1, 1) *= -1.0f;
443 gl->fUniformMatrix3fv(mLoc_uDestMatrix, 1, false, destMatrix.m);
444 gl->fUniformMatrix3fv(mLoc_uTexMatrix0, 1, false, args.texMatrix0.m);
446 MOZ_ASSERT(bool(argsYUV) == (mLoc_uColorMatrix != -1));
447 if (argsYUV) {
448 gl->fUniformMatrix3fv(mLoc_uTexMatrix1, 1, false, argsYUV->texMatrix1.m);
450 const auto& colorMatrix =
451 gfxUtils::YuvToRgbMatrix4x4ColumnMajor(argsYUV->colorSpace);
452 float mat4x3[4 * 3];
453 switch (mType_uColorMatrix) {
454 case LOCAL_GL_FLOAT_MAT4:
455 gl->fUniformMatrix4fv(mLoc_uColorMatrix, 1, false, colorMatrix);
456 break;
457 case LOCAL_GL_FLOAT_MAT4x3:
458 for (int x = 0; x < 4; x++) {
459 for (int y = 0; y < 3; y++) {
460 mat4x3[3 * x + y] = colorMatrix[4 * x + y];
463 gl->fUniformMatrix4x3fv(mLoc_uColorMatrix, 1, false, mat4x3);
464 break;
465 default:
466 gfxCriticalError() << "Bad mType_uColorMatrix: "
467 << gfx::hexa(mType_uColorMatrix);
471 // --
473 const ScopedDrawBlitState drawState(gl, args.destSize);
475 GLuint oldVAO;
476 GLint vaa0Enabled;
477 GLint vaa0Size;
478 GLenum vaa0Type;
479 GLint vaa0Normalized;
480 GLsizei vaa0Stride;
481 GLvoid* vaa0Pointer;
482 GLuint vaa0Buffer;
483 if (mParent.mQuadVAO) {
484 oldVAO = gl->GetIntAs<GLuint>(LOCAL_GL_VERTEX_ARRAY_BINDING);
485 gl->fBindVertexArray(mParent.mQuadVAO);
486 } else {
487 // clang-format off
488 gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED, &vaa0Enabled);
489 gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE, &vaa0Size);
490 gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE, (GLint*)&vaa0Type);
491 gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &vaa0Normalized);
492 gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE, (GLint*)&vaa0Stride);
493 gl->fGetVertexAttribPointerv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER, &vaa0Pointer);
494 // clang-format on
496 gl->fEnableVertexAttribArray(0);
497 const ScopedBindArrayBuffer bindVBO(gl, mParent.mQuadVBO);
498 vaa0Buffer = bindVBO.mOldVBO;
499 gl->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, false, 0, 0);
502 gl->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
504 if (mParent.mQuadVAO) {
505 gl->fBindVertexArray(oldVAO);
506 } else {
507 if (vaa0Enabled) {
508 gl->fEnableVertexAttribArray(0);
509 } else {
510 gl->fDisableVertexAttribArray(0);
512 // The current VERTEX_ARRAY_BINDING is not necessarily the same as the
513 // buffer set for vaa0Buffer.
514 const ScopedBindArrayBuffer bindVBO(gl, vaa0Buffer);
515 gl->fVertexAttribPointer(0, vaa0Size, vaa0Type, bool(vaa0Normalized),
516 vaa0Stride, vaa0Pointer);
520 // --
522 GLBlitHelper::GLBlitHelper(GLContext* const gl)
523 : mGL(gl),
524 mDrawBlitProg_VertShader(mGL->fCreateShader(LOCAL_GL_VERTEX_SHADER))
525 //, mYuvUploads_YSize(0, 0)
526 //, mYuvUploads_UVSize(0, 0)
528 mGL->fGenBuffers(1, &mQuadVBO);
530 const ScopedBindArrayBuffer bindVBO(mGL, mQuadVBO);
532 const float quadData[] = {0, 0, 1, 0, 0, 1, 1, 1};
533 const HeapCopyOfStackArray<float> heapQuadData(quadData);
534 mGL->fBufferData(LOCAL_GL_ARRAY_BUFFER, heapQuadData.ByteLength(),
535 heapQuadData.Data(), LOCAL_GL_STATIC_DRAW);
537 if (mGL->IsSupported(GLFeature::vertex_array_object)) {
538 const auto prev = mGL->GetIntAs<GLuint>(LOCAL_GL_VERTEX_ARRAY_BINDING);
540 mGL->fGenVertexArrays(1, &mQuadVAO);
541 mGL->fBindVertexArray(mQuadVAO);
542 mGL->fEnableVertexAttribArray(0);
543 mGL->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, false, 0, 0);
545 mGL->fBindVertexArray(prev);
549 // --
551 const auto glslVersion = mGL->ShadingLanguageVersion();
553 // Always use 100 on ES because some devices have OES_EGL_image_external but
554 // not OES_EGL_image_external_essl3. We could just use 100 in that particular
555 // case, but this is a lot easier and is not harmful to other usages.
556 if (mGL->IsGLES()) {
557 mDrawBlitProg_VersionLine = nsCString("#version 100\n");
558 } else if (glslVersion >= 130) {
559 mDrawBlitProg_VersionLine = nsPrintfCString("#version %u\n", glslVersion);
562 const char kVertSource[] =
564 #if __VERSION__ >= 130 \n\
565 #define ATTRIBUTE in \n\
566 #define VARYING out \n\
567 #else \n\
568 #define ATTRIBUTE attribute \n\
569 #define VARYING varying \n\
570 #endif \n\
572 ATTRIBUTE vec2 aVert; // [0.0-1.0] \n\
574 uniform mat3 uDestMatrix; \n\
575 uniform mat3 uTexMatrix0; \n\
576 uniform mat3 uTexMatrix1; \n\
578 VARYING vec2 vTexCoord0; \n\
579 VARYING vec2 vTexCoord1; \n\
581 void main(void) \n\
582 { \n\
583 vec2 destPos = (uDestMatrix * vec3(aVert, 1.0)).xy; \n\
584 gl_Position = vec4(destPos * 2.0 - 1.0, 0.0, 1.0); \n\
586 vTexCoord0 = (uTexMatrix0 * vec3(aVert, 1.0)).xy; \n\
587 vTexCoord1 = (uTexMatrix1 * vec3(aVert, 1.0)).xy; \n\
588 } \n\
590 const char* const parts[] = {mDrawBlitProg_VersionLine.get(), kVertSource};
591 mGL->fShaderSource(mDrawBlitProg_VertShader, ArrayLength(parts), parts,
592 nullptr);
593 mGL->fCompileShader(mDrawBlitProg_VertShader);
596 GLBlitHelper::~GLBlitHelper() {
597 for (const auto& pair : mDrawBlitProgs) {
598 const auto& ptr = pair.second;
599 delete ptr;
601 mDrawBlitProgs.clear();
603 if (!mGL->MakeCurrent()) return;
605 mGL->fDeleteShader(mDrawBlitProg_VertShader);
606 mGL->fDeleteBuffers(1, &mQuadVBO);
608 if (mQuadVAO) {
609 mGL->fDeleteVertexArrays(1, &mQuadVAO);
613 // --
615 const DrawBlitProg* GLBlitHelper::GetDrawBlitProg(
616 const DrawBlitProg::Key& key) const {
617 const auto& res = mDrawBlitProgs.insert({key, nullptr});
618 auto& pair = *(res.first);
619 const auto& didInsert = res.second;
620 if (didInsert) {
621 pair.second = CreateDrawBlitProg(pair.first);
623 return pair.second;
626 const DrawBlitProg* GLBlitHelper::CreateDrawBlitProg(
627 const DrawBlitProg::Key& key) const {
628 const char kFragHeader_Global[] =
630 #ifdef GL_ES \n\
631 #ifdef GL_FRAGMENT_PRECISION_HIGH \n\
632 precision highp float; \n\
633 #else \n\
634 precision mediump float; \n\
635 #endif \n\
636 #endif \n\
638 #if __VERSION__ >= 130 \n\
639 #define VARYING in \n\
640 #define FRAG_COLOR oFragColor \n\
641 out vec4 FRAG_COLOR; \n\
642 #else \n\
643 #define VARYING varying \n\
644 #define FRAG_COLOR gl_FragColor \n\
645 #endif \n\
647 #if __VERSION__ >= 120 \n\
648 #define MAT4X3 mat4x3 \n\
649 #else \n\
650 #define MAT4X3 mat4 \n\
651 #endif \n\
654 const ScopedShader fs(mGL, LOCAL_GL_FRAGMENT_SHADER);
655 const char* const parts[] = {mDrawBlitProg_VersionLine.get(), key.fragHeader,
656 kFragHeader_Global, key.fragBody};
657 mGL->fShaderSource(fs, ArrayLength(parts), parts, nullptr);
658 mGL->fCompileShader(fs);
660 const auto prog = mGL->fCreateProgram();
661 mGL->fAttachShader(prog, mDrawBlitProg_VertShader);
662 mGL->fAttachShader(prog, fs);
664 mGL->fBindAttribLocation(prog, 0, "aPosition");
665 mGL->fLinkProgram(prog);
667 GLenum status = 0;
668 mGL->fGetProgramiv(prog, LOCAL_GL_LINK_STATUS, (GLint*)&status);
669 if (status == LOCAL_GL_TRUE || !mGL->CheckContextLost()) {
670 const SaveRestoreCurrentProgram oldProg(mGL);
671 mGL->fUseProgram(prog);
672 const char* samplerNames[] = {"uTex0", "uTex1", "uTex2"};
673 for (int i = 0; i < 3; i++) {
674 const auto loc = mGL->fGetUniformLocation(prog, samplerNames[i]);
675 if (loc == -1) break;
676 mGL->fUniform1i(loc, i);
679 return new DrawBlitProg(this, prog);
682 GLuint progLogLen = 0;
683 mGL->fGetProgramiv(prog, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&progLogLen);
684 const UniquePtr<char[]> progLog(new char[progLogLen + 1]);
685 mGL->fGetProgramInfoLog(prog, progLogLen, nullptr, progLog.get());
686 progLog[progLogLen] = 0;
688 const auto& vs = mDrawBlitProg_VertShader;
689 GLuint vsLogLen = 0;
690 mGL->fGetShaderiv(vs, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&vsLogLen);
691 const UniquePtr<char[]> vsLog(new char[vsLogLen + 1]);
692 mGL->fGetShaderInfoLog(vs, vsLogLen, nullptr, vsLog.get());
693 vsLog[vsLogLen] = 0;
695 GLuint fsLogLen = 0;
696 mGL->fGetShaderiv(fs, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&fsLogLen);
697 const UniquePtr<char[]> fsLog(new char[fsLogLen + 1]);
698 mGL->fGetShaderInfoLog(fs, fsLogLen, nullptr, fsLog.get());
699 fsLog[fsLogLen] = 0;
701 gfxCriticalError() << "DrawBlitProg link failed:\n"
702 << "progLog: " << progLog.get() << "\n"
703 << "vsLog: " << vsLog.get() << "\n"
704 << "fsLog: " << fsLog.get() << "\n";
705 MOZ_CRASH();
708 // -----------------------------------------------------------------------------
710 #ifdef XP_MACOSX
711 static RefPtr<MacIOSurface> LookupSurface(
712 const layers::SurfaceDescriptorMacIOSurface& sd) {
713 return MacIOSurface::LookupSurface(sd.surfaceId(), !sd.isOpaque(),
714 sd.yUVColorSpace());
716 #endif
718 bool GLBlitHelper::BlitSdToFramebuffer(const layers::SurfaceDescriptor& asd,
719 const gfx::IntSize& destSize,
720 const OriginPos destOrigin) {
721 const auto sdType = asd.type();
722 switch (sdType) {
723 case layers::SurfaceDescriptor::TSurfaceDescriptorBuffer: {
724 const auto& sd = asd.get_SurfaceDescriptorBuffer();
725 const auto yuvData = PlanarYCbCrData::From(sd);
726 if (!yuvData) {
727 gfxCriticalNote << "[GLBlitHelper::BlitSdToFramebuffer] "
728 "PlanarYCbCrData::From failed";
729 return false;
731 return BlitPlanarYCbCr(*yuvData, destSize, destOrigin);
733 #ifdef XP_WIN
734 case layers::SurfaceDescriptor::TSurfaceDescriptorD3D10: {
735 const auto& sd = asd.get_SurfaceDescriptorD3D10();
736 return BlitDescriptor(sd, destSize, destOrigin);
738 case layers::SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr: {
739 const auto& sd = asd.get_SurfaceDescriptorDXGIYCbCr();
740 return BlitDescriptor(sd, destSize, destOrigin);
742 #endif
743 #ifdef XP_MACOSX
744 case layers::SurfaceDescriptor::TSurfaceDescriptorMacIOSurface: {
745 const auto& sd = asd.get_SurfaceDescriptorMacIOSurface();
746 const auto surf = LookupSurface(sd);
747 if (!surf) {
748 NS_WARNING("LookupSurface(MacIOSurface) failed");
749 // Sometimes that frame for our handle gone already. That's life, for
750 // now.
751 return false;
753 return BlitImage(surf, destSize, destOrigin);
755 #endif
756 #ifdef MOZ_WIDGET_ANDROID
757 case layers::SurfaceDescriptor::TSurfaceTextureDescriptor: {
758 const auto& sd = asd.get_SurfaceTextureDescriptor();
759 auto surfaceTexture = java::GeckoSurfaceTexture::Lookup(sd.handle());
760 return Blit(surfaceTexture, destSize, destOrigin);
762 #endif
763 default:
764 return false;
768 bool GLBlitHelper::BlitImageToFramebuffer(layers::Image* const srcImage,
769 const gfx::IntSize& destSize,
770 const OriginPos destOrigin) {
771 switch (srcImage->GetFormat()) {
772 case ImageFormat::PLANAR_YCBCR: {
773 const auto srcImage2 = static_cast<PlanarYCbCrImage*>(srcImage);
774 const auto data = srcImage2->GetData();
775 return BlitPlanarYCbCr(*data, destSize, destOrigin);
778 case ImageFormat::SURFACE_TEXTURE: {
779 #ifdef MOZ_WIDGET_ANDROID
780 auto* image = srcImage->AsSurfaceTextureImage();
781 MOZ_ASSERT(image);
782 auto surfaceTexture =
783 java::GeckoSurfaceTexture::Lookup(image->GetHandle());
784 return Blit(surfaceTexture, destSize, destOrigin);
785 #else
786 MOZ_ASSERT(false);
787 return false;
788 #endif
790 case ImageFormat::MAC_IOSURFACE:
791 #ifdef XP_MACOSX
792 return BlitImage(srcImage->AsMacIOSurfaceImage(), destSize, destOrigin);
793 #else
794 MOZ_ASSERT(false);
795 return false;
796 #endif
798 case ImageFormat::GPU_VIDEO:
799 return BlitImage(static_cast<layers::GPUVideoImage*>(srcImage), destSize,
800 destOrigin);
801 #ifdef XP_WIN
802 case ImageFormat::D3D11_SHARE_HANDLE_TEXTURE:
803 return BlitImage(static_cast<layers::D3D11ShareHandleImage*>(srcImage),
804 destSize, destOrigin);
805 case ImageFormat::D3D11_TEXTURE_IMF_SAMPLE:
806 return BlitImage(
807 static_cast<layers::D3D11TextureIMFSampleImage*>(srcImage), destSize,
808 destOrigin);
809 case ImageFormat::D3D11_YCBCR_IMAGE:
810 return BlitImage(static_cast<layers::D3D11YCbCrImage*>(srcImage),
811 destSize, destOrigin);
812 case ImageFormat::D3D9_RGB32_TEXTURE:
813 return false; // todo
814 #else
815 case ImageFormat::D3D11_SHARE_HANDLE_TEXTURE:
816 case ImageFormat::D3D11_TEXTURE_IMF_SAMPLE:
817 case ImageFormat::D3D11_YCBCR_IMAGE:
818 case ImageFormat::D3D9_RGB32_TEXTURE:
819 MOZ_ASSERT(false);
820 return false;
821 #endif
822 case ImageFormat::DMABUF:
823 #ifdef MOZ_WAYLAND
824 return BlitImage(static_cast<layers::DMABUFSurfaceImage*>(srcImage),
825 destSize, destOrigin);
826 #else
827 return false;
828 #endif
829 case ImageFormat::CAIRO_SURFACE:
830 case ImageFormat::NV_IMAGE:
831 case ImageFormat::OVERLAY_IMAGE:
832 case ImageFormat::SHARED_RGB:
833 case ImageFormat::TEXTURE_WRAPPER:
834 return false; // todo
836 return false;
839 // -------------------------------------
841 #ifdef MOZ_WIDGET_ANDROID
842 bool GLBlitHelper::Blit(const java::GeckoSurfaceTexture::Ref& surfaceTexture,
843 const gfx::IntSize& destSize,
844 const OriginPos destOrigin) const {
845 if (!surfaceTexture) {
846 return false;
849 const ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0);
851 if (!surfaceTexture->IsAttachedToGLContext((int64_t)mGL)) {
852 GLuint tex;
853 mGL->MakeCurrent();
854 mGL->fGenTextures(1, &tex);
856 if (NS_FAILED(surfaceTexture->AttachToGLContext((int64_t)mGL, tex))) {
857 mGL->fDeleteTextures(1, &tex);
858 return false;
862 const ScopedBindTexture savedTex(mGL, surfaceTexture->GetTexName(),
863 LOCAL_GL_TEXTURE_EXTERNAL);
864 surfaceTexture->UpdateTexImage();
865 const auto transform3 = Mat3::I();
866 const auto srcOrigin = OriginPos::TopLeft;
867 const bool yFlip = (srcOrigin != destOrigin);
868 const auto& prog = GetDrawBlitProg({kFragHeader_TexExt, kFragBody_RGBA});
869 const DrawBlitProg::BaseArgs baseArgs = {transform3, yFlip, destSize,
870 Nothing()};
871 prog->Draw(baseArgs, nullptr);
873 if (surfaceTexture->IsSingleBuffer()) {
874 surfaceTexture->ReleaseTexImage();
877 return true;
879 #endif
881 // -------------------------------------
883 bool GuessDivisors(const gfx::IntSize& ySize, const gfx::IntSize& uvSize,
884 gfx::IntSize* const out_divisors) {
885 const gfx::IntSize divisors((ySize.width == uvSize.width) ? 1 : 2,
886 (ySize.height == uvSize.height) ? 1 : 2);
887 if (uvSize.width * divisors.width != ySize.width ||
888 uvSize.height * divisors.height != ySize.height) {
889 return false;
891 *out_divisors = divisors;
892 return true;
895 bool GLBlitHelper::BlitPlanarYCbCr(const PlanarYCbCrData& yuvData,
896 const gfx::IntSize& destSize,
897 const OriginPos destOrigin) {
898 const auto& prog = GetDrawBlitProg({kFragHeader_Tex2D, kFragBody_PlanarYUV});
900 if (!mYuvUploads[0]) {
901 mGL->fGenTextures(3, mYuvUploads);
902 const ScopedBindTexture bindTex(mGL, mYuvUploads[0]);
903 mGL->TexParams_SetClampNoMips();
904 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[1]);
905 mGL->TexParams_SetClampNoMips();
906 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[2]);
907 mGL->TexParams_SetClampNoMips();
910 // --
912 auto ySize = yuvData.YDataSize();
913 auto cbcrSize = yuvData.CbCrDataSize();
914 if (yuvData.mYSkip || yuvData.mCbSkip || yuvData.mCrSkip || ySize.width < 0 ||
915 ySize.height < 0 || cbcrSize.width < 0 || cbcrSize.height < 0 ||
916 yuvData.mYStride < 0 || yuvData.mCbCrStride < 0) {
917 gfxCriticalError() << "Unusual PlanarYCbCrData: " << yuvData.mYSkip << ","
918 << yuvData.mCbSkip << "," << yuvData.mCrSkip << ", "
919 << ySize.width << "," << ySize.height << ", "
920 << cbcrSize.width << "," << cbcrSize.height << ", "
921 << yuvData.mYStride << "," << yuvData.mCbCrStride;
922 return false;
925 gfx::IntSize divisors;
926 switch (yuvData.mChromaSubsampling) {
927 case gfx::ChromaSubsampling::FULL:
928 divisors = gfx::IntSize(1, 1);
929 break;
930 case gfx::ChromaSubsampling::HALF_WIDTH:
931 divisors = gfx::IntSize(2, 1);
932 break;
933 case gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT:
934 divisors = gfx::IntSize(2, 2);
935 break;
936 default:
937 gfxCriticalError() << "Unknown chroma subsampling:"
938 << int(yuvData.mChromaSubsampling);
939 return false;
942 // --
944 // RED textures aren't valid in GLES2, and ALPHA textures are not valid in
945 // desktop GL Core Profiles. So use R8 textures on GL3.0+ and GLES3.0+, but
946 // LUMINANCE/LUMINANCE/UNSIGNED_BYTE otherwise.
947 GLenum internalFormat;
948 GLenum unpackFormat;
949 if (mGL->IsAtLeast(gl::ContextProfile::OpenGLCore, 300) ||
950 mGL->IsAtLeast(gl::ContextProfile::OpenGLES, 300)) {
951 internalFormat = LOCAL_GL_R8;
952 unpackFormat = LOCAL_GL_RED;
953 } else {
954 internalFormat = LOCAL_GL_LUMINANCE;
955 unpackFormat = LOCAL_GL_LUMINANCE;
958 // --
960 const ScopedSaveMultiTex saveTex(mGL, 3, LOCAL_GL_TEXTURE_2D);
961 const ResetUnpackState reset(mGL);
962 const gfx::IntSize yTexSize(yuvData.mYStride, yuvData.YDataSize().height);
963 const gfx::IntSize uvTexSize(yuvData.mCbCrStride,
964 yuvData.CbCrDataSize().height);
966 if (yTexSize != mYuvUploads_YSize || uvTexSize != mYuvUploads_UVSize) {
967 mYuvUploads_YSize = yTexSize;
968 mYuvUploads_UVSize = uvTexSize;
970 mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
971 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[0]);
972 mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, internalFormat, yTexSize.width,
973 yTexSize.height, 0, unpackFormat, LOCAL_GL_UNSIGNED_BYTE,
974 nullptr);
975 for (int i = 1; i < 3; i++) {
976 mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
977 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[i]);
978 mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, internalFormat, uvTexSize.width,
979 uvTexSize.height, 0, unpackFormat,
980 LOCAL_GL_UNSIGNED_BYTE, nullptr);
984 // --
986 mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
987 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[0]);
988 mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0, yTexSize.width,
989 yTexSize.height, unpackFormat, LOCAL_GL_UNSIGNED_BYTE,
990 yuvData.mYChannel);
991 mGL->fActiveTexture(LOCAL_GL_TEXTURE1);
992 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[1]);
993 mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0, uvTexSize.width,
994 uvTexSize.height, unpackFormat, LOCAL_GL_UNSIGNED_BYTE,
995 yuvData.mCbChannel);
996 mGL->fActiveTexture(LOCAL_GL_TEXTURE2);
997 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[2]);
998 mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0, uvTexSize.width,
999 uvTexSize.height, unpackFormat, LOCAL_GL_UNSIGNED_BYTE,
1000 yuvData.mCrChannel);
1002 // --
1004 const auto& clipRect = yuvData.mPictureRect;
1005 const auto srcOrigin = OriginPos::BottomLeft;
1006 const bool yFlip = (destOrigin != srcOrigin);
1008 const DrawBlitProg::BaseArgs baseArgs = {SubRectMat3(clipRect, yTexSize),
1009 yFlip, destSize, Nothing()};
1010 const DrawBlitProg::YUVArgs yuvArgs = {
1011 SubRectMat3(clipRect, uvTexSize, divisors), yuvData.mYUVColorSpace};
1012 prog->Draw(baseArgs, &yuvArgs);
1013 return true;
1016 // -------------------------------------
1018 #ifdef XP_MACOSX
1019 bool GLBlitHelper::BlitImage(layers::MacIOSurfaceImage* const srcImage,
1020 const gfx::IntSize& destSize,
1021 const OriginPos destOrigin) const {
1022 return BlitImage(srcImage->GetSurface(), destSize, destOrigin);
1025 static std::string IntAsAscii(const int x) {
1026 std::string str;
1027 str.reserve(6);
1028 auto u = static_cast<unsigned int>(x);
1029 while (u) {
1030 str.insert(str.begin(), u & 0xff);
1031 u >>= 8;
1033 str.insert(str.begin(), '\'');
1034 str.push_back('\'');
1035 return str;
1038 bool GLBlitHelper::BlitImage(MacIOSurface* const iosurf,
1039 const gfx::IntSize& destSize,
1040 const OriginPos destOrigin) const {
1041 if (!iosurf) {
1042 gfxCriticalError() << "Null MacIOSurface for GLBlitHelper::BlitImage";
1043 return false;
1045 if (mGL->GetContextType() != GLContextType::CGL) {
1046 MOZ_ASSERT(false);
1047 return false;
1049 const auto glCGL = static_cast<GLContextCGL*>(mGL);
1050 const auto cglContext = glCGL->GetCGLContext();
1052 const auto& srcOrigin = OriginPos::BottomLeft;
1054 DrawBlitProg::BaseArgs baseArgs;
1055 baseArgs.yFlip = (destOrigin != srcOrigin);
1056 baseArgs.destSize = destSize;
1058 // TODO: The colorspace is known by the IOSurface, why override it?
1059 // See GetYUVColorSpace/GetFullRange()
1060 DrawBlitProg::YUVArgs yuvArgs;
1061 yuvArgs.colorSpace = iosurf->GetYUVColorSpace();
1063 const DrawBlitProg::YUVArgs* pYuvArgs = nullptr;
1065 auto planes = iosurf->GetPlaneCount();
1066 if (!planes) {
1067 planes = 1; // Bad API. No cookie.
1070 const GLenum texTarget = LOCAL_GL_TEXTURE_RECTANGLE;
1071 const char* const fragHeader = kFragHeader_Tex2DRect;
1073 const ScopedSaveMultiTex saveTex(mGL, planes, texTarget);
1074 const ScopedTexture tex0(mGL);
1075 const ScopedTexture tex1(mGL);
1076 const ScopedTexture tex2(mGL);
1077 const GLuint texs[3] = {tex0, tex1, tex2};
1079 const auto pixelFormat = iosurf->GetPixelFormat();
1080 if (mGL->ShouldSpew()) {
1081 const auto formatStr = IntAsAscii(pixelFormat);
1082 printf_stderr("iosurf format: %s (0x%08x)\n", formatStr.c_str(),
1083 pixelFormat);
1086 const char* fragBody;
1087 switch (planes) {
1088 case 1:
1089 switch (pixelFormat) {
1090 case kCVPixelFormatType_24RGB:
1091 case kCVPixelFormatType_24BGR:
1092 case kCVPixelFormatType_32ARGB:
1093 case kCVPixelFormatType_32BGRA:
1094 case kCVPixelFormatType_32ABGR:
1095 case kCVPixelFormatType_32RGBA:
1096 case kCVPixelFormatType_64ARGB:
1097 case kCVPixelFormatType_48RGB:
1098 fragBody = kFragBody_RGBA;
1099 break;
1100 case kCVPixelFormatType_422YpCbCr8:
1101 case kCVPixelFormatType_422YpCbCr8_yuvs:
1102 fragBody = kFragBody_CrYCb;
1103 pYuvArgs = &yuvArgs;
1104 break;
1105 default: {
1106 std::string str;
1107 if (pixelFormat <= 0xff) {
1108 str = std::to_string(pixelFormat);
1109 } else {
1110 str = IntAsAscii(pixelFormat);
1112 gfxCriticalError() << "Unhandled kCVPixelFormatType_*: " << str;
1114 // Probably YUV though
1115 fragBody = kFragBody_CrYCb;
1116 pYuvArgs = &yuvArgs;
1117 break;
1119 break;
1120 case 2:
1121 fragBody = kFragBody_NV12;
1122 pYuvArgs = &yuvArgs;
1123 break;
1124 case 3:
1125 fragBody = kFragBody_PlanarYUV;
1126 pYuvArgs = &yuvArgs;
1127 break;
1128 default:
1129 gfxCriticalError() << "Unexpected plane count: " << planes;
1130 return false;
1133 for (uint32_t p = 0; p < planes; p++) {
1134 mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + p);
1135 mGL->fBindTexture(texTarget, texs[p]);
1136 mGL->TexParams_SetClampNoMips(texTarget);
1138 auto err = iosurf->CGLTexImageIOSurface2D(mGL, cglContext, p);
1139 if (err) {
1140 return false;
1143 if (p == 0) {
1144 const auto width = iosurf->GetDevicePixelWidth(p);
1145 const auto height = iosurf->GetDevicePixelHeight(p);
1146 baseArgs.texMatrix0 = SubRectMat3(0, 0, width, height);
1147 yuvArgs.texMatrix1 = SubRectMat3(0, 0, width / 2.0, height / 2.0);
1151 const auto& prog = GetDrawBlitProg({fragHeader, fragBody});
1152 prog->Draw(baseArgs, pYuvArgs);
1153 return true;
1155 #endif
1157 // -----------------------------------------------------------------------------
1159 void GLBlitHelper::DrawBlitTextureToFramebuffer(const GLuint srcTex,
1160 const gfx::IntSize& srcSize,
1161 const gfx::IntSize& destSize,
1162 const GLenum srcTarget,
1163 const bool srcIsBGRA) const {
1164 const char* fragHeader = nullptr;
1165 Mat3 texMatrix0;
1166 switch (srcTarget) {
1167 case LOCAL_GL_TEXTURE_2D:
1168 fragHeader = kFragHeader_Tex2D;
1169 texMatrix0 = Mat3::I();
1170 break;
1171 case LOCAL_GL_TEXTURE_RECTANGLE_ARB:
1172 fragHeader = kFragHeader_Tex2DRect;
1173 texMatrix0 = SubRectMat3(0, 0, srcSize.width, srcSize.height);
1174 break;
1175 default:
1176 gfxCriticalError() << "Unexpected srcTarget: " << srcTarget;
1177 return;
1179 const char* fragBody = srcIsBGRA ? kFragBody_BGRA : kFragBody_RGBA;
1180 const auto& prog = GetDrawBlitProg({fragHeader, fragBody});
1182 const ScopedSaveMultiTex saveTex(mGL, 1, srcTarget);
1183 mGL->fBindTexture(srcTarget, srcTex);
1185 const bool yFlip = false;
1186 const DrawBlitProg::BaseArgs baseArgs = {texMatrix0, yFlip, destSize,
1187 Nothing()};
1188 prog->Draw(baseArgs);
1191 // -----------------------------------------------------------------------------
1193 void GLBlitHelper::BlitFramebuffer(const gfx::IntRect& srcRect,
1194 const gfx::IntRect& destRect,
1195 GLuint filter) const {
1196 MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit));
1198 const ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false);
1199 mGL->fBlitFramebuffer(srcRect.x, srcRect.y, srcRect.XMost(), srcRect.YMost(),
1200 destRect.x, destRect.y, destRect.XMost(),
1201 destRect.YMost(), LOCAL_GL_COLOR_BUFFER_BIT, filter);
1204 // --
1206 void GLBlitHelper::BlitFramebufferToFramebuffer(const GLuint srcFB,
1207 const GLuint destFB,
1208 const gfx::IntRect& srcRect,
1209 const gfx::IntRect& destRect,
1210 GLuint filter) const {
1211 MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit));
1212 MOZ_GL_ASSERT(mGL, !srcFB || mGL->fIsFramebuffer(srcFB));
1213 MOZ_GL_ASSERT(mGL, !destFB || mGL->fIsFramebuffer(destFB));
1215 const ScopedBindFramebuffer boundFB(mGL);
1216 mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, srcFB);
1217 mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destFB);
1219 BlitFramebuffer(srcRect, destRect, filter);
1222 void GLBlitHelper::BlitTextureToFramebuffer(GLuint srcTex,
1223 const gfx::IntSize& srcSize,
1224 const gfx::IntSize& destSize,
1225 GLenum srcTarget) const {
1226 MOZ_GL_ASSERT(mGL, mGL->fIsTexture(srcTex));
1228 if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
1229 const ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget);
1230 const ScopedBindFramebuffer bindFB(mGL);
1231 mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, srcWrapper.FB());
1232 BlitFramebuffer(gfx::IntRect({}, srcSize), gfx::IntRect({}, destSize));
1233 return;
1236 DrawBlitTextureToFramebuffer(srcTex, srcSize, destSize, srcTarget);
1239 void GLBlitHelper::BlitFramebufferToTexture(GLuint destTex,
1240 const gfx::IntSize& srcSize,
1241 const gfx::IntSize& destSize,
1242 GLenum destTarget) const {
1243 MOZ_GL_ASSERT(mGL, mGL->fIsTexture(destTex));
1245 if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
1246 const ScopedFramebufferForTexture destWrapper(mGL, destTex, destTarget);
1247 const ScopedBindFramebuffer bindFB(mGL);
1248 mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destWrapper.FB());
1249 BlitFramebuffer(gfx::IntRect({}, srcSize), gfx::IntRect({}, destSize));
1250 return;
1253 ScopedBindTexture autoTex(mGL, destTex, destTarget);
1254 ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false);
1255 mGL->fCopyTexSubImage2D(destTarget, 0, 0, 0, 0, 0, srcSize.width,
1256 srcSize.height);
1259 void GLBlitHelper::BlitTextureToTexture(GLuint srcTex, GLuint destTex,
1260 const gfx::IntSize& srcSize,
1261 const gfx::IntSize& destSize,
1262 GLenum srcTarget,
1263 GLenum destTarget) const {
1264 MOZ_GL_ASSERT(mGL, mGL->fIsTexture(srcTex));
1265 MOZ_GL_ASSERT(mGL, mGL->fIsTexture(destTex));
1267 // Start down the CopyTexSubImage path, not the DrawBlit path.
1268 const ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget);
1269 const ScopedBindFramebuffer bindFB(mGL, srcWrapper.FB());
1270 BlitFramebufferToTexture(destTex, srcSize, destSize, destTarget);
1273 // -------------------------------------
1275 bool GLBlitHelper::BlitImage(layers::GPUVideoImage* const srcImage,
1276 const gfx::IntSize& destSize,
1277 const OriginPos destOrigin) const {
1278 const auto& data = srcImage->GetData();
1279 if (!data) return false;
1281 const auto& desc = data->SD();
1283 MOZ_ASSERT(
1284 desc.type() ==
1285 layers::SurfaceDescriptorGPUVideo::TSurfaceDescriptorRemoteDecoder);
1286 const auto& subdescUnion =
1287 desc.get_SurfaceDescriptorRemoteDecoder().subdesc();
1288 switch (subdescUnion.type()) {
1289 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorDMABuf: {
1290 // TODO.
1291 return false;
1293 #ifdef XP_WIN
1294 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorD3D10: {
1295 const auto& subdesc = subdescUnion.get_SurfaceDescriptorD3D10();
1296 return BlitDescriptor(subdesc, destSize, destOrigin);
1298 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorDXGIYCbCr: {
1299 const auto& subdesc = subdescUnion.get_SurfaceDescriptorDXGIYCbCr();
1300 return BlitDescriptor(subdesc, destSize, destOrigin);
1302 #endif
1303 #ifdef XP_MACOSX
1304 case layers::RemoteDecoderVideoSubDescriptor::
1305 TSurfaceDescriptorMacIOSurface: {
1306 const auto& subdesc = subdescUnion.get_SurfaceDescriptorMacIOSurface();
1307 RefPtr<MacIOSurface> surface = MacIOSurface::LookupSurface(
1308 subdesc.surfaceId(), !subdesc.isOpaque(), subdesc.yUVColorSpace());
1309 MOZ_ASSERT(surface);
1310 if (!surface) {
1311 return false;
1313 return BlitImage(surface, destSize, destOrigin);
1315 #endif
1316 case layers::RemoteDecoderVideoSubDescriptor::Tnull_t:
1317 // This GPUVideoImage isn't directly readable outside the GPU process.
1318 // Abort.
1319 return false;
1320 default:
1321 gfxCriticalError() << "Unhandled subdesc type: "
1322 << uint32_t(subdescUnion.type());
1323 return false;
1327 // -------------------------------------
1328 #ifdef MOZ_WAYLAND
1329 bool GLBlitHelper::BlitImage(layers::DMABUFSurfaceImage* srcImage,
1330 const gfx::IntSize& destSize,
1331 OriginPos destOrigin) const {
1332 DMABufSurface* surface = srcImage->GetSurface();
1333 if (!surface) {
1334 gfxCriticalError() << "Null DMABUFSurface for GLBlitHelper::BlitImage";
1335 return false;
1338 const auto& srcOrigin = OriginPos::BottomLeft;
1340 DrawBlitProg::BaseArgs baseArgs;
1341 baseArgs.yFlip = (destOrigin != srcOrigin);
1342 baseArgs.destSize = destSize;
1344 // TODO: The colorspace is known by the DMABUFSurface, why override it?
1345 // See GetYUVColorSpace/GetFullRange()
1346 DrawBlitProg::YUVArgs yuvArgs;
1347 yuvArgs.colorSpace = surface->GetYUVColorSpace();
1349 const DrawBlitProg::YUVArgs* pYuvArgs = nullptr;
1351 const auto planes = surface->GetTextureCount();
1352 const GLenum texTarget = LOCAL_GL_TEXTURE_2D;
1354 const ScopedSaveMultiTex saveTex(mGL, planes, texTarget);
1355 const auto pixelFormat = surface->GetSurfaceType();
1357 const char* fragBody;
1358 switch (pixelFormat) {
1359 case DMABufSurface::SURFACE_RGBA:
1360 fragBody = kFragBody_RGBA;
1361 break;
1362 case DMABufSurface::SURFACE_NV12:
1363 fragBody = kFragBody_NV12;
1364 pYuvArgs = &yuvArgs;
1365 break;
1366 case DMABufSurface::SURFACE_YUV420:
1367 fragBody = kFragBody_PlanarYUV;
1368 pYuvArgs = &yuvArgs;
1369 break;
1370 default:
1371 gfxCriticalError() << "Unexpected pixel format: " << pixelFormat;
1372 return false;
1375 for (const auto p : IntegerRange(planes)) {
1376 mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + p);
1377 mGL->fBindTexture(texTarget, surface->GetTexture(p));
1378 mGL->TexParams_SetClampNoMips(texTarget);
1381 // We support only NV12/YUV420 formats only with 1/2 texture scale.
1382 // We don't set cliprect as DMABus textures are created without padding.
1383 baseArgs.texMatrix0 = SubRectMat3(0, 0, 1, 1);
1384 yuvArgs.texMatrix1 = SubRectMat3(0, 0, 1, 1);
1386 const auto& prog = GetDrawBlitProg({kFragHeader_Tex2D, fragBody});
1387 prog->Draw(baseArgs, pYuvArgs);
1389 return true;
1391 #endif
1393 } // namespace gl
1394 } // namespace mozilla