WebGLTextureUpload.cpp (63516B)
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 "WebGLTextureUpload.h" 7 8 #include <algorithm> 9 10 #include "CanvasUtils.h" 11 #include "ClientWebGLContext.h" 12 #include "GLBlitHelper.h" 13 #include "GLContext.h" 14 #include "ScopedGLHelpers.h" 15 #include "TexUnpackBlob.h" 16 #include "WebGLBuffer.h" 17 #include "WebGLContext.h" 18 #include "WebGLContextUtils.h" 19 #include "WebGLFormats.h" 20 #include "WebGLFramebuffer.h" 21 #include "WebGLTexelConversions.h" 22 #include "WebGLTexture.h" 23 #include "mozilla/MathAlgorithms.h" 24 #include "mozilla/ScopeExit.h" 25 #include "mozilla/StaticPrefs_webgl.h" 26 #include "mozilla/dom/HTMLCanvasElement.h" 27 #include "mozilla/dom/HTMLVideoElement.h" 28 #include "mozilla/dom/ImageBitmap.h" 29 #include "mozilla/dom/ImageData.h" 30 #include "mozilla/dom/OffscreenCanvas.h" 31 #include "mozilla/gfx/2D.h" 32 #include "mozilla/gfx/Logging.h" 33 #include "mozilla/layers/SharedSurfacesChild.h" 34 #include "nsLayoutUtils.h" 35 36 namespace mozilla { 37 namespace webgl { 38 39 // The canvas spec says that drawImage should draw the first frame of 40 // animated images. The webgl spec doesn't mention the issue, so we do the 41 // same as drawImage. 42 static constexpr uint32_t kDefaultSurfaceFromElementFlags = 43 nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE | 44 nsLayoutUtils::SFE_USE_ELEMENT_SIZE_IF_VECTOR | 45 nsLayoutUtils::SFE_EXACT_SIZE_SURFACE | 46 nsLayoutUtils::SFE_ALLOW_NON_PREMULT; 47 48 Maybe<TexUnpackBlobDesc> FromImageBitmap(const GLenum target, Maybe<uvec3> size, 49 const dom::ImageBitmap& imageBitmap, 50 ErrorResult* const out_rv) { 51 if (imageBitmap.IsWriteOnly()) { 52 out_rv->Throw(NS_ERROR_DOM_SECURITY_ERR); 53 return {}; 54 } 55 56 const auto cloneData = imageBitmap.ToCloneData(); 57 if (!cloneData) { 58 return {}; 59 } 60 61 const RefPtr<gfx::DataSourceSurface> surf = cloneData->mSurface; 62 if (NS_WARN_IF(!surf)) { 63 return {}; 64 } 65 66 const auto imageSize = *uvec2::FromSize(surf->GetSize()); 67 if (!size) { 68 size.emplace(imageSize.x, imageSize.y, 1); 69 } 70 71 // For SourceSurfaceSharedData, try to get SurfaceDescriptorExternalImage. 72 Maybe<layers::SurfaceDescriptor> sd; 73 layers::SharedSurfacesChild::Share(surf, sd); 74 75 // WhatWG "HTML Living Standard" (30 October 2015): 76 // "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned as 77 // non-premultiplied alpha values." 78 return Some(TexUnpackBlobDesc{target, 79 size.value(), 80 cloneData->mAlphaType, 81 {}, 82 {}, 83 Some(imageSize), 84 nullptr, 85 sd, 86 surf, 87 {}, 88 false}); 89 } 90 91 static layers::SurfaceDescriptor Flatten(const layers::SurfaceDescriptor& sd) { 92 const auto sdType = sd.type(); 93 if (sdType != layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo) { 94 return sd; 95 } 96 const auto& sdv = sd.get_SurfaceDescriptorGPUVideo(); 97 const auto& sdvType = sdv.type(); 98 if (sdvType != 99 layers::SurfaceDescriptorGPUVideo::TSurfaceDescriptorRemoteDecoder) { 100 return sd; 101 } 102 103 const auto& sdrd = sdv.get_SurfaceDescriptorRemoteDecoder(); 104 const auto& subdesc = sdrd.subdesc(); 105 const auto& subdescType = subdesc.type(); 106 switch (subdescType) { 107 case layers::RemoteDecoderVideoSubDescriptor::T__None: 108 case layers::RemoteDecoderVideoSubDescriptor::Tnull_t: 109 return sd; 110 111 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorD3D10: 112 return subdesc.get_SurfaceDescriptorD3D10(); 113 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorDXGIYCbCr: 114 return subdesc.get_SurfaceDescriptorDXGIYCbCr(); 115 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorDMABuf: 116 return subdesc.get_SurfaceDescriptorDMABuf(); 117 case layers::RemoteDecoderVideoSubDescriptor:: 118 TSurfaceDescriptorMacIOSurface: 119 return subdesc.get_SurfaceDescriptorMacIOSurface(); 120 case layers::RemoteDecoderVideoSubDescriptor:: 121 TSurfaceDescriptorDcompSurface: 122 return subdesc.get_SurfaceDescriptorDcompSurface(); 123 } 124 MOZ_CRASH("unreachable"); 125 } 126 127 Maybe<webgl::TexUnpackBlobDesc> FromOffscreenCanvas( 128 const ClientWebGLContext& webgl, const GLenum target, Maybe<uvec3> size, 129 const dom::OffscreenCanvas& canvas, ErrorResult* const out_error) { 130 if (canvas.IsWriteOnly()) { 131 webgl.EnqueueWarning( 132 "OffscreenCanvas is write-only, thus cannot be uploaded."); 133 out_error->ThrowSecurityError( 134 "OffscreenCanvas is write-only, thus cannot be uploaded."); 135 return {}; 136 } 137 138 auto sfer = nsLayoutUtils::SurfaceFromOffscreenCanvas( 139 const_cast<dom::OffscreenCanvas*>(&canvas), 140 kDefaultSurfaceFromElementFlags); 141 return FromSurfaceFromElementResult(webgl, target, size, sfer, out_error); 142 } 143 144 Maybe<webgl::TexUnpackBlobDesc> FromVideoFrame( 145 const ClientWebGLContext& webgl, const GLenum target, Maybe<uvec3> size, 146 const dom::VideoFrame& videoFrame, ErrorResult* const out_error) { 147 auto sfer = nsLayoutUtils::SurfaceFromVideoFrame( 148 const_cast<dom::VideoFrame*>(&videoFrame), 149 kDefaultSurfaceFromElementFlags); 150 return FromSurfaceFromElementResult(webgl, target, size, sfer, out_error); 151 } 152 153 Maybe<webgl::TexUnpackBlobDesc> FromDomElem(const ClientWebGLContext& webgl, 154 const GLenum target, 155 Maybe<uvec3> size, 156 const dom::Element& elem, 157 ErrorResult* const out_error) { 158 if (elem.IsHTMLElement(nsGkAtoms::canvas)) { 159 const dom::HTMLCanvasElement* srcCanvas = 160 static_cast<const dom::HTMLCanvasElement*>(&elem); 161 if (srcCanvas->IsWriteOnly()) { 162 out_error->Throw(NS_ERROR_DOM_SECURITY_ERR); 163 return {}; 164 } 165 } 166 167 uint32_t flags = kDefaultSurfaceFromElementFlags; 168 const auto& unpacking = webgl.State().mPixelUnpackState; 169 if (unpacking.colorspaceConversion == LOCAL_GL_NONE) { 170 flags |= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION; 171 } 172 173 RefPtr<gfx::DrawTarget> idealDrawTarget = nullptr; // Don't care for now. 174 auto sfer = nsLayoutUtils::SurfaceFromElement( 175 const_cast<dom::Element*>(&elem), flags, idealDrawTarget); 176 return FromSurfaceFromElementResult(webgl, target, size, sfer, out_error); 177 } 178 179 Maybe<webgl::TexUnpackBlobDesc> FromSurfaceFromElementResult( 180 const ClientWebGLContext& webgl, const GLenum target, Maybe<uvec3> size, 181 SurfaceFromElementResult& sfer, ErrorResult* const out_error) { 182 uvec2 elemSize; 183 184 const auto& layersImage = sfer.mLayersImage; 185 Maybe<layers::SurfaceDescriptor> sd; 186 if (layersImage) { 187 elemSize = *uvec2::FromSize(layersImage->GetSize()); 188 189 sd = layersImage->GetDesc(); 190 if (sd) { 191 sd = Some(Flatten(*sd)); 192 } 193 if (!sd) { 194 NS_WARNING("No SurfaceDescriptor for layers::Image!"); 195 } 196 } 197 198 RefPtr<gfx::SourceSurface> sourceSurf; 199 if (!sd && sfer.GetSourceSurface()) { 200 const auto surf = sfer.GetSourceSurface(); 201 elemSize = *uvec2::FromSize(surf->GetSize()); 202 if (surf->GetType() == gfx::SurfaceType::RECORDING) { 203 // If we find a recording surface, then try to create a descriptor for it 204 // to avoid transferring data for it. The underlying Canvas2D commands 205 // have most likely not been processed yet, so trying to access the data 206 // here would cause cross-process synchronization. The descriptor avoids 207 // having to synchronize and then read back Canvas2D data across a process 208 // gap. If CanvasSurface descriptors are unsupported, then the underlying 209 // source surface will be converted to data for transferring later. 210 layers::SurfaceDescriptor desc; 211 if (surf->GetSurfaceDescriptor(desc)) { 212 sd = Some(desc); 213 sourceSurf = surf; 214 } 215 } 216 if (!sd) { 217 // WARNING: OSX can lose our MakeCurrent here. 218 sourceSurf = surf->GetDataSurface(); 219 } 220 } 221 222 if (!sd) { 223 // For SourceSurfaceSharedData, try to get SurfaceDescriptorExternalImage. 224 layers::SharedSurfacesChild::Share(sourceSurf, sd); 225 } 226 227 ////// 228 229 if (!size) { 230 size.emplace(elemSize.x, elemSize.y, 1); 231 } 232 233 //// 234 235 if (!sd && !sourceSurf) { 236 webgl.EnqueueWarning("Resource has no data (yet?). Uploading zeros."); 237 if (!size) { 238 size.emplace(0, 0, 1); 239 } 240 return Some( 241 TexUnpackBlobDesc{target, size.value(), gfxAlphaType::NonPremult}); 242 } 243 244 ////// 245 246 // While it's counter-intuitive, the shape of the SFEResult API means that we 247 // should try to pull out a surface first, and then, if we do pull out a 248 // surface, check CORS/write-only/etc.. 249 250 if (!sfer.mCORSUsed) { 251 auto& srcPrincipal = sfer.mPrincipal; 252 nsIPrincipal* dstPrincipal = webgl.PrincipalOrNull(); 253 if (!dstPrincipal || !dstPrincipal->Subsumes(srcPrincipal)) { 254 webgl.EnqueueWarning("Cross-origin elements require CORS."); 255 out_error->Throw(NS_ERROR_DOM_SECURITY_ERR); 256 return {}; 257 } 258 } 259 260 if (sfer.mIsWriteOnly) { 261 // mIsWriteOnly defaults to true, and so will be true even if SFE merely 262 // failed. Thus we must test mIsWriteOnly after successfully retrieving an 263 // Image or SourceSurface. 264 webgl.EnqueueWarning("Element is write-only, thus cannot be uploaded."); 265 out_error->Throw(NS_ERROR_DOM_SECURITY_ERR); 266 return {}; 267 } 268 269 ////// 270 // Ok, we're good! 271 272 return Some(TexUnpackBlobDesc{target, 273 size.value(), 274 sfer.mAlphaType, 275 {}, 276 {}, 277 Some(elemSize), 278 layersImage, 279 sd, 280 sourceSurf}); 281 } 282 283 } // namespace webgl 284 285 ////////////////////////////////////////////////////////////////////////////////////////// 286 ////////////////////////////////////////////////////////////////////////////////////////// 287 288 static bool ValidateTexImage(WebGLContext* webgl, WebGLTexture* texture, 289 TexImageTarget target, uint32_t level, 290 webgl::ImageInfo** const out_imageInfo) { 291 // Check level 292 if (level >= WebGLTexture::kMaxLevelCount) { 293 webgl->ErrorInvalidValue("`level` is too large."); 294 return false; 295 } 296 297 auto& imageInfo = texture->ImageInfoAt(target, level); 298 *out_imageInfo = &imageInfo; 299 return true; 300 } 301 302 // For *TexImage* 303 bool WebGLTexture::ValidateTexImageSpecification( 304 TexImageTarget target, uint32_t level, const uvec3& size, 305 webgl::ImageInfo** const out_imageInfo) { 306 if (mImmutable) { 307 mContext->ErrorInvalidOperation("Specified texture is immutable."); 308 return false; 309 } 310 311 // Do this early to validate `level`. 312 webgl::ImageInfo* imageInfo; 313 if (!ValidateTexImage(mContext, this, target, level, &imageInfo)) 314 return false; 315 316 if (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP && size.x != size.y) { 317 mContext->ErrorInvalidValue("Cube map images must be square."); 318 return false; 319 } 320 321 /* GLES 3.0.4, p133-134: 322 * GL_MAX_TEXTURE_SIZE is *not* the max allowed texture size. Rather, it is 323 * the max (width/height) size guaranteed not to generate an INVALID_VALUE for 324 * too-large dimensions. Sizes larger than GL_MAX_TEXTURE_SIZE *may or may 325 * not* result in an INVALID_VALUE, or possibly GL_OOM. 326 * 327 * However, we have needed to set our maximums lower in the past to prevent 328 * resource corruption. Therefore we have limits.maxTex2dSize, which is 329 * neither necessarily lower nor higher than MAX_TEXTURE_SIZE. 330 * 331 * Note that limits.maxTex2dSize must be >= than the advertized 332 * MAX_TEXTURE_SIZE. For simplicity, we advertize MAX_TEXTURE_SIZE as 333 * limits.maxTex2dSize. 334 */ 335 336 uint32_t maxWidthHeight = 0; 337 uint32_t maxDepth = 0; 338 uint32_t maxLevel = 0; 339 340 const auto& limits = mContext->Limits(); 341 MOZ_ASSERT(level <= 31); 342 switch (target.get()) { 343 case LOCAL_GL_TEXTURE_2D: 344 maxWidthHeight = limits.maxTex2dSize >> level; 345 maxDepth = 1; 346 maxLevel = CeilingLog2(limits.maxTex2dSize); 347 break; 348 349 case LOCAL_GL_TEXTURE_3D: 350 maxWidthHeight = limits.maxTex3dSize >> level; 351 maxDepth = maxWidthHeight; 352 maxLevel = CeilingLog2(limits.maxTex3dSize); 353 break; 354 355 case LOCAL_GL_TEXTURE_2D_ARRAY: 356 maxWidthHeight = limits.maxTex2dSize >> level; 357 // "The maximum number of layers for two-dimensional array textures 358 // (depth) must be at least MAX_ARRAY_TEXTURE_LAYERS for all levels." 359 maxDepth = limits.maxTexArrayLayers; 360 maxLevel = CeilingLog2(limits.maxTex2dSize); 361 break; 362 363 default: // cube maps 364 MOZ_ASSERT(IsCubeMap()); 365 maxWidthHeight = limits.maxTexCubeSize >> level; 366 maxDepth = 1; 367 maxLevel = CeilingLog2(limits.maxTexCubeSize); 368 break; 369 } 370 371 if (level > maxLevel) { 372 mContext->ErrorInvalidValue("Requested level is not supported for target."); 373 return false; 374 } 375 376 if (size.x > maxWidthHeight || size.y > maxWidthHeight || size.z > maxDepth) { 377 mContext->ErrorInvalidValue("Requested size at this level is unsupported."); 378 return false; 379 } 380 381 { 382 /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification 383 * "If level is greater than zero, and either width or 384 * height is not a power-of-two, the error INVALID_VALUE is 385 * generated." 386 * 387 * This restriction does not apply to GL ES Version 3.0+. 388 */ 389 bool requirePOT = (!mContext->IsWebGL2() && level != 0); 390 391 if (requirePOT) { 392 if (!IsPowerOfTwo(size.x) || !IsPowerOfTwo(size.y)) { 393 mContext->ErrorInvalidValue( 394 "For level > 0, width and height must be" 395 " powers of two."); 396 return false; 397 } 398 } 399 } 400 401 *out_imageInfo = imageInfo; 402 return true; 403 } 404 405 // For *TexSubImage* 406 bool WebGLTexture::ValidateTexImageSelection( 407 TexImageTarget target, uint32_t level, const uvec3& offset, 408 const uvec3& size, webgl::ImageInfo** const out_imageInfo) { 409 webgl::ImageInfo* imageInfo; 410 if (!ValidateTexImage(mContext, this, target, level, &imageInfo)) 411 return false; 412 413 if (!imageInfo->IsDefined()) { 414 mContext->ErrorInvalidOperation( 415 "The specified TexImage has not yet been" 416 " specified."); 417 return false; 418 } 419 420 const auto totalX = CheckedUint32(offset.x) + size.x; 421 const auto totalY = CheckedUint32(offset.y) + size.y; 422 const auto totalZ = CheckedUint32(offset.z) + size.z; 423 424 if (!totalX.isValid() || totalX.value() > imageInfo->mWidth || 425 !totalY.isValid() || totalY.value() > imageInfo->mHeight || 426 !totalZ.isValid() || totalZ.value() > imageInfo->mDepth) { 427 mContext->ErrorInvalidValue( 428 "Offset+size must be <= the size of the existing" 429 " specified image."); 430 return false; 431 } 432 433 *out_imageInfo = imageInfo; 434 return true; 435 } 436 437 static bool ValidateCompressedTexUnpack(WebGLContext* webgl, const uvec3& size, 438 const webgl::FormatInfo* format, 439 size_t dataSize) { 440 auto compression = format->compression; 441 442 auto bytesPerBlock = compression->bytesPerBlock; 443 auto blockWidth = compression->blockWidth; 444 auto blockHeight = compression->blockHeight; 445 446 auto widthInBlocks = CheckedUint32(size.x) / blockWidth; 447 auto heightInBlocks = CheckedUint32(size.y) / blockHeight; 448 if (size.x % blockWidth) widthInBlocks += 1; 449 if (size.y % blockHeight) heightInBlocks += 1; 450 451 const CheckedUint32 blocksPerImage = widthInBlocks * heightInBlocks; 452 const CheckedUint32 bytesPerImage = bytesPerBlock * blocksPerImage; 453 const CheckedUint32 bytesNeeded = bytesPerImage * size.z; 454 455 if (!bytesNeeded.isValid()) { 456 webgl->ErrorOutOfMemory("Overflow while computing the needed buffer size."); 457 return false; 458 } 459 460 if (dataSize != bytesNeeded.value()) { 461 webgl->ErrorInvalidValue( 462 "Provided buffer's size must match expected size." 463 " (needs %u, has %zu)", 464 bytesNeeded.value(), dataSize); 465 return false; 466 } 467 468 return true; 469 } 470 471 static bool DoChannelsMatchForCopyTexImage(const webgl::FormatInfo* srcFormat, 472 const webgl::FormatInfo* dstFormat) { 473 // GLES 3.0.4 p140 Table 3.16 "Valid CopyTexImage source 474 // framebuffer/destination texture base internal format combinations." 475 476 switch (srcFormat->unsizedFormat) { 477 case webgl::UnsizedFormat::RGBA: 478 switch (dstFormat->unsizedFormat) { 479 case webgl::UnsizedFormat::A: 480 case webgl::UnsizedFormat::L: 481 case webgl::UnsizedFormat::LA: 482 case webgl::UnsizedFormat::R: 483 case webgl::UnsizedFormat::RG: 484 case webgl::UnsizedFormat::RGB: 485 case webgl::UnsizedFormat::RGBA: 486 return true; 487 default: 488 return false; 489 } 490 491 case webgl::UnsizedFormat::RGB: 492 switch (dstFormat->unsizedFormat) { 493 case webgl::UnsizedFormat::L: 494 case webgl::UnsizedFormat::R: 495 case webgl::UnsizedFormat::RG: 496 case webgl::UnsizedFormat::RGB: 497 return true; 498 default: 499 return false; 500 } 501 502 case webgl::UnsizedFormat::RG: 503 switch (dstFormat->unsizedFormat) { 504 case webgl::UnsizedFormat::L: 505 case webgl::UnsizedFormat::R: 506 case webgl::UnsizedFormat::RG: 507 return true; 508 default: 509 return false; 510 } 511 512 case webgl::UnsizedFormat::R: 513 switch (dstFormat->unsizedFormat) { 514 case webgl::UnsizedFormat::L: 515 case webgl::UnsizedFormat::R: 516 return true; 517 default: 518 return false; 519 } 520 521 default: 522 return false; 523 } 524 } 525 526 static bool EnsureImageDataInitializedForUpload( 527 WebGLTexture* tex, TexImageTarget target, uint32_t level, 528 const uvec3& offset, const uvec3& size, webgl::ImageInfo* imageInfo, 529 bool* const out_expectsInit = nullptr) { 530 if (out_expectsInit) { 531 *out_expectsInit = false; 532 } 533 if (!imageInfo->mUninitializedSlices) return true; 534 535 if (size.x == imageInfo->mWidth && size.y == imageInfo->mHeight) { 536 bool expectsInit = false; 537 auto& isSliceUninit = *imageInfo->mUninitializedSlices; 538 for (const auto z : IntegerRange(offset.z, offset.z + size.z)) { 539 if (!isSliceUninit[z]) continue; 540 expectsInit = true; 541 isSliceUninit[z] = false; 542 } 543 if (out_expectsInit) { 544 *out_expectsInit = expectsInit; 545 } 546 547 if (!expectsInit) return true; 548 549 bool hasUninitialized = false; 550 for (const auto z : IntegerRange(imageInfo->mDepth)) { 551 hasUninitialized |= isSliceUninit[z]; 552 } 553 if (!hasUninitialized) { 554 imageInfo->mUninitializedSlices.reset(); 555 } 556 return true; 557 } 558 559 WebGLContext* webgl = tex->mContext; 560 webgl->GenerateWarning( 561 "Texture has not been initialized prior to a" 562 " partial upload, forcing the browser to clear it." 563 " This may be slow."); 564 if (!tex->EnsureImageDataInitialized(target, level)) { 565 MOZ_ASSERT(false, "Unexpected failure to init image data."); 566 return false; 567 } 568 569 return true; 570 } 571 572 ////////////////////////////////////////////////////////////////////////////////////////// 573 ////////////////////////////////////////////////////////////////////////////////////////// 574 // Actual calls 575 576 static inline GLenum DoTexStorage(gl::GLContext* gl, TexTarget target, 577 GLsizei levels, GLenum sizedFormat, 578 GLsizei width, GLsizei height, 579 GLsizei depth) { 580 gl::GLContext::LocalErrorScope errorScope(*gl); 581 582 switch (target.get()) { 583 case LOCAL_GL_TEXTURE_2D: 584 case LOCAL_GL_TEXTURE_CUBE_MAP: 585 MOZ_ASSERT(depth == 1); 586 gl->fTexStorage2D(target.get(), levels, sizedFormat, width, height); 587 break; 588 589 case LOCAL_GL_TEXTURE_3D: 590 case LOCAL_GL_TEXTURE_2D_ARRAY: 591 gl->fTexStorage3D(target.get(), levels, sizedFormat, width, height, 592 depth); 593 break; 594 595 default: 596 MOZ_CRASH("GFX: bad target"); 597 } 598 599 return errorScope.GetError(); 600 } 601 602 bool IsTarget3D(TexImageTarget target) { 603 switch (target.get()) { 604 case LOCAL_GL_TEXTURE_2D: 605 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X: 606 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X: 607 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y: 608 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: 609 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z: 610 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: 611 return false; 612 613 case LOCAL_GL_TEXTURE_3D: 614 case LOCAL_GL_TEXTURE_2D_ARRAY: 615 return true; 616 617 default: 618 MOZ_CRASH("GFX: bad target"); 619 } 620 } 621 622 GLenum DoTexImage(gl::GLContext* gl, TexImageTarget target, GLint level, 623 const webgl::DriverUnpackInfo* dui, GLsizei width, 624 GLsizei height, GLsizei depth, const void* data) { 625 const GLint border = 0; 626 627 gl::GLContext::LocalErrorScope errorScope(*gl); 628 629 if (IsTarget3D(target)) { 630 gl->fTexImage3D(target.get(), level, dui->internalFormat, width, height, 631 depth, border, dui->unpackFormat, dui->unpackType, data); 632 } else { 633 MOZ_ASSERT(depth == 1); 634 gl->fTexImage2D(target.get(), level, dui->internalFormat, width, height, 635 border, dui->unpackFormat, dui->unpackType, data); 636 } 637 638 return errorScope.GetError(); 639 } 640 641 GLenum DoTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level, 642 GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width, 643 GLsizei height, GLsizei depth, 644 const webgl::PackingInfo& pi, const void* data) { 645 gl::GLContext::LocalErrorScope errorScope(*gl); 646 647 if (IsTarget3D(target)) { 648 gl->fTexSubImage3D(target.get(), level, xOffset, yOffset, zOffset, width, 649 height, depth, pi.format, pi.type, data); 650 } else { 651 MOZ_ASSERT(zOffset == 0); 652 MOZ_ASSERT(depth == 1); 653 gl->fTexSubImage2D(target.get(), level, xOffset, yOffset, width, height, 654 pi.format, pi.type, data); 655 } 656 657 return errorScope.GetError(); 658 } 659 660 static inline GLenum DoCompressedTexImage(gl::GLContext* gl, 661 TexImageTarget target, GLint level, 662 GLenum internalFormat, GLsizei width, 663 GLsizei height, GLsizei depth, 664 GLsizei dataSize, const void* data) { 665 const GLint border = 0; 666 667 gl::GLContext::LocalErrorScope errorScope(*gl); 668 669 if (IsTarget3D(target)) { 670 gl->fCompressedTexImage3D(target.get(), level, internalFormat, width, 671 height, depth, border, dataSize, data); 672 } else { 673 MOZ_ASSERT(depth == 1); 674 gl->fCompressedTexImage2D(target.get(), level, internalFormat, width, 675 height, border, dataSize, data); 676 } 677 678 return errorScope.GetError(); 679 } 680 681 GLenum DoCompressedTexSubImage(gl::GLContext* gl, TexImageTarget target, 682 GLint level, GLint xOffset, GLint yOffset, 683 GLint zOffset, GLsizei width, GLsizei height, 684 GLsizei depth, GLenum sizedUnpackFormat, 685 GLsizei dataSize, const void* data) { 686 gl::GLContext::LocalErrorScope errorScope(*gl); 687 688 if (IsTarget3D(target)) { 689 gl->fCompressedTexSubImage3D(target.get(), level, xOffset, yOffset, zOffset, 690 width, height, depth, sizedUnpackFormat, 691 dataSize, data); 692 } else { 693 MOZ_ASSERT(zOffset == 0); 694 MOZ_ASSERT(depth == 1); 695 gl->fCompressedTexSubImage2D(target.get(), level, xOffset, yOffset, width, 696 height, sizedUnpackFormat, dataSize, data); 697 } 698 699 return errorScope.GetError(); 700 } 701 702 static inline GLenum DoCopyTexSubImage(gl::GLContext* gl, TexImageTarget target, 703 GLint level, GLint xOffset, 704 GLint yOffset, GLint zOffset, GLint x, 705 GLint y, GLsizei width, GLsizei height) { 706 gl::GLContext::LocalErrorScope errorScope(*gl); 707 708 if (IsTarget3D(target)) { 709 gl->fCopyTexSubImage3D(target.get(), level, xOffset, yOffset, zOffset, x, y, 710 width, height); 711 } else { 712 MOZ_ASSERT(zOffset == 0); 713 gl->fCopyTexSubImage2D(target.get(), level, xOffset, yOffset, x, y, width, 714 height); 715 } 716 717 return errorScope.GetError(); 718 } 719 720 ////////////////////////////////////////////////////////////////////////////////////////// 721 ////////////////////////////////////////////////////////////////////////////////////////// 722 // Actual (mostly generic) function implementations 723 724 static bool ValidateCompressedTexImageRestrictions( 725 const WebGLContext* webgl, TexImageTarget target, uint32_t level, 726 const webgl::FormatInfo* format, const uvec3& size) { 727 const auto fnIsDimValid_S3TC = [&](const char* const name, uint32_t levelSize, 728 uint32_t blockSize) { 729 const auto impliedBaseSize = levelSize << level; 730 if (impliedBaseSize % blockSize == 0) return true; 731 webgl->ErrorInvalidOperation( 732 "%u is never a valid %s for level %u, because it implies a base mip %s " 733 "of %u." 734 " %s requires that base mip levels have a %s multiple of %u.", 735 levelSize, name, level, name, impliedBaseSize, format->name, name, 736 blockSize); 737 return false; 738 }; 739 740 switch (format->compression->family) { 741 case webgl::CompressionFamily::ASTC: 742 if (target == LOCAL_GL_TEXTURE_3D && 743 !webgl->gl->IsExtensionSupported( 744 gl::GLContext::KHR_texture_compression_astc_hdr)) { 745 webgl->ErrorInvalidOperation("TEXTURE_3D requires ASTC's hdr profile."); 746 return false; 747 } 748 break; 749 750 case webgl::CompressionFamily::PVRTC: 751 if (!IsPowerOfTwo(size.x) || !IsPowerOfTwo(size.y)) { 752 webgl->ErrorInvalidValue("%s requires power-of-two width and height.", 753 format->name); 754 return false; 755 } 756 break; 757 758 case webgl::CompressionFamily::BPTC: 759 case webgl::CompressionFamily::RGTC: 760 case webgl::CompressionFamily::S3TC: 761 if (!fnIsDimValid_S3TC("width", size.x, 762 format->compression->blockWidth) || 763 !fnIsDimValid_S3TC("height", size.y, 764 format->compression->blockHeight)) { 765 return false; 766 } 767 break; 768 769 // Default: There are no restrictions on CompressedTexImage. 770 case webgl::CompressionFamily::ES3: 771 case webgl::CompressionFamily::ETC1: 772 break; 773 } 774 775 return true; 776 } 777 778 static bool ValidateFormatAndSize(const WebGLContext* webgl, 779 TexImageTarget target, 780 const webgl::FormatInfo* format, 781 const uvec3& size) { 782 // Check if texture size will likely be rejected by the driver and give a more 783 // meaningful error message. 784 auto baseImageSize = CheckedInt<uint64_t>(format->estimatedBytesPerPixel) * 785 (uint32_t)size.x * (uint32_t)size.y * (uint32_t)size.z; 786 if (target == LOCAL_GL_TEXTURE_CUBE_MAP) { 787 baseImageSize *= 6; 788 } 789 if (!baseImageSize.isValid() || 790 baseImageSize.value() > 791 (uint64_t)StaticPrefs::webgl_max_size_per_texture_mib() * 792 (1024 * 1024)) { 793 webgl->ErrorOutOfMemory( 794 "Texture size too large; base image mebibytes > " 795 "webgl.max-size-per-texture-mib"); 796 return false; 797 } 798 799 // GLES 3.0.4 p127: 800 // "Textures with a base internal format of DEPTH_COMPONENT or DEPTH_STENCIL 801 // are supported by texture image specification commands only if `target` is 802 // TEXTURE_2D, TEXTURE_2D_ARRAY, or TEXTURE_CUBE_MAP. Using these formats in 803 // conjunction with any other `target` will result in an INVALID_OPERATION 804 // error." 805 const bool ok = [&]() { 806 if (bool(format->d) & (target == LOCAL_GL_TEXTURE_3D)) return false; 807 808 if (format->compression) { 809 switch (format->compression->family) { 810 case webgl::CompressionFamily::ES3: 811 case webgl::CompressionFamily::S3TC: 812 if (target == LOCAL_GL_TEXTURE_3D) return false; 813 break; 814 815 case webgl::CompressionFamily::ETC1: 816 case webgl::CompressionFamily::PVRTC: 817 case webgl::CompressionFamily::RGTC: 818 if (target == LOCAL_GL_TEXTURE_3D || 819 target == LOCAL_GL_TEXTURE_2D_ARRAY) { 820 return false; 821 } 822 break; 823 default: 824 break; 825 } 826 } 827 return true; 828 }(); 829 if (!ok) { 830 webgl->ErrorInvalidOperation("Format %s cannot be used with target %s.", 831 format->name, GetEnumName(target.get())); 832 return false; 833 } 834 835 return true; 836 } 837 838 void WebGLTexture::TexStorage(TexTarget target, uint32_t levels, 839 GLenum sizedFormat, const uvec3& size) { 840 // Check levels 841 if (levels < 1) { 842 mContext->ErrorInvalidValue("`levels` must be >= 1."); 843 return; 844 } 845 846 if (!size.x || !size.y || !size.z) { 847 mContext->ErrorInvalidValue("Dimensions must be non-zero."); 848 return; 849 } 850 851 const TexImageTarget testTarget = 852 IsCubeMap() ? LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X : target.get(); 853 webgl::ImageInfo* baseImageInfo; 854 if (!ValidateTexImageSpecification(testTarget, 0, size, &baseImageInfo)) { 855 return; 856 } 857 MOZ_ALWAYS_TRUE(baseImageInfo); 858 859 auto dstUsage = mContext->mFormatUsage->GetSizedTexUsage(sizedFormat); 860 if (!dstUsage) { 861 mContext->ErrorInvalidEnumInfo("internalformat", sizedFormat); 862 return; 863 } 864 auto dstFormat = dstUsage->format; 865 866 if (!ValidateFormatAndSize(mContext, testTarget, dstFormat, size)) return; 867 868 if (dstFormat->compression) { 869 if (!ValidateCompressedTexImageRestrictions(mContext, testTarget, 0, 870 dstFormat, size)) { 871 return; 872 } 873 } 874 875 //////////////////////////////////// 876 877 const bool levelsOk = [&]() { 878 // Right-shift is only defined for bits-1, which is too large anyways. 879 const auto lastLevel = uint32_t(levels - 1); 880 if (lastLevel > 31) return false; 881 882 const auto lastLevelWidth = uint32_t(size.x) >> lastLevel; 883 const auto lastLevelHeight = uint32_t(size.y) >> lastLevel; 884 885 // If these are all zero, then some earlier level was the final 1x1(x1) 886 // level. 887 bool ok = lastLevelWidth || lastLevelHeight; 888 if (target == LOCAL_GL_TEXTURE_3D) { 889 const auto lastLevelDepth = uint32_t(size.z) >> lastLevel; 890 ok |= bool(lastLevelDepth); 891 } 892 return ok; 893 }(); 894 if (!levelsOk) { 895 mContext->ErrorInvalidOperation( 896 "Too many levels requested for the given" 897 " dimensions. (levels: %u, width: %u, height: %u," 898 " depth: %u)", 899 levels, size.x, size.y, size.z); 900 return; 901 } 902 903 //////////////////////////////////// 904 // Do the thing! 905 906 GLenum error = DoTexStorage(mContext->gl, target.get(), levels, sizedFormat, 907 size.x, size.y, size.z); 908 909 mContext->OnDataAllocCall(); 910 911 if (error == LOCAL_GL_OUT_OF_MEMORY) { 912 mContext->ErrorOutOfMemory("Ran out of memory during texture allocation."); 913 Truncate(); 914 return; 915 } 916 if (error) { 917 mContext->GenerateError(error, "Unexpected error from driver."); 918 const nsPrintfCString call( 919 "DoTexStorage(0x%04x, %i, 0x%04x, %i,%i,%i) -> 0x%04x", target.get(), 920 levels, sizedFormat, size.x, size.y, size.z, error); 921 gfxCriticalError() << "Unexpected error from driver: " 922 << call.BeginReading(); 923 return; 924 } 925 926 //////////////////////////////////// 927 // Update our specification data. 928 929 std::vector<bool> uninitializedSlices(size.z, true); 930 const webgl::ImageInfo newInfo{dstUsage, size.x, size.y, size.z, 931 std::move(uninitializedSlices)}; 932 933 { 934 const auto base_level = mBaseMipmapLevel; 935 mBaseMipmapLevel = 0; 936 937 ImageInfoAtFace(0, 0) = newInfo; 938 PopulateMipChain(levels - 1); 939 940 mBaseMipmapLevel = base_level; 941 } 942 943 mImmutable = true; 944 mImmutableLevelCount = AutoAssertCast(levels); 945 ClampLevelBaseAndMax(); 946 } 947 948 //////////////////////////////////////// 949 // Tex(Sub)Image 950 951 // TexSubImage iff `!respectFormat` 952 void WebGLTexture::TexImage(uint32_t level, GLenum respecFormat, 953 const uvec3& offset, const webgl::PackingInfo& pi, 954 const webgl::TexUnpackBlobDesc& src) { 955 const auto blob = webgl::TexUnpackBlob::Create(src); 956 if (!blob) { 957 MOZ_ASSERT(false); 958 return; 959 } 960 961 const auto imageTarget = blob->mDesc.imageTarget; 962 auto size = blob->mDesc.size; 963 964 if (!IsTarget3D(imageTarget)) { 965 size.z = 1; 966 } 967 968 //////////////////////////////////// 969 // Get dest info 970 971 const auto& fua = mContext->mFormatUsage; 972 const auto fnValidateUnpackEnums = [&]() { 973 if (!fua->AreUnpackEnumsValid(pi.format, pi.type)) { 974 mContext->ErrorInvalidEnum("Invalid unpack format/type: %s/%s", 975 EnumString(pi.format).c_str(), 976 EnumString(pi.type).c_str()); 977 return false; 978 } 979 return true; 980 }; 981 982 webgl::ImageInfo* imageInfo; 983 const webgl::FormatUsageInfo* dstUsage; 984 if (respecFormat) { 985 if (!ValidateTexImageSpecification(imageTarget, level, size, &imageInfo)) 986 return; 987 MOZ_ASSERT(imageInfo); 988 989 if (!fua->IsInternalFormatEnumValid(respecFormat)) { 990 mContext->ErrorInvalidValue("Invalid internalformat: 0x%04x", 991 respecFormat); 992 return; 993 } 994 995 dstUsage = fua->GetSizedTexUsage(respecFormat); 996 if (!dstUsage) { 997 if (respecFormat != pi.format) { 998 /* GL ES Version 3.0.4 - 3.8.3 Texture Image Specification 999 * "Specifying a combination of values for format, type, and 1000 * internalformat that is not listed as a valid combination 1001 * in tables 3.2 or 3.3 generates the error INVALID_OPERATION." 1002 */ 1003 if (!fnValidateUnpackEnums()) return; 1004 mContext->ErrorInvalidOperation( 1005 "Unsized internalFormat must match" 1006 " unpack format."); 1007 return; 1008 } 1009 1010 dstUsage = fua->GetUnsizedTexUsage(pi); 1011 } 1012 1013 if (!dstUsage) { 1014 if (!fnValidateUnpackEnums()) return; 1015 mContext->ErrorInvalidOperation( 1016 "Invalid internalformat/format/type:" 1017 " 0x%04x/0x%04x/0x%04x", 1018 respecFormat, pi.format, pi.type); 1019 return; 1020 } 1021 1022 const auto& dstFormat = dstUsage->format; 1023 if (!ValidateFormatAndSize(mContext, imageTarget, dstFormat, size)) return; 1024 1025 if (!mContext->IsWebGL2() && dstFormat->d) { 1026 if (imageTarget != LOCAL_GL_TEXTURE_2D || blob->HasData() || level != 0) { 1027 mContext->ErrorInvalidOperation( 1028 "With format %s, this function may only" 1029 " be called with target=TEXTURE_2D," 1030 " data=null, and level=0.", 1031 dstFormat->name); 1032 return; 1033 } 1034 } 1035 } else { 1036 if (!ValidateTexImageSelection(imageTarget, level, offset, size, 1037 &imageInfo)) { 1038 return; 1039 } 1040 MOZ_ASSERT(imageInfo); 1041 dstUsage = imageInfo->mFormat; 1042 1043 const auto& dstFormat = dstUsage->format; 1044 if (!mContext->IsWebGL2() && dstFormat->d) { 1045 mContext->ErrorInvalidOperation( 1046 "Function may not be called on a texture of" 1047 " format %s.", 1048 dstFormat->name); 1049 return; 1050 } 1051 } 1052 1053 //////////////////////////////////// 1054 // Get source info 1055 1056 const webgl::DriverUnpackInfo* driverUnpackInfo; 1057 if (!dstUsage->IsUnpackValid(pi, &driverUnpackInfo)) { 1058 if (!fnValidateUnpackEnums()) return; 1059 mContext->ErrorInvalidOperation( 1060 "Mismatched internalFormat and format/type:" 1061 " 0x%04x and 0x%04x/0x%04x", 1062 respecFormat, pi.format, pi.type); 1063 return; 1064 } 1065 1066 if (!blob->Validate(mContext, pi)) return; 1067 1068 //////////////////////////////////// 1069 // Do the thing! 1070 1071 Maybe<webgl::ImageInfo> newImageInfo; 1072 bool isRespec = false; 1073 if (respecFormat) { 1074 // It's tempting to do allocation first, and TexSubImage second, but this is 1075 // generally slower. 1076 newImageInfo = Some(webgl::ImageInfo{dstUsage, size.x, size.y, size.z}); 1077 if (!blob->HasData()) { 1078 newImageInfo->mUninitializedSlices.emplace(size.z, true); 1079 } 1080 1081 isRespec = (imageInfo->mWidth != newImageInfo->mWidth || 1082 imageInfo->mHeight != newImageInfo->mHeight || 1083 imageInfo->mDepth != newImageInfo->mDepth || 1084 imageInfo->mFormat != newImageInfo->mFormat); 1085 } else { 1086 if (!blob->HasData()) { 1087 mContext->ErrorInvalidValue("`source` cannot be null."); 1088 return; 1089 } 1090 if (!EnsureImageDataInitializedForUpload(this, imageTarget, level, offset, 1091 size, imageInfo)) { 1092 return; 1093 } 1094 } 1095 1096 webgl::PixelPackingState{}.AssertCurrentUnpack(*mContext->gl, 1097 mContext->IsWebGL2()); 1098 1099 blob->mDesc.unpacking.ApplyUnpack(*mContext->gl, mContext->IsWebGL2(), size); 1100 const auto revertUnpacking = MakeScopeExit([&]() { 1101 webgl::PixelPackingState{}.ApplyUnpack(*mContext->gl, mContext->IsWebGL2(), 1102 size); 1103 }); 1104 1105 const bool isSubImage = !respecFormat; 1106 GLenum glError = 0; 1107 if (!blob->TexOrSubImage(isSubImage, isRespec, this, level, driverUnpackInfo, 1108 offset.x, offset.y, offset.z, pi, &glError)) { 1109 return; 1110 } 1111 1112 if (glError == LOCAL_GL_OUT_OF_MEMORY) { 1113 mContext->ErrorOutOfMemory("Driver ran out of memory during upload."); 1114 Truncate(); 1115 return; 1116 } 1117 1118 if (glError) { 1119 const auto enumStr = EnumString(glError); 1120 const nsPrintfCString dui( 1121 "Unexpected error %s during upload. (dui: %x/%x/%x)", enumStr.c_str(), 1122 driverUnpackInfo->internalFormat, driverUnpackInfo->unpackFormat, 1123 driverUnpackInfo->unpackType); 1124 mContext->ErrorInvalidOperation("%s", dui.BeginReading()); 1125 gfxCriticalError() << mContext->FuncName() << ": " << dui.BeginReading(); 1126 return; 1127 } 1128 1129 //////////////////////////////////// 1130 // Update our specification data? 1131 1132 if (respecFormat) { 1133 mContext->OnDataAllocCall(); 1134 *imageInfo = *newImageInfo; 1135 InvalidateCaches(); 1136 } 1137 } 1138 1139 //////////////////////////////////////// 1140 // CompressedTex(Sub)Image 1141 1142 static inline bool IsSubImageBlockAligned( 1143 const webgl::CompressedFormatInfo* compression, 1144 const webgl::ImageInfo* imageInfo, GLint xOffset, GLint yOffset, 1145 uint32_t width, uint32_t height) { 1146 if (xOffset % compression->blockWidth != 0 || 1147 yOffset % compression->blockHeight != 0) { 1148 return false; 1149 } 1150 1151 if (width % compression->blockWidth != 0 && 1152 xOffset + width != imageInfo->mWidth) 1153 return false; 1154 1155 if (height % compression->blockHeight != 0 && 1156 yOffset + height != imageInfo->mHeight) 1157 return false; 1158 1159 return true; 1160 } 1161 1162 // CompressedTexSubImage iff `sub` 1163 void WebGLTexture::CompressedTexImage(bool sub, GLenum imageTarget, 1164 uint32_t level, GLenum formatEnum, 1165 const uvec3& offset, const uvec3& size, 1166 const Range<const uint8_t>& src, 1167 const uint32_t pboImageSize, 1168 const Maybe<uint64_t>& pboOffset) { 1169 auto imageSize = pboImageSize; 1170 if (pboOffset) { 1171 const auto& buffer = 1172 mContext->ValidateBufferSelection(LOCAL_GL_PIXEL_UNPACK_BUFFER); 1173 if (!buffer) return; 1174 auto availBytes = buffer->ByteLength(); 1175 if (*pboOffset > availBytes) { 1176 mContext->GenerateError( 1177 LOCAL_GL_INVALID_OPERATION, 1178 "`offset` (%llu) must be <= PIXEL_UNPACK_BUFFER size (%llu).", 1179 *pboOffset, availBytes); 1180 return; 1181 } 1182 availBytes -= *pboOffset; 1183 if (availBytes < pboImageSize) { 1184 mContext->GenerateError( 1185 LOCAL_GL_INVALID_OPERATION, 1186 "PIXEL_UNPACK_BUFFER size minus `offset` (%llu) too small for" 1187 " `pboImageSize` (%u).", 1188 availBytes, pboImageSize); 1189 return; 1190 } 1191 } else { 1192 if (mContext->mBoundPixelUnpackBuffer) { 1193 mContext->GenerateError(LOCAL_GL_INVALID_OPERATION, 1194 "PIXEL_UNPACK_BUFFER is non-null."); 1195 return; 1196 } 1197 imageSize = src.length(); 1198 } 1199 1200 // - 1201 1202 const auto usage = mContext->mFormatUsage->GetSizedTexUsage(formatEnum); 1203 if (!usage || !usage->format->compression) { 1204 mContext->ErrorInvalidEnumArg("format", formatEnum); 1205 return; 1206 } 1207 1208 webgl::ImageInfo* imageInfo; 1209 if (!sub) { 1210 if (!ValidateTexImageSpecification(imageTarget, level, size, &imageInfo)) { 1211 return; 1212 } 1213 MOZ_ASSERT(imageInfo); 1214 1215 if (!ValidateFormatAndSize(mContext, imageTarget, usage->format, size)) 1216 return; 1217 if (!ValidateCompressedTexImageRestrictions(mContext, imageTarget, level, 1218 usage->format, size)) { 1219 return; 1220 } 1221 } else { 1222 if (!ValidateTexImageSelection(imageTarget, level, offset, size, 1223 &imageInfo)) 1224 return; 1225 MOZ_ASSERT(imageInfo); 1226 1227 const auto dstUsage = imageInfo->mFormat; 1228 if (usage != dstUsage) { 1229 mContext->ErrorInvalidOperation( 1230 "`format` must match the format of the" 1231 " existing texture image."); 1232 return; 1233 } 1234 1235 const auto& format = usage->format; 1236 switch (format->compression->family) { 1237 // Forbidden: 1238 case webgl::CompressionFamily::ETC1: 1239 mContext->ErrorInvalidOperation( 1240 "Format does not allow sub-image" 1241 " updates."); 1242 return; 1243 1244 // Block-aligned: 1245 case webgl::CompressionFamily::ES3: // Yes, the ES3 formats don't match 1246 // the ES3 1247 case webgl::CompressionFamily::S3TC: // default behavior. 1248 case webgl::CompressionFamily::BPTC: 1249 case webgl::CompressionFamily::RGTC: 1250 if (!IsSubImageBlockAligned(format->compression, imageInfo, offset.x, 1251 offset.y, size.x, size.y)) { 1252 mContext->ErrorInvalidOperation( 1253 "Format requires block-aligned sub-image" 1254 " updates."); 1255 return; 1256 } 1257 break; 1258 1259 // Full-only: (The ES3 default) 1260 case webgl::CompressionFamily::ASTC: 1261 case webgl::CompressionFamily::PVRTC: 1262 if (offset.x || offset.y || size.x != imageInfo->mWidth || 1263 size.y != imageInfo->mHeight) { 1264 mContext->ErrorInvalidOperation( 1265 "Format does not allow partial sub-image" 1266 " updates."); 1267 return; 1268 } 1269 break; 1270 } 1271 } 1272 1273 switch (usage->format->compression->family) { 1274 case webgl::CompressionFamily::BPTC: 1275 case webgl::CompressionFamily::RGTC: 1276 if (level == 0) { 1277 if (size.x % 4 != 0 || size.y % 4 != 0) { 1278 mContext->ErrorInvalidOperation( 1279 "For level == 0, width and height must be multiples of 4."); 1280 return; 1281 } 1282 } 1283 break; 1284 1285 default: 1286 break; 1287 } 1288 1289 if (!ValidateCompressedTexUnpack(mContext, size, usage->format, imageSize)) 1290 return; 1291 1292 //////////////////////////////////// 1293 // Do the thing! 1294 1295 if (sub) { 1296 if (!EnsureImageDataInitializedForUpload(this, imageTarget, level, offset, 1297 size, imageInfo)) { 1298 return; 1299 } 1300 } 1301 1302 const ScopedLazyBind bindPBO(mContext->gl, LOCAL_GL_PIXEL_UNPACK_BUFFER, 1303 mContext->mBoundPixelUnpackBuffer); 1304 GLenum error; 1305 const void* ptr; 1306 if (pboOffset) { 1307 ptr = reinterpret_cast<const void*>(*pboOffset); 1308 } else { 1309 ptr = reinterpret_cast<const void*>(src.begin().get()); 1310 } 1311 1312 if (!sub) { 1313 error = DoCompressedTexImage(mContext->gl, imageTarget, level, formatEnum, 1314 size.x, size.y, size.z, imageSize, ptr); 1315 } else { 1316 error = DoCompressedTexSubImage(mContext->gl, imageTarget, level, offset.x, 1317 offset.y, offset.z, size.x, size.y, size.z, 1318 formatEnum, imageSize, ptr); 1319 } 1320 if (error == LOCAL_GL_OUT_OF_MEMORY) { 1321 mContext->ErrorOutOfMemory("Ran out of memory during upload."); 1322 Truncate(); 1323 return; 1324 } 1325 if (error) { 1326 mContext->GenerateError(error, "Unexpected error from driver."); 1327 nsCString call; 1328 if (!sub) { 1329 call = nsPrintfCString( 1330 "DoCompressedTexImage(0x%04x, %u, 0x%04x, %u,%u,%u, %u, %p)", 1331 imageTarget, level, formatEnum, size.x, size.y, size.z, imageSize, 1332 ptr); 1333 } else { 1334 call = nsPrintfCString( 1335 "DoCompressedTexSubImage(0x%04x, %u, %u,%u,%u, %u,%u,%u, 0x%04x, %u, " 1336 "%p)", 1337 imageTarget, level, offset.x, offset.y, offset.z, size.x, size.y, 1338 size.z, formatEnum, imageSize, ptr); 1339 } 1340 gfxCriticalError() << "Unexpected error " << gfx::hexa(error) 1341 << " from driver: " << call.BeginReading(); 1342 return; 1343 } 1344 1345 //////////////////////////////////// 1346 // Update our specification data? 1347 1348 if (!sub) { 1349 constexpr auto uninitializedSlices = std::nullopt; 1350 const webgl::ImageInfo newImageInfo{usage, size.x, size.y, size.z, 1351 uninitializedSlices}; 1352 *imageInfo = newImageInfo; 1353 InvalidateCaches(); 1354 } 1355 } 1356 1357 //////////////////////////////////////// 1358 // CopyTex(Sub)Image 1359 1360 static bool ValidateCopyTexImageFormats(WebGLContext* webgl, 1361 const webgl::FormatInfo* srcFormat, 1362 const webgl::FormatInfo* dstFormat) { 1363 MOZ_ASSERT(!srcFormat->compression); 1364 if (dstFormat->compression) { 1365 webgl->ErrorInvalidEnum( 1366 "Specified destination must not have a compressed" 1367 " format."); 1368 return false; 1369 } 1370 1371 if (dstFormat->effectiveFormat == webgl::EffectiveFormat::RGB9_E5) { 1372 webgl->ErrorInvalidOperation( 1373 "RGB9_E5 is an invalid destination for" 1374 " CopyTex(Sub)Image. (GLES 3.0.4 p145)"); 1375 return false; 1376 } 1377 1378 if (!DoChannelsMatchForCopyTexImage(srcFormat, dstFormat)) { 1379 webgl->ErrorInvalidOperation( 1380 "Destination channels must be compatible with" 1381 " source channels. (GLES 3.0.4 p140 Table 3.16)"); 1382 return false; 1383 } 1384 1385 return true; 1386 } 1387 1388 //////////////////////////////////////////////////////////////////////////////// 1389 1390 class ScopedCopyTexImageSource { 1391 WebGLContext* const mWebGL; 1392 GLuint mRB; 1393 GLuint mFB; 1394 1395 public: 1396 ScopedCopyTexImageSource(WebGLContext* webgl, uint32_t srcWidth, 1397 uint32_t srcHeight, 1398 const webgl::FormatInfo* srcFormat, 1399 const webgl::FormatUsageInfo* dstUsage); 1400 ~ScopedCopyTexImageSource(); 1401 }; 1402 1403 ScopedCopyTexImageSource::ScopedCopyTexImageSource( 1404 WebGLContext* webgl, uint32_t srcWidth, uint32_t srcHeight, 1405 const webgl::FormatInfo* srcFormat, const webgl::FormatUsageInfo* dstUsage) 1406 : mWebGL(webgl), mRB(0), mFB(0) { 1407 switch (dstUsage->format->unsizedFormat) { 1408 case webgl::UnsizedFormat::L: 1409 case webgl::UnsizedFormat::A: 1410 case webgl::UnsizedFormat::LA: 1411 webgl->GenerateWarning( 1412 "Copying to a LUMINANCE, ALPHA, or LUMINANCE_ALPHA" 1413 " is deprecated, and has severely reduced performance" 1414 " on some platforms."); 1415 break; 1416 1417 default: 1418 MOZ_ASSERT(!dstUsage->textureSwizzleRGBA); 1419 return; 1420 } 1421 1422 if (!dstUsage->textureSwizzleRGBA) return; 1423 1424 gl::GLContext* gl = webgl->gl; 1425 1426 GLenum sizedFormat; 1427 1428 switch (srcFormat->componentType) { 1429 case webgl::ComponentType::NormUInt: 1430 sizedFormat = LOCAL_GL_RGBA8; 1431 break; 1432 1433 case webgl::ComponentType::Float: 1434 if (webgl->IsExtensionEnabled( 1435 WebGLExtensionID::WEBGL_color_buffer_float)) { 1436 sizedFormat = LOCAL_GL_RGBA32F; 1437 webgl->WarnIfImplicit(WebGLExtensionID::WEBGL_color_buffer_float); 1438 break; 1439 } 1440 1441 if (webgl->IsExtensionEnabled( 1442 WebGLExtensionID::EXT_color_buffer_half_float)) { 1443 sizedFormat = LOCAL_GL_RGBA16F; 1444 webgl->WarnIfImplicit(WebGLExtensionID::EXT_color_buffer_half_float); 1445 break; 1446 } 1447 MOZ_CRASH("GFX: Should be able to request CopyTexImage from Float."); 1448 1449 default: 1450 MOZ_CRASH("GFX: Should be able to request CopyTexImage from this type."); 1451 } 1452 1453 gl::ScopedTexture scopedTex(gl); 1454 gl::ScopedBindTexture scopedBindTex(gl, scopedTex.Texture(), 1455 LOCAL_GL_TEXTURE_2D); 1456 1457 gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, 1458 LOCAL_GL_NEAREST); 1459 gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, 1460 LOCAL_GL_NEAREST); 1461 1462 GLint blitSwizzle[4] = {LOCAL_GL_ZERO}; 1463 switch (dstUsage->format->unsizedFormat) { 1464 case webgl::UnsizedFormat::L: 1465 blitSwizzle[0] = LOCAL_GL_RED; 1466 break; 1467 1468 case webgl::UnsizedFormat::A: 1469 blitSwizzle[0] = LOCAL_GL_ALPHA; 1470 break; 1471 1472 case webgl::UnsizedFormat::LA: 1473 blitSwizzle[0] = LOCAL_GL_RED; 1474 blitSwizzle[1] = LOCAL_GL_ALPHA; 1475 break; 1476 1477 default: 1478 MOZ_CRASH("GFX: Unhandled unsizedFormat."); 1479 } 1480 1481 gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_R, 1482 blitSwizzle[0]); 1483 gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_G, 1484 blitSwizzle[1]); 1485 gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_B, 1486 blitSwizzle[2]); 1487 gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_A, 1488 blitSwizzle[3]); 1489 1490 gl->fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0, sizedFormat, 0, 0, srcWidth, 1491 srcHeight, 0); 1492 1493 // Now create the swizzled FB we'll be exposing. 1494 1495 GLuint rgbaRB = 0; 1496 GLuint rgbaFB = 0; 1497 { 1498 gl->fGenRenderbuffers(1, &rgbaRB); 1499 gl::ScopedBindRenderbuffer scopedRB(gl, rgbaRB); 1500 gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, sizedFormat, srcWidth, 1501 srcHeight); 1502 1503 gl->fGenFramebuffers(1, &rgbaFB); 1504 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, rgbaFB); 1505 gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, 1506 LOCAL_GL_COLOR_ATTACHMENT0, 1507 LOCAL_GL_RENDERBUFFER, rgbaRB); 1508 1509 const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); 1510 if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) { 1511 MOZ_CRASH("GFX: Temp framebuffer is not complete."); 1512 } 1513 } 1514 1515 // Draw-blit rgbaTex into rgbaFB. 1516 const gfx::IntSize srcSize(srcWidth, srcHeight); 1517 { 1518 const gl::ScopedBindFramebuffer bindFB(gl, rgbaFB); 1519 gl->BlitHelper()->DrawBlitTextureToFramebuffer(scopedTex.Texture(), srcSize, 1520 srcSize); 1521 } 1522 1523 // Leave RB and FB alive, and FB bound. 1524 mRB = rgbaRB; 1525 mFB = rgbaFB; 1526 } 1527 1528 template <typename T> 1529 static inline GLenum ToGLHandle(const T& obj) { 1530 return (obj ? obj->mGLName : 0); 1531 } 1532 1533 ScopedCopyTexImageSource::~ScopedCopyTexImageSource() { 1534 if (!mFB) { 1535 MOZ_ASSERT(!mRB); 1536 return; 1537 } 1538 MOZ_ASSERT(mRB); 1539 1540 gl::GLContext* gl = mWebGL->gl; 1541 1542 // If we're swizzling, it's because we're on a GL core (3.2+) profile, which 1543 // has split framebuffer support. 1544 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, 1545 ToGLHandle(mWebGL->mBoundDrawFramebuffer)); 1546 gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, 1547 ToGLHandle(mWebGL->mBoundReadFramebuffer)); 1548 1549 gl->fDeleteFramebuffers(1, &mFB); 1550 gl->fDeleteRenderbuffers(1, &mRB); 1551 } 1552 1553 //////////////////////////////////////////////////////////////////////////////// 1554 1555 static bool GetUnsizedFormatForCopy(GLenum internalFormat, 1556 webgl::UnsizedFormat* const out) { 1557 switch (internalFormat) { 1558 case LOCAL_GL_RED: 1559 *out = webgl::UnsizedFormat::R; 1560 break; 1561 case LOCAL_GL_RG: 1562 *out = webgl::UnsizedFormat::RG; 1563 break; 1564 case LOCAL_GL_RGB: 1565 *out = webgl::UnsizedFormat::RGB; 1566 break; 1567 case LOCAL_GL_RGBA: 1568 *out = webgl::UnsizedFormat::RGBA; 1569 break; 1570 case LOCAL_GL_LUMINANCE: 1571 *out = webgl::UnsizedFormat::L; 1572 break; 1573 case LOCAL_GL_ALPHA: 1574 *out = webgl::UnsizedFormat::A; 1575 break; 1576 case LOCAL_GL_LUMINANCE_ALPHA: 1577 *out = webgl::UnsizedFormat::LA; 1578 break; 1579 1580 default: 1581 return false; 1582 } 1583 1584 return true; 1585 } 1586 1587 static const webgl::FormatUsageInfo* ValidateCopyDestUsage( 1588 WebGLContext* webgl, const webgl::FormatInfo* srcFormat, 1589 GLenum internalFormat) { 1590 const auto& fua = webgl->mFormatUsage; 1591 1592 switch (internalFormat) { 1593 case LOCAL_GL_R8_SNORM: 1594 case LOCAL_GL_RG8_SNORM: 1595 case LOCAL_GL_RGB8_SNORM: 1596 case LOCAL_GL_RGBA8_SNORM: 1597 webgl->ErrorInvalidEnum("SNORM formats are invalid for CopyTexImage."); 1598 return nullptr; 1599 } 1600 1601 auto dstUsage = fua->GetSizedTexUsage(internalFormat); 1602 if (!dstUsage) { 1603 // Ok, maybe it's unsized. 1604 webgl::UnsizedFormat unsizedFormat; 1605 if (!GetUnsizedFormatForCopy(internalFormat, &unsizedFormat)) { 1606 webgl->ErrorInvalidEnumInfo("internalFormat", internalFormat); 1607 return nullptr; 1608 } 1609 1610 const auto dstFormat = srcFormat->GetCopyDecayFormat(unsizedFormat); 1611 if (dstFormat) { 1612 dstUsage = fua->GetUsage(dstFormat->effectiveFormat); 1613 } 1614 if (!dstUsage) { 1615 webgl->ErrorInvalidOperation( 1616 "0x%04x is not a valid unsized format for" 1617 " source format %s.", 1618 internalFormat, srcFormat->name); 1619 return nullptr; 1620 } 1621 1622 return dstUsage; 1623 } 1624 // Alright, it's sized. 1625 1626 const auto dstFormat = dstUsage->format; 1627 1628 if (dstFormat->componentType != srcFormat->componentType) { 1629 webgl->ErrorInvalidOperation( 1630 "For sized internalFormats, source and dest" 1631 " component types must match. (source: %s, dest:" 1632 " %s)", 1633 srcFormat->name, dstFormat->name); 1634 return nullptr; 1635 } 1636 1637 bool componentSizesMatch = true; 1638 if (dstFormat->r) { 1639 componentSizesMatch &= (dstFormat->r == srcFormat->r); 1640 } 1641 if (dstFormat->g) { 1642 componentSizesMatch &= (dstFormat->g == srcFormat->g); 1643 } 1644 if (dstFormat->b) { 1645 componentSizesMatch &= (dstFormat->b == srcFormat->b); 1646 } 1647 if (dstFormat->a) { 1648 componentSizesMatch &= (dstFormat->a == srcFormat->a); 1649 } 1650 1651 if (!componentSizesMatch) { 1652 webgl->ErrorInvalidOperation( 1653 "For sized internalFormats, source and dest" 1654 " component sizes must match exactly. (source: %s," 1655 " dest: %s)", 1656 srcFormat->name, dstFormat->name); 1657 return nullptr; 1658 } 1659 1660 return dstUsage; 1661 } 1662 1663 static bool ValidateCopyTexImageForFeedback(const WebGLContext& webgl, 1664 const WebGLTexture& tex, 1665 const uint32_t mipLevel, 1666 const uint32_t zLayer) { 1667 const auto& fb = webgl.BoundReadFb(); 1668 if (fb) { 1669 MOZ_ASSERT(fb->ColorReadBuffer()); 1670 const auto& attach = *fb->ColorReadBuffer(); 1671 MOZ_ASSERT(attach.ZLayerCount() == 1672 1); // Multiview invalid for copyTexImage. 1673 1674 if (attach.Texture() == &tex && attach.Layer() == zLayer && 1675 attach.MipLevel() == mipLevel) { 1676 // Note that the TexImageTargets *don't* have to match for this to be 1677 // undefined per GLES 3.0.4 p211, thus an INVALID_OP in WebGL. 1678 webgl.ErrorInvalidOperation( 1679 "Feedback loop detected, as this texture" 1680 " is already attached to READ_FRAMEBUFFER's" 1681 " READ_BUFFER-selected COLOR_ATTACHMENT%u.", 1682 attach.mAttachmentPoint); 1683 return false; 1684 } 1685 } 1686 return true; 1687 } 1688 1689 static bool DoCopyTexOrSubImage(WebGLContext* webgl, bool isSubImage, 1690 bool needsInit, WebGLTexture* const tex, 1691 const TexImageTarget target, GLint level, 1692 GLint xWithinSrc, GLint yWithinSrc, 1693 uint32_t srcTotalWidth, uint32_t srcTotalHeight, 1694 const webgl::FormatUsageInfo* srcUsage, 1695 GLint xOffset, GLint yOffset, GLint zOffset, 1696 uint32_t dstWidth, uint32_t dstHeight, 1697 const webgl::FormatUsageInfo* dstUsage) { 1698 const auto& gl = webgl->gl; 1699 1700 //// 1701 1702 int32_t readX, readY; 1703 int32_t writeX, writeY; 1704 int32_t rwWidth, rwHeight; 1705 if (!Intersect(srcTotalWidth, xWithinSrc, dstWidth, &readX, &writeX, 1706 &rwWidth) || 1707 !Intersect(srcTotalHeight, yWithinSrc, dstHeight, &readY, &writeY, 1708 &rwHeight)) { 1709 webgl->ErrorOutOfMemory("Bad subrect selection."); 1710 return false; 1711 } 1712 1713 writeX += xOffset; 1714 writeY += yOffset; 1715 1716 //// 1717 1718 GLenum error = 0; 1719 nsCString errorText; 1720 do { 1721 const auto& idealUnpack = dstUsage->idealUnpack; 1722 const auto& pi = idealUnpack->ToPacking(); 1723 1724 UniqueBuffer zeros; 1725 const bool fullOverwrite = 1726 (uint32_t(rwWidth) == dstWidth && uint32_t(rwHeight) == dstHeight); 1727 if (needsInit && !fullOverwrite) { 1728 CheckedInt<size_t> byteCount = BytesPerPixel(pi); 1729 byteCount *= dstWidth; 1730 byteCount *= dstHeight; 1731 1732 if (byteCount.isValid()) { 1733 zeros = UniqueBuffer::Take(calloc(1u, byteCount.value())); 1734 } 1735 1736 if (!zeros.get()) { 1737 webgl->ErrorOutOfMemory("Ran out of memory allocating zeros."); 1738 return false; 1739 } 1740 } 1741 1742 if (!isSubImage || zeros) { 1743 webgl::PixelPackingState{}.AssertCurrentUnpack(*gl, webgl->IsWebGL2()); 1744 1745 gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); 1746 const auto revert = MakeScopeExit( 1747 [&]() { gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); }); 1748 if (!isSubImage) { 1749 error = DoTexImage(gl, target, level, idealUnpack, dstWidth, dstHeight, 1750 1, nullptr); 1751 if (error) { 1752 errorText = nsPrintfCString( 1753 "DoTexImage(0x%04x, %i, {0x%04x, 0x%04x, 0x%04x}, %u,%u,1) -> " 1754 "0x%04x", 1755 target.get(), level, idealUnpack->internalFormat, 1756 idealUnpack->unpackFormat, idealUnpack->unpackType, dstWidth, 1757 dstHeight, error); 1758 break; 1759 } 1760 } 1761 if (zeros) { 1762 error = DoTexSubImage(gl, target, level, xOffset, yOffset, zOffset, 1763 dstWidth, dstHeight, 1, pi, zeros.get()); 1764 if (error) { 1765 errorText = nsPrintfCString( 1766 "DoTexSubImage(0x%04x, %i, %i,%i,%i, %u,%u,1, {0x%04x, 0x%04x}) " 1767 "-> " 1768 "0x%04x", 1769 target.get(), level, xOffset, yOffset, zOffset, dstWidth, 1770 dstHeight, idealUnpack->unpackFormat, idealUnpack->unpackType, 1771 error); 1772 break; 1773 } 1774 } 1775 } 1776 1777 if (!rwWidth || !rwHeight) { 1778 // There aren't any pixels to copy, so we're 'done'. 1779 return true; 1780 } 1781 1782 const auto& srcFormat = srcUsage->format; 1783 ScopedCopyTexImageSource maybeSwizzle(webgl, srcTotalWidth, srcTotalHeight, 1784 srcFormat, dstUsage); 1785 1786 error = DoCopyTexSubImage(gl, target, level, writeX, writeY, zOffset, readX, 1787 readY, rwWidth, rwHeight); 1788 if (error) { 1789 errorText = nsPrintfCString( 1790 "DoCopyTexSubImage(0x%04x, %i, %i,%i,%i, %i,%i, %u,%u) -> 0x%04x", 1791 target.get(), level, writeX, writeY, zOffset, readX, readY, rwWidth, 1792 rwHeight, error); 1793 break; 1794 } 1795 1796 return true; 1797 } while (false); 1798 1799 if (error == LOCAL_GL_OUT_OF_MEMORY) { 1800 webgl->ErrorOutOfMemory("Ran out of memory during texture copy."); 1801 tex->Truncate(); 1802 return false; 1803 } 1804 1805 if (gl->IsANGLE() && error == LOCAL_GL_INVALID_OPERATION) { 1806 webgl->ErrorImplementationBug( 1807 "ANGLE is particular about CopyTexSubImage" 1808 " formats matching exactly."); 1809 return false; 1810 } 1811 1812 webgl->GenerateError(error, "Unexpected error from driver."); 1813 gfxCriticalError() << "Unexpected error from driver: " 1814 << errorText.BeginReading(); 1815 return false; 1816 } 1817 1818 // CopyTexSubImage if `!respecFormat` 1819 void WebGLTexture::CopyTexImage(GLenum imageTarget, uint32_t level, 1820 GLenum respecFormat, const uvec3& dstOffset, 1821 const ivec2& srcOffset, const uvec2& size2) { 1822 //////////////////////////////////// 1823 // Get source info 1824 1825 const webgl::FormatUsageInfo* srcUsage; 1826 uint32_t srcTotalWidth; 1827 uint32_t srcTotalHeight; 1828 if (!mContext->BindCurFBForColorRead(&srcUsage, &srcTotalWidth, 1829 &srcTotalHeight)) { 1830 return; 1831 } 1832 const auto& srcFormat = srcUsage->format; 1833 1834 if (!ValidateCopyTexImageForFeedback(*mContext, *this, level, dstOffset.z)) 1835 return; 1836 1837 const auto size = uvec3{size2.x, size2.y, 1}; 1838 1839 //////////////////////////////////// 1840 // Get dest info 1841 1842 webgl::ImageInfo* imageInfo; 1843 const webgl::FormatUsageInfo* dstUsage; 1844 if (respecFormat) { 1845 if (!ValidateTexImageSpecification(imageTarget, level, size, &imageInfo)) 1846 return; 1847 MOZ_ASSERT(imageInfo); 1848 1849 dstUsage = ValidateCopyDestUsage(mContext, srcFormat, respecFormat); 1850 if (!dstUsage) return; 1851 1852 if (!ValidateFormatAndSize(mContext, imageTarget, dstUsage->format, size)) 1853 return; 1854 } else { 1855 if (!ValidateTexImageSelection(imageTarget, level, dstOffset, size, 1856 &imageInfo)) { 1857 return; 1858 } 1859 MOZ_ASSERT(imageInfo); 1860 1861 dstUsage = imageInfo->mFormat; 1862 MOZ_ASSERT(dstUsage); 1863 } 1864 1865 const auto& dstFormat = dstUsage->format; 1866 if (!mContext->IsWebGL2() && dstFormat->d) { 1867 mContext->ErrorInvalidOperation( 1868 "Function may not be called with format %s.", dstFormat->name); 1869 return; 1870 } 1871 1872 //////////////////////////////////// 1873 // Check that source and dest info are compatible 1874 1875 if (!ValidateCopyTexImageFormats(mContext, srcFormat, dstFormat)) return; 1876 1877 //////////////////////////////////// 1878 // Do the thing! 1879 1880 const bool isSubImage = !respecFormat; 1881 bool expectsInit = true; 1882 if (isSubImage) { 1883 if (!EnsureImageDataInitializedForUpload(this, imageTarget, level, 1884 dstOffset, size, imageInfo, 1885 &expectsInit)) { 1886 return; 1887 } 1888 } 1889 1890 if (!DoCopyTexOrSubImage(mContext, isSubImage, expectsInit, this, imageTarget, 1891 level, srcOffset.x, srcOffset.y, srcTotalWidth, 1892 srcTotalHeight, srcUsage, dstOffset.x, dstOffset.y, 1893 dstOffset.z, size.x, size.y, dstUsage)) { 1894 Truncate(); 1895 return; 1896 } 1897 1898 mContext->OnDataAllocCall(); 1899 1900 //////////////////////////////////// 1901 // Update our specification data? 1902 1903 if (respecFormat) { 1904 constexpr auto uninitializedSlices = std::nullopt; 1905 const webgl::ImageInfo newImageInfo{dstUsage, size.x, size.y, size.z, 1906 uninitializedSlices}; 1907 *imageInfo = newImageInfo; 1908 InvalidateCaches(); 1909 } 1910 } 1911 1912 } // namespace mozilla