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/. */
6 #include "WebGLTexture.h"
11 #include "CanvasUtils.h"
12 #include "ClientWebGLContext.h"
13 #include "GLBlitHelper.h"
14 #include "GLContext.h"
15 #include "mozilla/Casting.h"
16 #include "mozilla/gfx/2D.h"
17 #include "mozilla/gfx/Logging.h"
18 #include "mozilla/dom/HTMLCanvasElement.h"
19 #include "mozilla/dom/HTMLVideoElement.h"
20 #include "mozilla/dom/ImageBitmap.h"
21 #include "mozilla/dom/ImageData.h"
22 #include "mozilla/MathAlgorithms.h"
23 #include "mozilla/Scoped.h"
24 #include "mozilla/ScopeExit.h"
25 #include "mozilla/StaticPrefs_webgl.h"
26 #include "mozilla/Unused.h"
27 #include "nsLayoutUtils.h"
28 #include "ScopedGLHelpers.h"
29 #include "TexUnpackBlob.h"
30 #include "WebGLBuffer.h"
31 #include "WebGLContext.h"
32 #include "WebGLContextUtils.h"
33 #include "WebGLFramebuffer.h"
34 #include "WebGLTexelConversions.h"
39 Maybe
<TexUnpackBlobDesc
> FromImageBitmap(const GLenum target
, uvec3 size
,
40 const dom::ImageBitmap
& imageBitmap
,
41 ErrorResult
* const out_rv
) {
42 if (imageBitmap
.IsWriteOnly()) {
43 out_rv
->Throw(NS_ERROR_DOM_SECURITY_ERR
);
47 const auto cloneData
= imageBitmap
.ToCloneData();
52 const RefPtr
<gfx::DataSourceSurface
> surf
= cloneData
->mSurface
;
53 const auto imageSize
= *uvec2::FromSize(surf
->GetSize());
63 // WhatWG "HTML Living Standard" (30 October 2015):
64 // "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned as
65 // non-premultiplied alpha values."
66 return Some(TexUnpackBlobDesc
{target
,
68 cloneData
->mAlphaType
,
79 TexUnpackBlobDesc
FromImageData(const GLenum target
, uvec3 size
,
80 const dom::ImageData
& imageData
,
81 dom::Uint8ClampedArray
* const scopedArr
) {
82 MOZ_RELEASE_ASSERT(scopedArr
->Init(imageData
.GetDataObject()));
83 scopedArr
->ComputeState();
84 const size_t dataSize
= scopedArr
->Length();
85 const auto data
= reinterpret_cast<uint8_t*>(scopedArr
->Data());
87 const gfx::IntSize
imageISize(imageData
.Width(), imageData
.Height());
88 const auto imageUSize
= *uvec2::FromSize(imageISize
);
89 const size_t stride
= imageUSize
.x
* 4;
90 const gfx::SurfaceFormat surfFormat
= gfx::SurfaceFormat::R8G8B8A8
;
91 MOZ_ALWAYS_TRUE(dataSize
== stride
* imageUSize
.y
);
93 const RefPtr
<gfx::DataSourceSurface
> surf
=
94 gfx::Factory::CreateWrappingDataSourceSurface(data
, stride
, imageISize
,
101 size
.x
= imageUSize
.x
;
105 size
.y
= imageUSize
.y
;
110 // WhatWG "HTML Living Standard" (30 October 2015):
111 // "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned as
112 // non-premultiplied alpha values."
113 return {target
, size
, gfxAlphaType::NonPremult
, {}, {}, imageUSize
, nullptr,
117 static layers::SurfaceDescriptor
Flatten(const layers::SurfaceDescriptor
& sd
) {
118 const auto sdType
= sd
.type();
119 if (sdType
!= layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo
) {
122 const auto& sdv
= sd
.get_SurfaceDescriptorGPUVideo();
123 const auto& sdvType
= sdv
.type();
125 layers::SurfaceDescriptorGPUVideo::TSurfaceDescriptorRemoteDecoder
) {
129 const auto& sdrd
= sdv
.get_SurfaceDescriptorRemoteDecoder();
130 const auto& subdesc
= sdrd
.subdesc();
131 const auto& subdescType
= subdesc
.type();
132 switch (subdescType
) {
133 case layers::RemoteDecoderVideoSubDescriptor::T__None
:
134 case layers::RemoteDecoderVideoSubDescriptor::Tnull_t
:
137 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorD3D10
:
138 return subdesc
.get_SurfaceDescriptorD3D10();
139 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorDXGIYCbCr
:
140 return subdesc
.get_SurfaceDescriptorDXGIYCbCr();
141 case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorDMABuf
:
142 return subdesc
.get_SurfaceDescriptorDMABuf();
143 case layers::RemoteDecoderVideoSubDescriptor::
144 TSurfaceDescriptorMacIOSurface
:
145 return subdesc
.get_SurfaceDescriptorMacIOSurface();
147 MOZ_CRASH("unreachable");
150 Maybe
<webgl::TexUnpackBlobDesc
> FromDomElem(const ClientWebGLContext
& webgl
,
151 const GLenum target
, uvec3 size
,
152 const dom::Element
& elem
,
153 ErrorResult
* const out_error
) {
154 const auto& canvas
= *webgl
.GetCanvas();
156 if (elem
.IsHTMLElement(nsGkAtoms::canvas
)) {
157 const dom::HTMLCanvasElement
* srcCanvas
=
158 static_cast<const dom::HTMLCanvasElement
*>(&elem
);
159 if (srcCanvas
->IsWriteOnly()) {
160 out_error
->Throw(NS_ERROR_DOM_SECURITY_ERR
);
165 // The canvas spec says that drawImage should draw the first frame of
166 // animated images. The webgl spec doesn't mention the issue, so we do the
167 // same as drawImage.
168 uint32_t flags
= nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE
|
169 nsLayoutUtils::SFE_USE_ELEMENT_SIZE_IF_VECTOR
|
170 nsLayoutUtils::SFE_EXACT_SIZE_SURFACE
|
171 nsLayoutUtils::SFE_ALLOW_NON_PREMULT
;
172 const auto& unpacking
= webgl
.State().mPixelUnpackState
;
173 if (unpacking
.mColorspaceConversion
== LOCAL_GL_NONE
) {
174 flags
|= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION
;
177 RefPtr
<gfx::DrawTarget
> idealDrawTarget
= nullptr; // Don't care for now.
178 auto sfer
= nsLayoutUtils::SurfaceFromElement(
179 const_cast<dom::Element
*>(&elem
), flags
, idealDrawTarget
);
185 const auto& layersImage
= sfer
.mLayersImage
;
186 Maybe
<layers::SurfaceDescriptor
> sd
;
188 elemSize
= *uvec2::FromSize(layersImage
->GetSize());
190 sd
= layersImage
->GetDesc();
192 sd
= Some(Flatten(*sd
));
195 NS_WARNING("No SurfaceDescriptor for layers::Image!");
199 RefPtr
<gfx::DataSourceSurface
> dataSurf
;
200 if (!sd
&& sfer
.GetSourceSurface()) {
201 const auto surf
= sfer
.GetSourceSurface();
202 elemSize
= *uvec2::FromSize(surf
->GetSize());
204 // WARNING: OSX can lose our MakeCurrent here.
205 dataSurf
= surf
->GetDataSurface();
220 if (!sd
&& !dataSurf
) {
221 webgl
.EnqueueWarning("Resource has no data (yet?). Uploading zeros.");
222 return Some(TexUnpackBlobDesc
{target
, size
, gfxAlphaType::NonPremult
});
227 // While it's counter-intuitive, the shape of the SFEResult API means that we
228 // should try to pull out a surface first, and then, if we do pull out a
229 // surface, check CORS/write-only/etc..
231 if (!sfer
.mCORSUsed
) {
232 auto& srcPrincipal
= sfer
.mPrincipal
;
233 nsIPrincipal
* dstPrincipal
= canvas
.NodePrincipal();
235 if (!dstPrincipal
->Subsumes(srcPrincipal
)) {
236 webgl
.EnqueueWarning("Cross-origin elements require CORS.");
237 out_error
->Throw(NS_ERROR_DOM_SECURITY_ERR
);
242 if (sfer
.mIsWriteOnly
) {
243 // mIsWriteOnly defaults to true, and so will be true even if SFE merely
244 // failed. Thus we must test mIsWriteOnly after successfully retrieving an
245 // Image or SourceSurface.
246 webgl
.EnqueueWarning("Element is write-only, thus cannot be uploaded.");
247 out_error
->Throw(NS_ERROR_DOM_SECURITY_ERR
);
254 return Some(TexUnpackBlobDesc
{target
,
267 //////////////////////////////////////////////////////////////////////////////////////////
268 //////////////////////////////////////////////////////////////////////////////////////////
270 static bool ValidateTexImage(WebGLContext
* webgl
, WebGLTexture
* texture
,
271 TexImageTarget target
, uint32_t level
,
272 webgl::ImageInfo
** const out_imageInfo
) {
274 if (level
>= WebGLTexture::kMaxLevelCount
) {
275 webgl
->ErrorInvalidValue("`level` is too large.");
279 auto& imageInfo
= texture
->ImageInfoAt(target
, level
);
280 *out_imageInfo
= &imageInfo
;
285 bool WebGLTexture::ValidateTexImageSpecification(
286 TexImageTarget target
, uint32_t level
, const uvec3
& size
,
287 webgl::ImageInfo
** const out_imageInfo
) {
289 mContext
->ErrorInvalidOperation("Specified texture is immutable.");
293 // Do this early to validate `level`.
294 webgl::ImageInfo
* imageInfo
;
295 if (!ValidateTexImage(mContext
, this, target
, level
, &imageInfo
))
298 if (mTarget
== LOCAL_GL_TEXTURE_CUBE_MAP
&& size
.x
!= size
.y
) {
299 mContext
->ErrorInvalidValue("Cube map images must be square.");
303 /* GLES 3.0.4, p133-134:
304 * GL_MAX_TEXTURE_SIZE is *not* the max allowed texture size. Rather, it is
305 * the max (width/height) size guaranteed not to generate an INVALID_VALUE for
306 * too-large dimensions. Sizes larger than GL_MAX_TEXTURE_SIZE *may or may
307 * not* result in an INVALID_VALUE, or possibly GL_OOM.
309 * However, we have needed to set our maximums lower in the past to prevent
310 * resource corruption. Therefore we have limits.maxTex2dSize, which is
311 * neither necessarily lower nor higher than MAX_TEXTURE_SIZE.
313 * Note that limits.maxTex2dSize must be >= than the advertized
314 * MAX_TEXTURE_SIZE. For simplicity, we advertize MAX_TEXTURE_SIZE as
315 * limits.maxTex2dSize.
318 uint32_t maxWidthHeight
= 0;
319 uint32_t maxDepth
= 0;
320 uint32_t maxLevel
= 0;
322 const auto& limits
= mContext
->Limits();
323 MOZ_ASSERT(level
<= 31);
324 switch (target
.get()) {
325 case LOCAL_GL_TEXTURE_2D
:
326 maxWidthHeight
= limits
.maxTex2dSize
>> level
;
328 maxLevel
= CeilingLog2(limits
.maxTex2dSize
);
331 case LOCAL_GL_TEXTURE_3D
:
332 maxWidthHeight
= limits
.maxTex3dSize
>> level
;
333 maxDepth
= maxWidthHeight
;
334 maxLevel
= CeilingLog2(limits
.maxTex3dSize
);
337 case LOCAL_GL_TEXTURE_2D_ARRAY
:
338 maxWidthHeight
= limits
.maxTex2dSize
>> level
;
339 // "The maximum number of layers for two-dimensional array textures
340 // (depth) must be at least MAX_ARRAY_TEXTURE_LAYERS for all levels."
341 maxDepth
= limits
.maxTexArrayLayers
;
342 maxLevel
= CeilingLog2(limits
.maxTex2dSize
);
345 default: // cube maps
346 MOZ_ASSERT(IsCubeMap());
347 maxWidthHeight
= limits
.maxTexCubeSize
>> level
;
349 maxLevel
= CeilingLog2(limits
.maxTexCubeSize
);
353 if (level
> maxLevel
) {
354 mContext
->ErrorInvalidValue("Requested level is not supported for target.");
358 if (size
.x
> maxWidthHeight
|| size
.y
> maxWidthHeight
|| size
.z
> maxDepth
) {
359 mContext
->ErrorInvalidValue("Requested size at this level is unsupported.");
364 /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification
365 * "If level is greater than zero, and either width or
366 * height is not a power-of-two, the error INVALID_VALUE is
369 * This restriction does not apply to GL ES Version 3.0+.
371 bool requirePOT
= (!mContext
->IsWebGL2() && level
!= 0);
374 if (!IsPowerOfTwo(size
.x
) || !IsPowerOfTwo(size
.y
)) {
375 mContext
->ErrorInvalidValue(
376 "For level > 0, width and height must be"
383 *out_imageInfo
= imageInfo
;
388 bool WebGLTexture::ValidateTexImageSelection(
389 TexImageTarget target
, uint32_t level
, const uvec3
& offset
,
390 const uvec3
& size
, webgl::ImageInfo
** const out_imageInfo
) {
391 webgl::ImageInfo
* imageInfo
;
392 if (!ValidateTexImage(mContext
, this, target
, level
, &imageInfo
))
395 if (!imageInfo
->IsDefined()) {
396 mContext
->ErrorInvalidOperation(
397 "The specified TexImage has not yet been"
402 const auto totalX
= CheckedUint32(offset
.x
) + size
.x
;
403 const auto totalY
= CheckedUint32(offset
.y
) + size
.y
;
404 const auto totalZ
= CheckedUint32(offset
.z
) + size
.z
;
406 if (!totalX
.isValid() || totalX
.value() > imageInfo
->mWidth
||
407 !totalY
.isValid() || totalY
.value() > imageInfo
->mHeight
||
408 !totalZ
.isValid() || totalZ
.value() > imageInfo
->mDepth
) {
409 mContext
->ErrorInvalidValue(
410 "Offset+size must be <= the size of the existing"
411 " specified image.");
415 *out_imageInfo
= imageInfo
;
419 static bool ValidateCompressedTexUnpack(WebGLContext
* webgl
, const uvec3
& size
,
420 const webgl::FormatInfo
* format
,
422 auto compression
= format
->compression
;
424 auto bytesPerBlock
= compression
->bytesPerBlock
;
425 auto blockWidth
= compression
->blockWidth
;
426 auto blockHeight
= compression
->blockHeight
;
428 auto widthInBlocks
= CheckedUint32(size
.x
) / blockWidth
;
429 auto heightInBlocks
= CheckedUint32(size
.y
) / blockHeight
;
430 if (size
.x
% blockWidth
) widthInBlocks
+= 1;
431 if (size
.y
% blockHeight
) heightInBlocks
+= 1;
433 const CheckedUint32 blocksPerImage
= widthInBlocks
* heightInBlocks
;
434 const CheckedUint32 bytesPerImage
= bytesPerBlock
* blocksPerImage
;
435 const CheckedUint32 bytesNeeded
= bytesPerImage
* size
.z
;
437 if (!bytesNeeded
.isValid()) {
438 webgl
->ErrorOutOfMemory("Overflow while computing the needed buffer size.");
442 if (dataSize
!= bytesNeeded
.value()) {
443 webgl
->ErrorInvalidValue(
444 "Provided buffer's size must match expected size."
445 " (needs %u, has %zu)",
446 bytesNeeded
.value(), dataSize
);
453 static bool DoChannelsMatchForCopyTexImage(const webgl::FormatInfo
* srcFormat
,
454 const webgl::FormatInfo
* dstFormat
) {
455 // GLES 3.0.4 p140 Table 3.16 "Valid CopyTexImage source
456 // framebuffer/destination texture base internal format combinations."
458 switch (srcFormat
->unsizedFormat
) {
459 case webgl::UnsizedFormat::RGBA
:
460 switch (dstFormat
->unsizedFormat
) {
461 case webgl::UnsizedFormat::A
:
462 case webgl::UnsizedFormat::L
:
463 case webgl::UnsizedFormat::LA
:
464 case webgl::UnsizedFormat::R
:
465 case webgl::UnsizedFormat::RG
:
466 case webgl::UnsizedFormat::RGB
:
467 case webgl::UnsizedFormat::RGBA
:
473 case webgl::UnsizedFormat::RGB
:
474 switch (dstFormat
->unsizedFormat
) {
475 case webgl::UnsizedFormat::L
:
476 case webgl::UnsizedFormat::R
:
477 case webgl::UnsizedFormat::RG
:
478 case webgl::UnsizedFormat::RGB
:
484 case webgl::UnsizedFormat::RG
:
485 switch (dstFormat
->unsizedFormat
) {
486 case webgl::UnsizedFormat::L
:
487 case webgl::UnsizedFormat::R
:
488 case webgl::UnsizedFormat::RG
:
494 case webgl::UnsizedFormat::R
:
495 switch (dstFormat
->unsizedFormat
) {
496 case webgl::UnsizedFormat::L
:
497 case webgl::UnsizedFormat::R
:
508 static bool EnsureImageDataInitializedForUpload(
509 WebGLTexture
* tex
, TexImageTarget target
, uint32_t level
,
510 const uvec3
& offset
, const uvec3
& size
, webgl::ImageInfo
* imageInfo
,
511 bool* const out_expectsInit
= nullptr) {
512 if (out_expectsInit
) {
513 *out_expectsInit
= false;
515 if (!imageInfo
->mUninitializedSlices
) return true;
517 if (size
.x
== imageInfo
->mWidth
&& size
.y
== imageInfo
->mHeight
) {
518 bool expectsInit
= false;
519 auto& isSliceUninit
= *imageInfo
->mUninitializedSlices
;
520 for (const auto z
: IntegerRange(offset
.z
, offset
.z
+ size
.z
)) {
521 if (!isSliceUninit
[z
]) continue;
523 isSliceUninit
[z
] = false;
525 if (out_expectsInit
) {
526 *out_expectsInit
= expectsInit
;
529 if (!expectsInit
) return true;
531 bool hasUninitialized
= false;
532 for (const auto z
: IntegerRange(imageInfo
->mDepth
)) {
533 hasUninitialized
|= isSliceUninit
[z
];
535 if (!hasUninitialized
) {
536 imageInfo
->mUninitializedSlices
= Nothing();
541 WebGLContext
* webgl
= tex
->mContext
;
542 webgl
->GenerateWarning(
543 "Texture has not been initialized prior to a"
544 " partial upload, forcing the browser to clear it."
545 " This may be slow.");
546 if (!tex
->EnsureImageDataInitialized(target
, level
)) {
547 MOZ_ASSERT(false, "Unexpected failure to init image data.");
554 //////////////////////////////////////////////////////////////////////////////////////////
555 //////////////////////////////////////////////////////////////////////////////////////////
558 static inline GLenum
DoTexStorage(gl::GLContext
* gl
, TexTarget target
,
559 GLsizei levels
, GLenum sizedFormat
,
560 GLsizei width
, GLsizei height
,
562 gl::GLContext::LocalErrorScope
errorScope(*gl
);
564 switch (target
.get()) {
565 case LOCAL_GL_TEXTURE_2D
:
566 case LOCAL_GL_TEXTURE_CUBE_MAP
:
567 MOZ_ASSERT(depth
== 1);
568 gl
->fTexStorage2D(target
.get(), levels
, sizedFormat
, width
, height
);
571 case LOCAL_GL_TEXTURE_3D
:
572 case LOCAL_GL_TEXTURE_2D_ARRAY
:
573 gl
->fTexStorage3D(target
.get(), levels
, sizedFormat
, width
, height
,
578 MOZ_CRASH("GFX: bad target");
581 return errorScope
.GetError();
584 bool IsTarget3D(TexImageTarget target
) {
585 switch (target
.get()) {
586 case LOCAL_GL_TEXTURE_2D
:
587 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X
:
588 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X
:
589 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y
:
590 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
:
591 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z
:
592 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
:
595 case LOCAL_GL_TEXTURE_3D
:
596 case LOCAL_GL_TEXTURE_2D_ARRAY
:
600 MOZ_CRASH("GFX: bad target");
604 GLenum
DoTexImage(gl::GLContext
* gl
, TexImageTarget target
, GLint level
,
605 const webgl::DriverUnpackInfo
* dui
, GLsizei width
,
606 GLsizei height
, GLsizei depth
, const void* data
) {
607 const GLint border
= 0;
609 gl::GLContext::LocalErrorScope
errorScope(*gl
);
611 if (IsTarget3D(target
)) {
612 gl
->fTexImage3D(target
.get(), level
, dui
->internalFormat
, width
, height
,
613 depth
, border
, dui
->unpackFormat
, dui
->unpackType
, data
);
615 MOZ_ASSERT(depth
== 1);
616 gl
->fTexImage2D(target
.get(), level
, dui
->internalFormat
, width
, height
,
617 border
, dui
->unpackFormat
, dui
->unpackType
, data
);
620 return errorScope
.GetError();
623 GLenum
DoTexSubImage(gl::GLContext
* gl
, TexImageTarget target
, GLint level
,
624 GLint xOffset
, GLint yOffset
, GLint zOffset
, GLsizei width
,
625 GLsizei height
, GLsizei depth
,
626 const webgl::PackingInfo
& pi
, const void* data
) {
627 gl::GLContext::LocalErrorScope
errorScope(*gl
);
629 if (IsTarget3D(target
)) {
630 gl
->fTexSubImage3D(target
.get(), level
, xOffset
, yOffset
, zOffset
, width
,
631 height
, depth
, pi
.format
, pi
.type
, data
);
633 MOZ_ASSERT(zOffset
== 0);
634 MOZ_ASSERT(depth
== 1);
635 gl
->fTexSubImage2D(target
.get(), level
, xOffset
, yOffset
, width
, height
,
636 pi
.format
, pi
.type
, data
);
639 return errorScope
.GetError();
642 static inline GLenum
DoCompressedTexImage(gl::GLContext
* gl
,
643 TexImageTarget target
, GLint level
,
644 GLenum internalFormat
, GLsizei width
,
645 GLsizei height
, GLsizei depth
,
646 GLsizei dataSize
, const void* data
) {
647 const GLint border
= 0;
649 gl::GLContext::LocalErrorScope
errorScope(*gl
);
651 if (IsTarget3D(target
)) {
652 gl
->fCompressedTexImage3D(target
.get(), level
, internalFormat
, width
,
653 height
, depth
, border
, dataSize
, data
);
655 MOZ_ASSERT(depth
== 1);
656 gl
->fCompressedTexImage2D(target
.get(), level
, internalFormat
, width
,
657 height
, border
, dataSize
, data
);
660 return errorScope
.GetError();
663 GLenum
DoCompressedTexSubImage(gl::GLContext
* gl
, TexImageTarget target
,
664 GLint level
, GLint xOffset
, GLint yOffset
,
665 GLint zOffset
, GLsizei width
, GLsizei height
,
666 GLsizei depth
, GLenum sizedUnpackFormat
,
667 GLsizei dataSize
, const void* data
) {
668 gl::GLContext::LocalErrorScope
errorScope(*gl
);
670 if (IsTarget3D(target
)) {
671 gl
->fCompressedTexSubImage3D(target
.get(), level
, xOffset
, yOffset
, zOffset
,
672 width
, height
, depth
, sizedUnpackFormat
,
675 MOZ_ASSERT(zOffset
== 0);
676 MOZ_ASSERT(depth
== 1);
677 gl
->fCompressedTexSubImage2D(target
.get(), level
, xOffset
, yOffset
, width
,
678 height
, sizedUnpackFormat
, dataSize
, data
);
681 return errorScope
.GetError();
684 static inline GLenum
DoCopyTexSubImage(gl::GLContext
* gl
, TexImageTarget target
,
685 GLint level
, GLint xOffset
,
686 GLint yOffset
, GLint zOffset
, GLint x
,
687 GLint y
, GLsizei width
, GLsizei height
) {
688 gl::GLContext::LocalErrorScope
errorScope(*gl
);
690 if (IsTarget3D(target
)) {
691 gl
->fCopyTexSubImage3D(target
.get(), level
, xOffset
, yOffset
, zOffset
, x
, y
,
694 MOZ_ASSERT(zOffset
== 0);
695 gl
->fCopyTexSubImage2D(target
.get(), level
, xOffset
, yOffset
, x
, y
, width
,
699 return errorScope
.GetError();
702 //////////////////////////////////////////////////////////////////////////////////////////
703 //////////////////////////////////////////////////////////////////////////////////////////
704 // Actual (mostly generic) function implementations
706 static bool ValidateCompressedTexImageRestrictions(
707 const WebGLContext
* webgl
, TexImageTarget target
, uint32_t level
,
708 const webgl::FormatInfo
* format
, const uvec3
& size
) {
709 const auto fnIsDimValid_S3TC
= [&](const char* const name
, uint32_t levelSize
,
710 uint32_t blockSize
) {
711 const auto impliedBaseSize
= levelSize
<< level
;
712 if (impliedBaseSize
% blockSize
== 0) return true;
713 webgl
->ErrorInvalidOperation(
714 "%u is never a valid %s for level %u, because it implies a base mip %s "
716 " %s requires that base mip levels have a %s multiple of %u.",
717 levelSize
, name
, level
, name
, impliedBaseSize
, format
->name
, name
,
722 switch (format
->compression
->family
) {
723 case webgl::CompressionFamily::ASTC
:
724 if (target
== LOCAL_GL_TEXTURE_3D
&&
725 !webgl
->gl
->IsExtensionSupported(
726 gl::GLContext::KHR_texture_compression_astc_hdr
)) {
727 webgl
->ErrorInvalidOperation("TEXTURE_3D requires ASTC's hdr profile.");
732 case webgl::CompressionFamily::PVRTC
:
733 if (!IsPowerOfTwo(size
.x
) || !IsPowerOfTwo(size
.y
)) {
734 webgl
->ErrorInvalidValue("%s requires power-of-two width and height.",
740 case webgl::CompressionFamily::BPTC
:
741 case webgl::CompressionFamily::RGTC
:
742 case webgl::CompressionFamily::S3TC
:
743 if (!fnIsDimValid_S3TC("width", size
.x
,
744 format
->compression
->blockWidth
) ||
745 !fnIsDimValid_S3TC("height", size
.y
,
746 format
->compression
->blockHeight
)) {
751 // Default: There are no restrictions on CompressedTexImage.
752 case webgl::CompressionFamily::ES3
:
753 case webgl::CompressionFamily::ETC1
:
760 static bool ValidateTargetForFormat(const WebGLContext
* webgl
,
761 TexImageTarget target
,
762 const webgl::FormatInfo
* format
) {
764 // "Textures with a base internal format of DEPTH_COMPONENT or DEPTH_STENCIL
765 // are supported by texture image specification commands only if `target` is
766 // TEXTURE_2D, TEXTURE_2D_ARRAY, or TEXTURE_CUBE_MAP. Using these formats in
767 // conjunction with any other `target` will result in an INVALID_OPERATION
769 const bool ok
= [&]() {
770 if (bool(format
->d
) & (target
== LOCAL_GL_TEXTURE_3D
)) return false;
772 if (format
->compression
) {
773 switch (format
->compression
->family
) {
774 case webgl::CompressionFamily::ES3
:
775 case webgl::CompressionFamily::S3TC
:
776 if (target
== LOCAL_GL_TEXTURE_3D
) return false;
779 case webgl::CompressionFamily::ETC1
:
780 case webgl::CompressionFamily::PVRTC
:
781 case webgl::CompressionFamily::RGTC
:
782 if (target
== LOCAL_GL_TEXTURE_3D
||
783 target
== LOCAL_GL_TEXTURE_2D_ARRAY
) {
794 webgl
->ErrorInvalidOperation("Format %s cannot be used with target %s.",
795 format
->name
, GetEnumName(target
.get()));
802 void WebGLTexture::TexStorage(TexTarget target
, uint32_t levels
,
803 GLenum sizedFormat
, const uvec3
& size
) {
806 mContext
->ErrorInvalidValue("`levels` must be >= 1.");
810 if (!size
.x
|| !size
.y
|| !size
.z
) {
811 mContext
->ErrorInvalidValue("Dimensions must be non-zero.");
815 const TexImageTarget testTarget
=
816 IsCubeMap() ? LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X
: target
.get();
817 webgl::ImageInfo
* baseImageInfo
;
818 if (!ValidateTexImageSpecification(testTarget
, 0, size
, &baseImageInfo
)) {
821 MOZ_ALWAYS_TRUE(baseImageInfo
);
823 auto dstUsage
= mContext
->mFormatUsage
->GetSizedTexUsage(sizedFormat
);
825 mContext
->ErrorInvalidEnumInfo("internalformat", sizedFormat
);
828 auto dstFormat
= dstUsage
->format
;
830 if (!ValidateTargetForFormat(mContext
, testTarget
, dstFormat
)) return;
832 if (dstFormat
->compression
) {
833 if (!ValidateCompressedTexImageRestrictions(mContext
, testTarget
, 0,
839 ////////////////////////////////////
841 const bool levelsOk
= [&]() {
842 // Right-shift is only defined for bits-1, which is too large anyways.
843 const auto lastLevel
= uint32_t(levels
- 1);
844 if (lastLevel
> 31) return false;
846 const auto lastLevelWidth
= uint32_t(size
.x
) >> lastLevel
;
847 const auto lastLevelHeight
= uint32_t(size
.y
) >> lastLevel
;
849 // If these are all zero, then some earlier level was the final 1x1(x1)
851 bool ok
= lastLevelWidth
|| lastLevelHeight
;
852 if (target
== LOCAL_GL_TEXTURE_3D
) {
853 const auto lastLevelDepth
= uint32_t(size
.z
) >> lastLevel
;
854 ok
|= bool(lastLevelDepth
);
859 mContext
->ErrorInvalidOperation(
860 "Too many levels requested for the given"
861 " dimensions. (levels: %u, width: %u, height: %u,"
863 levels
, size
.x
, size
.y
, size
.z
);
867 ////////////////////////////////////
870 GLenum error
= DoTexStorage(mContext
->gl
, target
.get(), levels
, sizedFormat
,
871 size
.x
, size
.y
, size
.z
);
873 mContext
->OnDataAllocCall();
875 if (error
== LOCAL_GL_OUT_OF_MEMORY
) {
876 mContext
->ErrorOutOfMemory("Ran out of memory during texture allocation.");
881 mContext
->GenerateError(error
, "Unexpected error from driver.");
882 const nsPrintfCString
call(
883 "DoTexStorage(0x%04x, %i, 0x%04x, %i,%i,%i) -> 0x%04x", target
.get(),
884 levels
, sizedFormat
, size
.x
, size
.y
, size
.z
, error
);
885 gfxCriticalError() << "Unexpected error from driver: "
886 << call
.BeginReading();
890 ////////////////////////////////////
891 // Update our specification data.
893 auto uninitializedSlices
= Some(std::vector
<bool>(size
.z
, true));
894 const webgl::ImageInfo newInfo
{dstUsage
, size
.x
, size
.y
, size
.z
,
895 std::move(uninitializedSlices
)};
898 const auto base_level
= mBaseMipmapLevel
;
899 mBaseMipmapLevel
= 0;
901 ImageInfoAtFace(0, 0) = newInfo
;
902 PopulateMipChain(levels
- 1);
904 mBaseMipmapLevel
= base_level
;
908 mImmutableLevelCount
= AutoAssertCast(levels
);
909 ClampLevelBaseAndMax();
912 ////////////////////////////////////////
915 // TexSubImage iff `!respectFormat`
916 void WebGLTexture::TexImage(uint32_t level
, GLenum respecFormat
,
917 const uvec3
& offset
, const webgl::PackingInfo
& pi
,
918 const webgl::TexUnpackBlobDesc
& src
) {
919 Maybe
<RawBuffer
<>> cpuDataView
;
921 cpuDataView
= Some(RawBuffer
<>{src
.cpuData
->Data()});
923 const auto srcViewDesc
= webgl::TexUnpackBlobDesc
{src
.imageTarget
,
926 std::move(cpuDataView
),
933 src
.applyUnpackTransforms
};
935 const auto blob
= webgl::TexUnpackBlob::Create(srcViewDesc
);
941 const auto imageTarget
= blob
->mDesc
.imageTarget
;
942 auto size
= blob
->mDesc
.size
;
944 if (!IsTarget3D(imageTarget
)) {
948 ////////////////////////////////////
951 const auto& fua
= mContext
->mFormatUsage
;
952 const auto fnValidateUnpackEnums
= [&]() {
953 if (!fua
->AreUnpackEnumsValid(pi
.format
, pi
.type
)) {
954 mContext
->ErrorInvalidEnum("Invalid unpack format/type: %s/%s",
955 EnumString(pi
.format
).c_str(),
956 EnumString(pi
.type
).c_str());
962 webgl::ImageInfo
* imageInfo
;
963 const webgl::FormatUsageInfo
* dstUsage
;
965 if (!ValidateTexImageSpecification(imageTarget
, level
, size
, &imageInfo
))
967 MOZ_ASSERT(imageInfo
);
969 if (!fua
->IsInternalFormatEnumValid(respecFormat
)) {
970 mContext
->ErrorInvalidValue("Invalid internalformat: 0x%04x",
975 dstUsage
= fua
->GetSizedTexUsage(respecFormat
);
977 if (respecFormat
!= pi
.format
) {
978 /* GL ES Version 3.0.4 - 3.8.3 Texture Image Specification
979 * "Specifying a combination of values for format, type, and
980 * internalformat that is not listed as a valid combination
981 * in tables 3.2 or 3.3 generates the error INVALID_OPERATION."
983 if (!fnValidateUnpackEnums()) return;
984 mContext
->ErrorInvalidOperation(
985 "Unsized internalFormat must match"
990 dstUsage
= fua
->GetUnsizedTexUsage(pi
);
994 if (!fnValidateUnpackEnums()) return;
995 mContext
->ErrorInvalidOperation(
996 "Invalid internalformat/format/type:"
997 " 0x%04x/0x%04x/0x%04x",
998 respecFormat
, pi
.format
, pi
.type
);
1002 const auto& dstFormat
= dstUsage
->format
;
1003 if (!ValidateTargetForFormat(mContext
, imageTarget
, dstFormat
)) return;
1005 if (!mContext
->IsWebGL2() && dstFormat
->d
) {
1006 if (imageTarget
!= LOCAL_GL_TEXTURE_2D
|| blob
->HasData() || level
!= 0) {
1007 mContext
->ErrorInvalidOperation(
1008 "With format %s, this function may only"
1009 " be called with target=TEXTURE_2D,"
1010 " data=null, and level=0.",
1016 if (!ValidateTexImageSelection(imageTarget
, level
, offset
, size
,
1020 MOZ_ASSERT(imageInfo
);
1021 dstUsage
= imageInfo
->mFormat
;
1023 const auto& dstFormat
= dstUsage
->format
;
1024 if (!mContext
->IsWebGL2() && dstFormat
->d
) {
1025 mContext
->ErrorInvalidOperation(
1026 "Function may not be called on a texture of"
1033 ////////////////////////////////////
1036 const webgl::DriverUnpackInfo
* driverUnpackInfo
;
1037 if (!dstUsage
->IsUnpackValid(pi
, &driverUnpackInfo
)) {
1038 if (!fnValidateUnpackEnums()) return;
1039 mContext
->ErrorInvalidOperation(
1040 "Mismatched internalFormat and format/type:"
1041 " 0x%04x and 0x%04x/0x%04x",
1042 respecFormat
, pi
.format
, pi
.type
);
1046 if (!blob
->Validate(mContext
, pi
)) return;
1048 ////////////////////////////////////
1051 Maybe
<webgl::ImageInfo
> newImageInfo
;
1052 bool isRespec
= false;
1054 // It's tempting to do allocation first, and TexSubImage second, but this is
1055 // generally slower.
1056 newImageInfo
= Some(webgl::ImageInfo
{dstUsage
, size
.x
, size
.y
, size
.z
});
1057 if (!blob
->HasData()) {
1058 newImageInfo
->mUninitializedSlices
=
1059 Some(std::vector
<bool>(size
.z
, true));
1062 isRespec
= (imageInfo
->mWidth
!= newImageInfo
->mWidth
||
1063 imageInfo
->mHeight
!= newImageInfo
->mHeight
||
1064 imageInfo
->mDepth
!= newImageInfo
->mDepth
||
1065 imageInfo
->mFormat
!= newImageInfo
->mFormat
);
1067 if (!blob
->HasData()) {
1068 mContext
->ErrorInvalidValue("`source` cannot be null.");
1071 if (!EnsureImageDataInitializedForUpload(this, imageTarget
, level
, offset
,
1077 WebGLPixelStore::AssertDefault(*mContext
->gl
, mContext
->IsWebGL2());
1079 blob
->mDesc
.unpacking
.Apply(*mContext
->gl
, mContext
->IsWebGL2(), size
);
1080 const auto revertUnpacking
= MakeScopeExit([&]() {
1081 const WebGLPixelStore defaultUnpacking
;
1082 defaultUnpacking
.Apply(*mContext
->gl
, mContext
->IsWebGL2(), size
);
1085 const bool isSubImage
= !respecFormat
;
1087 if (!blob
->TexOrSubImage(isSubImage
, isRespec
, this, level
, driverUnpackInfo
,
1088 offset
.x
, offset
.y
, offset
.z
, pi
, &glError
)) {
1092 if (glError
== LOCAL_GL_OUT_OF_MEMORY
) {
1093 mContext
->ErrorOutOfMemory("Driver ran out of memory during upload.");
1099 const auto enumStr
= EnumString(glError
);
1100 const nsPrintfCString
dui(
1101 "Unexpected error %s during upload. (dui: %x/%x/%x)", enumStr
.c_str(),
1102 driverUnpackInfo
->internalFormat
, driverUnpackInfo
->unpackFormat
,
1103 driverUnpackInfo
->unpackType
);
1104 mContext
->ErrorInvalidOperation("%s", dui
.BeginReading());
1105 gfxCriticalError() << mContext
->FuncName() << ": " << dui
.BeginReading();
1109 ////////////////////////////////////
1110 // Update our specification data?
1113 mContext
->OnDataAllocCall();
1114 *imageInfo
= *newImageInfo
;
1119 ////////////////////////////////////////
1120 // CompressedTex(Sub)Image
1122 static inline bool IsSubImageBlockAligned(
1123 const webgl::CompressedFormatInfo
* compression
,
1124 const webgl::ImageInfo
* imageInfo
, GLint xOffset
, GLint yOffset
,
1125 uint32_t width
, uint32_t height
) {
1126 if (xOffset
% compression
->blockWidth
!= 0 ||
1127 yOffset
% compression
->blockHeight
!= 0) {
1131 if (width
% compression
->blockWidth
!= 0 &&
1132 xOffset
+ width
!= imageInfo
->mWidth
)
1135 if (height
% compression
->blockHeight
!= 0 &&
1136 yOffset
+ height
!= imageInfo
->mHeight
)
1142 // CompressedTexSubImage iff `sub`
1143 void WebGLTexture::CompressedTexImage(bool sub
, GLenum imageTarget
,
1144 uint32_t level
, GLenum formatEnum
,
1145 const uvec3
& offset
, const uvec3
& size
,
1146 const Range
<const uint8_t>& src
,
1147 const uint32_t pboImageSize
,
1148 const Maybe
<uint64_t>& pboOffset
) {
1149 auto imageSize
= pboImageSize
;
1151 const auto& buffer
=
1152 mContext
->ValidateBufferSelection(LOCAL_GL_PIXEL_UNPACK_BUFFER
);
1153 if (!buffer
) return;
1154 auto availBytes
= buffer
->ByteLength();
1155 if (*pboOffset
> availBytes
) {
1156 mContext
->GenerateError(
1157 LOCAL_GL_INVALID_OPERATION
,
1158 "`offset` (%llu) must be <= PIXEL_UNPACK_BUFFER size (%llu).",
1159 *pboOffset
, availBytes
);
1162 availBytes
-= *pboOffset
;
1163 if (availBytes
< pboImageSize
) {
1164 mContext
->GenerateError(
1165 LOCAL_GL_INVALID_OPERATION
,
1166 "PIXEL_UNPACK_BUFFER size minus `offset` (%llu) too small for"
1167 " `pboImageSize` (%u).",
1168 availBytes
, pboImageSize
);
1172 if (mContext
->mBoundPixelUnpackBuffer
) {
1173 mContext
->GenerateError(LOCAL_GL_INVALID_OPERATION
,
1174 "PIXEL_UNPACK_BUFFER is non-null.");
1177 imageSize
= src
.length();
1182 const auto usage
= mContext
->mFormatUsage
->GetSizedTexUsage(formatEnum
);
1183 if (!usage
|| !usage
->format
->compression
) {
1184 mContext
->ErrorInvalidEnumArg("format", formatEnum
);
1188 webgl::ImageInfo
* imageInfo
;
1190 if (!ValidateTexImageSpecification(imageTarget
, level
, size
, &imageInfo
)) {
1193 MOZ_ASSERT(imageInfo
);
1195 if (!ValidateTargetForFormat(mContext
, imageTarget
, usage
->format
)) return;
1196 if (!ValidateCompressedTexImageRestrictions(mContext
, imageTarget
, level
,
1197 usage
->format
, size
)) {
1201 if (!ValidateTexImageSelection(imageTarget
, level
, offset
, size
,
1204 MOZ_ASSERT(imageInfo
);
1206 const auto dstUsage
= imageInfo
->mFormat
;
1207 if (usage
!= dstUsage
) {
1208 mContext
->ErrorInvalidOperation(
1209 "`format` must match the format of the"
1210 " existing texture image.");
1214 const auto& format
= usage
->format
;
1215 switch (format
->compression
->family
) {
1217 case webgl::CompressionFamily::ETC1
:
1218 mContext
->ErrorInvalidOperation(
1219 "Format does not allow sub-image"
1224 case webgl::CompressionFamily::ES3
: // Yes, the ES3 formats don't match
1226 case webgl::CompressionFamily::S3TC
: // default behavior.
1227 case webgl::CompressionFamily::BPTC
:
1228 case webgl::CompressionFamily::RGTC
:
1229 if (!IsSubImageBlockAligned(format
->compression
, imageInfo
, offset
.x
,
1230 offset
.y
, size
.x
, size
.y
)) {
1231 mContext
->ErrorInvalidOperation(
1232 "Format requires block-aligned sub-image"
1238 // Full-only: (The ES3 default)
1239 case webgl::CompressionFamily::ASTC
:
1240 case webgl::CompressionFamily::PVRTC
:
1241 if (offset
.x
|| offset
.y
|| size
.x
!= imageInfo
->mWidth
||
1242 size
.y
!= imageInfo
->mHeight
) {
1243 mContext
->ErrorInvalidOperation(
1244 "Format does not allow partial sub-image"
1252 switch (usage
->format
->compression
->family
) {
1253 case webgl::CompressionFamily::BPTC
:
1254 case webgl::CompressionFamily::RGTC
:
1256 if (size
.x
% 4 != 0 || size
.y
% 4 != 0) {
1257 mContext
->ErrorInvalidOperation(
1258 "For level == 0, width and height must be multiples of 4.");
1268 if (!ValidateCompressedTexUnpack(mContext
, size
, usage
->format
, imageSize
))
1271 ////////////////////////////////////
1275 if (!EnsureImageDataInitializedForUpload(this, imageTarget
, level
, offset
,
1281 const ScopedLazyBind
bindPBO(mContext
->gl
, LOCAL_GL_PIXEL_UNPACK_BUFFER
,
1282 mContext
->mBoundPixelUnpackBuffer
);
1286 ptr
= reinterpret_cast<const void*>(*pboOffset
);
1288 ptr
= reinterpret_cast<const void*>(src
.begin().get());
1292 error
= DoCompressedTexImage(mContext
->gl
, imageTarget
, level
, formatEnum
,
1293 size
.x
, size
.y
, size
.z
, imageSize
, ptr
);
1295 error
= DoCompressedTexSubImage(mContext
->gl
, imageTarget
, level
, offset
.x
,
1296 offset
.y
, offset
.z
, size
.x
, size
.y
, size
.z
,
1297 formatEnum
, imageSize
, ptr
);
1299 if (error
== LOCAL_GL_OUT_OF_MEMORY
) {
1300 mContext
->ErrorOutOfMemory("Ran out of memory during upload.");
1305 mContext
->GenerateError(error
, "Unexpected error from driver.");
1308 call
= nsPrintfCString(
1309 "DoCompressedTexImage(0x%04x, %u, 0x%04x, %u,%u,%u, %u, %p)",
1310 imageTarget
, level
, formatEnum
, size
.x
, size
.y
, size
.z
, imageSize
,
1313 call
= nsPrintfCString(
1314 "DoCompressedTexSubImage(0x%04x, %u, %u,%u,%u, %u,%u,%u, 0x%04x, %u, "
1316 imageTarget
, level
, offset
.x
, offset
.y
, offset
.z
, size
.x
, size
.y
,
1317 size
.z
, formatEnum
, imageSize
, ptr
);
1319 gfxCriticalError() << "Unexpected error " << gfx::hexa(error
)
1320 << " from driver: " << call
.BeginReading();
1324 ////////////////////////////////////
1325 // Update our specification data?
1328 const auto uninitializedSlices
= Nothing();
1329 const webgl::ImageInfo newImageInfo
{usage
, size
.x
, size
.y
, size
.z
,
1330 uninitializedSlices
};
1331 *imageInfo
= newImageInfo
;
1336 ////////////////////////////////////////
1337 // CopyTex(Sub)Image
1339 static bool ValidateCopyTexImageFormats(WebGLContext
* webgl
,
1340 const webgl::FormatInfo
* srcFormat
,
1341 const webgl::FormatInfo
* dstFormat
) {
1342 MOZ_ASSERT(!srcFormat
->compression
);
1343 if (dstFormat
->compression
) {
1344 webgl
->ErrorInvalidEnum(
1345 "Specified destination must not have a compressed"
1350 if (dstFormat
->effectiveFormat
== webgl::EffectiveFormat::RGB9_E5
) {
1351 webgl
->ErrorInvalidOperation(
1352 "RGB9_E5 is an invalid destination for"
1353 " CopyTex(Sub)Image. (GLES 3.0.4 p145)");
1357 if (!DoChannelsMatchForCopyTexImage(srcFormat
, dstFormat
)) {
1358 webgl
->ErrorInvalidOperation(
1359 "Destination channels must be compatible with"
1360 " source channels. (GLES 3.0.4 p140 Table 3.16)");
1367 ////////////////////////////////////////////////////////////////////////////////
1369 class ScopedCopyTexImageSource
{
1370 WebGLContext
* const mWebGL
;
1375 ScopedCopyTexImageSource(WebGLContext
* webgl
, uint32_t srcWidth
,
1377 const webgl::FormatInfo
* srcFormat
,
1378 const webgl::FormatUsageInfo
* dstUsage
);
1379 ~ScopedCopyTexImageSource();
1382 ScopedCopyTexImageSource::ScopedCopyTexImageSource(
1383 WebGLContext
* webgl
, uint32_t srcWidth
, uint32_t srcHeight
,
1384 const webgl::FormatInfo
* srcFormat
, const webgl::FormatUsageInfo
* dstUsage
)
1385 : mWebGL(webgl
), mRB(0), mFB(0) {
1386 switch (dstUsage
->format
->unsizedFormat
) {
1387 case webgl::UnsizedFormat::L
:
1388 case webgl::UnsizedFormat::A
:
1389 case webgl::UnsizedFormat::LA
:
1390 webgl
->GenerateWarning(
1391 "Copying to a LUMINANCE, ALPHA, or LUMINANCE_ALPHA"
1392 " is deprecated, and has severely reduced performance"
1393 " on some platforms.");
1397 MOZ_ASSERT(!dstUsage
->textureSwizzleRGBA
);
1401 if (!dstUsage
->textureSwizzleRGBA
) return;
1403 gl::GLContext
* gl
= webgl
->gl
;
1407 switch (srcFormat
->componentType
) {
1408 case webgl::ComponentType::NormUInt
:
1409 sizedFormat
= LOCAL_GL_RGBA8
;
1412 case webgl::ComponentType::Float
:
1413 if (webgl
->IsExtensionEnabled(
1414 WebGLExtensionID::WEBGL_color_buffer_float
)) {
1415 sizedFormat
= LOCAL_GL_RGBA32F
;
1416 webgl
->WarnIfImplicit(WebGLExtensionID::WEBGL_color_buffer_float
);
1420 if (webgl
->IsExtensionEnabled(
1421 WebGLExtensionID::EXT_color_buffer_half_float
)) {
1422 sizedFormat
= LOCAL_GL_RGBA16F
;
1423 webgl
->WarnIfImplicit(WebGLExtensionID::EXT_color_buffer_half_float
);
1426 MOZ_CRASH("GFX: Should be able to request CopyTexImage from Float.");
1429 MOZ_CRASH("GFX: Should be able to request CopyTexImage from this type.");
1432 gl::ScopedTexture
scopedTex(gl
);
1433 gl::ScopedBindTexture
scopedBindTex(gl
, scopedTex
.Texture(),
1434 LOCAL_GL_TEXTURE_2D
);
1436 gl
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_MIN_FILTER
,
1438 gl
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_MAG_FILTER
,
1441 GLint blitSwizzle
[4] = {LOCAL_GL_ZERO
};
1442 switch (dstUsage
->format
->unsizedFormat
) {
1443 case webgl::UnsizedFormat::L
:
1444 blitSwizzle
[0] = LOCAL_GL_RED
;
1447 case webgl::UnsizedFormat::A
:
1448 blitSwizzle
[0] = LOCAL_GL_ALPHA
;
1451 case webgl::UnsizedFormat::LA
:
1452 blitSwizzle
[0] = LOCAL_GL_RED
;
1453 blitSwizzle
[1] = LOCAL_GL_ALPHA
;
1457 MOZ_CRASH("GFX: Unhandled unsizedFormat.");
1460 gl
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_SWIZZLE_R
,
1462 gl
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_SWIZZLE_G
,
1464 gl
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_SWIZZLE_B
,
1466 gl
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_SWIZZLE_A
,
1469 gl
->fCopyTexImage2D(LOCAL_GL_TEXTURE_2D
, 0, sizedFormat
, 0, 0, srcWidth
,
1472 // Now create the swizzled FB we'll be exposing.
1477 gl
->fGenRenderbuffers(1, &rgbaRB
);
1478 gl::ScopedBindRenderbuffer
scopedRB(gl
, rgbaRB
);
1479 gl
->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER
, sizedFormat
, srcWidth
,
1482 gl
->fGenFramebuffers(1, &rgbaFB
);
1483 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, rgbaFB
);
1484 gl
->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER
,
1485 LOCAL_GL_COLOR_ATTACHMENT0
,
1486 LOCAL_GL_RENDERBUFFER
, rgbaRB
);
1488 const GLenum status
= gl
->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER
);
1489 if (status
!= LOCAL_GL_FRAMEBUFFER_COMPLETE
) {
1490 MOZ_CRASH("GFX: Temp framebuffer is not complete.");
1494 // Draw-blit rgbaTex into rgbaFB.
1495 const gfx::IntSize
srcSize(srcWidth
, srcHeight
);
1497 const gl::ScopedBindFramebuffer
bindFB(gl
, rgbaFB
);
1498 gl
->BlitHelper()->DrawBlitTextureToFramebuffer(scopedTex
.Texture(), srcSize
,
1502 // Leave RB and FB alive, and FB bound.
1507 template <typename T
>
1508 static inline GLenum
ToGLHandle(const T
& obj
) {
1509 return (obj
? obj
->mGLName
: 0);
1512 ScopedCopyTexImageSource::~ScopedCopyTexImageSource() {
1519 gl::GLContext
* gl
= mWebGL
->gl
;
1521 // If we're swizzling, it's because we're on a GL core (3.2+) profile, which
1522 // has split framebuffer support.
1523 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
,
1524 ToGLHandle(mWebGL
->mBoundDrawFramebuffer
));
1525 gl
->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
,
1526 ToGLHandle(mWebGL
->mBoundReadFramebuffer
));
1528 gl
->fDeleteFramebuffers(1, &mFB
);
1529 gl
->fDeleteRenderbuffers(1, &mRB
);
1532 ////////////////////////////////////////////////////////////////////////////////
1534 static bool GetUnsizedFormatForCopy(GLenum internalFormat
,
1535 webgl::UnsizedFormat
* const out
) {
1536 switch (internalFormat
) {
1538 *out
= webgl::UnsizedFormat::R
;
1541 *out
= webgl::UnsizedFormat::RG
;
1544 *out
= webgl::UnsizedFormat::RGB
;
1547 *out
= webgl::UnsizedFormat::RGBA
;
1549 case LOCAL_GL_LUMINANCE
:
1550 *out
= webgl::UnsizedFormat::L
;
1552 case LOCAL_GL_ALPHA
:
1553 *out
= webgl::UnsizedFormat::A
;
1555 case LOCAL_GL_LUMINANCE_ALPHA
:
1556 *out
= webgl::UnsizedFormat::LA
;
1566 static const webgl::FormatUsageInfo
* ValidateCopyDestUsage(
1567 WebGLContext
* webgl
, const webgl::FormatInfo
* srcFormat
,
1568 GLenum internalFormat
) {
1569 const auto& fua
= webgl
->mFormatUsage
;
1571 switch (internalFormat
) {
1572 case LOCAL_GL_R8_SNORM
:
1573 case LOCAL_GL_RG8_SNORM
:
1574 case LOCAL_GL_RGB8_SNORM
:
1575 case LOCAL_GL_RGBA8_SNORM
:
1576 webgl
->ErrorInvalidEnum("SNORM formats are invalid for CopyTexImage.");
1580 auto dstUsage
= fua
->GetSizedTexUsage(internalFormat
);
1582 // Ok, maybe it's unsized.
1583 webgl::UnsizedFormat unsizedFormat
;
1584 if (!GetUnsizedFormatForCopy(internalFormat
, &unsizedFormat
)) {
1585 webgl
->ErrorInvalidEnumInfo("internalFormat", internalFormat
);
1589 const auto dstFormat
= srcFormat
->GetCopyDecayFormat(unsizedFormat
);
1591 dstUsage
= fua
->GetUsage(dstFormat
->effectiveFormat
);
1594 webgl
->ErrorInvalidOperation(
1595 "0x%04x is not a valid unsized format for"
1596 " source format %s.",
1597 internalFormat
, srcFormat
->name
);
1603 // Alright, it's sized.
1605 const auto dstFormat
= dstUsage
->format
;
1607 if (dstFormat
->componentType
!= srcFormat
->componentType
) {
1608 webgl
->ErrorInvalidOperation(
1609 "For sized internalFormats, source and dest"
1610 " component types must match. (source: %s, dest:"
1612 srcFormat
->name
, dstFormat
->name
);
1616 bool componentSizesMatch
= true;
1618 componentSizesMatch
&= (dstFormat
->r
== srcFormat
->r
);
1621 componentSizesMatch
&= (dstFormat
->g
== srcFormat
->g
);
1624 componentSizesMatch
&= (dstFormat
->b
== srcFormat
->b
);
1627 componentSizesMatch
&= (dstFormat
->a
== srcFormat
->a
);
1630 if (!componentSizesMatch
) {
1631 webgl
->ErrorInvalidOperation(
1632 "For sized internalFormats, source and dest"
1633 " component sizes must match exactly. (source: %s,"
1635 srcFormat
->name
, dstFormat
->name
);
1642 static bool ValidateCopyTexImageForFeedback(const WebGLContext
& webgl
,
1643 const WebGLTexture
& tex
,
1644 const uint32_t mipLevel
,
1645 const uint32_t zLayer
) {
1646 const auto& fb
= webgl
.BoundReadFb();
1648 MOZ_ASSERT(fb
->ColorReadBuffer());
1649 const auto& attach
= *fb
->ColorReadBuffer();
1650 MOZ_ASSERT(attach
.ZLayerCount() ==
1651 1); // Multiview invalid for copyTexImage.
1653 if (attach
.Texture() == &tex
&& attach
.Layer() == zLayer
&&
1654 attach
.MipLevel() == mipLevel
) {
1655 // Note that the TexImageTargets *don't* have to match for this to be
1656 // undefined per GLES 3.0.4 p211, thus an INVALID_OP in WebGL.
1657 webgl
.ErrorInvalidOperation(
1658 "Feedback loop detected, as this texture"
1659 " is already attached to READ_FRAMEBUFFER's"
1660 " READ_BUFFER-selected COLOR_ATTACHMENT%u.",
1661 attach
.mAttachmentPoint
);
1668 static bool DoCopyTexOrSubImage(WebGLContext
* webgl
, bool isSubImage
,
1669 bool needsInit
, WebGLTexture
* const tex
,
1670 const TexImageTarget target
, GLint level
,
1671 GLint xWithinSrc
, GLint yWithinSrc
,
1672 uint32_t srcTotalWidth
, uint32_t srcTotalHeight
,
1673 const webgl::FormatUsageInfo
* srcUsage
,
1674 GLint xOffset
, GLint yOffset
, GLint zOffset
,
1675 uint32_t dstWidth
, uint32_t dstHeight
,
1676 const webgl::FormatUsageInfo
* dstUsage
) {
1677 const auto& gl
= webgl
->gl
;
1681 int32_t readX
, readY
;
1682 int32_t writeX
, writeY
;
1683 int32_t rwWidth
, rwHeight
;
1684 if (!Intersect(srcTotalWidth
, xWithinSrc
, dstWidth
, &readX
, &writeX
,
1686 !Intersect(srcTotalHeight
, yWithinSrc
, dstHeight
, &readY
, &writeY
,
1688 webgl
->ErrorOutOfMemory("Bad subrect selection.");
1698 nsCString errorText
;
1700 const auto& idealUnpack
= dstUsage
->idealUnpack
;
1701 const auto& pi
= idealUnpack
->ToPacking();
1704 const bool fullOverwrite
=
1705 (uint32_t(rwWidth
) == dstWidth
&& uint32_t(rwHeight
) == dstHeight
);
1706 if (needsInit
&& !fullOverwrite
) {
1707 CheckedInt
<size_t> byteCount
= BytesPerPixel(pi
);
1708 byteCount
*= dstWidth
;
1709 byteCount
*= dstHeight
;
1711 if (byteCount
.isValid()) {
1712 zeros
= calloc(1u, byteCount
.value());
1716 webgl
->ErrorOutOfMemory("Ran out of memory allocating zeros.");
1721 if (!isSubImage
|| zeros
) {
1722 WebGLPixelStore::AssertDefault(*gl
, webgl
->IsWebGL2());
1724 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 1);
1725 const auto revert
= MakeScopeExit(
1726 [&]() { gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 4); });
1728 error
= DoTexImage(gl
, target
, level
, idealUnpack
, dstWidth
, dstHeight
,
1731 errorText
= nsPrintfCString(
1732 "DoTexImage(0x%04x, %i, {0x%04x, 0x%04x, 0x%04x}, %u,%u,1) -> "
1734 target
.get(), level
, idealUnpack
->internalFormat
,
1735 idealUnpack
->unpackFormat
, idealUnpack
->unpackType
, dstWidth
,
1741 error
= DoTexSubImage(gl
, target
, level
, xOffset
, yOffset
, zOffset
,
1742 dstWidth
, dstHeight
, 1, pi
, zeros
.get());
1744 errorText
= nsPrintfCString(
1745 "DoTexSubImage(0x%04x, %i, %i,%i,%i, %u,%u,1, {0x%04x, 0x%04x}) "
1748 target
.get(), level
, xOffset
, yOffset
, zOffset
, dstWidth
,
1749 dstHeight
, idealUnpack
->unpackFormat
, idealUnpack
->unpackType
,
1756 if (!rwWidth
|| !rwHeight
) {
1757 // There aren't any pixels to copy, so we're 'done'.
1761 const auto& srcFormat
= srcUsage
->format
;
1762 ScopedCopyTexImageSource
maybeSwizzle(webgl
, srcTotalWidth
, srcTotalHeight
,
1763 srcFormat
, dstUsage
);
1765 error
= DoCopyTexSubImage(gl
, target
, level
, writeX
, writeY
, zOffset
, readX
,
1766 readY
, rwWidth
, rwHeight
);
1768 errorText
= nsPrintfCString(
1769 "DoCopyTexSubImage(0x%04x, %i, %i,%i,%i, %i,%i, %u,%u) -> 0x%04x",
1770 target
.get(), level
, writeX
, writeY
, zOffset
, readX
, readY
, rwWidth
,
1778 if (error
== LOCAL_GL_OUT_OF_MEMORY
) {
1779 webgl
->ErrorOutOfMemory("Ran out of memory during texture copy.");
1784 if (gl
->IsANGLE() && error
== LOCAL_GL_INVALID_OPERATION
) {
1785 webgl
->ErrorImplementationBug(
1786 "ANGLE is particular about CopyTexSubImage"
1787 " formats matching exactly.");
1791 webgl
->GenerateError(error
, "Unexpected error from driver.");
1792 gfxCriticalError() << "Unexpected error from driver: "
1793 << errorText
.BeginReading();
1797 // CopyTexSubImage if `!respecFormat`
1798 void WebGLTexture::CopyTexImage(GLenum imageTarget
, uint32_t level
,
1799 GLenum respecFormat
, const uvec3
& dstOffset
,
1800 const ivec2
& srcOffset
, const uvec2
& size2
) {
1801 ////////////////////////////////////
1804 const webgl::FormatUsageInfo
* srcUsage
;
1805 uint32_t srcTotalWidth
;
1806 uint32_t srcTotalHeight
;
1807 if (!mContext
->BindCurFBForColorRead(&srcUsage
, &srcTotalWidth
,
1811 const auto& srcFormat
= srcUsage
->format
;
1813 if (!ValidateCopyTexImageForFeedback(*mContext
, *this, level
, dstOffset
.z
))
1816 const auto size
= uvec3
{size2
.x
, size2
.y
, 1};
1818 ////////////////////////////////////
1821 webgl::ImageInfo
* imageInfo
;
1822 const webgl::FormatUsageInfo
* dstUsage
;
1824 if (!ValidateTexImageSpecification(imageTarget
, level
, size
, &imageInfo
))
1826 MOZ_ASSERT(imageInfo
);
1828 dstUsage
= ValidateCopyDestUsage(mContext
, srcFormat
, respecFormat
);
1829 if (!dstUsage
) return;
1831 if (!ValidateTargetForFormat(mContext
, imageTarget
, dstUsage
->format
))
1834 if (!ValidateTexImageSelection(imageTarget
, level
, dstOffset
, size
,
1838 MOZ_ASSERT(imageInfo
);
1840 dstUsage
= imageInfo
->mFormat
;
1841 MOZ_ASSERT(dstUsage
);
1844 const auto& dstFormat
= dstUsage
->format
;
1845 if (!mContext
->IsWebGL2() && dstFormat
->d
) {
1846 mContext
->ErrorInvalidOperation(
1847 "Function may not be called with format %s.", dstFormat
->name
);
1851 ////////////////////////////////////
1852 // Check that source and dest info are compatible
1854 if (!ValidateCopyTexImageFormats(mContext
, srcFormat
, dstFormat
)) return;
1856 ////////////////////////////////////
1859 const bool isSubImage
= !respecFormat
;
1860 bool expectsInit
= true;
1862 if (!EnsureImageDataInitializedForUpload(this, imageTarget
, level
,
1863 dstOffset
, size
, imageInfo
,
1869 if (!DoCopyTexOrSubImage(mContext
, isSubImage
, expectsInit
, this, imageTarget
,
1870 level
, srcOffset
.x
, srcOffset
.y
, srcTotalWidth
,
1871 srcTotalHeight
, srcUsage
, dstOffset
.x
, dstOffset
.y
,
1872 dstOffset
.z
, size
.x
, size
.y
, dstUsage
)) {
1876 mContext
->OnDataAllocCall();
1878 ////////////////////////////////////
1879 // Update our specification data?
1882 const auto uninitializedSlices
= Nothing();
1883 const webgl::ImageInfo newImageInfo
{dstUsage
, size
.x
, size
.y
, size
.z
,
1884 uninitializedSlices
};
1885 *imageInfo
= newImageInfo
;
1890 } // namespace mozilla