TexUnpackBlob.cpp (43206B)
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "TexUnpackBlob.h" 7 8 #include "GLBlitHelper.h" 9 #include "GLContext.h" 10 #include "WebGLBuffer.h" 11 #include "WebGLContext.h" 12 #include "WebGLFormats.h" 13 #include "WebGLTexelConversions.h" 14 #include "WebGLTexture.h" 15 #include "mozilla/RefPtr.h" 16 #include "mozilla/dom/Element.h" 17 #include "mozilla/dom/HTMLCanvasElement.h" 18 #include "mozilla/gfx/CanvasManagerParent.h" 19 #include "mozilla/gfx/Logging.h" 20 #include "mozilla/layers/ImageDataSerializer.h" 21 #include "mozilla/layers/SharedSurfacesParent.h" 22 #include "mozilla/layers/TextureHost.h" 23 #include "mozilla/layers/VideoBridgeParent.h" 24 #include "nsLayoutUtils.h" 25 26 namespace mozilla { 27 28 bool webgl::PixelPackingState::AssertCurrentUnpack(gl::GLContext& gl, 29 const bool isWebgl2) const { 30 if (!kIsDebug) return true; 31 32 auto actual = PixelPackingState{}; 33 gl.GetInt(LOCAL_GL_UNPACK_ALIGNMENT, &actual.alignmentInTypeElems); 34 if (isWebgl2) { 35 gl.GetInt(LOCAL_GL_UNPACK_ROW_LENGTH, &actual.rowLength); 36 gl.GetInt(LOCAL_GL_UNPACK_IMAGE_HEIGHT, &actual.imageHeight); 37 38 gl.GetInt(LOCAL_GL_UNPACK_SKIP_PIXELS, &actual.skipPixels); 39 gl.GetInt(LOCAL_GL_UNPACK_SKIP_ROWS, &actual.skipRows); 40 gl.GetInt(LOCAL_GL_UNPACK_SKIP_IMAGES, &actual.skipImages); 41 } 42 if (*this == actual) return true; 43 44 const auto ToStr = [](const PixelPackingState& x) { 45 const auto text = nsPrintfCString( 46 "%u,%u,%u;%u,%u,%u", x.alignmentInTypeElems, x.rowLength, x.imageHeight, 47 x.skipPixels, x.skipRows, x.skipImages); 48 return mozilla::ToString(text); 49 }; 50 51 const auto was = ToStr(actual); 52 const auto expected = ToStr(*this); 53 gfxCriticalError() << "PixelUnpackStateGl was not current. Was " << was 54 << ". Expected << " << expected << "."; 55 return false; 56 } 57 58 void webgl::PixelPackingState::ApplyUnpack(gl::GLContext& gl, 59 const bool isWebgl2, 60 const uvec3& uploadSize) const { 61 gl.fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 62 AssertedCast<GLsizei>(alignmentInTypeElems)); 63 if (!isWebgl2) return; 64 65 // Re-simplify. (ANGLE seems to have an issue with imageHeight == 66 // uploadSize.y) 67 auto rowLengthOrZero = rowLength; 68 auto imageHeightOrZero = imageHeight; 69 if (rowLengthOrZero == uploadSize.x) { 70 rowLengthOrZero = 0; 71 } 72 if (imageHeightOrZero == uploadSize.y) { 73 imageHeightOrZero = 0; 74 } 75 76 gl.fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 77 AssertedCast<GLsizei>(rowLengthOrZero)); 78 gl.fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, 79 AssertedCast<GLsizei>(imageHeightOrZero)); 80 81 gl.fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS, 82 AssertedCast<GLsizei>(skipPixels)); 83 gl.fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, AssertedCast<GLsizei>(skipRows)); 84 gl.fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, 85 AssertedCast<GLsizei>(skipImages)); 86 } 87 88 namespace webgl { 89 90 static bool IsPIValidForDOM(const webgl::PackingInfo& pi) { 91 // https://www.khronos.org/registry/webgl/specs/latest/2.0/#TEXTURE_TYPES_FORMATS_FROM_DOM_ELEMENTS_TABLE 92 93 // Just check for invalid individual formats and types, not combinations. 94 switch (pi.format) { 95 case LOCAL_GL_RGB: 96 case LOCAL_GL_RGBA: 97 case LOCAL_GL_LUMINANCE_ALPHA: 98 case LOCAL_GL_LUMINANCE: 99 case LOCAL_GL_ALPHA: 100 case LOCAL_GL_RED: 101 case LOCAL_GL_RED_INTEGER: 102 case LOCAL_GL_RG: 103 case LOCAL_GL_RG_INTEGER: 104 case LOCAL_GL_RGB_INTEGER: 105 case LOCAL_GL_RGBA_INTEGER: 106 break; 107 108 case LOCAL_GL_SRGB: 109 case LOCAL_GL_SRGB_ALPHA: 110 // Allowed in WebGL1+EXT_srgb 111 break; 112 113 default: 114 return false; 115 } 116 117 switch (pi.type) { 118 case LOCAL_GL_UNSIGNED_BYTE: 119 case LOCAL_GL_UNSIGNED_SHORT_5_6_5: 120 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: 121 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: 122 case LOCAL_GL_HALF_FLOAT: 123 case LOCAL_GL_HALF_FLOAT_OES: 124 case LOCAL_GL_FLOAT: 125 case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV: 126 break; 127 128 default: 129 return false; 130 } 131 132 return true; 133 } 134 135 static bool ValidatePIForDOM(const WebGLContext* const webgl, 136 const webgl::PackingInfo& pi) { 137 if (!IsPIValidForDOM(pi)) { 138 webgl->ErrorInvalidValue("Format or type is invalid for DOM sources."); 139 return false; 140 } 141 return true; 142 } 143 144 static WebGLTexelFormat FormatForPackingInfo(const PackingInfo& pi) { 145 switch (pi.type) { 146 case LOCAL_GL_UNSIGNED_BYTE: 147 switch (pi.format) { 148 case LOCAL_GL_RED: 149 case LOCAL_GL_LUMINANCE: 150 case LOCAL_GL_RED_INTEGER: 151 return WebGLTexelFormat::R8; 152 153 case LOCAL_GL_ALPHA: 154 return WebGLTexelFormat::A8; 155 156 case LOCAL_GL_LUMINANCE_ALPHA: 157 return WebGLTexelFormat::RA8; 158 159 case LOCAL_GL_RGB: 160 case LOCAL_GL_RGB_INTEGER: 161 case LOCAL_GL_SRGB: 162 return WebGLTexelFormat::RGB8; 163 164 case LOCAL_GL_RGBA: 165 case LOCAL_GL_RGBA_INTEGER: 166 case LOCAL_GL_SRGB_ALPHA: 167 return WebGLTexelFormat::RGBA8; 168 169 case LOCAL_GL_RG: 170 case LOCAL_GL_RG_INTEGER: 171 return WebGLTexelFormat::RG8; 172 173 default: 174 break; 175 } 176 break; 177 178 case LOCAL_GL_UNSIGNED_SHORT_5_6_5: 179 if (pi.format == LOCAL_GL_RGB) return WebGLTexelFormat::RGB565; 180 break; 181 182 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: 183 if (pi.format == LOCAL_GL_RGBA) return WebGLTexelFormat::RGBA5551; 184 break; 185 186 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: 187 if (pi.format == LOCAL_GL_RGBA) return WebGLTexelFormat::RGBA4444; 188 break; 189 190 case LOCAL_GL_HALF_FLOAT: 191 case LOCAL_GL_HALF_FLOAT_OES: 192 switch (pi.format) { 193 case LOCAL_GL_RED: 194 case LOCAL_GL_LUMINANCE: 195 return WebGLTexelFormat::R16F; 196 197 case LOCAL_GL_ALPHA: 198 return WebGLTexelFormat::A16F; 199 case LOCAL_GL_LUMINANCE_ALPHA: 200 return WebGLTexelFormat::RA16F; 201 case LOCAL_GL_RG: 202 return WebGLTexelFormat::RG16F; 203 case LOCAL_GL_RGB: 204 return WebGLTexelFormat::RGB16F; 205 case LOCAL_GL_RGBA: 206 return WebGLTexelFormat::RGBA16F; 207 208 default: 209 break; 210 } 211 break; 212 213 case LOCAL_GL_FLOAT: 214 switch (pi.format) { 215 case LOCAL_GL_RED: 216 case LOCAL_GL_LUMINANCE: 217 return WebGLTexelFormat::R32F; 218 219 case LOCAL_GL_ALPHA: 220 return WebGLTexelFormat::A32F; 221 case LOCAL_GL_LUMINANCE_ALPHA: 222 return WebGLTexelFormat::RA32F; 223 case LOCAL_GL_RG: 224 return WebGLTexelFormat::RG32F; 225 case LOCAL_GL_RGB: 226 return WebGLTexelFormat::RGB32F; 227 case LOCAL_GL_RGBA: 228 return WebGLTexelFormat::RGBA32F; 229 230 default: 231 break; 232 } 233 break; 234 235 case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV: 236 if (pi.format == LOCAL_GL_RGB) return WebGLTexelFormat::RGB11F11F10F; 237 break; 238 239 default: 240 break; 241 } 242 243 return WebGLTexelFormat::FormatNotSupportingAnyConversion; 244 } 245 246 //////////////////// 247 248 static uint32_t ZeroOn2D(const GLenum target, const uint32_t val) { 249 const bool is2d = !IsTexTarget3D(target); 250 if (is2d) return 0; 251 return val; 252 } 253 254 static bool ValidateUnpackPixels(const WebGLContext* webgl, 255 const webgl::PackingInfo& pi, 256 const uint32_t availRows, 257 const webgl::TexUnpackBlob& blob) { 258 const auto& unpackingRes = blob.mDesc.ExplicitUnpacking(pi, {}); 259 if (!unpackingRes.isOk()) { 260 webgl->ErrorInvalidOperation("%s", unpackingRes.inspectErr().c_str()); 261 return false; 262 } 263 const auto& unpacking = unpackingRes.inspect(); 264 265 if (availRows < unpacking.metrics.totalRows) { 266 webgl->ErrorInvalidOperation( 267 "Desired upload requires more rows (%zu) than is" 268 " available (%zu).", 269 unpacking.metrics.totalRows, availRows); 270 return false; 271 } 272 273 return true; 274 } 275 276 static bool ValidateUnpackBytes(const WebGLContext* const webgl, 277 const webgl::PackingInfo& pi, 278 const size_t availByteCount, 279 const webgl::TexUnpackBlob& blob) { 280 const auto& unpackingRes = blob.mDesc.ExplicitUnpacking(pi, {}); 281 if (!unpackingRes.isOk()) { 282 webgl->ErrorInvalidOperation("%s", unpackingRes.inspectErr().c_str()); 283 return false; 284 } 285 const auto& unpacking = unpackingRes.inspect(); 286 287 if (availByteCount < unpacking.metrics.totalBytesUsed) { 288 webgl->ErrorInvalidOperation( 289 "Desired upload requires more bytes (%zu) than are" 290 " available (%zu).", 291 unpacking.metrics.totalBytesUsed, availByteCount); 292 return false; 293 } 294 295 return true; 296 } 297 298 //////////////////// 299 300 // Check if the surface descriptor describes a memory which contains a single 301 // RGBA data source. 302 static bool SDIsRGBBuffer(const layers::SurfaceDescriptor& sd) { 303 return sd.type() == layers::SurfaceDescriptor::TSurfaceDescriptorBuffer && 304 sd.get_SurfaceDescriptorBuffer().desc().type() == 305 layers::BufferDescriptor::TRGBDescriptor; 306 } 307 308 // Check if the surface descriptor describes a GPUVideo texture for which we 309 // only have an opaque source/handle from SurfaceDescriptorRemoteDecoder to 310 // derive the actual texture from. 311 static bool SDIsNullRemoteDecoder(const layers::SurfaceDescriptor& sd) { 312 return sd.type() == layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo && 313 sd.get_SurfaceDescriptorGPUVideo() 314 .get_SurfaceDescriptorRemoteDecoder() 315 .subdesc() 316 .type() == layers::RemoteDecoderVideoSubDescriptor::Tnull_t; 317 } 318 319 // Check if the surface descriptor describes an ExternalImage surface for which 320 // we only have an opaque source/handle to derive the actual surface from. 321 static bool SDIsExternalImage(const layers::SurfaceDescriptor& sd) { 322 return sd.type() == 323 layers::SurfaceDescriptor::TSurfaceDescriptorExternalImage && 324 sd.get_SurfaceDescriptorExternalImage().source() == 325 wr::ExternalImageSource::SharedSurfaces; 326 } 327 328 static bool SDIsCanvasSurface(const layers::SurfaceDescriptor& sd) { 329 return sd.type() == 330 layers::SurfaceDescriptor::TSurfaceDescriptorCanvasSurface; 331 } 332 333 // static 334 std::unique_ptr<TexUnpackBlob> TexUnpackBlob::Create( 335 const TexUnpackBlobDesc& desc) { 336 return std::unique_ptr<TexUnpackBlob>{[&]() -> TexUnpackBlob* { 337 if (!IsTarget3D(desc.imageTarget) && desc.size.z != 1) { 338 MOZ_ASSERT(false); 339 return nullptr; 340 } 341 342 switch (desc.unpacking.alignmentInTypeElems) { 343 case 1: 344 case 2: 345 case 4: 346 case 8: 347 break; 348 default: 349 MOZ_ASSERT(false); 350 return nullptr; 351 } 352 353 if (desc.sd || desc.sourceSurf) { 354 return new TexUnpackSurface(desc); 355 } 356 357 if (desc.srcAlphaType != gfxAlphaType::NonPremult) { 358 MOZ_ASSERT(false); 359 return nullptr; 360 } 361 return new TexUnpackBytes(desc); 362 }()}; 363 } 364 365 static bool HasColorAndAlpha(const WebGLTexelFormat format) { 366 switch (format) { 367 case WebGLTexelFormat::RA8: 368 case WebGLTexelFormat::RA16F: 369 case WebGLTexelFormat::RA32F: 370 case WebGLTexelFormat::RGBA8: 371 case WebGLTexelFormat::RGBA5551: 372 case WebGLTexelFormat::RGBA4444: 373 case WebGLTexelFormat::RGBA16F: 374 case WebGLTexelFormat::RGBA32F: 375 case WebGLTexelFormat::BGRA8: 376 return true; 377 default: 378 return false; 379 } 380 } 381 382 bool TexUnpackBlob::ConvertIfNeeded( 383 const WebGLContext* const webgl, const uint32_t rowLength, 384 const uint32_t rowCount, WebGLTexelFormat srcFormat, 385 const uint8_t* const srcBegin, const ptrdiff_t srcStride, 386 WebGLTexelFormat dstFormat, const ptrdiff_t dstStride, 387 const uint8_t** const out_begin, 388 UniqueBuffer* const out_anchoredBuffer) const { 389 MOZ_ASSERT(srcFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion); 390 MOZ_ASSERT(dstFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion); 391 392 *out_begin = srcBegin; 393 394 const auto& unpacking = mDesc.unpacking; 395 396 if (!rowLength || !rowCount) return true; 397 398 auto minSrcStride = 399 CheckedInt<size_t>( 400 WebGLTexelConversions::TexelBytesForFormat(srcFormat)) * 401 rowLength; 402 auto minDstStride = 403 CheckedInt<size_t>( 404 WebGLTexelConversions::TexelBytesForFormat(dstFormat)) * 405 rowLength; 406 if (srcStride <= 0 || dstStride <= 0 || !minSrcStride.isValid() || 407 !minDstStride.isValid() || size_t(srcStride) < minSrcStride.value() || 408 size_t(dstStride) < minDstStride.value()) { 409 webgl->ErrorInvalidOperation("Invalid stride."); 410 return false; 411 } 412 413 const auto srcIsPremult = (mDesc.srcAlphaType == gfxAlphaType::Premult); 414 auto dstIsPremult = unpacking.premultiplyAlpha; 415 const auto fnHasPremultMismatch = [&]() { 416 if (mDesc.srcAlphaType == gfxAlphaType::Opaque) return false; 417 418 if (!HasColorAndAlpha(srcFormat)) return false; 419 420 return srcIsPremult != dstIsPremult; 421 }; 422 423 const auto srcOrigin = 424 (unpacking.flipY ? gl::OriginPos::TopLeft : gl::OriginPos::BottomLeft); 425 auto dstOrigin = gl::OriginPos::BottomLeft; 426 427 if (!mDesc.applyUnpackTransforms) { 428 dstIsPremult = srcIsPremult; 429 dstOrigin = srcOrigin; 430 } 431 432 // TODO (Bug 754256): Figure out the source colorSpace. 433 dom::PredefinedColorSpace srcColorSpace = dom::PredefinedColorSpace::Srgb; 434 dom::PredefinedColorSpace dstColorSpace = 435 webgl->mUnpackColorSpace ? *webgl->mUnpackColorSpace 436 : dom::PredefinedColorSpace::Srgb; 437 438 if (srcFormat != dstFormat) { 439 webgl->GeneratePerfWarning( 440 "Conversion requires pixel reformatting. (%u->%u)", uint32_t(srcFormat), 441 uint32_t(dstFormat)); 442 } else if (fnHasPremultMismatch()) { 443 webgl->GeneratePerfWarning( 444 "Conversion requires change in" 445 " alpha-premultiplication."); 446 } else if (srcOrigin != dstOrigin) { 447 webgl->GeneratePerfWarning("Conversion requires y-flip."); 448 } else if (srcStride != dstStride) { 449 webgl->GeneratePerfWarning("Conversion requires change in stride. (%u->%u)", 450 uint32_t(srcStride), uint32_t(dstStride)); 451 } else if (srcColorSpace != dstColorSpace) { 452 webgl->GeneratePerfWarning( 453 "Conversion requires colorSpace conversion. (%u->%u)", 454 uint32_t(srcColorSpace), uint32_t(dstColorSpace)); 455 } else { 456 return true; 457 } 458 459 //// 460 461 const auto dstTotalBytes = CheckedUint32(rowCount) * dstStride; 462 if (!dstTotalBytes.isValid()) { 463 webgl->ErrorOutOfMemory("Calculation failed."); 464 return false; 465 } 466 467 auto dstBuffer = UniqueBuffer::Take(calloc(1u, dstTotalBytes.value())); 468 if (!dstBuffer.get()) { 469 webgl->ErrorOutOfMemory("Failed to allocate dest buffer."); 470 return false; 471 } 472 const auto dstBegin = static_cast<uint8_t*>(dstBuffer.get()); 473 474 //// 475 476 // And go!: 477 bool wasTrivial; 478 if (!ConvertImage(rowLength, rowCount, srcBegin, srcStride, srcOrigin, 479 srcFormat, srcIsPremult, dstBegin, dstStride, dstOrigin, 480 dstFormat, dstIsPremult, srcColorSpace, dstColorSpace, 481 &wasTrivial)) { 482 webgl->ErrorImplementationBug("ConvertImage failed."); 483 return false; 484 } 485 486 *out_begin = dstBegin; 487 *out_anchoredBuffer = std::move(dstBuffer); 488 return true; 489 } 490 491 static GLenum DoTexOrSubImage(bool isSubImage, gl::GLContext* gl, 492 TexImageTarget target, GLint level, 493 const DriverUnpackInfo* dui, GLint xOffset, 494 GLint yOffset, GLint zOffset, GLsizei width, 495 GLsizei height, GLsizei depth, const void* data) { 496 if (isSubImage) { 497 return DoTexSubImage(gl, target, level, xOffset, yOffset, zOffset, width, 498 height, depth, dui->ToPacking(), data); 499 } else { 500 return DoTexImage(gl, target, level, dui, width, height, depth, data); 501 } 502 } 503 504 ////////////////////////////////////////////////////////////////////////////////////////// 505 // TexUnpackBytes 506 507 bool TexUnpackBytes::Validate(const WebGLContext* const webgl, 508 const webgl::PackingInfo& pi) { 509 if (!HasData()) return true; 510 511 CheckedInt<size_t> availBytes = 0; 512 if (mDesc.cpuData) { 513 availBytes = mDesc.cpuData->size(); 514 } else if (mDesc.pboOffset) { 515 const auto& pboOffset = *mDesc.pboOffset; 516 517 const auto& pbo = 518 webgl->ValidateBufferSelection(LOCAL_GL_PIXEL_UNPACK_BUFFER); 519 if (!pbo) return false; // Might be invalid e.g. due to in-use by TF. 520 availBytes = pbo->ByteLength(); 521 availBytes -= pboOffset; 522 } else { 523 MOZ_ASSERT(false, "Must be one of the above"); 524 } 525 if (!availBytes.isValid()) { 526 webgl->ErrorInvalidOperation("Offset is passed end of buffer."); 527 return false; 528 } 529 530 return ValidateUnpackBytes(webgl, pi, availBytes.value(), *this); 531 } 532 533 bool TexUnpackBytes::TexOrSubImage(bool isSubImage, bool needsRespec, 534 WebGLTexture* tex, GLint level, 535 const webgl::DriverUnpackInfo* dui, 536 GLint xOffset, GLint yOffset, GLint zOffset, 537 const webgl::PackingInfo& pi, 538 GLenum* const out_error) const { 539 const auto& webgl = tex->mContext; 540 const auto& target = mDesc.imageTarget; 541 const auto& size = mDesc.size; 542 const auto& webglUnpackState = mDesc.unpacking; 543 544 const auto unpackingRes = mDesc.ExplicitUnpacking(pi, {}); 545 546 const auto format = FormatForPackingInfo(pi); 547 548 const uint8_t* uploadPtr = nullptr; 549 if (mDesc.cpuData) { 550 uploadPtr = mDesc.cpuData->data(); 551 } else if (mDesc.pboOffset) { 552 uploadPtr = reinterpret_cast<const uint8_t*>(*mDesc.pboOffset); 553 } 554 555 UniqueBuffer tempBuffer; 556 557 do { 558 if (mDesc.pboOffset || !uploadPtr) break; 559 560 if (!webglUnpackState.flipY && !webglUnpackState.premultiplyAlpha) { 561 break; 562 } 563 564 webgl->GenerateWarning( 565 "Alpha-premult and y-flip are deprecated for" 566 " non-DOM-Element uploads."); 567 568 MOZ_RELEASE_ASSERT(unpackingRes.isOk()); 569 const auto& unpacking = unpackingRes.inspect(); 570 const auto stride = unpacking.metrics.bytesPerRowStride; 571 // clang-format off 572 if (!ConvertIfNeeded(webgl, unpacking.state.rowLength, 573 unpacking.metrics.totalRows, 574 format, uploadPtr, AutoAssertCast(stride), 575 format, AutoAssertCast(stride), &uploadPtr, &tempBuffer)) { 576 return false; 577 } 578 // clang-format on 579 } while (false); 580 581 ////// 582 583 const auto& gl = webgl->gl; 584 585 bool useParanoidHandling = false; 586 if (mNeedsExactUpload && webgl->mBoundPixelUnpackBuffer) { 587 webgl->GenerateWarning( 588 "Uploads from a buffer with a final row with a byte" 589 " count smaller than the row stride can incur extra" 590 " overhead."); 591 592 if (gl->WorkAroundDriverBugs()) { 593 useParanoidHandling |= (gl->Vendor() == gl::GLVendor::NVIDIA); 594 } 595 } 596 597 if (!useParanoidHandling) { 598 const ScopedLazyBind bindPBO(gl, LOCAL_GL_PIXEL_UNPACK_BUFFER, 599 webgl->mBoundPixelUnpackBuffer); 600 601 *out_error = 602 DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset, 603 zOffset, size.x, size.y, size.z, uploadPtr); 604 return true; 605 } 606 607 ////// 608 609 MOZ_ASSERT(webgl->mBoundPixelUnpackBuffer); 610 611 if (!isSubImage) { 612 // Alloc first to catch OOMs. 613 AssertUintParamCorrect(gl, LOCAL_GL_PIXEL_UNPACK_BUFFER_BINDING, 0); 614 *out_error = 615 DoTexOrSubImage(false, gl, target, level, dui, xOffset, yOffset, 616 zOffset, size.x, size.y, size.z, nullptr); 617 if (*out_error) return true; 618 } 619 if (!size.x || !size.y || !size.z) { 620 // Nothing to do. 621 return true; 622 } 623 624 MOZ_RELEASE_ASSERT(unpackingRes.isOk()); 625 const auto& unpacking = unpackingRes.inspect(); 626 627 const ScopedLazyBind bindPBO(gl, LOCAL_GL_PIXEL_UNPACK_BUFFER, 628 webgl->mBoundPixelUnpackBuffer); 629 630 ////// 631 632 // Make our sometimes-implicit values explicit. Also this keeps them constant 633 // when we ask for height=mHeight-1 and such. 634 gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 635 AutoAssertCast(unpacking.state.rowLength)); 636 gl->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, 637 AutoAssertCast(unpacking.state.imageHeight)); 638 639 if (size.z > 1) { 640 *out_error = 641 DoTexOrSubImage(true, gl, target, level, dui, xOffset, yOffset, zOffset, 642 size.x, size.y, size.z - 1, uploadPtr); 643 } 644 645 // Skip the images we uploaded. 646 const auto skipImages = ZeroOn2D(target, unpacking.state.skipImages); 647 gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, skipImages + size.z - 1); 648 649 if (size.y > 1) { 650 *out_error = 651 DoTexOrSubImage(true, gl, target, level, dui, xOffset, yOffset, 652 zOffset + size.z - 1, size.x, size.y - 1, 1, uploadPtr); 653 } 654 655 // - 656 657 const auto lastRowOffset = 658 unpacking.metrics.totalBytesStrided - unpacking.metrics.bytesPerRowStride; 659 const auto lastRowPtr = 660 mDesc.pboOffset 661 ? reinterpret_cast<const uint8_t*>(*mDesc.pboOffset + lastRowOffset) 662 : (uploadPtr ? uploadPtr + lastRowOffset : nullptr); 663 664 gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // No stride padding. 665 gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0); // No padding in general. 666 gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, 0); // Don't skip images, 667 gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, 668 0); // or rows. 669 // Keep skipping pixels though! 670 *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset, 671 yOffset + size.y - 1, zOffset + size.z - 1, 672 AutoAssertCast(size.x), 1, 1, lastRowPtr); 673 674 // Caller will reset all our modified PixelStorei state. 675 676 return true; 677 } 678 679 //////////////////////////////////////////////////////////////////////////////// 680 //////////////////////////////////////////////////////////////////////////////// 681 // TexUnpackSurface 682 683 TexUnpackSurface::~TexUnpackSurface() = default; 684 685 bool TexUnpackSurface::Validate(const WebGLContext* const webgl, 686 const webgl::PackingInfo& pi) { 687 if (!ValidatePIForDOM(webgl, pi)) return false; 688 689 if (!mDesc.structuredSrcSize) { 690 gfxCriticalError() << "TexUnpackSurface missing structuredSrcSize."; 691 return false; 692 } 693 const auto& elemSize = *mDesc.structuredSrcSize; 694 if (mDesc.sourceSurf) { 695 const auto& surfSize = mDesc.sourceSurf->GetSize(); 696 const auto surfSize2 = ivec2::FromSize(surfSize)->StaticCast<uvec2>(); 697 if (uvec2{elemSize.x, elemSize.y} != surfSize2) { 698 gfxCriticalError() 699 << "TexUnpackSurface mismatched structuredSrcSize for sourceSurf."; 700 return false; 701 } 702 } 703 704 const auto fullRows = elemSize.y; 705 return ValidateUnpackPixels(webgl, pi, fullRows, *this); 706 } 707 708 const char* BlitPreventReason( 709 GLenum target, int32_t level, const ivec3& offset, GLenum internalFormat, 710 const webgl::PackingInfo& pi, const TexUnpackBlobDesc& desc, 711 OptionalRenderableFormatBits optionalRenderableFormatBits, 712 bool sameColorSpace, bool allowConversion, bool allowSRGB, bool allow3D) { 713 const auto& size = desc.size; 714 const auto& unpacking = desc.unpacking; 715 716 if (size.z != 1) { 717 return "depth is not 1"; 718 } 719 if (offset.z != 0) { 720 return "zOffset is not 0"; 721 } 722 723 if (unpacking.skipPixels || unpacking.skipRows || unpacking.skipImages) { 724 return "non-zero UNPACK_SKIP_* not yet supported"; 725 } 726 727 if (desc.srcAlphaType != gfxAlphaType::Opaque) { 728 const bool srcIsPremult = (desc.srcAlphaType == gfxAlphaType::Premult); 729 const auto& dstIsPremult = unpacking.premultiplyAlpha; 730 if (srcIsPremult != dstIsPremult && !allowConversion) { 731 return dstIsPremult ? "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not true" 732 : "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not false"; 733 } 734 } 735 736 if (!sameColorSpace) { 737 return "not same colorSpace"; 738 } 739 740 const auto formatReason = [&]() -> const char* { 741 if (pi.type != LOCAL_GL_UNSIGNED_BYTE) { 742 return "`unpackType` must be `UNSIGNED_BYTE`"; 743 } 744 745 // GL_FRAMEBUFFER_SRGB is not available in some GLES implementations, 746 // which is required for blitting to emulate Tex(Sub)Image, which does no 747 // encoding when uploading to an sRGB texture. In GLES (but not desktop 748 // GL) blitting to an sRGB framebuffer encodes by default unless 749 // explicitly disabled. To workaround the availability of this extension, 750 // just disallow it for now. Note, this is also an optional boolean as 751 // some internal data transfers intentionally rely on this conversion. 752 switch (internalFormat) { 753 case LOCAL_GL_SRGB: 754 case LOCAL_GL_SRGB8: 755 case LOCAL_GL_SRGB_ALPHA: 756 case LOCAL_GL_SRGB8_ALPHA8: 757 if (!allowSRGB) { 758 return "sRGB-encoded internal formats are not supported"; 759 } 760 break; 761 } 762 763 switch (pi.format) { 764 case LOCAL_GL_RGBA: 765 return nullptr; // All internalFormats for unpackFormat=RGBA are 766 // renderable. 767 768 case LOCAL_GL_RGB: 769 break; 770 771 default: 772 return "`unpackFormat` must be `RGBA` or maybe `RGB`"; 773 } 774 775 // - 776 777 struct { 778 OptionalRenderableFormatBits bits; 779 const char* errorMsg; 780 } required; 781 782 switch (internalFormat) { 783 case LOCAL_GL_RGB565: 784 return nullptr; 785 case LOCAL_GL_RGB: 786 case LOCAL_GL_RGB8: 787 required = { 788 OptionalRenderableFormatBits::RGB8, 789 "Unavailable, as blitting internalFormats RGB or RGB8 requires " 790 "that RGB8 must be a renderable format.", 791 }; 792 break; 793 case LOCAL_GL_SRGB: 794 case LOCAL_GL_SRGB8: 795 required = { 796 OptionalRenderableFormatBits::SRGB8, 797 "Unavailable, as blitting internalFormats SRGB or SRGB8 requires " 798 "that SRGB8 must be a renderable format.", 799 }; 800 break; 801 case 0: 802 // texSubImage, so internalFormat is unknown, and could be anything! 803 required = { 804 OptionalRenderableFormatBits::RGB8 | 805 OptionalRenderableFormatBits::SRGB8, 806 "Unavailable, as blitting texSubImage with unpackFormat=RGB " 807 "requires that RGB8 and SRGB8 must be renderable formats.", 808 }; 809 break; 810 default: 811 gfxCriticalError() 812 << "Unexpected internalFormat for unpackFormat=RGB: 0x" 813 << gfx::hexa(internalFormat); 814 return "Unexpected internalFormat for unpackFormat=RGB"; 815 } 816 817 const auto availableBits = optionalRenderableFormatBits; 818 if ((required.bits | availableBits) != availableBits) { 819 return required.errorMsg; 820 } 821 822 // - 823 824 return nullptr; 825 }(); 826 if (formatReason) return formatReason; 827 828 // If the texture has multiple faces or layers that are not allocated, 829 // then the framebuffer attachment required to blit the descriptor may 830 // be determined incomplete. Avoid this for now by restricting to 2D 831 // targets. 832 if (!allow3D) { 833 bool is2D = ImageToTexTarget(target) == target && !IsTexTarget3D(target); 834 if (!is2D) { 835 return "texture is not 2D"; 836 } 837 } 838 839 return nullptr; 840 } 841 842 bool TexUnpackSurface::AllowBlitSd(WebGLContext* const webgl, 843 const GLenum target, const int32_t level, 844 const ivec3& offset, 845 const GLenum internalFormat, 846 const webgl::PackingInfo& pi, 847 bool allowConversion, bool allowSRGB, 848 bool allow3D, bool warn) const { 849 dom::PredefinedColorSpace srcColorSpace = dom::PredefinedColorSpace::Srgb; 850 dom::PredefinedColorSpace dstColorSpace = 851 webgl->mUnpackColorSpace ? *webgl->mUnpackColorSpace 852 : dom::PredefinedColorSpace::Srgb; 853 bool sameColorSpace = srcColorSpace == dstColorSpace; 854 if (const char* reason = BlitPreventReason( 855 target, level, offset, internalFormat, pi, mDesc, 856 webgl->mOptionalRenderableFormatBits, sameColorSpace, allowConversion, 857 allowSRGB, allow3D)) { 858 if (warn) { 859 webgl->GeneratePerfWarning( 860 "Failed to hit GPU-copy fast-path." 861 " (%s) Falling back to CPU upload.", 862 reason); 863 } 864 return false; 865 } 866 return true; 867 } 868 869 // The texture may be mipmap incomplete which will prevent the framebuffer 870 // from being complete while drawing to it. To avoid this scenario, override 871 // the texture base and max level temporarily to ignore incomplete mipmaps 872 // while blitting to it. Depending on GL implementation (desktop vs ES), the 873 // min filter may contribute to mipmap completeness. 874 struct AutoRestoreMipmapState { 875 AutoRestoreMipmapState(gl::GLContext* gl, GLenum target, GLint level) 876 : mGL(gl), mTarget(target), mLevel(level) { 877 mGL->fGetTexParameteriv(mTarget, LOCAL_GL_TEXTURE_MIN_FILTER, &mMinFilter); 878 if (IsTexMipmapFilter(mMinFilter)) { 879 mGL->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_MIN_FILTER, 880 LOCAL_GL_NEAREST); 881 } 882 if (mGL->HasTexParamMipmapLevel()) { 883 mGL->fGetTexParameteriv(mTarget, LOCAL_GL_TEXTURE_BASE_LEVEL, 884 &mLevelBase); 885 mGL->fGetTexParameteriv(mTarget, LOCAL_GL_TEXTURE_MAX_LEVEL, &mLevelMax); 886 if (mLevelBase != mLevel) { 887 mGL->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_BASE_LEVEL, mLevel); 888 } 889 if (mLevelMax != mLevel) { 890 mGL->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_MAX_LEVEL, mLevel); 891 } 892 } 893 } 894 895 ~AutoRestoreMipmapState() { 896 if (IsTexMipmapFilter(mMinFilter)) { 897 mGL->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_MIN_FILTER, mMinFilter); 898 } 899 if (mGL->HasTexParamMipmapLevel()) { 900 if (mLevelBase != mLevel) { 901 mGL->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_BASE_LEVEL, mLevelBase); 902 } 903 if (mLevelMax != mLevel) { 904 mGL->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_MAX_LEVEL, mLevelMax); 905 } 906 } 907 } 908 909 gl::GLContext* mGL = nullptr; 910 GLenum mTarget = 0; 911 GLint mLevel = 0; 912 GLint mMinFilter = 0; 913 GLint mLevelBase = 0; 914 GLint mLevelMax = 0; 915 }; 916 917 bool TexUnpackSurface::BlitSd( 918 const layers::SurfaceDescriptor& sd, bool isSubImage, bool needsRespec, 919 WebGLTexture* tex, GLint level, const webgl::DriverUnpackInfo* dui, 920 GLint xOffset, GLint yOffset, GLint zOffset, const webgl::PackingInfo& pi, 921 GLenum* const out_error, bool allowFallback) const { 922 MOZ_ASSERT_IF(needsRespec, !isSubImage); 923 924 const auto& webgl = tex->mContext; 925 const auto& target = mDesc.imageTarget; 926 const auto& size = mDesc.size; 927 const auto& unpacking = mDesc.unpacking; 928 929 const auto& gl = webgl->GL(); 930 931 // - 932 933 if (needsRespec) { 934 *out_error = 935 DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset, 936 zOffset, size.x, size.y, size.z, nullptr); 937 if (*out_error) return true; 938 } 939 940 { 941 gl::ScopedBindTexture scopedTex(gl, tex->mGLName, target); 942 AutoRestoreMipmapState restoreMipmapState(gl, target, level); 943 944 gl::ScopedFramebuffer scopedFB(gl); 945 gl::ScopedBindFramebuffer bindFB(gl, scopedFB.FB()); 946 947 { 948 gl::GLContext::LocalErrorScope errorScope(*gl); 949 950 gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, 951 LOCAL_GL_COLOR_ATTACHMENT0, target, 952 tex->mGLName, level); 953 954 const auto err = errorScope.GetError(); 955 if (err) { 956 if (allowFallback) { 957 return false; 958 } 959 MOZ_DIAGNOSTIC_CRASH("BlitSd failed attaching texture to framebuffer"); 960 } 961 } 962 963 const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); 964 if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) { 965 if (allowFallback) { 966 return false; 967 } 968 MOZ_DIAGNOSTIC_CRASH("BlitSd framebuffer is not complete"); 969 } 970 971 const auto dstOrigin = 972 (unpacking.flipY ? gl::OriginPos::TopLeft : gl::OriginPos::BottomLeft); 973 gfx::IntSize fbSize(size.x, size.y); 974 if (isSubImage) { 975 const auto& imageInfo = tex->ImageInfoAt(target, level); 976 fbSize = gfx::IntSize(imageInfo.mWidth, imageInfo.mHeight); 977 } 978 Maybe<gfxAlphaType> convertAlpha; 979 if (mDesc.srcAlphaType != gfxAlphaType::Opaque) { 980 const bool srcIsPremult = mDesc.srcAlphaType == gfxAlphaType::Premult; 981 const bool dstIsPremult = unpacking.premultiplyAlpha; 982 if (srcIsPremult != dstIsPremult) { 983 convertAlpha = Some(dstIsPremult ? gfxAlphaType::Premult 984 : gfxAlphaType::NonPremult); 985 } 986 } 987 if (!gl->BlitHelper()->BlitSdToFramebuffer( 988 sd, {xOffset, yOffset, size.x, size.y}, dstOrigin, fbSize, 989 convertAlpha)) { 990 gfxCriticalNote << "BlitSdToFramebuffer failed for type " 991 << int(sd.type()); 992 if (allowFallback) { 993 return false; 994 } 995 // Maybe the resource isn't valid anymore? 996 gl->fClearColor(0.2, 0.0, 0.2, 1.0); 997 gl->fClear(LOCAL_GL_COLOR_BUFFER_BIT); 998 const auto& cur = webgl->mColorClearValue; 999 gl->fClearColor(cur[0], cur[1], cur[2], cur[3]); 1000 webgl->GenerateWarning( 1001 "Fast Tex(Sub)Image upload failed without recourse, clearing to " 1002 "[0.2, 0.0, 0.2, 1.0]. Please file a bug!"); 1003 } 1004 } 1005 1006 return true; 1007 } 1008 1009 static bool GetFormatForSurf(const gfx::SourceSurface* surf, 1010 WebGLTexelFormat* const out_texelFormat, 1011 uint8_t* const out_bpp) { 1012 const auto surfFormat = surf->GetFormat(); 1013 switch (surfFormat) { 1014 case gfx::SurfaceFormat::B8G8R8A8: 1015 *out_texelFormat = WebGLTexelFormat::BGRA8; 1016 *out_bpp = 4; 1017 return true; 1018 1019 case gfx::SurfaceFormat::B8G8R8X8: 1020 *out_texelFormat = WebGLTexelFormat::BGRX8; 1021 *out_bpp = 4; 1022 return true; 1023 1024 case gfx::SurfaceFormat::R8G8B8A8: 1025 *out_texelFormat = WebGLTexelFormat::RGBA8; 1026 *out_bpp = 4; 1027 return true; 1028 1029 case gfx::SurfaceFormat::R8G8B8X8: 1030 *out_texelFormat = WebGLTexelFormat::RGBX8; 1031 *out_bpp = 4; 1032 return true; 1033 1034 case gfx::SurfaceFormat::R5G6B5_UINT16: 1035 *out_texelFormat = WebGLTexelFormat::RGB565; 1036 *out_bpp = 2; 1037 return true; 1038 1039 case gfx::SurfaceFormat::A8: 1040 *out_texelFormat = WebGLTexelFormat::A8; 1041 *out_bpp = 1; 1042 return true; 1043 1044 case gfx::SurfaceFormat::YUV420: 1045 // Ugh... 1046 NS_ERROR("We don't handle uploads from YUV sources yet."); 1047 // When we want to, check out gfx/ycbcr/YCbCrUtils.h. (specifically 1048 // GetYCbCrToRGBDestFormatAndSize and ConvertYCbCrToRGB) 1049 return false; 1050 1051 default: 1052 return false; 1053 } 1054 } 1055 1056 bool TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, 1057 WebGLTexture* tex, GLint level, 1058 const webgl::DriverUnpackInfo* dui, 1059 GLint xOffset, GLint yOffset, 1060 GLint zOffset, 1061 const webgl::PackingInfo& dstPI, 1062 GLenum* const out_error) const { 1063 const auto& webgl = tex->mContext; 1064 const auto& size = mDesc.size; 1065 RefPtr<gfx::DataSourceSurface> surf; 1066 if (mDesc.sd) { 1067 // First check if the SD describes an RGBA Shmem. 1068 const auto& sd = *(mDesc.sd); 1069 if (SDIsCanvasSurface(sd)) { 1070 // The canvas surface resides on a 2D canvas within the same content 1071 // process as the WebGL canvas. Query it for the surface. 1072 const auto& sdc = sd.get_SurfaceDescriptorCanvasSurface(); 1073 uint32_t managerId = sdc.managerId(); 1074 mozilla::ipc::ActorId canvasId = sdc.canvasId(); 1075 uintptr_t surfaceId = sdc.surfaceId(); 1076 Maybe<layers::SurfaceDescriptor> exportSd; 1077 // If the texture has multiple faces or layers that are not allocated, 1078 // then the framebuffer attachment required to blit the descriptor may 1079 // be determined incomplete. Avoid this for now by restricting to 2D 1080 // targets. 1081 bool allowBlit = AllowBlitSd( 1082 webgl, mDesc.imageTarget, level, {xOffset, yOffset, zOffset}, 1083 dui->internalFormat, dstPI, true, false, false, false); 1084 if (RefPtr<gfx::SourceSurface> data = 1085 gfx::CanvasManagerParent::GetCanvasSurface( 1086 webgl->GetContentId(), managerId, canvasId, surfaceId, 1087 allowBlit ? &exportSd : nullptr)) { 1088 if (exportSd && !SDIsRGBBuffer(*exportSd) && 1089 BlitSd(*exportSd, isSubImage, needsRespec, tex, level, dui, xOffset, 1090 yOffset, zOffset, dstPI, out_error, true)) { 1091 return true; 1092 } 1093 surf = data->GetDataSurface(); 1094 } 1095 if (!surf) { 1096 gfxCriticalError() << "TexUnpackSurface failed to get CanvasSurface"; 1097 return false; 1098 } 1099 } else if (SDIsRGBBuffer(sd)) { 1100 const auto& sdb = sd.get_SurfaceDescriptorBuffer(); 1101 const auto& rgb = sdb.desc().get_RGBDescriptor(); 1102 const auto& data = sdb.data(); 1103 MOZ_ASSERT(data.type() == layers::MemoryOrShmem::TShmem); 1104 const auto& shmem = data.get_Shmem(); 1105 size_t shmemSize = shmem.Size<uint8_t>(); 1106 int32_t stride = layers::ImageDataSerializer::GetRGBStride(rgb); 1107 if (stride <= 0) { 1108 gfxCriticalError() << "TexUnpackSurface failed to get rgb stride"; 1109 return false; 1110 } 1111 size_t bufSize = layers::ImageDataSerializer::ComputeRGBBufferSize( 1112 rgb.size(), rgb.format()); 1113 if (!bufSize || bufSize > shmemSize) { 1114 gfxCriticalError() << "TexUnpackSurface failed to get rgb buffer size"; 1115 return false; 1116 } 1117 surf = gfx::Factory::CreateWrappingDataSourceSurface( 1118 shmem.get<uint8_t>(), stride, rgb.size(), rgb.format()); 1119 } else if (SDIsNullRemoteDecoder(sd)) { 1120 const auto& sdrd = sd.get_SurfaceDescriptorGPUVideo() 1121 .get_SurfaceDescriptorRemoteDecoder(); 1122 RefPtr<layers::VideoBridgeParent> parent = 1123 layers::VideoBridgeParent::GetSingleton(sdrd.source()); 1124 if (!parent) { 1125 gfxCriticalNote << "TexUnpackSurface failed to get VideoBridgeParent"; 1126 return false; 1127 } 1128 RefPtr<layers::TextureHost> texture = 1129 parent->LookupTexture(webgl->GetContentId(), sdrd.handle()); 1130 if (!texture) { 1131 gfxCriticalNote << "TexUnpackSurface failed to get TextureHost"; 1132 return false; 1133 } 1134 surf = texture->GetAsSurface(); 1135 } else if (SDIsExternalImage(sd)) { 1136 const auto& sdei = sd.get_SurfaceDescriptorExternalImage(); 1137 if (auto* sharedSurfacesHolder = webgl->GetSharedSurfacesHolder()) { 1138 surf = sharedSurfacesHolder->Get(sdei.id()); 1139 } 1140 if (!surf) { 1141 // Most likely the content process crashed before it was able to finish 1142 // sharing the surface with the compositor process. 1143 gfxCriticalNote << "TexUnpackSurface failed to get ExternalImage"; 1144 return false; 1145 } 1146 } else if (AllowBlitSd(webgl, mDesc.imageTarget, level, 1147 {xOffset, yOffset, zOffset}, dui->internalFormat, 1148 dstPI, false, true, true, true) && 1149 BlitSd(sd, isSubImage, needsRespec, tex, level, dui, xOffset, 1150 yOffset, zOffset, dstPI, out_error)) { 1151 // The SD wasn't an RGBA shmem, but were able to blit the SD directly to 1152 // the texture. 1153 return true; 1154 } else if (mDesc.sourceSurf) { 1155 // In case neither a wrapper was created nor a blit succeeded, check for a 1156 // backup source surface. 1157 surf = mDesc.sourceSurf->GetDataSurface(); 1158 } 1159 if (!surf) { 1160 gfxCriticalError() << "TexUnpackSurface failed to create wrapping " 1161 "DataSourceSurface for Shmem."; 1162 return false; 1163 } 1164 } else if (mDesc.sourceSurf) { 1165 surf = mDesc.sourceSurf->GetDataSurface(); 1166 if (!surf) { 1167 gfxCriticalError() << "TexUnpackSurface failed to get data for " 1168 "sourceSurf."; 1169 return false; 1170 } 1171 } 1172 1173 //// 1174 1175 WebGLTexelFormat srcFormat; 1176 uint8_t srcBPP; 1177 if (!GetFormatForSurf(surf, &srcFormat, &srcBPP)) { 1178 webgl->ErrorImplementationBug( 1179 "GetFormatForSurf failed for" 1180 " WebGLTexelFormat::%u.", 1181 uint32_t(surf->GetFormat())); 1182 return false; 1183 } 1184 1185 gfx::DataSourceSurface::ScopedMap map(surf, 1186 gfx::DataSourceSurface::MapType::READ); 1187 if (!map.IsMapped()) { 1188 webgl->ErrorOutOfMemory("Failed to map source surface for upload."); 1189 return false; 1190 } 1191 1192 const auto& srcBegin = map.GetData(); 1193 const auto srcStride = static_cast<size_t>(map.GetStride()); 1194 1195 // - 1196 1197 const auto dstFormat = FormatForPackingInfo(dstPI); 1198 const size_t dstBpp = BytesPerPixel(dstPI); 1199 const size_t dstUsedBytesPerRow = dstBpp * surf->GetSize().width; 1200 size_t dstStride = dstFormat == srcFormat ? srcStride // Try To match 1201 : dstUsedBytesPerRow; 1202 1203 // - 1204 1205 auto dstUnpackingRes = mDesc.ExplicitUnpacking(dstPI, Some(dstStride)); 1206 if (dstUnpackingRes.isOk()) { 1207 const auto& dstUnpacking = dstUnpackingRes.inspect(); 1208 if (!webgl->IsWebGL2() && dstUnpacking.state.rowLength != size.x) { 1209 dstUnpackingRes = Err("WebGL1 can't handle rowLength != size.x"); 1210 } 1211 } 1212 if (!dstUnpackingRes.isOk()) { 1213 dstStride = dstUsedBytesPerRow; 1214 dstUnpackingRes = mDesc.ExplicitUnpacking(dstPI, Some(dstStride)); 1215 } 1216 if (!dstUnpackingRes.isOk()) { 1217 gfxCriticalError() << dstUnpackingRes.inspectErr(); 1218 webgl->ErrorImplementationBug("ExplicitUnpacking failed: %s", 1219 dstUnpackingRes.inspectErr().c_str()); 1220 return false; 1221 } 1222 const auto& dstUnpacking = dstUnpackingRes.inspect(); 1223 MOZ_ASSERT(dstUnpacking.metrics.bytesPerRowStride == dstStride); 1224 1225 // - 1226 1227 const uint8_t* dstBegin = srcBegin; 1228 UniqueBuffer tempBuffer; 1229 // clang-format off 1230 if (!ConvertIfNeeded(webgl, surf->GetSize().width, surf->GetSize().height, 1231 srcFormat, srcBegin, AutoAssertCast(srcStride), 1232 dstFormat, AutoAssertCast(dstUnpacking.metrics.bytesPerRowStride), &dstBegin, 1233 &tempBuffer)) { 1234 return false; 1235 } 1236 // clang-format on 1237 1238 //// 1239 1240 const auto& gl = webgl->gl; 1241 if (!gl->MakeCurrent()) { 1242 *out_error = LOCAL_GL_CONTEXT_LOST; 1243 return true; 1244 } 1245 1246 dstUnpacking.state.ApplyUnpack(*gl, webgl->IsWebGL2(), size); 1247 1248 *out_error = 1249 DoTexOrSubImage(isSubImage, gl, mDesc.imageTarget, level, dui, xOffset, 1250 yOffset, zOffset, size.x, size.y, size.z, dstBegin); 1251 1252 // Caller will reset all our modified PixelStorei state. 1253 1254 return true; 1255 } 1256 1257 } // namespace webgl 1258 } // namespace mozilla