GLBlitHelper.cpp (73784B)
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/. */ 6 7 #include "GLBlitHelper.h" 8 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 "GLUploadHelpers.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/BuildConstants.h" 23 #include "mozilla/gfx/Logging.h" 24 #include "mozilla/gfx/Matrix.h" 25 #include "mozilla/layers/ImageDataSerializer.h" 26 #include "mozilla/layers/LayersSurfaces.h" 27 28 #ifdef MOZ_WIDGET_ANDROID 29 # include "AndroidSurfaceTexture.h" 30 # include "GLImages.h" 31 # include "GLLibraryEGL.h" 32 #endif 33 34 #ifdef XP_MACOSX 35 # include "GLContextCGL.h" 36 # include "MacIOSurfaceImage.h" 37 #endif 38 39 #ifdef XP_WIN 40 # include "mozilla/layers/D3D11ShareHandleImage.h" 41 # include "mozilla/layers/D3D11ZeroCopyTextureImage.h" 42 # include "mozilla/layers/D3D11YCbCrImage.h" 43 #endif 44 45 #ifdef MOZ_WIDGET_GTK 46 # include "mozilla/layers/DMABUFSurfaceImage.h" 47 # include "mozilla/widget/DMABufSurface.h" 48 # include "mozilla/widget/DMABufDevice.h" 49 #endif 50 51 using mozilla::layers::PlanarYCbCrData; 52 using mozilla::layers::PlanarYCbCrImage; 53 54 namespace mozilla { 55 namespace gl { 56 57 // -- 58 59 static const char kFragPreprocHeader[] = R"( 60 #ifdef GL_ES 61 #ifdef GL_FRAGMENT_PRECISION_HIGH 62 #define MAXP highp 63 #endif 64 #else 65 #define MAXP highp 66 #endif 67 #ifndef MAXP 68 #define MAXP mediump 69 #endif 70 71 #if __VERSION__ >= 130 72 #define VARYING in 73 #else 74 #define VARYING varying 75 #endif 76 #if __VERSION__ >= 120 77 #define MAT4X3 mat4x3 78 #else 79 #define MAT4X3 mat4 80 #endif 81 )"; 82 83 // - 84 85 const char* const kFragHeader_Tex2D = R"( 86 #define SAMPLER sampler2D 87 #if __VERSION__ >= 130 88 #define TEXTURE texture 89 #else 90 #define TEXTURE texture2D 91 #endif 92 )"; 93 const char* const kFragHeader_Tex2DRect = R"( 94 #define SAMPLER sampler2DRect 95 #if __VERSION__ >= 130 96 #define TEXTURE texture 97 #else 98 #define TEXTURE texture2DRect 99 #endif 100 )"; 101 const char* const kFragHeader_TexExt = R"( 102 #extension GL_OES_EGL_image_external : enable 103 #extension GL_OES_EGL_image_external_essl3 : enable 104 #if __VERSION__ >= 130 105 #define TEXTURE texture 106 #else 107 #define TEXTURE texture2D 108 #endif 109 #define SAMPLER samplerExternalOES 110 )"; 111 112 // - 113 114 static const char kFragDeclHeader[] = R"( 115 precision PRECISION float; 116 #if __VERSION__ >= 130 117 #define FRAG_COLOR oFragColor 118 out vec4 FRAG_COLOR; 119 #else 120 #define FRAG_COLOR gl_FragColor 121 #endif 122 )"; 123 124 // - 125 126 const char* const kFragSample_OnePlane = R"( 127 VARYING mediump vec2 vTexCoord0; 128 uniform PRECISION SAMPLER uTex0; 129 130 vec4 metaSample() { 131 vec4 src = TEXTURE(uTex0, vTexCoord0); 132 return src; 133 } 134 )"; 135 // Ideally this would just change the color-matrix it uses, but this is 136 // acceptable debt for now. 137 // `extern` so that we don't get ifdef-dependent const-var-unused Werrors. 138 extern const char* const kFragSample_OnePlane_YUV_via_GBR = R"( 139 VARYING mediump vec2 vTexCoord0; 140 uniform PRECISION SAMPLER uTex0; 141 142 vec4 metaSample() { 143 vec4 yuva = TEXTURE(uTex0, vTexCoord0).gbra; 144 return yuva; 145 } 146 )"; 147 const char* const kFragSample_TwoPlane = R"( 148 VARYING mediump vec2 vTexCoord0; 149 VARYING mediump vec2 vTexCoord1; 150 uniform PRECISION SAMPLER uTex0; 151 uniform PRECISION SAMPLER uTex1; 152 153 vec4 metaSample() { 154 vec4 src = TEXTURE(uTex0, vTexCoord0); // Keep r and a. 155 src.gb = TEXTURE(uTex1, vTexCoord1).rg; 156 return src; 157 } 158 )"; 159 const char* const kFragSample_ThreePlane = R"( 160 VARYING mediump vec2 vTexCoord0; 161 VARYING mediump vec2 vTexCoord1; 162 uniform PRECISION SAMPLER uTex0; 163 uniform PRECISION SAMPLER uTex1; 164 uniform PRECISION SAMPLER uTex2; 165 166 vec4 metaSample() { 167 vec4 src = TEXTURE(uTex0, vTexCoord0); // Keep r and a. 168 src.g = TEXTURE(uTex1, vTexCoord1).r; 169 src.b = TEXTURE(uTex2, vTexCoord1).r; 170 return src; 171 } 172 )"; 173 174 extern const char* const kFragSample_TwoPlaneUV = R"( 175 VARYING mediump vec2 vTexCoord0; 176 uniform PRECISION SAMPLER uTex1; 177 uniform PRECISION SAMPLER uTex2; 178 179 vec4 metaSample() { 180 vec4 src = TEXTURE(uTex1, vTexCoord0); 181 src.g = TEXTURE(uTex2, vTexCoord0).r; 182 return src; 183 } 184 )"; 185 186 // - 187 188 const char* const kFragConvert_None = R"( 189 vec3 metaConvert(vec3 src) { 190 return src; 191 } 192 )"; 193 const char* const kFragConvert_BGR = R"( 194 vec3 metaConvert(vec3 src) { 195 return src.bgr; 196 } 197 )"; 198 const char* const kFragConvert_ColorMatrix = R"( 199 uniform mediump MAT4X3 uColorMatrix; 200 201 vec3 metaConvert(vec3 src) { 202 return (uColorMatrix * vec4(src, 1)).rgb; 203 } 204 )"; 205 const char* const kFragConvert_ColorLut3d = R"( 206 uniform PRECISION sampler3D uColorLut; 207 208 vec3 metaConvert(vec3 src) { 209 // Half-texel filtering hazard! 210 // E.g. For texture size of 2, 211 // E.g. x=0.25 is still sampling 100% of texel x=0, 0% of texel x=1. 212 // For the LUT, we need r=0.25 to filter 75/25 from texel 0 and 1. 213 // That is, we need to adjust our sampling point such that it starts in the 214 // center of texel 0, and ends in the center of texel N-1. 215 // We need, for N=2: 216 // v=0.0|N=2 => v'=0.5/2 217 // v=1.0|N=2 => v'=1.5/2 218 // For N=3: 219 // v=0.0|N=3 => v'=0.5/3 220 // v=1.0|N=3 => v'=2.5/3 221 // => v' = ( 0.5 + v * (3 - 1) )/3 222 vec3 size = vec3(textureSize(uColorLut, 0)); 223 src = (0.5 + src * (size - 1.0)) / size; 224 return texture(uColorLut, src).rgb; 225 } 226 )"; 227 // Delete if unused after 2024-10-01: 228 const char* const kFragConvert_ColorLut2d = R"( 229 uniform PRECISION sampler2D uColorLut; 230 uniform mediump vec3 uColorLut3dSize; 231 232 vec3 metaConvert(vec3 src) { 233 // Half-texel filtering hazard! 234 // E.g. For texture size of 2, 235 // E.g. x=0.25 is still sampling 100% of texel x=0, 0% of texel x=1. 236 // For the LUT, we need r=0.25 to filter 75/25 from texel 0 and 1. 237 // That is, we need to adjust our sampling point such that it starts in the 238 // center of texel 0, and ends in the center of texel N-1. 239 // We need, for N=2: 240 // v=0.0|N=2 => v'=0.5/2 241 // v=1.0|N=2 => v'=1.5/2 242 // For N=3: 243 // v=0.0|N=3 => v'=0.5/3 244 // v=1.0|N=3 => v'=2.5/3 245 // => v' = ( 0.5 + v * (3 - 1) )/3 246 src = clamp(src, vec3(0,0,0), vec3(1,1,1)); 247 vec3 lut3dSize = uColorLut3dSize; 248 vec2 lut2dSize = vec2(lut3dSize.x, lut3dSize.y * lut3dSize.z); 249 vec3 texelSrc3d = 0.5 + src * (lut3dSize - 1.0); 250 251 vec3 texelSrc3d_zFloor = texelSrc3d; 252 texelSrc3d_zFloor.z = floor(texelSrc3d_zFloor.z); 253 vec3 texelSrc3d_zNext = texelSrc3d_zFloor + vec3(0,0,1); 254 texelSrc3d_zNext.z = min(texelSrc3d_zNext.z, lut3dSize.z - 1.0); 255 256 vec2 texelSrc2d_zFloor = texelSrc3d_zFloor.xy + vec2(0, texelSrc3d_zFloor.z * lut3dSize.y); 257 vec2 texelSrc2d_zNext = texelSrc3d_zNext.xy + vec2(0, texelSrc3d_zNext.z * lut3dSize.y); 258 259 vec4 dst_zFloor = texture(uColorLut, texelSrc2d_zFloor / lut2dSize); 260 vec4 dst_zNext = texture(uColorLut, texelSrc2d_zNext / lut2dSize); 261 262 return mix(dst_zFloor, dst_zNext, texelSrc3d.z - texelSrc3d_zFloor.z); 263 } 264 )"; 265 266 extern const char* const kFragConvertYUVP010 = R"( 267 vec3 metaConvert(vec3 src) { 268 // YUV420P10 and P010 are both 10-bit formats stored in 16-bit integer. 269 // P010 has 6 lower bits 0 (value is shifted to upper bits) 270 // while YUV420P10 has upper 6 bites zeroed. 271 src *= 64.0; 272 return src; 273 } 274 )"; 275 276 // - 277 278 const char* const kFragMixin_AlphaMultColors = R"( 279 #define MIXIN_ALPHA_MULT_COLORS 280 )"; 281 const char* const kFragMixin_AlphaUnpremultColors = R"( 282 #define MIXIN_ALPHA_UNPREMULT_COLORS 283 )"; 284 const char* const kFragMixin_AlphaClampColors = R"( 285 #define MIXIN_ALPHA_CLAMP_COLORS 286 )"; 287 const char* const kFragMixin_AlphaOne = R"( 288 #define MIXIN_ALPHA_ONE 289 )"; 290 291 // - 292 293 static const char kFragBody[] = R"( 294 void main(void) { 295 vec4 src = metaSample(); 296 vec4 dst = vec4(metaConvert(src.rgb), src.a); 297 298 #ifdef MIXIN_ALPHA_MULT_COLORS 299 dst.rgb *= dst.a; 300 #endif 301 #ifdef MIXIN_ALPHA_UNPREMULT_COLORS 302 dst.rgb = dst.a != 0.0 ? dst.rgb / dst.a : dst.rgb; 303 #endif 304 #ifdef MIXIN_ALPHA_CLAMP_COLORS 305 dst.rgb = min(dst.rgb, vec3(dst.a)); // Ensure valid premult-alpha colors. 306 #endif 307 #ifdef MIXIN_ALPHA_ONE 308 dst.a = 1.0; 309 #endif 310 311 FRAG_COLOR = dst; 312 } 313 )"; 314 315 // -- 316 317 Mat3 SubRectMat3(const float x, const float y, const float w, const float h) { 318 auto ret = Mat3{}; 319 ret.at(0, 0) = w; 320 ret.at(1, 1) = h; 321 ret.at(2, 0) = x; 322 ret.at(2, 1) = y; 323 ret.at(2, 2) = 1.0f; 324 return ret; 325 } 326 327 Mat3 SubRectMat3(const gfx::IntRect& subrect, const gfx::IntSize& size) { 328 return SubRectMat3(float(subrect.X()) / size.width, 329 float(subrect.Y()) / size.height, 330 float(subrect.Width()) / size.width, 331 float(subrect.Height()) / size.height); 332 } 333 334 Mat3 SubRectMat3(const gfx::IntRect& bigSubrect, const gfx::IntSize& smallSize, 335 const gfx::IntSize& divisors) { 336 const float x = float(bigSubrect.X()) / divisors.width; 337 const float y = float(bigSubrect.Y()) / divisors.height; 338 const float w = float(bigSubrect.Width()) / divisors.width; 339 const float h = float(bigSubrect.Height()) / divisors.height; 340 return SubRectMat3(x / smallSize.width, y / smallSize.height, 341 w / smallSize.width, h / smallSize.height); 342 } 343 344 Mat3 MatrixToMat3(const gfx::Matrix& aMatrix) { 345 auto ret = Mat3(); 346 ret.at(0, 0) = aMatrix._11; 347 ret.at(1, 0) = aMatrix._21; 348 ret.at(2, 0) = aMatrix._31; 349 ret.at(0, 1) = aMatrix._12; 350 ret.at(1, 1) = aMatrix._22; 351 ret.at(2, 1) = aMatrix._32; 352 ret.at(0, 2) = 0.0f; 353 ret.at(1, 2) = 0.0f; 354 ret.at(2, 2) = 1.0f; 355 return ret; 356 } 357 358 // -- 359 360 ScopedSaveMultiTex::ScopedSaveMultiTex(GLContext* const gl, 361 const size_t texUnits, 362 const GLenum texTarget) 363 : mGL(*gl), 364 mTexUnits(texUnits), 365 mTexTarget(texTarget), 366 mOldTexUnit(mGL.GetIntAs<GLenum>(LOCAL_GL_ACTIVE_TEXTURE)) { 367 MOZ_RELEASE_ASSERT(texUnits >= 1); 368 369 GLenum texBinding; 370 switch (mTexTarget) { 371 case LOCAL_GL_TEXTURE_2D: 372 texBinding = LOCAL_GL_TEXTURE_BINDING_2D; 373 break; 374 case LOCAL_GL_TEXTURE_3D: 375 texBinding = LOCAL_GL_TEXTURE_BINDING_3D; 376 break; 377 case LOCAL_GL_TEXTURE_RECTANGLE: 378 texBinding = LOCAL_GL_TEXTURE_BINDING_RECTANGLE; 379 break; 380 case LOCAL_GL_TEXTURE_EXTERNAL: 381 texBinding = LOCAL_GL_TEXTURE_BINDING_EXTERNAL; 382 break; 383 default: 384 gfxCriticalError() << "Unhandled texTarget: " << texTarget; 385 MOZ_CRASH(); 386 } 387 388 for (const auto i : IntegerRange(mTexUnits)) { 389 mGL.fActiveTexture(LOCAL_GL_TEXTURE0 + i); 390 if (mGL.IsSupported(GLFeature::sampler_objects)) { 391 mOldTexSampler[i] = mGL.GetIntAs<GLuint>(LOCAL_GL_SAMPLER_BINDING); 392 mGL.fBindSampler(i, 0); 393 } 394 mOldTex[i] = mGL.GetIntAs<GLuint>(texBinding); 395 } 396 } 397 398 ScopedSaveMultiTex::~ScopedSaveMultiTex() { 399 // Unbind in reverse order, in case we have repeats. 400 // Order matters because we unbound samplers during ctor, so now we have to 401 // make sure we rebind them in the right order. 402 for (const auto i : Reversed(IntegerRange(mTexUnits))) { 403 mGL.fActiveTexture(LOCAL_GL_TEXTURE0 + i); 404 if (mGL.IsSupported(GLFeature::sampler_objects)) { 405 mGL.fBindSampler(i, mOldTexSampler[i]); 406 } 407 mGL.fBindTexture(mTexTarget, mOldTex[i]); 408 } 409 mGL.fActiveTexture(mOldTexUnit); 410 } 411 412 // -- 413 414 class ScopedBindArrayBuffer final { 415 public: 416 GLContext& mGL; 417 const GLuint mOldVBO; 418 419 ScopedBindArrayBuffer(GLContext* const gl, const GLuint vbo) 420 : mGL(*gl), mOldVBO(mGL.GetIntAs<GLuint>(LOCAL_GL_ARRAY_BUFFER_BINDING)) { 421 mGL.fBindBuffer(LOCAL_GL_ARRAY_BUFFER, vbo); 422 } 423 424 ~ScopedBindArrayBuffer() { mGL.fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mOldVBO); } 425 }; 426 427 // -- 428 429 class ScopedShader final { 430 GLContext& mGL; 431 const GLuint mName; 432 433 public: 434 ScopedShader(GLContext* const gl, const GLenum shaderType) 435 : mGL(*gl), mName(mGL.fCreateShader(shaderType)) {} 436 437 ~ScopedShader() { mGL.fDeleteShader(mName); } 438 439 operator GLuint() const { return mName; } 440 }; 441 442 // -- 443 444 class SaveRestoreCurrentProgram final { 445 GLContext& mGL; 446 const GLuint mOld; 447 448 public: 449 explicit SaveRestoreCurrentProgram(GLContext* const gl) 450 : mGL(*gl), mOld(mGL.GetIntAs<GLuint>(LOCAL_GL_CURRENT_PROGRAM)) {} 451 452 ~SaveRestoreCurrentProgram() { mGL.fUseProgram(mOld); } 453 }; 454 455 // -- 456 457 class ScopedDrawBlitState final { 458 GLContext& mGL; 459 460 const bool blend; 461 const bool cullFace; 462 const bool depthTest; 463 const bool dither; 464 const bool polyOffsFill; 465 const bool sampleAToC; 466 const bool sampleCover; 467 const bool scissor; 468 const bool stencil; 469 Maybe<bool> rasterizerDiscard; 470 471 realGLboolean colorMask[4]; 472 GLint viewport[4]; 473 474 public: 475 ScopedDrawBlitState(GLContext* const gl, const gfx::IntSize& fbSize) 476 : mGL(*gl), 477 blend(mGL.PushEnabled(LOCAL_GL_BLEND, false)), 478 cullFace(mGL.PushEnabled(LOCAL_GL_CULL_FACE, false)), 479 depthTest(mGL.PushEnabled(LOCAL_GL_DEPTH_TEST, false)), 480 dither(mGL.PushEnabled(LOCAL_GL_DITHER, true)), 481 polyOffsFill(mGL.PushEnabled(LOCAL_GL_POLYGON_OFFSET_FILL, false)), 482 sampleAToC(mGL.PushEnabled(LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE, false)), 483 sampleCover(mGL.PushEnabled(LOCAL_GL_SAMPLE_COVERAGE, false)), 484 scissor(mGL.PushEnabled(LOCAL_GL_SCISSOR_TEST, false)), 485 stencil(mGL.PushEnabled(LOCAL_GL_STENCIL_TEST, false)) { 486 if (mGL.IsSupported(GLFeature::transform_feedback2)) { 487 // Technically transform_feedback2 requires transform_feedback, which 488 // actually adds RASTERIZER_DISCARD. 489 rasterizerDiscard = 490 Some(mGL.PushEnabled(LOCAL_GL_RASTERIZER_DISCARD, false)); 491 } 492 493 mGL.fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK, colorMask); 494 if (mGL.IsSupported(GLFeature::draw_buffers_indexed)) { 495 mGL.fColorMaski(0, true, true, true, true); 496 } else { 497 mGL.fColorMask(true, true, true, true); 498 } 499 500 mGL.fGetIntegerv(LOCAL_GL_VIEWPORT, viewport); 501 MOZ_ASSERT(fbSize.width && fbSize.height); 502 mGL.fViewport(0, 0, fbSize.width, fbSize.height); 503 } 504 505 ~ScopedDrawBlitState() { 506 mGL.SetEnabled(LOCAL_GL_BLEND, blend); 507 mGL.SetEnabled(LOCAL_GL_CULL_FACE, cullFace); 508 mGL.SetEnabled(LOCAL_GL_DEPTH_TEST, depthTest); 509 mGL.SetEnabled(LOCAL_GL_DITHER, dither); 510 mGL.SetEnabled(LOCAL_GL_POLYGON_OFFSET_FILL, polyOffsFill); 511 mGL.SetEnabled(LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE, sampleAToC); 512 mGL.SetEnabled(LOCAL_GL_SAMPLE_COVERAGE, sampleCover); 513 mGL.SetEnabled(LOCAL_GL_SCISSOR_TEST, scissor); 514 mGL.SetEnabled(LOCAL_GL_STENCIL_TEST, stencil); 515 if (rasterizerDiscard) { 516 mGL.SetEnabled(LOCAL_GL_RASTERIZER_DISCARD, rasterizerDiscard.value()); 517 } 518 519 if (mGL.IsSupported(GLFeature::draw_buffers_indexed)) { 520 mGL.fColorMaski(0, colorMask[0], colorMask[1], colorMask[2], 521 colorMask[3]); 522 } else { 523 mGL.fColorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]); 524 } 525 mGL.fViewport(viewport[0], viewport[1], viewport[2], viewport[3]); 526 } 527 }; 528 529 // -- 530 531 DrawBlitProg::DrawBlitProg(const GLBlitHelper* const parent, const GLuint prog) 532 : mParent(*parent), 533 mProg(prog), 534 mLoc_uDestMatrix(mParent.mGL->fGetUniformLocation(mProg, "uDestMatrix")), 535 mLoc_uTexMatrix0(mParent.mGL->fGetUniformLocation(mProg, "uTexMatrix0")), 536 mLoc_uTexMatrix1(mParent.mGL->fGetUniformLocation(mProg, "uTexMatrix1")), 537 mLoc_uColorLut(mParent.mGL->fGetUniformLocation(mProg, "uColorLut")), 538 mLoc_uColorMatrix( 539 mParent.mGL->fGetUniformLocation(mProg, "uColorMatrix")) { 540 const auto& gl = mParent.mGL; 541 MOZ_GL_ASSERT(gl, mLoc_uDestMatrix != -1); // Required 542 MOZ_GL_ASSERT(gl, mLoc_uTexMatrix0 != -1); // Required 543 if (mLoc_uColorMatrix != -1) { 544 MOZ_GL_ASSERT(gl, mLoc_uTexMatrix1 != -1); 545 546 int32_t numActiveUniforms = 0; 547 gl->fGetProgramiv(mProg, LOCAL_GL_ACTIVE_UNIFORMS, &numActiveUniforms); 548 549 const size_t kMaxNameSize = 32; 550 char name[kMaxNameSize] = {0}; 551 GLint size = 0; 552 GLenum type = 0; 553 for (int32_t i = 0; i < numActiveUniforms; i++) { 554 gl->fGetActiveUniform(mProg, i, kMaxNameSize, nullptr, &size, &type, 555 name); 556 if (strcmp("uColorMatrix", name) == 0) { 557 mType_uColorMatrix = type; 558 break; 559 } 560 } 561 MOZ_GL_ASSERT(gl, mType_uColorMatrix); 562 } 563 } 564 565 DrawBlitProg::~DrawBlitProg() { 566 const auto& gl = mParent.mGL; 567 if (!gl->MakeCurrent()) return; 568 569 gl->fDeleteProgram(mProg); 570 } 571 572 void DrawBlitProg::Draw(const BaseArgs& args, 573 const YUVArgs* const argsYUV) const { 574 const auto& gl = mParent.mGL; 575 576 const SaveRestoreCurrentProgram oldProg(gl); 577 gl->fUseProgram(mProg); 578 579 // -- 580 581 gfx::IntSize fbSize = args.fbSize; 582 Mat3 destMatrix; 583 if (!args.destRect.IsEmpty()) { 584 const gfx::IntRect& destRect = args.destRect; 585 if (fbSize.IsEmpty()) { 586 fbSize = destRect.Size(); 587 } 588 destMatrix = SubRectMat3(float(destRect.X()) / fbSize.width, 589 float(destRect.Y()) / fbSize.height, 590 float(destRect.Width()) / fbSize.width, 591 float(destRect.Height()) / fbSize.height); 592 } else { 593 MOZ_ASSERT(!fbSize.IsEmpty()); 594 destMatrix = Mat3::I(); 595 } 596 597 if (args.yFlip) { 598 // Apply the y-flip matrix before the destMatrix. 599 // That is, flip y=[0-1] to y=[1-0] before we restrict to the destRect. 600 destMatrix.at(2, 1) += destMatrix.at(1, 1); 601 destMatrix.at(1, 1) *= -1.0f; 602 } 603 604 Mat3 texScale = Mat3::I(); 605 if (!args.texSize.IsEmpty()) { 606 const gfx::IntSize clipSize = 607 args.destRect.IsEmpty() ? fbSize : args.destRect.Size(); 608 const gfx::IntSize& texSize = args.texSize; 609 texScale = SubRectMat3( 610 gfx::IntRect( 611 gfx::IntPoint(0, args.yFlip ? texSize.height - clipSize.height : 0), 612 clipSize), 613 texSize); 614 } 615 616 gl->fUniformMatrix3fv(mLoc_uDestMatrix, 1, false, destMatrix.m); 617 618 Mat3 texMatrix0 = args.texMatrix0 * texScale; 619 gl->fUniformMatrix3fv(mLoc_uTexMatrix0, 1, false, texMatrix0.m); 620 621 MOZ_ASSERT(bool(argsYUV) == (mLoc_uColorMatrix != -1)); 622 if (argsYUV) { 623 Mat3 texMatrix1 = argsYUV->texMatrix1 * texScale; 624 gl->fUniformMatrix3fv(mLoc_uTexMatrix1, 1, false, texMatrix1.m); 625 626 if (mLoc_uColorMatrix != -1) { 627 const auto& colorMatrix = 628 gfxUtils::YuvToRgbMatrix4x4ColumnMajor(*argsYUV->colorSpaceForMatrix); 629 float mat4x3[4 * 3]; 630 switch (mType_uColorMatrix) { 631 case LOCAL_GL_FLOAT_MAT4: 632 gl->fUniformMatrix4fv(mLoc_uColorMatrix, 1, false, colorMatrix); 633 break; 634 case LOCAL_GL_FLOAT_MAT4x3: 635 for (int x = 0; x < 4; x++) { 636 for (int y = 0; y < 3; y++) { 637 mat4x3[3 * x + y] = colorMatrix[4 * x + y]; 638 } 639 } 640 gl->fUniformMatrix4x3fv(mLoc_uColorMatrix, 1, false, mat4x3); 641 break; 642 default: 643 gfxCriticalError() 644 << "Bad mType_uColorMatrix: " << gfx::hexa(mType_uColorMatrix); 645 } 646 } 647 } 648 649 // -- 650 651 const ScopedDrawBlitState drawState(gl, fbSize); 652 653 GLuint oldVAO; 654 GLint vaa0Enabled; 655 GLint vaa0Size; 656 GLenum vaa0Type; 657 GLint vaa0Normalized; 658 GLsizei vaa0Stride; 659 GLvoid* vaa0Pointer; 660 GLuint vaa0Buffer; 661 if (mParent.mQuadVAO) { 662 oldVAO = gl->GetIntAs<GLuint>(LOCAL_GL_VERTEX_ARRAY_BINDING); 663 gl->fBindVertexArray(mParent.mQuadVAO); 664 } else { 665 // clang-format off 666 gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, (GLint*)&vaa0Buffer); 667 gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED, &vaa0Enabled); 668 gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE, &vaa0Size); 669 gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE, (GLint*)&vaa0Type); 670 gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &vaa0Normalized); 671 gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE, (GLint*)&vaa0Stride); 672 gl->fGetVertexAttribPointerv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER, &vaa0Pointer); 673 // clang-format on 674 675 gl->fEnableVertexAttribArray(0); 676 const ScopedBindArrayBuffer bindVBO(gl, mParent.mQuadVBO); 677 gl->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, false, 0, 0); 678 } 679 680 gl->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4); 681 682 if (mParent.mQuadVAO) { 683 gl->fBindVertexArray(oldVAO); 684 } else { 685 if (vaa0Enabled) { 686 gl->fEnableVertexAttribArray(0); 687 } else { 688 gl->fDisableVertexAttribArray(0); 689 } 690 // The current VERTEX_ARRAY_BINDING is not necessarily the same as the 691 // buffer set for vaa0Buffer. 692 const ScopedBindArrayBuffer bindVBO(gl, vaa0Buffer); 693 gl->fVertexAttribPointer(0, vaa0Size, vaa0Type, bool(vaa0Normalized), 694 vaa0Stride, vaa0Pointer); 695 } 696 } 697 698 // -- 699 700 GLBlitHelper::GLBlitHelper(GLContext* const gl) 701 : mGL(gl), 702 mDrawBlitProg_VertShader(mGL->fCreateShader(LOCAL_GL_VERTEX_SHADER)) 703 //, mYuvUploads_YSize(0, 0) 704 //, mYuvUploads_UVSize(0, 0) 705 { 706 mGL->fGenBuffers(1, &mQuadVBO); 707 { 708 const ScopedBindArrayBuffer bindVBO(mGL, mQuadVBO); 709 710 const float quadData[] = {0, 0, 1, 0, 0, 1, 1, 1}; 711 const HeapCopyOfStackArray<float> heapQuadData(quadData); 712 mGL->fBufferData(LOCAL_GL_ARRAY_BUFFER, heapQuadData.ByteLength(), 713 heapQuadData.Data(), LOCAL_GL_STATIC_DRAW); 714 715 if (mGL->IsSupported(GLFeature::vertex_array_object)) { 716 const auto prev = mGL->GetIntAs<GLuint>(LOCAL_GL_VERTEX_ARRAY_BINDING); 717 718 mGL->fGenVertexArrays(1, &mQuadVAO); 719 mGL->fBindVertexArray(mQuadVAO); 720 mGL->fEnableVertexAttribArray(0); 721 mGL->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, false, 0, 0); 722 723 mGL->fBindVertexArray(prev); 724 } 725 } 726 727 // -- 728 729 const auto glslVersion = mGL->ShadingLanguageVersion(); 730 731 if (mGL->IsGLES()) { 732 // If you run into problems on old android devices, it might be because some 733 // devices have OES_EGL_image_external but not OES_EGL_image_external_essl3. 734 // We could just use 100 in that particular case, but then we lose out on 735 // e.g. sampler3D. Let's just try 300 for now, and if we get regressions 736 // we'll add an essl100 fallback. 737 if (glslVersion >= 300) { 738 mDrawBlitProg_VersionLine = nsCString("#version 300 es\n"); 739 } else { 740 mDrawBlitProg_VersionLine = nsCString("#version 100\n"); 741 } 742 } else if (glslVersion >= 130) { 743 mDrawBlitProg_VersionLine = nsPrintfCString("#version %u\n", glslVersion); 744 } 745 746 const char kVertSource[] = 747 "\ 748 #if __VERSION__ >= 130 \n\ 749 #define ATTRIBUTE in \n\ 750 #define VARYING out \n\ 751 #else \n\ 752 #define ATTRIBUTE attribute \n\ 753 #define VARYING varying \n\ 754 #endif \n\ 755 \n\ 756 ATTRIBUTE vec2 aVert; // [0.0-1.0] \n\ 757 \n\ 758 uniform mat3 uDestMatrix; \n\ 759 uniform mat3 uTexMatrix0; \n\ 760 uniform mat3 uTexMatrix1; \n\ 761 \n\ 762 VARYING vec2 vTexCoord0; \n\ 763 VARYING vec2 vTexCoord1; \n\ 764 \n\ 765 void main(void) \n\ 766 { \n\ 767 vec2 destPos = (uDestMatrix * vec3(aVert, 1.0)).xy; \n\ 768 gl_Position = vec4(destPos * 2.0 - 1.0, 0.0, 1.0); \n\ 769 \n\ 770 vTexCoord0 = (uTexMatrix0 * vec3(aVert, 1.0)).xy; \n\ 771 vTexCoord1 = (uTexMatrix1 * vec3(aVert, 1.0)).xy; \n\ 772 } \n\ 773 "; 774 const char* const parts[] = {mDrawBlitProg_VersionLine.get(), kVertSource}; 775 mGL->fShaderSource(mDrawBlitProg_VertShader, std::size(parts), parts, 776 nullptr); 777 mGL->fCompileShader(mDrawBlitProg_VertShader); 778 } 779 780 GLBlitHelper::~GLBlitHelper() { 781 mDrawBlitProgs.clear(); 782 783 if (!mGL->MakeCurrent()) return; 784 785 mGL->fDeleteShader(mDrawBlitProg_VertShader); 786 mGL->fDeleteBuffers(1, &mQuadVBO); 787 788 if (mQuadVAO) { 789 mGL->fDeleteVertexArrays(1, &mQuadVAO); 790 } 791 } 792 793 // -- 794 795 const DrawBlitProg& GLBlitHelper::GetDrawBlitProg( 796 const DrawBlitProg::Key& key) const { 797 auto& ret = mDrawBlitProgs[key]; 798 if (!ret) { 799 ret = CreateDrawBlitProg(key); 800 } 801 return *ret; 802 } 803 804 std::unique_ptr<const DrawBlitProg> GLBlitHelper::CreateDrawBlitProg( 805 const DrawBlitProg::Key& key) const { 806 const auto precisionPref = StaticPrefs::gfx_blithelper_precision(); 807 const char* precision; 808 switch (precisionPref) { 809 case 0: 810 precision = "lowp"; 811 break; 812 case 1: 813 precision = "mediump"; 814 break; 815 default: 816 if (precisionPref != 2) { 817 NS_WARNING("gfx.blithelper.precision clamped to 2."); 818 } 819 precision = "MAXP"; 820 break; 821 } 822 823 nsPrintfCString precisionLine("\n#define PRECISION %s\n", precision); 824 825 // - 826 827 const ScopedShader fs(mGL, LOCAL_GL_FRAGMENT_SHADER); 828 829 std::vector<const char*> parts; 830 { 831 parts.push_back(mDrawBlitProg_VersionLine.get()); 832 parts.push_back(kFragPreprocHeader); 833 if (key.fragHeader) { 834 parts.push_back(key.fragHeader); 835 } 836 parts.push_back(precisionLine.BeginReading()); 837 parts.push_back(kFragDeclHeader); 838 for (const auto& part : key.fragParts) { 839 if (part) { 840 parts.push_back(part); 841 } 842 } 843 parts.push_back(kFragBody); 844 } 845 846 const auto PrintFragSource = [&]() { 847 printf_stderr("Frag source:\n"); 848 int i = 0; 849 for (const auto& part : parts) { 850 printf_stderr("// parts[%i]:\n%s\n", i, part); 851 i += 1; 852 } 853 }; 854 if (gfxEnv::MOZ_DUMP_GLBLITHELPER()) { 855 PrintFragSource(); 856 } 857 858 mGL->fShaderSource(fs, AssertedCast<GLint>(parts.size()), parts.data(), 859 nullptr); 860 mGL->fCompileShader(fs); 861 862 const auto prog = mGL->fCreateProgram(); 863 mGL->fAttachShader(prog, mDrawBlitProg_VertShader); 864 mGL->fAttachShader(prog, fs); 865 866 mGL->fBindAttribLocation(prog, 0, "aVert"); 867 mGL->fLinkProgram(prog); 868 869 GLenum status = 0; 870 mGL->fGetProgramiv(prog, LOCAL_GL_LINK_STATUS, (GLint*)&status); 871 if (status == LOCAL_GL_TRUE || mGL->CheckContextLost()) { 872 const SaveRestoreCurrentProgram oldProg(mGL); 873 mGL->fUseProgram(prog); 874 const char* samplerNames[] = {"uTex0", "uTex1", "uTex2"}; 875 for (int i = 0; i < 3; i++) { 876 const auto loc = mGL->fGetUniformLocation(prog, samplerNames[i]); 877 if (loc == -1) continue; 878 mGL->fUniform1i(loc, i); 879 } 880 881 return std::make_unique<DrawBlitProg>(this, prog); 882 } 883 884 GLuint progLogLen = 0; 885 mGL->fGetProgramiv(prog, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&progLogLen); 886 const UniquePtr<char[]> progLog(new char[progLogLen + 1]); 887 mGL->fGetProgramInfoLog(prog, progLogLen, nullptr, progLog.get()); 888 progLog[progLogLen] = 0; 889 890 const auto& vs = mDrawBlitProg_VertShader; 891 GLuint vsLogLen = 0; 892 mGL->fGetShaderiv(vs, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&vsLogLen); 893 const UniquePtr<char[]> vsLog(new char[vsLogLen + 1]); 894 mGL->fGetShaderInfoLog(vs, vsLogLen, nullptr, vsLog.get()); 895 vsLog[vsLogLen] = 0; 896 897 GLuint fsLogLen = 0; 898 mGL->fGetShaderiv(fs, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&fsLogLen); 899 const UniquePtr<char[]> fsLog(new char[fsLogLen + 1]); 900 mGL->fGetShaderInfoLog(fs, fsLogLen, nullptr, fsLog.get()); 901 fsLog[fsLogLen] = 0; 902 903 const auto logs = 904 std::string("DrawBlitProg link failed:\n") + "progLog: " + progLog.get() + 905 "\n" + "vsLog: " + vsLog.get() + "\n" + "fsLog: " + fsLog.get() + "\n"; 906 gfxCriticalError() << logs; 907 908 PrintFragSource(); 909 910 MOZ_CRASH("DrawBlitProg link failed"); 911 } 912 913 // ----------------------------------------------------------------------------- 914 915 #ifdef XP_MACOSX 916 static RefPtr<MacIOSurface> LookupSurface( 917 const layers::SurfaceDescriptorMacIOSurface& sd) { 918 return MacIOSurface::LookupSurface(sd.surfaceId(), !sd.isOpaque(), 919 sd.yUVColorSpace()); 920 } 921 #endif 922 923 bool GLBlitHelper::BlitSdToFramebuffer(const layers::SurfaceDescriptor& asd, 924 const gfx::IntRect& destRect, 925 const OriginPos destOrigin, 926 const gfx::IntSize& fbSize, 927 Maybe<gfxAlphaType> convertAlpha) { 928 const auto sdType = asd.type(); 929 switch (sdType) { 930 case layers::SurfaceDescriptor::TSurfaceDescriptorBuffer: { 931 const auto& sd = asd.get_SurfaceDescriptorBuffer(); 932 const auto yuvData = PlanarYCbCrData::From(sd); 933 if (!yuvData) { 934 gfxCriticalNote << "[GLBlitHelper::BlitSdToFramebuffer] " 935 "PlanarYCbCrData::From failed"; 936 return false; 937 } 938 return BlitPlanarYCbCr(*yuvData, destRect, destOrigin, fbSize, 939 convertAlpha); 940 } 941 #ifdef XP_WIN 942 case layers::SurfaceDescriptor::TSurfaceDescriptorD3D10: { 943 const auto& sd = asd.get_SurfaceDescriptorD3D10(); 944 return BlitDescriptor(sd, destRect, destOrigin, fbSize, convertAlpha); 945 } 946 case layers::SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr: { 947 const auto& sd = asd.get_SurfaceDescriptorDXGIYCbCr(); 948 return BlitDescriptor(sd, destRect, destOrigin, fbSize, convertAlpha); 949 } 950 #endif 951 #ifdef XP_MACOSX 952 case layers::SurfaceDescriptor::TSurfaceDescriptorMacIOSurface: { 953 const auto& sd = asd.get_SurfaceDescriptorMacIOSurface(); 954 const auto surf = LookupSurface(sd); 955 if (!surf) { 956 NS_WARNING("LookupSurface(MacIOSurface) failed"); 957 // Sometimes that frame for our handle gone already. That's life, for 958 // now. 959 return false; 960 } 961 return BlitImage(surf, destRect, destOrigin, fbSize, convertAlpha); 962 } 963 #endif 964 #ifdef MOZ_WIDGET_ANDROID 965 case layers::SurfaceDescriptor::TSurfaceTextureDescriptor: { 966 const auto& sd = asd.get_SurfaceTextureDescriptor(); 967 auto surfaceTexture = java::GeckoSurfaceTexture::Lookup(sd.handle()); 968 return Blit(surfaceTexture, sd.size(), destRect, destOrigin, fbSize, 969 convertAlpha); 970 } 971 case layers::SurfaceDescriptor::TEGLImageDescriptor: { 972 const auto& sd = asd.get_EGLImageDescriptor(); 973 return Blit((EGLImage)sd.image(), (EGLSync)sd.fence(), sd.size(), 974 destRect, destOrigin, fbSize, convertAlpha); 975 } 976 #endif 977 #ifdef MOZ_WIDGET_GTK 978 case layers::SurfaceDescriptor::TSurfaceDescriptorDMABuf: { 979 const auto& sd = asd.get_SurfaceDescriptorDMABuf(); 980 RefPtr<DMABufSurface> surface = DMABufSurface::CreateDMABufSurface(sd); 981 return Blit(surface, destRect, destOrigin, fbSize, convertAlpha); 982 } 983 #endif 984 default: 985 return false; 986 } 987 } 988 989 bool GLBlitHelper::BlitImageToFramebuffer(layers::Image* const srcImage, 990 const gfx::IntRect& destRect, 991 const OriginPos destOrigin, 992 const gfx::IntSize& fbSize) { 993 switch (srcImage->GetFormat()) { 994 case ImageFormat::PLANAR_YCBCR: { 995 const auto srcImage2 = static_cast<PlanarYCbCrImage*>(srcImage); 996 const auto data = srcImage2->GetData(); 997 return BlitPlanarYCbCr(*data, destRect, destOrigin, fbSize); 998 } 999 1000 case ImageFormat::SURFACE_TEXTURE: { 1001 #ifdef MOZ_WIDGET_ANDROID 1002 auto* image = srcImage->AsSurfaceTextureImage(); 1003 MOZ_ASSERT(image); 1004 auto surfaceTexture = 1005 java::GeckoSurfaceTexture::Lookup(image->GetHandle()); 1006 return Blit(surfaceTexture, image->GetSize(), destRect, destOrigin, 1007 fbSize); 1008 #else 1009 MOZ_ASSERT(false); 1010 return false; 1011 #endif 1012 } 1013 case ImageFormat::MAC_IOSURFACE: 1014 #ifdef XP_MACOSX 1015 return BlitImage(srcImage->AsMacIOSurfaceImage(), destRect, destOrigin, 1016 fbSize); 1017 #else 1018 MOZ_ASSERT(false); 1019 return false; 1020 #endif 1021 1022 case ImageFormat::GPU_VIDEO: 1023 return BlitImage(static_cast<layers::GPUVideoImage*>(srcImage), destRect, 1024 destOrigin, fbSize); 1025 #ifdef XP_WIN 1026 case ImageFormat::D3D11_SHARE_HANDLE_TEXTURE: 1027 return BlitImage(static_cast<layers::D3D11ShareHandleImage*>(srcImage), 1028 destRect, destOrigin, fbSize); 1029 case ImageFormat::D3D11_TEXTURE_ZERO_COPY: 1030 return BlitImage( 1031 static_cast<layers::D3D11ZeroCopyTextureImage*>(srcImage), destRect, 1032 destOrigin, fbSize); 1033 case ImageFormat::D3D9_RGB32_TEXTURE: 1034 return false; // todo 1035 case ImageFormat::DCOMP_SURFACE: 1036 return false; 1037 #else 1038 case ImageFormat::D3D11_SHARE_HANDLE_TEXTURE: 1039 case ImageFormat::D3D11_TEXTURE_ZERO_COPY: 1040 case ImageFormat::D3D9_RGB32_TEXTURE: 1041 case ImageFormat::DCOMP_SURFACE: 1042 MOZ_ASSERT(false); 1043 return false; 1044 #endif 1045 case ImageFormat::DMABUF: 1046 #ifdef MOZ_WIDGET_GTK 1047 return BlitImage(static_cast<layers::DMABUFSurfaceImage*>(srcImage), 1048 destRect, destOrigin, fbSize); 1049 #else 1050 return false; 1051 #endif 1052 case ImageFormat::MOZ2D_SURFACE: 1053 case ImageFormat::NV_IMAGE: 1054 case ImageFormat::OVERLAY_IMAGE: 1055 case ImageFormat::SHARED_RGB: 1056 case ImageFormat::TEXTURE_WRAPPER: 1057 return false; // todo 1058 } 1059 return false; 1060 } 1061 1062 // ------------------------------------- 1063 1064 const char* GLBlitHelper::GetAlphaMixin( 1065 Maybe<gfxAlphaType> convertAlpha) const { 1066 if (!convertAlpha) { 1067 return nullptr; 1068 } 1069 switch (convertAlpha.value()) { 1070 case gfxAlphaType::Premult: 1071 return kFragMixin_AlphaMultColors; 1072 case gfxAlphaType::NonPremult: 1073 return kFragMixin_AlphaUnpremultColors; 1074 case gfxAlphaType::Opaque: 1075 return kFragMixin_AlphaOne; 1076 default: 1077 return nullptr; 1078 } 1079 } 1080 1081 #ifdef MOZ_WIDGET_ANDROID 1082 bool GLBlitHelper::Blit(const java::GeckoSurfaceTexture::Ref& surfaceTexture, 1083 const gfx::IntSize& texSize, 1084 const gfx::IntRect& destRect, 1085 const OriginPos destOrigin, const gfx::IntSize& fbSize, 1086 Maybe<gfxAlphaType> convertAlpha) const { 1087 if (!surfaceTexture) { 1088 return false; 1089 } 1090 1091 const ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0); 1092 1093 if (!surfaceTexture->IsAttachedToGLContext((int64_t)mGL)) { 1094 GLuint tex; 1095 mGL->MakeCurrent(); 1096 mGL->fGenTextures(1, &tex); 1097 1098 if (NS_FAILED(surfaceTexture->AttachToGLContext((int64_t)mGL, tex))) { 1099 mGL->fDeleteTextures(1, &tex); 1100 return false; 1101 } 1102 } 1103 1104 const ScopedBindTexture savedTex(mGL, surfaceTexture->GetTexName(), 1105 LOCAL_GL_TEXTURE_EXTERNAL); 1106 surfaceTexture->UpdateTexImage(); 1107 1108 gfx::Matrix4x4 transform; 1109 const auto surf = java::sdk::SurfaceTexture::LocalRef::From(surfaceTexture); 1110 gl::AndroidSurfaceTexture::GetTransformMatrix(surf, &transform); 1111 // SurfaceTexture transforms should always be 2D 1112 MOZ_DIAGNOSTIC_ASSERT(transform.Is2D()); 1113 const auto transform3 = MatrixToMat3(transform.As2D()); 1114 1115 // The origin is top-left here due to the transform supplied in the surface. 1116 const auto srcOrigin = OriginPos::TopLeft; 1117 const bool yFlip = (srcOrigin != destOrigin); 1118 const auto& prog = GetDrawBlitProg( 1119 {kFragHeader_TexExt, 1120 {kFragSample_OnePlane, kFragConvert_None, GetAlphaMixin(convertAlpha)}}); 1121 const DrawBlitProg::BaseArgs baseArgs = {transform3, yFlip, fbSize, destRect, 1122 texSize}; 1123 prog.Draw(baseArgs, nullptr); 1124 1125 if (surfaceTexture->IsSingleBuffer()) { 1126 surfaceTexture->ReleaseTexImage(); 1127 } 1128 1129 return true; 1130 } 1131 1132 bool GLBlitHelper::Blit(EGLImage image, EGLSync fence, 1133 const gfx::IntSize& texSize, 1134 const gfx::IntRect& destRect, 1135 const OriginPos destOrigin, const gfx::IntSize& fbSize, 1136 Maybe<gfxAlphaType> convertAlpha) const { 1137 if (!image) { 1138 return false; 1139 } 1140 1141 GLenum target = mGL->GetPreferredEGLImageTextureTarget(); 1142 const ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0); 1143 ScopedTexture tex(mGL); 1144 ScopedBindTexture bindTex(mGL, tex.Texture(), target); 1145 1146 mGL->TexParams_SetClampNoMips(target); 1147 mGL->fEGLImageTargetTexture2D(target, image); 1148 1149 const auto srcOrigin = OriginPos::BottomLeft; 1150 const bool yFlip = (srcOrigin != destOrigin); 1151 const auto& prog = GetDrawBlitProg( 1152 {target == LOCAL_GL_TEXTURE_2D ? kFragHeader_Tex2D : kFragHeader_TexExt, 1153 {kFragSample_OnePlane, kFragConvert_None, GetAlphaMixin(convertAlpha)}}); 1154 const DrawBlitProg::BaseArgs baseArgs = {SubRectMat3(0, 0, 1, 1), yFlip, 1155 fbSize, destRect, texSize}; 1156 prog.Draw(baseArgs); 1157 1158 return true; 1159 } 1160 #endif 1161 1162 // ------------------------------------- 1163 1164 bool GuessDivisors(const gfx::IntSize& ySize, const gfx::IntSize& uvSize, 1165 gfx::IntSize* const out_divisors) { 1166 const gfx::IntSize divisors((ySize.width == uvSize.width) ? 1 : 2, 1167 (ySize.height == uvSize.height) ? 1 : 2); 1168 if (uvSize.width * divisors.width != ySize.width || 1169 uvSize.height * divisors.height != ySize.height) { 1170 return false; 1171 } 1172 *out_divisors = divisors; 1173 return true; 1174 } 1175 1176 bool GLBlitHelper::BlitPlanarYCbCr(const PlanarYCbCrData& yuvData, 1177 const gfx::IntRect& destRect, 1178 const OriginPos destOrigin, 1179 const gfx::IntSize& fbSize, 1180 Maybe<gfxAlphaType> convertAlpha) { 1181 const auto& prog = 1182 GetDrawBlitProg({kFragHeader_Tex2D, 1183 {kFragSample_ThreePlane, kFragConvert_ColorMatrix, 1184 GetAlphaMixin(convertAlpha)}}); 1185 1186 if (!mYuvUploads[0]) { 1187 mGL->fGenTextures(3, mYuvUploads); 1188 const ScopedBindTexture bindTex(mGL, mYuvUploads[0], LOCAL_GL_TEXTURE_2D); 1189 mGL->TexParams_SetClampNoMips(LOCAL_GL_TEXTURE_2D); 1190 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[1]); 1191 mGL->TexParams_SetClampNoMips(LOCAL_GL_TEXTURE_2D); 1192 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[2]); 1193 mGL->TexParams_SetClampNoMips(LOCAL_GL_TEXTURE_2D); 1194 } 1195 1196 // -- 1197 1198 auto ySize = yuvData.YDataSize(); 1199 auto cbcrSize = yuvData.CbCrDataSize(); 1200 if (yuvData.mYSkip || yuvData.mCbSkip || yuvData.mCrSkip || ySize.width < 0 || 1201 ySize.height < 0 || cbcrSize.width < 0 || cbcrSize.height < 0 || 1202 yuvData.mYStride < 0 || yuvData.mCbCrStride < 0) { 1203 gfxCriticalError() << "Unusual PlanarYCbCrData: " << yuvData.mYSkip << "," 1204 << yuvData.mCbSkip << "," << yuvData.mCrSkip << ", " 1205 << ySize.width << "," << ySize.height << ", " 1206 << cbcrSize.width << "," << cbcrSize.height << ", " 1207 << yuvData.mYStride << "," << yuvData.mCbCrStride; 1208 return false; 1209 } 1210 1211 gfx::IntSize divisors; 1212 switch (yuvData.mChromaSubsampling) { 1213 case gfx::ChromaSubsampling::FULL: 1214 divisors = gfx::IntSize(1, 1); 1215 break; 1216 case gfx::ChromaSubsampling::HALF_WIDTH: 1217 divisors = gfx::IntSize(2, 1); 1218 break; 1219 case gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT: 1220 divisors = gfx::IntSize(2, 2); 1221 break; 1222 default: 1223 gfxCriticalError() << "Unknown chroma subsampling:" 1224 << int(yuvData.mChromaSubsampling); 1225 return false; 1226 } 1227 1228 // -- 1229 1230 // RED textures aren't valid in GLES2, and ALPHA textures are not valid in 1231 // desktop GL Core Profiles. So use R8 textures on GL3.0+ and GLES3.0+, but 1232 // LUMINANCE/LUMINANCE/UNSIGNED_BYTE otherwise. 1233 GLenum internalFormat; 1234 GLenum unpackFormat; 1235 if (mGL->IsAtLeast(gl::ContextProfile::OpenGLCore, 300) || 1236 mGL->IsAtLeast(gl::ContextProfile::OpenGLES, 300)) { 1237 internalFormat = LOCAL_GL_R8; 1238 unpackFormat = LOCAL_GL_RED; 1239 } else { 1240 internalFormat = LOCAL_GL_LUMINANCE; 1241 unpackFormat = LOCAL_GL_LUMINANCE; 1242 } 1243 1244 // -- 1245 1246 const ScopedSaveMultiTex saveTex(mGL, 3, LOCAL_GL_TEXTURE_2D); 1247 const ResetUnpackState reset(mGL); 1248 const gfx::IntSize yTexSize(yuvData.mYStride, yuvData.YDataSize().height); 1249 const gfx::IntSize uvTexSize(yuvData.mCbCrStride, 1250 yuvData.CbCrDataSize().height); 1251 1252 if (yTexSize != mYuvUploads_YSize || uvTexSize != mYuvUploads_UVSize) { 1253 mYuvUploads_YSize = yTexSize; 1254 mYuvUploads_UVSize = uvTexSize; 1255 1256 mGL->fActiveTexture(LOCAL_GL_TEXTURE0); 1257 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[0]); 1258 mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, internalFormat, yTexSize.width, 1259 yTexSize.height, 0, unpackFormat, LOCAL_GL_UNSIGNED_BYTE, 1260 nullptr); 1261 for (int i = 1; i < 3; i++) { 1262 mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i); 1263 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[i]); 1264 mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, internalFormat, uvTexSize.width, 1265 uvTexSize.height, 0, unpackFormat, 1266 LOCAL_GL_UNSIGNED_BYTE, nullptr); 1267 } 1268 } 1269 1270 // -- 1271 1272 mGL->fActiveTexture(LOCAL_GL_TEXTURE0); 1273 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[0]); 1274 mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0, yTexSize.width, 1275 yTexSize.height, unpackFormat, LOCAL_GL_UNSIGNED_BYTE, 1276 yuvData.mYChannel); 1277 mGL->fActiveTexture(LOCAL_GL_TEXTURE1); 1278 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[1]); 1279 mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0, uvTexSize.width, 1280 uvTexSize.height, unpackFormat, LOCAL_GL_UNSIGNED_BYTE, 1281 yuvData.mCbChannel); 1282 mGL->fActiveTexture(LOCAL_GL_TEXTURE2); 1283 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[2]); 1284 mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0, uvTexSize.width, 1285 uvTexSize.height, unpackFormat, LOCAL_GL_UNSIGNED_BYTE, 1286 yuvData.mCrChannel); 1287 1288 // -- 1289 1290 const auto& clipRect = yuvData.mPictureRect; 1291 const auto srcOrigin = OriginPos::BottomLeft; 1292 const bool yFlip = (destOrigin != srcOrigin); 1293 1294 const DrawBlitProg::BaseArgs baseArgs = {SubRectMat3(clipRect, yTexSize), 1295 yFlip, fbSize, destRect, 1296 clipRect.Size()}; 1297 const DrawBlitProg::YUVArgs yuvArgs = { 1298 SubRectMat3(clipRect, uvTexSize, divisors), Some(yuvData.mYUVColorSpace)}; 1299 prog.Draw(baseArgs, &yuvArgs); 1300 return true; 1301 } 1302 1303 // ------------------------------------- 1304 1305 #ifdef XP_MACOSX 1306 bool GLBlitHelper::BlitImage(layers::MacIOSurfaceImage* const srcImage, 1307 const gfx::IntRect& destRect, 1308 const OriginPos destOrigin, 1309 const gfx::IntSize& fbSize) const { 1310 return BlitImage(srcImage->GetSurface(), destRect, destOrigin, fbSize); 1311 } 1312 1313 static std::string IntAsAscii(const int x) { 1314 std::string str; 1315 str.reserve(6); 1316 auto u = static_cast<unsigned int>(x); 1317 while (u) { 1318 str.insert(str.begin(), u & 0xff); 1319 u >>= 8; 1320 } 1321 str.insert(str.begin(), '\''); 1322 str.push_back('\''); 1323 return str; 1324 } 1325 1326 bool GLBlitHelper::BlitImage(MacIOSurface* const iosurf, 1327 const gfx::IntRect& destRect, 1328 const OriginPos destOrigin, 1329 const gfx::IntSize& fbSize, 1330 Maybe<gfxAlphaType> convertAlpha) const { 1331 if (!iosurf) { 1332 gfxCriticalError() << "Null MacIOSurface for GLBlitHelper::BlitImage"; 1333 return false; 1334 } 1335 if (mGL->GetContextType() != GLContextType::CGL) { 1336 MOZ_ASSERT(false); 1337 return false; 1338 } 1339 1340 const auto& srcOrigin = OriginPos::BottomLeft; 1341 1342 DrawBlitProg::BaseArgs baseArgs; 1343 baseArgs.yFlip = (destOrigin != srcOrigin); 1344 baseArgs.fbSize = fbSize; 1345 baseArgs.destRect = destRect; 1346 1347 // TODO: The colorspace is known by the IOSurface, why override it? 1348 // See GetYUVColorSpace/GetFullRange() 1349 DrawBlitProg::YUVArgs yuvArgs; 1350 yuvArgs.colorSpaceForMatrix = Some(iosurf->GetYUVColorSpace()); 1351 1352 auto planes = iosurf->GetPlaneCount(); 1353 if (!planes) { 1354 planes = 1; // Bad API. No cookie. 1355 } 1356 1357 const auto pixelFormat = iosurf->GetPixelFormat(); 1358 if (mGL->ShouldSpew()) { 1359 const auto formatStr = IntAsAscii(pixelFormat); 1360 printf_stderr("iosurf format: %s (0x%08x)\n", formatStr.c_str(), 1361 pixelFormat); 1362 } 1363 1364 const char* fragSample = nullptr; 1365 bool yuv = true; 1366 switch (planes) { 1367 case 1: 1368 switch (pixelFormat) { 1369 case kCVPixelFormatType_24RGB: 1370 case kCVPixelFormatType_24BGR: 1371 case kCVPixelFormatType_32ARGB: 1372 case kCVPixelFormatType_32BGRA: 1373 case kCVPixelFormatType_32ABGR: 1374 case kCVPixelFormatType_32RGBA: 1375 case kCVPixelFormatType_64ARGB: 1376 case kCVPixelFormatType_48RGB: 1377 fragSample = kFragSample_OnePlane; 1378 yuv = false; 1379 break; 1380 case kCVPixelFormatType_422YpCbCr8: 1381 case kCVPixelFormatType_422YpCbCr8_yuvs: 1382 fragSample = kFragSample_OnePlane_YUV_via_GBR; 1383 break; 1384 default: { 1385 std::string str; 1386 if (pixelFormat <= 0xff) { 1387 str = std::to_string(pixelFormat); 1388 } else { 1389 str = IntAsAscii(pixelFormat); 1390 } 1391 gfxCriticalError() << "Unhandled kCVPixelFormatType_*: " << str; 1392 // Probably YUV though 1393 fragSample = kFragSample_OnePlane_YUV_via_GBR; 1394 break; 1395 } 1396 } 1397 break; 1398 case 2: 1399 fragSample = kFragSample_TwoPlane; 1400 break; 1401 case 3: 1402 fragSample = kFragSample_ThreePlane; 1403 break; 1404 default: 1405 gfxCriticalError() << "Unexpected plane count: " << planes; 1406 return false; 1407 } 1408 1409 const GLenum texTarget = LOCAL_GL_TEXTURE_RECTANGLE; 1410 const ScopedSaveMultiTex saveTex(mGL, planes, texTarget); 1411 Maybe<ScopedTexture> texs[3]; 1412 1413 for (uint32_t p = 0; p < planes; p++) { 1414 texs[p].emplace(mGL); 1415 mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + p); 1416 mGL->fBindTexture(texTarget, texs[p]->Texture()); 1417 mGL->TexParams_SetClampNoMips(texTarget); 1418 1419 if (!iosurf->BindTexImage(mGL, p)) { 1420 return false; 1421 } 1422 1423 if (p == 0) { 1424 const auto width = iosurf->GetDevicePixelWidth(p); 1425 const auto height = iosurf->GetDevicePixelHeight(p); 1426 baseArgs.texMatrix0 = SubRectMat3(0, 0, width, height); 1427 baseArgs.texSize = gfx::IntSize(width, height); 1428 yuvArgs.texMatrix1 = SubRectMat3(0, 0, width / 2.0, height / 2.0); 1429 } 1430 } 1431 1432 const char* fragConvert = yuv ? kFragConvert_ColorMatrix : kFragConvert_None; 1433 const auto& prog = GetDrawBlitProg({ 1434 kFragHeader_Tex2DRect, 1435 {fragSample, fragConvert, GetAlphaMixin(convertAlpha)}, 1436 }); 1437 prog.Draw(baseArgs, yuv ? &yuvArgs : nullptr); 1438 return true; 1439 } 1440 #endif 1441 1442 // ----------------------------------------------------------------------------- 1443 1444 void GLBlitHelper::DrawBlitTextureToFramebuffer( 1445 const GLuint srcTex, const gfx::IntSize& srcSize, 1446 const gfx::IntSize& destSize, const GLenum srcTarget, const bool srcIsBGRA, 1447 const bool yFlip, const Maybe<gfxAlphaType> convertAlpha) const { 1448 const char* fragHeader = nullptr; 1449 Mat3 texMatrix0; 1450 switch (srcTarget) { 1451 case LOCAL_GL_TEXTURE_2D: 1452 fragHeader = kFragHeader_Tex2D; 1453 texMatrix0 = Mat3::I(); 1454 break; 1455 case LOCAL_GL_TEXTURE_RECTANGLE_ARB: 1456 fragHeader = kFragHeader_Tex2DRect; 1457 texMatrix0 = SubRectMat3(0, 0, srcSize.width, srcSize.height); 1458 break; 1459 default: 1460 gfxCriticalError() << "Unexpected srcTarget: " << srcTarget; 1461 return; 1462 } 1463 const auto fragConvert = srcIsBGRA ? kFragConvert_BGR : kFragConvert_None; 1464 const auto& prog = GetDrawBlitProg( 1465 {fragHeader, 1466 {kFragSample_OnePlane, fragConvert, GetAlphaMixin(convertAlpha)}}); 1467 1468 const ScopedSaveMultiTex saveTex(mGL, 1, srcTarget); 1469 mGL->fActiveTexture(LOCAL_GL_TEXTURE0); 1470 mGL->fBindTexture(srcTarget, srcTex); 1471 1472 const DrawBlitProg::BaseArgs baseArgs = {texMatrix0, yFlip, destSize, {}}; 1473 prog.Draw(baseArgs); 1474 } 1475 1476 // ----------------------------------------------------------------------------- 1477 1478 void GLBlitHelper::BlitFramebuffer(const gfx::IntRect& srcRect, 1479 const gfx::IntRect& destRect, 1480 GLuint filter) const { 1481 MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit)); 1482 1483 const ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false); 1484 mGL->fBlitFramebuffer(srcRect.x, srcRect.y, srcRect.XMost(), srcRect.YMost(), 1485 destRect.x, destRect.y, destRect.XMost(), 1486 destRect.YMost(), LOCAL_GL_COLOR_BUFFER_BIT, filter); 1487 } 1488 1489 // -- 1490 1491 void GLBlitHelper::BlitFramebufferToFramebuffer(const GLuint srcFB, 1492 const GLuint destFB, 1493 const gfx::IntRect& srcRect, 1494 const gfx::IntRect& destRect, 1495 GLuint filter) const { 1496 MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit)); 1497 MOZ_GL_ASSERT(mGL, !srcFB || mGL->fIsFramebuffer(srcFB)); 1498 MOZ_GL_ASSERT(mGL, !destFB || mGL->fIsFramebuffer(destFB)); 1499 1500 const ScopedBindFramebuffer boundFB(mGL); 1501 mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, srcFB); 1502 mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destFB); 1503 1504 BlitFramebuffer(srcRect, destRect, filter); 1505 } 1506 1507 void GLBlitHelper::BlitTextureToFramebuffer(GLuint srcTex, 1508 const gfx::IntSize& srcSize, 1509 const gfx::IntSize& destSize, 1510 GLenum srcTarget) const { 1511 MOZ_GL_ASSERT(mGL, mGL->fIsTexture(srcTex)); 1512 1513 if (mGL->IsSupported(GLFeature::framebuffer_blit)) { 1514 const ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget); 1515 const ScopedBindFramebuffer bindFB(mGL); 1516 mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, srcWrapper.FB()); 1517 BlitFramebuffer(gfx::IntRect({}, srcSize), gfx::IntRect({}, destSize)); 1518 return; 1519 } 1520 1521 DrawBlitTextureToFramebuffer(srcTex, srcSize, destSize, srcTarget); 1522 } 1523 1524 void GLBlitHelper::BlitFramebufferToTexture(GLuint destTex, 1525 const gfx::IntSize& srcSize, 1526 const gfx::IntSize& destSize, 1527 GLenum destTarget) const { 1528 MOZ_GL_ASSERT(mGL, mGL->fIsTexture(destTex)); 1529 1530 if (mGL->IsSupported(GLFeature::framebuffer_blit)) { 1531 const ScopedFramebufferForTexture destWrapper(mGL, destTex, destTarget); 1532 const ScopedBindFramebuffer bindFB(mGL); 1533 mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destWrapper.FB()); 1534 BlitFramebuffer(gfx::IntRect({}, srcSize), gfx::IntRect({}, destSize)); 1535 return; 1536 } 1537 1538 ScopedBindTexture autoTex(mGL, destTex, destTarget); 1539 ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false); 1540 mGL->fCopyTexSubImage2D(destTarget, 0, 0, 0, 0, 0, srcSize.width, 1541 srcSize.height); 1542 } 1543 1544 void GLBlitHelper::BlitTextureToTexture(GLuint srcTex, GLuint destTex, 1545 const gfx::IntSize& srcSize, 1546 const gfx::IntSize& destSize, 1547 GLenum srcTarget, 1548 GLenum destTarget) const { 1549 MOZ_GL_ASSERT(mGL, mGL->fIsTexture(srcTex)); 1550 MOZ_GL_ASSERT(mGL, mGL->fIsTexture(destTex)); 1551 1552 // Start down the CopyTexSubImage path, not the DrawBlit path. 1553 const ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget); 1554 const ScopedBindFramebuffer bindFB(mGL, srcWrapper.FB()); 1555 BlitFramebufferToTexture(destTex, srcSize, destSize, destTarget); 1556 } 1557 1558 // ------------------------------------- 1559 1560 bool GLBlitHelper::BlitImage(layers::GPUVideoImage* const srcImage, 1561 const gfx::IntRect& destRect, 1562 const OriginPos destOrigin, 1563 const gfx::IntSize& fbSize) const { 1564 const auto& data = srcImage->GetData(); 1565 if (!data) return false; 1566 1567 const auto& desc = data->SD(); 1568 1569 MOZ_ASSERT( 1570 desc.type() == 1571 layers::SurfaceDescriptorGPUVideo::TSurfaceDescriptorRemoteDecoder); 1572 const auto& subdescUnion = 1573 desc.get_SurfaceDescriptorRemoteDecoder().subdesc(); 1574 switch (subdescUnion.type()) { 1575 #ifdef MOZ_WIDGET_GTK 1576 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorDMABuf: { 1577 const auto& subdesc = subdescUnion.get_SurfaceDescriptorDMABuf(); 1578 RefPtr<DMABufSurface> surface = 1579 DMABufSurface::CreateDMABufSurface(subdesc); 1580 return Blit(surface, destRect, destOrigin, fbSize); 1581 } 1582 #endif 1583 #ifdef XP_WIN 1584 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorD3D10: { 1585 const auto& subdesc = subdescUnion.get_SurfaceDescriptorD3D10(); 1586 return BlitDescriptor(subdesc, destRect, destOrigin, fbSize); 1587 } 1588 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorDXGIYCbCr: { 1589 const auto& subdesc = subdescUnion.get_SurfaceDescriptorDXGIYCbCr(); 1590 return BlitDescriptor(subdesc, destRect, destOrigin, fbSize); 1591 } 1592 #endif 1593 #ifdef XP_MACOSX 1594 case layers::RemoteDecoderVideoSubDescriptor:: 1595 TSurfaceDescriptorMacIOSurface: { 1596 const auto& subdesc = subdescUnion.get_SurfaceDescriptorMacIOSurface(); 1597 RefPtr<MacIOSurface> surface = MacIOSurface::LookupSurface( 1598 subdesc.surfaceId(), !subdesc.isOpaque(), subdesc.yUVColorSpace()); 1599 MOZ_ASSERT(surface); 1600 if (!surface) { 1601 return false; 1602 } 1603 return BlitImage(surface, destRect, destOrigin, fbSize); 1604 } 1605 #endif 1606 case layers::RemoteDecoderVideoSubDescriptor::Tnull_t: 1607 // This GPUVideoImage isn't directly readable outside the GPU process. 1608 // Abort. 1609 return false; 1610 default: 1611 gfxCriticalError() << "Unhandled subdesc type: " 1612 << uint32_t(subdescUnion.type()); 1613 return false; 1614 } 1615 } 1616 1617 // ------------------------------------- 1618 #ifdef MOZ_WIDGET_GTK 1619 bool GLBlitHelper::Blit(DMABufSurface* surface, const gfx::IntRect& destRect, 1620 OriginPos destOrigin, const gfx::IntSize& fbSize, 1621 Maybe<gfxAlphaType> convertAlpha) const { 1622 const auto& srcOrigin = OriginPos::BottomLeft; 1623 1624 DrawBlitProg::BaseArgs baseArgs; 1625 baseArgs.yFlip = (destOrigin != srcOrigin); 1626 baseArgs.fbSize = fbSize; 1627 baseArgs.destRect = destRect; 1628 1629 // TODO: The colorspace is known by the DMABUFSurface, why override it? 1630 // See GetYUVColorSpace/GetFullRange() 1631 DrawBlitProg::YUVArgs yuvArgs; 1632 yuvArgs.colorSpaceForMatrix = Some(surface->GetYUVColorSpace()); 1633 1634 const DrawBlitProg::YUVArgs* pYuvArgs = nullptr; 1635 const auto planes = surface->GetTextureCount(); 1636 1637 // - 1638 // Ensure textures for all planes have been created. 1639 1640 const bool createTextures = [&]() { 1641 for (int i = 0; i < planes; i++) { 1642 if (!surface->GetTexture(i)) { 1643 return true; 1644 } 1645 } 1646 return false; 1647 }(); 1648 1649 bool didCreateTexture = false; 1650 auto releaseTextures = mozilla::MakeScopeExit([&] { 1651 if (didCreateTexture) { 1652 surface->ReleaseTextures(); 1653 } 1654 }); 1655 1656 if (createTextures) { 1657 for (int i = 0; i < planes; i++) { 1658 if (surface->GetTexture(i)) { 1659 continue; 1660 } 1661 if (!surface->CreateTexture(mGL, i)) { 1662 LOGDMABUF(("GLBlitHelper::Blit(): Failed to create DMABuf textures.")); 1663 return false; 1664 } 1665 didCreateTexture = true; 1666 } 1667 } 1668 1669 // - 1670 1671 const GLenum texTarget = LOCAL_GL_TEXTURE_2D; 1672 1673 const ScopedSaveMultiTex saveTex(mGL, planes, texTarget); 1674 const auto pixelFormat = surface->GetSurfaceType(); 1675 1676 const char* fragSample = nullptr; 1677 auto fragConvert = kFragConvert_None; 1678 switch (pixelFormat) { 1679 case DMABufSurface::SURFACE_RGBA: 1680 fragSample = kFragSample_OnePlane; 1681 break; 1682 case DMABufSurface::SURFACE_YUV: 1683 if (surface->GetTextureCount() == 2) { 1684 fragSample = kFragSample_TwoPlane; 1685 } else if (surface->GetTextureCount() == 3) { 1686 fragSample = kFragSample_ThreePlane; 1687 } else { 1688 gfxCriticalError() << "Unexpected planes count: " 1689 << surface->GetTextureCount(); 1690 return false; 1691 } 1692 pYuvArgs = &yuvArgs; 1693 fragConvert = kFragConvert_ColorMatrix; 1694 break; 1695 default: 1696 return false; 1697 } 1698 1699 for (const auto p : IntegerRange(planes)) { 1700 mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + p); 1701 mGL->fBindTexture(texTarget, surface->GetTexture(p)); 1702 mGL->TexParams_SetClampNoMips(texTarget); 1703 } 1704 1705 // We support only NV12/YUV420 formats only with 1/2 texture scale. 1706 // We don't set cliprect as DMABus textures are created without padding. 1707 baseArgs.texMatrix0 = SubRectMat3(0, 0, 1, 1); 1708 baseArgs.texSize = gfx::IntSize(surface->GetWidth(), surface->GetHeight()); 1709 yuvArgs.texMatrix1 = SubRectMat3(0, 0, 1, 1); 1710 1711 const auto& prog = 1712 GetDrawBlitProg({kFragHeader_Tex2D, 1713 {fragSample, fragConvert, GetAlphaMixin(convertAlpha)}}); 1714 prog.Draw(baseArgs, pYuvArgs); 1715 1716 return true; 1717 } 1718 1719 bool GLBlitHelper::BlitImage(layers::DMABUFSurfaceImage* srcImage, 1720 const gfx::IntRect& destRect, OriginPos destOrigin, 1721 const gfx::IntSize& fbSize) const { 1722 DMABufSurface* surface = srcImage->GetSurface(); 1723 if (!surface) { 1724 gfxCriticalError() << "Null DMABUFSurface for GLBlitHelper::BlitImage"; 1725 return false; 1726 } 1727 return Blit(surface, destRect, destOrigin, fbSize); 1728 } 1729 1730 bool GLBlitHelper::BlitYCbCrImageToDMABuf(const PlanarYCbCrData& yuvData, 1731 DMABufSurface* surface) { 1732 if ((!mGL->IsAtLeast(gl::ContextProfile::OpenGLCore, 300) && 1733 !mGL->IsAtLeast(gl::ContextProfile::OpenGLES, 300)) || 1734 !mGL->HasPBOState()) { 1735 gfxCriticalError() << "BlitYCbCrImageToDMABuf: old GL version"; 1736 return false; 1737 } 1738 1739 auto ySize = yuvData.YDataSize(); 1740 auto cbcrSize = yuvData.CbCrDataSize(); 1741 if (yuvData.mYSkip || yuvData.mCbSkip || yuvData.mCrSkip || ySize.width < 0 || 1742 ySize.height < 0 || cbcrSize.width < 0 || cbcrSize.height < 0 || 1743 yuvData.mYStride < 0 || yuvData.mCbCrStride < 0) { 1744 gfxCriticalError() << "Unusual PlanarYCbCrData: " << yuvData.mYSkip << "," 1745 << yuvData.mCbSkip << "," << yuvData.mCrSkip << ", " 1746 << ySize.width << "," << ySize.height << ", " 1747 << cbcrSize.width << "," << cbcrSize.height << ", " 1748 << yuvData.mYStride << "," << yuvData.mCbCrStride; 1749 return false; 1750 } 1751 1752 GLenum internalFormat; 1753 GLenum unpackFormat; 1754 GLenum sizeFormat; 1755 switch (yuvData.mColorDepth) { 1756 case gfx::ColorDepth::COLOR_8: 1757 internalFormat = LOCAL_GL_R8; 1758 unpackFormat = LOCAL_GL_RED; 1759 sizeFormat = LOCAL_GL_UNSIGNED_BYTE; 1760 break; 1761 case gfx::ColorDepth::COLOR_10: 1762 internalFormat = LOCAL_GL_R16; 1763 unpackFormat = LOCAL_GL_RED; 1764 sizeFormat = LOCAL_GL_UNSIGNED_SHORT; 1765 break; 1766 default: 1767 gfxCriticalError() << "BlitYCbCrImageToDMABuf: Unsupported color depth"; 1768 return false; 1769 } 1770 1771 if (!mYuvUploads[0]) { 1772 mGL->fGenTextures(3, mYuvUploads); 1773 const ScopedBindTexture bindTex(mGL, mYuvUploads[0], LOCAL_GL_TEXTURE_2D); 1774 mGL->TexParams_SetClampNoMips(LOCAL_GL_TEXTURE_2D); 1775 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[1]); 1776 mGL->TexParams_SetClampNoMips(LOCAL_GL_TEXTURE_2D); 1777 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[2]); 1778 mGL->TexParams_SetClampNoMips(LOCAL_GL_TEXTURE_2D); 1779 } 1780 1781 // -- 1782 1783 const ScopedSaveMultiTex saveTex(mGL, 3, LOCAL_GL_TEXTURE_2D); 1784 const ResetUnpackState reset(mGL); 1785 1786 gfx::IntSize yTexSize(yuvData.mYStride, yuvData.YDataSize().height); 1787 gfx::IntSize uvTexSize(yuvData.mCbCrStride, yuvData.CbCrDataSize().height); 1788 1789 // If we upload short integer type (16bit per pixel), 1790 // we need to divide texture width as it's derived from stride in bytes. 1791 if (sizeFormat == LOCAL_GL_UNSIGNED_SHORT) { 1792 yTexSize.width >>= 1; 1793 uvTexSize.width >>= 1; 1794 } 1795 1796 if (yTexSize != mYuvUploads_YSize || uvTexSize != mYuvUploads_UVSize) { 1797 mYuvUploads_YSize = yTexSize; 1798 mYuvUploads_UVSize = uvTexSize; 1799 1800 mGL->fActiveTexture(LOCAL_GL_TEXTURE0); 1801 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[0]); 1802 mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, internalFormat, yTexSize.width, 1803 yTexSize.height, 0, unpackFormat, sizeFormat, nullptr); 1804 for (int i = 1; i < 3; i++) { 1805 mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i); 1806 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[i]); 1807 mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, internalFormat, uvTexSize.width, 1808 uvTexSize.height, 0, unpackFormat, sizeFormat, nullptr); 1809 } 1810 } 1811 1812 // -- 1813 1814 mGL->fActiveTexture(LOCAL_GL_TEXTURE0); 1815 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[0]); 1816 mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0, yTexSize.width, 1817 yTexSize.height, unpackFormat, sizeFormat, 1818 yuvData.mYChannel); 1819 mGL->fActiveTexture(LOCAL_GL_TEXTURE1); 1820 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[1]); 1821 mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0, uvTexSize.width, 1822 uvTexSize.height, unpackFormat, sizeFormat, 1823 yuvData.mCbChannel); 1824 mGL->fActiveTexture(LOCAL_GL_TEXTURE2); 1825 mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[2]); 1826 mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0, uvTexSize.width, 1827 uvTexSize.height, unpackFormat, sizeFormat, 1828 yuvData.mCrChannel); 1829 1830 // -- 1831 1832 DrawBlitProg::BaseArgs baseArgs; 1833 baseArgs.yFlip = false; 1834 const auto& clipRect = yuvData.mPictureRect; 1835 baseArgs.texMatrix0 = SubRectMat3(clipRect, yTexSize); 1836 1837 const char* fragConvert = yuvData.mColorDepth == gfx::ColorDepth::COLOR_10 1838 ? kFragConvertYUVP010 1839 : kFragConvert_None; 1840 1841 // Blit Y plane 1842 { 1843 ScopedFramebufferForTexture autoFBForTex(mGL, surface->GetTexture(0)); 1844 if (!autoFBForTex.IsComplete()) { 1845 gfxCriticalError() << "GLBlitHelper::BlitYCbCrImageToDMABuf: " 1846 "ScopedFramebufferForTexture failed."; 1847 return false; 1848 } 1849 const ScopedBindFramebuffer bindFB(mGL, autoFBForTex.FB()); 1850 1851 baseArgs.fbSize = gfx::IntSize(surface->GetWidth(), surface->GetHeight()); 1852 const auto& prog = GetDrawBlitProg( 1853 {kFragHeader_Tex2D, {kFragSample_OnePlane, fragConvert}}); 1854 prog.Draw(baseArgs); 1855 } 1856 1857 // Blit UV planes 1858 { 1859 ScopedFramebufferForTexture autoFBForTex(mGL, surface->GetTexture(1)); 1860 if (!autoFBForTex.IsComplete()) { 1861 gfxCriticalError() << "GLBlitHelper::BlitYCbCrImageToDMABuf: " 1862 "ScopedFramebufferForTexture failed."; 1863 return false; 1864 } 1865 const ScopedBindFramebuffer bindFB(mGL, autoFBForTex.FB()); 1866 1867 baseArgs.fbSize = gfx::IntSize(surface->GetWidth(1), surface->GetHeight(1)); 1868 const auto& prog = GetDrawBlitProg( 1869 {kFragHeader_Tex2D, {kFragSample_TwoPlaneUV, fragConvert}}); 1870 prog.Draw(baseArgs); 1871 } 1872 1873 return true; 1874 } 1875 #endif 1876 1877 // - 1878 1879 template <size_t N> 1880 static void PushUnorm(uint32_t* const out, const float inVal) { 1881 const uint32_t mask = (1 << N) - 1; 1882 auto fval = inVal; 1883 fval = std::clamp(fval, 0.0f, 1.0f); 1884 fval *= mask; 1885 fval = roundf(fval); 1886 auto ival = static_cast<uint32_t>(fval); 1887 ival &= mask; 1888 1889 *out <<= N; 1890 *out |= ival; 1891 } 1892 1893 static uint32_t toRgb10A2(const color::vec4& val) { 1894 // R in LSB 1895 uint32_t ret = 0; 1896 PushUnorm<2>(&ret, val.w()); 1897 PushUnorm<10>(&ret, val.z()); 1898 PushUnorm<10>(&ret, val.y()); 1899 PushUnorm<10>(&ret, val.x()); 1900 return ret; 1901 } 1902 1903 // - 1904 1905 color::ColorspaceDesc ToColorspaceDesc(const gfx::YUVRangedColorSpace cs) { 1906 switch (cs) { 1907 case gfx::YUVRangedColorSpace::BT601_Narrow: 1908 return { 1909 .chrom = color::Chromaticities::Rec601_525_Ntsc(), 1910 .tf = color::PiecewiseGammaDesc::Rec709(), 1911 .yuv = 1912 color::YuvDesc{ 1913 .yCoeffs = color::YuvLumaCoeffs::Rec709(), 1914 .ycbcr = color::YcbcrDesc::Narrow8(), 1915 }, 1916 }; 1917 case gfx::YUVRangedColorSpace::BT601_Full: 1918 return { 1919 .chrom = color::Chromaticities::Rec601_525_Ntsc(), 1920 .tf = color::PiecewiseGammaDesc::Rec709(), 1921 .yuv = 1922 color::YuvDesc{ 1923 .yCoeffs = color::YuvLumaCoeffs::Rec709(), 1924 .ycbcr = color::YcbcrDesc::Full8(), 1925 }, 1926 }; 1927 case gfx::YUVRangedColorSpace::BT709_Narrow: 1928 return { 1929 .chrom = color::Chromaticities::Rec709(), 1930 .tf = color::PiecewiseGammaDesc::Rec709(), 1931 .yuv = 1932 color::YuvDesc{ 1933 .yCoeffs = color::YuvLumaCoeffs::Rec709(), 1934 .ycbcr = color::YcbcrDesc::Narrow8(), 1935 }, 1936 }; 1937 case gfx::YUVRangedColorSpace::BT709_Full: 1938 return { 1939 .chrom = color::Chromaticities::Rec709(), 1940 .tf = color::PiecewiseGammaDesc::Rec709(), 1941 .yuv = 1942 color::YuvDesc{ 1943 .yCoeffs = color::YuvLumaCoeffs::Rec709(), 1944 .ycbcr = color::YcbcrDesc::Full8(), 1945 }, 1946 }; 1947 case gfx::YUVRangedColorSpace::BT2020_Narrow: 1948 return { 1949 .chrom = color::Chromaticities::Rec2020(), 1950 .tf = color::PiecewiseGammaDesc::Rec2020_12bit(), 1951 .yuv = 1952 color::YuvDesc{ 1953 .yCoeffs = color::YuvLumaCoeffs::Rec709(), 1954 .ycbcr = color::YcbcrDesc::Narrow8(), 1955 }, 1956 }; 1957 case gfx::YUVRangedColorSpace::BT2020_Full: 1958 return { 1959 .chrom = color::Chromaticities::Rec2020(), 1960 .tf = color::PiecewiseGammaDesc::Rec2020_12bit(), 1961 .yuv = 1962 color::YuvDesc{ 1963 .yCoeffs = color::YuvLumaCoeffs::Rec2020(), 1964 .ycbcr = color::YcbcrDesc::Full8(), 1965 }, 1966 }; 1967 case gfx::YUVRangedColorSpace::GbrIdentity: 1968 return { 1969 .chrom = color::Chromaticities::Rec709(), 1970 .tf = color::PiecewiseGammaDesc::Rec709(), 1971 .yuv = 1972 color::YuvDesc{ 1973 .yCoeffs = color::YuvLumaCoeffs::Gbr(), 1974 .ycbcr = color::YcbcrDesc::Full8(), 1975 }, 1976 }; 1977 } 1978 MOZ_CRASH("Bad YUVRangedColorSpace."); 1979 } 1980 1981 } // namespace gl 1982 namespace gfx { 1983 1984 color::ColorProfileDesc QueryOutputColorProfile(); 1985 1986 } // namespace gfx 1987 namespace gl { 1988 1989 // - 1990 1991 /* static */ 1992 std::optional<color::ColorProfileDesc> GLBlitHelper::ToColorProfileDesc( 1993 const gfx::ColorSpace2 cspace) { 1994 color::ColorspaceDesc cspaceDesc; 1995 switch (cspace) { 1996 case gfx::ColorSpace2::Display: 1997 if (kIsWindows) { 1998 #ifdef XP_WIN 1999 return gfx::QueryOutputColorProfile(); 2000 #endif 2001 } 2002 return {}; 2003 2004 case gfx::ColorSpace2::SRGB: 2005 cspaceDesc = {.chrom = color::Chromaticities::Srgb(), 2006 .tf = color::PiecewiseGammaDesc::Srgb()}; 2007 break; 2008 case gfx::ColorSpace2::DISPLAY_P3: 2009 cspaceDesc = {.chrom = color::Chromaticities::DisplayP3(), 2010 .tf = color::PiecewiseGammaDesc::DisplayP3()}; 2011 break; 2012 case gfx::ColorSpace2::BT601_525: // aka smpte170m NTSC 2013 cspaceDesc = {.chrom = color::Chromaticities::Rec601_525_Ntsc(), 2014 .tf = color::PiecewiseGammaDesc::Rec709()}; 2015 break; 2016 case gfx::ColorSpace2::BT709: // Same gamut as SRGB, but different gamma. 2017 cspaceDesc = {.chrom = color::Chromaticities::Rec709(), 2018 .tf = color::PiecewiseGammaDesc::Rec709()}; 2019 break; 2020 case gfx::ColorSpace2::BT2020: 2021 cspaceDesc = {.chrom = color::Chromaticities::Rec2020(), 2022 .tf = color::PiecewiseGammaDesc::Rec2020_12bit()}; 2023 break; 2024 } 2025 const auto profileDesc = color::ColorProfileDesc::From(cspaceDesc); 2026 return profileDesc; 2027 } 2028 2029 // - 2030 2031 // For std::visit 2032 template <class... Ts> 2033 struct overloaded : Ts... { 2034 using Ts::operator()...; 2035 }; 2036 // explicit deduction guide (not needed as of C++20) 2037 template <class... Ts> 2038 overloaded(Ts...) -> overloaded<Ts...>; 2039 2040 // - 2041 2042 template <typename C, typename K> 2043 inline auto MaybeFind(C& container, const K& key) 2044 -> decltype(&(container.find(key)->second)) { 2045 const auto itr = container.find(key); 2046 if (itr == container.end()) return nullptr; 2047 return &(itr->second); 2048 } 2049 2050 // - 2051 2052 std::shared_ptr<gl::Texture> GLBlitHelper::GetColorLutTex( 2053 const ColorLutKey& request) const { 2054 if (const auto found = gl::MaybeFind(mColorLutTexMap, request)) { 2055 return *found; // Might be *Some(nullptr) -> nullptr! 2056 } 2057 2058 return mColorLutTexMap[request] = [&]() -> std::shared_ptr<gl::Texture> { 2059 auto& gl = *mGL; 2060 const auto tex = std::make_shared<gl::Texture>(gl); 2061 2062 // - 2063 2064 const std::optional<color::ColorProfileDesc> srcProfile = 2065 std::visit(overloaded{ 2066 [&](const gfx::ColorSpace2& cs) 2067 -> std::optional<color::ColorProfileDesc> { 2068 MOZ_ASSERT(cs != request.dst); 2069 const auto cpd = ToColorProfileDesc(cs); 2070 return cpd; 2071 }, 2072 [&](const gfx::YUVRangedColorSpace& cs) 2073 -> std::optional<color::ColorProfileDesc> { 2074 const auto csd = ToColorspaceDesc(cs); 2075 const auto cpd = color::ColorProfileDesc::From(csd); 2076 return cpd; 2077 }, 2078 }, 2079 request.src); 2080 MOZ_ASSERT(srcProfile); 2081 2082 const auto dstProfile = ToColorProfileDesc(request.dst); 2083 if (kIsWindows) { 2084 MOZ_ASSERT(dstProfile); 2085 } 2086 if (!srcProfile || !dstProfile) return nullptr; 2087 const auto conversion = color::ColorProfileConversionDesc::From({ 2088 .src = *srcProfile, 2089 .dst = *dstProfile, 2090 }); 2091 2092 // - 2093 2094 const auto minLutSize = color::ivec3{2}; 2095 const auto maxLutSize = color::ivec3{256}; 2096 auto lutSize = minLutSize; 2097 const bool isYcbcr = 2098 (conversion.srcRgbFromSrcYuv != color::mat4::Identity()); 2099 if (isYcbcr) { 2100 lutSize.x(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_y())); 2101 lutSize.y(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_cb())); 2102 lutSize.z(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_cr())); 2103 } else { 2104 lutSize.x(int(StaticPrefs::gfx_blithelper_lut_size_rgb_r())); 2105 lutSize.y(int(StaticPrefs::gfx_blithelper_lut_size_rgb_g())); 2106 lutSize.z(int(StaticPrefs::gfx_blithelper_lut_size_rgb_b())); 2107 } 2108 lutSize = clamp(lutSize, minLutSize, maxLutSize); 2109 2110 const auto lut = [&]() { 2111 auto lut = color::Lut3::Create(lutSize); 2112 lut.SetMap( 2113 [&](const color::vec3& src) { return conversion.DstFromSrc(src); }); 2114 return lut; 2115 }(); 2116 const auto& size = lut.size; 2117 2118 // - 2119 2120 constexpr GLenum target = LOCAL_GL_TEXTURE_3D; 2121 const auto bind = gl::ScopedBindTexture(&gl, tex->name, target); 2122 gl.fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); 2123 gl.fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); 2124 gl.fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_R, LOCAL_GL_CLAMP_TO_EDGE); 2125 gl.fTexParameteri(target, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR); 2126 gl.fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR); 2127 2128 bool useFloat16 = true; 2129 if (useFloat16) { 2130 // Use rgba16f, which we can thankfully upload as rgba32f 2131 static_assert(sizeof(color::vec4) == sizeof(float) * 4); 2132 std::vector<color::vec4> uploadData; 2133 uploadData.reserve(lut.data.size()); 2134 for (const auto& src : lut.data) { 2135 const auto dst = color::vec4{src, 1}; 2136 uploadData.push_back(dst); 2137 } 2138 2139 gl.fTexStorage3D(target, 1, LOCAL_GL_RGBA16F, size.x(), size.y(), 2140 size.z()); 2141 gl.fTexSubImage3D(target, 0, 0, 0, 0, size.x(), size.y(), size.z(), 2142 LOCAL_GL_RGBA, LOCAL_GL_FLOAT, uploadData.data()); 2143 } else { 2144 // Use Rgb10A2 2145 std::vector<uint32_t> uploadData; 2146 uploadData.reserve(lut.data.size()); 2147 for (const auto& src : lut.data) { 2148 const auto dst = toRgb10A2({src, 1}); 2149 uploadData.push_back(dst); 2150 } 2151 2152 gl.fTexStorage3D(target, 1, LOCAL_GL_RGB10_A2, size.x(), size.y(), 2153 size.z()); 2154 gl.fTexSubImage3D(target, 0, 0, 0, 0, size.x(), size.y(), size.z(), 2155 LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV, 2156 uploadData.data()); 2157 } 2158 return tex; 2159 }(); 2160 } 2161 2162 } // namespace gl 2163 } // namespace mozilla