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/dom/HTMLCanvasElement.h"
18 #include "mozilla/dom/HTMLVideoElement.h"
19 #include "mozilla/dom/ImageBitmap.h"
20 #include "mozilla/dom/ImageData.h"
21 #include "mozilla/MathAlgorithms.h"
22 #include "mozilla/Scoped.h"
23 #include "mozilla/StaticPrefs_webgl.h"
24 #include "mozilla/Unused.h"
25 #include "nsLayoutUtils.h"
26 #include "ScopedGLHelpers.h"
27 #include "TexUnpackBlob.h"
28 #include "WebGLBuffer.h"
29 #include "WebGLContext.h"
30 #include "WebGLContextUtils.h"
31 #include "WebGLFramebuffer.h"
32 #include "WebGLTexelConversions.h"
37 Maybe
<TexUnpackBlobDesc
> FromImageBitmap(const GLenum target
, uvec3 size
,
38 const dom::ImageBitmap
& imageBitmap
,
39 ErrorResult
* const out_rv
) {
40 if (imageBitmap
.IsWriteOnly()) {
41 out_rv
->Throw(NS_ERROR_DOM_SECURITY_ERR
);
45 const auto cloneData
= imageBitmap
.ToCloneData();
50 const RefPtr
<gfx::DataSourceSurface
> surf
= cloneData
->mSurface
;
53 size
.x
= surf
->GetSize().width
;
57 size
.y
= surf
->GetSize().height
;
60 // WhatWG "HTML Living Standard" (30 October 2015):
61 // "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned as
62 // non-premultiplied alpha values."
64 TexUnpackBlobDesc
{target
, size
, cloneData
->mAlphaType
, {}, {}, {}, surf
});
67 TexUnpackBlobDesc
FromImageData(const GLenum target
, uvec3 size
,
68 const dom::ImageData
& imageData
,
69 dom::Uint8ClampedArray
* const scopedArr
) {
70 MOZ_RELEASE_ASSERT(scopedArr
->Init(imageData
.GetDataObject()));
71 scopedArr
->ComputeState();
72 const size_t dataSize
= scopedArr
->Length();
73 const auto data
= reinterpret_cast<uint8_t*>(scopedArr
->Data());
75 const gfx::IntSize
imageSize(imageData
.Width(), imageData
.Height());
76 const size_t stride
= imageSize
.width
* 4;
77 const gfx::SurfaceFormat surfFormat
= gfx::SurfaceFormat::R8G8B8A8
;
78 MOZ_ALWAYS_TRUE(dataSize
== stride
* imageSize
.height
);
80 const RefPtr
<gfx::DataSourceSurface
> surf
=
81 gfx::Factory::CreateWrappingDataSourceSurface(data
, stride
, imageSize
,
88 size
.x
= imageData
.Width();
92 size
.y
= imageData
.Height();
97 // WhatWG "HTML Living Standard" (30 October 2015):
98 // "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned as
99 // non-premultiplied alpha values."
100 return {target
, size
, gfxAlphaType::NonPremult
, {}, {}, {}, surf
};
103 Maybe
<webgl::TexUnpackBlobDesc
> FromDomElem(const ClientWebGLContext
& webgl
,
104 const GLenum target
, uvec3 size
,
105 const dom::Element
& elem
,
106 const bool allowBlitImage
,
107 ErrorResult
* const out_error
) {
108 const auto& canvas
= *webgl
.GetCanvas();
110 if (elem
.IsHTMLElement(nsGkAtoms::canvas
)) {
111 const dom::HTMLCanvasElement
* srcCanvas
=
112 static_cast<const dom::HTMLCanvasElement
*>(&elem
);
113 if (srcCanvas
->IsWriteOnly()) {
114 out_error
->Throw(NS_ERROR_DOM_SECURITY_ERR
);
119 // The canvas spec says that drawImage should draw the first frame of
120 // animated images. The webgl spec doesn't mention the issue, so we do the
121 // same as drawImage.
122 uint32_t flags
= nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE
|
123 nsLayoutUtils::SFE_WANT_IMAGE_SURFACE
|
124 nsLayoutUtils::SFE_USE_ELEMENT_SIZE_IF_VECTOR
|
125 nsLayoutUtils::SFE_ALLOW_NON_PREMULT
;
126 const auto& unpacking
= webgl
.State().mPixelUnpackState
;
127 if (unpacking
.mColorspaceConversion
== LOCAL_GL_NONE
) {
128 flags
|= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION
;
131 RefPtr
<gfx::DrawTarget
> idealDrawTarget
= nullptr; // Don't care for now.
132 auto sfer
= nsLayoutUtils::SurfaceFromElement(
133 const_cast<dom::Element
*>(&elem
), flags
, idealDrawTarget
);
137 uint32_t elemWidth
= 0;
138 uint32_t elemHeight
= 0;
139 layers::Image
* layersImage
= nullptr;
141 if (sfer
.mLayersImage
&& allowBlitImage
) {
142 layersImage
= sfer
.mLayersImage
;
143 elemWidth
= layersImage
->GetSize().width
;
144 elemHeight
= layersImage
->GetSize().height
;
147 RefPtr
<gfx::DataSourceSurface
> dataSurf
;
148 if (!layersImage
&& sfer
.GetSourceSurface()) {
149 const auto surf
= sfer
.GetSourceSurface();
150 elemWidth
= surf
->GetSize().width
;
151 elemHeight
= surf
->GetSize().height
;
153 // WARNING: OSX can lose our MakeCurrent here.
154 dataSurf
= surf
->GetDataSurface();
169 if (!layersImage
&& !dataSurf
) {
170 return Some(TexUnpackBlobDesc
{target
, size
, gfxAlphaType::NonPremult
});
175 // While it's counter-intuitive, the shape of the SFEResult API means that we
176 // should try to pull out a surface first, and then, if we do pull out a
177 // surface, check CORS/write-only/etc..
179 if (!sfer
.mCORSUsed
) {
180 auto& srcPrincipal
= sfer
.mPrincipal
;
181 nsIPrincipal
* dstPrincipal
= canvas
.NodePrincipal();
183 if (!dstPrincipal
->Subsumes(srcPrincipal
)) {
184 webgl
.EnqueueWarning("Cross-origin elements require CORS.");
185 out_error
->Throw(NS_ERROR_DOM_SECURITY_ERR
);
190 if (sfer
.mIsWriteOnly
) {
191 // mIsWriteOnly defaults to true, and so will be true even if SFE merely
192 // failed. Thus we must test mIsWriteOnly after successfully retrieving an
193 // Image or SourceSurface.
194 webgl
.EnqueueWarning("Element is write-only, thus cannot be uploaded.");
195 out_error
->Throw(NS_ERROR_DOM_SECURITY_ERR
);
204 TexUnpackBlobDesc
{target
, size
, sfer
.mAlphaType
, {}, {}, layersImage
});
207 MOZ_ASSERT(dataSurf
);
209 TexUnpackBlobDesc
{target
, size
, sfer
.mAlphaType
, {}, {}, {}, dataSurf
});
214 //////////////////////////////////////////////////////////////////////////////////////////
215 //////////////////////////////////////////////////////////////////////////////////////////
217 static bool ValidateTexImage(WebGLContext
* webgl
, WebGLTexture
* texture
,
218 TexImageTarget target
, uint32_t level
,
219 webgl::ImageInfo
** const out_imageInfo
) {
221 if (level
>= WebGLTexture::kMaxLevelCount
) {
222 webgl
->ErrorInvalidValue("`level` is too large.");
226 auto& imageInfo
= texture
->ImageInfoAt(target
, level
);
227 *out_imageInfo
= &imageInfo
;
232 bool WebGLTexture::ValidateTexImageSpecification(
233 TexImageTarget target
, uint32_t level
, const uvec3
& size
,
234 webgl::ImageInfo
** const out_imageInfo
) {
236 mContext
->ErrorInvalidOperation("Specified texture is immutable.");
240 // Do this early to validate `level`.
241 webgl::ImageInfo
* imageInfo
;
242 if (!ValidateTexImage(mContext
, this, target
, level
, &imageInfo
))
245 if (mTarget
== LOCAL_GL_TEXTURE_CUBE_MAP
&& size
.x
!= size
.y
) {
246 mContext
->ErrorInvalidValue("Cube map images must be square.");
250 /* GLES 3.0.4, p133-134:
251 * GL_MAX_TEXTURE_SIZE is *not* the max allowed texture size. Rather, it is
252 * the max (width/height) size guaranteed not to generate an INVALID_VALUE for
253 * too-large dimensions. Sizes larger than GL_MAX_TEXTURE_SIZE *may or may
254 * not* result in an INVALID_VALUE, or possibly GL_OOM.
256 * However, we have needed to set our maximums lower in the past to prevent
257 * resource corruption. Therefore we have limits.maxTex2dSize, which is
258 * neither necessarily lower nor higher than MAX_TEXTURE_SIZE.
260 * Note that limits.maxTex2dSize must be >= than the advertized
261 * MAX_TEXTURE_SIZE. For simplicity, we advertize MAX_TEXTURE_SIZE as
262 * limits.maxTex2dSize.
265 uint32_t maxWidthHeight
= 0;
266 uint32_t maxDepth
= 0;
267 uint32_t maxLevel
= 0;
269 const auto& limits
= mContext
->Limits();
270 MOZ_ASSERT(level
<= 31);
271 switch (target
.get()) {
272 case LOCAL_GL_TEXTURE_2D
:
273 maxWidthHeight
= limits
.maxTex2dSize
>> level
;
275 maxLevel
= CeilingLog2(limits
.maxTex2dSize
);
278 case LOCAL_GL_TEXTURE_3D
:
279 maxWidthHeight
= limits
.maxTex3dSize
>> level
;
280 maxDepth
= maxWidthHeight
;
281 maxLevel
= CeilingLog2(limits
.maxTex3dSize
);
284 case LOCAL_GL_TEXTURE_2D_ARRAY
:
285 maxWidthHeight
= limits
.maxTex2dSize
>> level
;
286 // "The maximum number of layers for two-dimensional array textures
287 // (depth) must be at least MAX_ARRAY_TEXTURE_LAYERS for all levels."
288 maxDepth
= limits
.maxTexArrayLayers
;
289 maxLevel
= CeilingLog2(limits
.maxTex2dSize
);
292 default: // cube maps
293 MOZ_ASSERT(IsCubeMap());
294 maxWidthHeight
= limits
.maxTexCubeSize
>> level
;
296 maxLevel
= CeilingLog2(limits
.maxTexCubeSize
);
300 if (level
> maxLevel
) {
301 mContext
->ErrorInvalidValue("Requested level is not supported for target.");
305 if (size
.x
> maxWidthHeight
|| size
.y
> maxWidthHeight
|| size
.z
> maxDepth
) {
306 mContext
->ErrorInvalidValue("Requested size at this level is unsupported.");
311 /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification
312 * "If level is greater than zero, and either width or
313 * height is not a power-of-two, the error INVALID_VALUE is
316 * This restriction does not apply to GL ES Version 3.0+.
318 bool requirePOT
= (!mContext
->IsWebGL2() && level
!= 0);
321 if (!IsPowerOfTwo(size
.x
) || !IsPowerOfTwo(size
.y
)) {
322 mContext
->ErrorInvalidValue(
323 "For level > 0, width and height must be"
330 *out_imageInfo
= imageInfo
;
335 bool WebGLTexture::ValidateTexImageSelection(
336 TexImageTarget target
, uint32_t level
, const uvec3
& offset
,
337 const uvec3
& size
, webgl::ImageInfo
** const out_imageInfo
) {
338 webgl::ImageInfo
* imageInfo
;
339 if (!ValidateTexImage(mContext
, this, target
, level
, &imageInfo
))
342 if (!imageInfo
->IsDefined()) {
343 mContext
->ErrorInvalidOperation(
344 "The specified TexImage has not yet been"
349 const auto totalX
= CheckedUint32(offset
.x
) + size
.x
;
350 const auto totalY
= CheckedUint32(offset
.y
) + size
.y
;
351 const auto totalZ
= CheckedUint32(offset
.z
) + size
.z
;
353 if (!totalX
.isValid() || totalX
.value() > imageInfo
->mWidth
||
354 !totalY
.isValid() || totalY
.value() > imageInfo
->mHeight
||
355 !totalZ
.isValid() || totalZ
.value() > imageInfo
->mDepth
) {
356 mContext
->ErrorInvalidValue(
357 "Offset+size must be <= the size of the existing"
358 " specified image.");
362 *out_imageInfo
= imageInfo
;
366 static bool ValidateCompressedTexUnpack(WebGLContext
* webgl
, const uvec3
& size
,
367 const webgl::FormatInfo
* format
,
369 auto compression
= format
->compression
;
371 auto bytesPerBlock
= compression
->bytesPerBlock
;
372 auto blockWidth
= compression
->blockWidth
;
373 auto blockHeight
= compression
->blockHeight
;
375 auto widthInBlocks
= CheckedUint32(size
.x
) / blockWidth
;
376 auto heightInBlocks
= CheckedUint32(size
.y
) / blockHeight
;
377 if (size
.x
% blockWidth
) widthInBlocks
+= 1;
378 if (size
.y
% blockHeight
) heightInBlocks
+= 1;
380 const CheckedUint32 blocksPerImage
= widthInBlocks
* heightInBlocks
;
381 const CheckedUint32 bytesPerImage
= bytesPerBlock
* blocksPerImage
;
382 const CheckedUint32 bytesNeeded
= bytesPerImage
* size
.z
;
384 if (!bytesNeeded
.isValid()) {
385 webgl
->ErrorOutOfMemory("Overflow while computing the needed buffer size.");
389 if (dataSize
!= bytesNeeded
.value()) {
390 webgl
->ErrorInvalidValue(
391 "Provided buffer's size must match expected size."
392 " (needs %u, has %zu)",
393 bytesNeeded
.value(), dataSize
);
400 static bool DoChannelsMatchForCopyTexImage(const webgl::FormatInfo
* srcFormat
,
401 const webgl::FormatInfo
* dstFormat
) {
402 // GLES 3.0.4 p140 Table 3.16 "Valid CopyTexImage source
403 // framebuffer/destination texture base internal format combinations."
405 switch (srcFormat
->unsizedFormat
) {
406 case webgl::UnsizedFormat::RGBA
:
407 switch (dstFormat
->unsizedFormat
) {
408 case webgl::UnsizedFormat::A
:
409 case webgl::UnsizedFormat::L
:
410 case webgl::UnsizedFormat::LA
:
411 case webgl::UnsizedFormat::R
:
412 case webgl::UnsizedFormat::RG
:
413 case webgl::UnsizedFormat::RGB
:
414 case webgl::UnsizedFormat::RGBA
:
420 case webgl::UnsizedFormat::RGB
:
421 switch (dstFormat
->unsizedFormat
) {
422 case webgl::UnsizedFormat::L
:
423 case webgl::UnsizedFormat::R
:
424 case webgl::UnsizedFormat::RG
:
425 case webgl::UnsizedFormat::RGB
:
431 case webgl::UnsizedFormat::RG
:
432 switch (dstFormat
->unsizedFormat
) {
433 case webgl::UnsizedFormat::L
:
434 case webgl::UnsizedFormat::R
:
435 case webgl::UnsizedFormat::RG
:
441 case webgl::UnsizedFormat::R
:
442 switch (dstFormat
->unsizedFormat
) {
443 case webgl::UnsizedFormat::L
:
444 case webgl::UnsizedFormat::R
:
455 static bool EnsureImageDataInitializedForUpload(
456 WebGLTexture
* tex
, TexImageTarget target
, uint32_t level
,
457 const uvec3
& offset
, const uvec3
& size
, webgl::ImageInfo
* imageInfo
,
458 bool* const out_expectsInit
= nullptr) {
459 if (out_expectsInit
) {
460 *out_expectsInit
= false;
462 if (!imageInfo
->mUninitializedSlices
) return true;
464 if (size
.x
== imageInfo
->mWidth
&& size
.y
== imageInfo
->mHeight
) {
465 bool expectsInit
= false;
466 auto& isSliceUninit
= *imageInfo
->mUninitializedSlices
;
467 for (const auto z
: IntegerRange(offset
.z
, offset
.z
+ size
.z
)) {
468 if (!isSliceUninit
[z
]) continue;
470 isSliceUninit
[z
] = false;
472 if (out_expectsInit
) {
473 *out_expectsInit
= expectsInit
;
476 if (!expectsInit
) return true;
478 bool hasUninitialized
= false;
479 for (const auto z
: IntegerRange(imageInfo
->mDepth
)) {
480 hasUninitialized
|= isSliceUninit
[z
];
482 if (!hasUninitialized
) {
483 imageInfo
->mUninitializedSlices
= Nothing();
488 WebGLContext
* webgl
= tex
->mContext
;
489 webgl
->GenerateWarning(
490 "Texture has not been initialized prior to a"
491 " partial upload, forcing the browser to clear it."
492 " This may be slow.");
493 if (!tex
->EnsureImageDataInitialized(target
, level
)) {
494 MOZ_ASSERT(false, "Unexpected failure to init image data.");
501 //////////////////////////////////////////////////////////////////////////////////////////
502 //////////////////////////////////////////////////////////////////////////////////////////
505 static inline GLenum
DoTexStorage(gl::GLContext
* gl
, TexTarget target
,
506 GLsizei levels
, GLenum sizedFormat
,
507 GLsizei width
, GLsizei height
,
509 gl::GLContext::LocalErrorScope
errorScope(*gl
);
511 switch (target
.get()) {
512 case LOCAL_GL_TEXTURE_2D
:
513 case LOCAL_GL_TEXTURE_CUBE_MAP
:
514 MOZ_ASSERT(depth
== 1);
515 gl
->fTexStorage2D(target
.get(), levels
, sizedFormat
, width
, height
);
518 case LOCAL_GL_TEXTURE_3D
:
519 case LOCAL_GL_TEXTURE_2D_ARRAY
:
520 gl
->fTexStorage3D(target
.get(), levels
, sizedFormat
, width
, height
,
525 MOZ_CRASH("GFX: bad target");
528 return errorScope
.GetError();
531 bool IsTarget3D(TexImageTarget target
) {
532 switch (target
.get()) {
533 case LOCAL_GL_TEXTURE_2D
:
534 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X
:
535 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X
:
536 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y
:
537 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
:
538 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z
:
539 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
:
542 case LOCAL_GL_TEXTURE_3D
:
543 case LOCAL_GL_TEXTURE_2D_ARRAY
:
547 MOZ_CRASH("GFX: bad target");
551 GLenum
DoTexImage(gl::GLContext
* gl
, TexImageTarget target
, GLint level
,
552 const webgl::DriverUnpackInfo
* dui
, GLsizei width
,
553 GLsizei height
, GLsizei depth
, const void* data
) {
554 const GLint border
= 0;
556 gl::GLContext::LocalErrorScope
errorScope(*gl
);
558 if (IsTarget3D(target
)) {
559 gl
->fTexImage3D(target
.get(), level
, dui
->internalFormat
, width
, height
,
560 depth
, border
, dui
->unpackFormat
, dui
->unpackType
, data
);
562 MOZ_ASSERT(depth
== 1);
563 gl
->fTexImage2D(target
.get(), level
, dui
->internalFormat
, width
, height
,
564 border
, dui
->unpackFormat
, dui
->unpackType
, data
);
567 return errorScope
.GetError();
570 GLenum
DoTexSubImage(gl::GLContext
* gl
, TexImageTarget target
, GLint level
,
571 GLint xOffset
, GLint yOffset
, GLint zOffset
, GLsizei width
,
572 GLsizei height
, GLsizei depth
,
573 const webgl::PackingInfo
& pi
, const void* data
) {
574 gl::GLContext::LocalErrorScope
errorScope(*gl
);
576 if (IsTarget3D(target
)) {
577 gl
->fTexSubImage3D(target
.get(), level
, xOffset
, yOffset
, zOffset
, width
,
578 height
, depth
, pi
.format
, pi
.type
, data
);
580 MOZ_ASSERT(zOffset
== 0);
581 MOZ_ASSERT(depth
== 1);
582 gl
->fTexSubImage2D(target
.get(), level
, xOffset
, yOffset
, width
, height
,
583 pi
.format
, pi
.type
, data
);
586 return errorScope
.GetError();
589 static inline GLenum
DoCompressedTexImage(gl::GLContext
* gl
,
590 TexImageTarget target
, GLint level
,
591 GLenum internalFormat
, GLsizei width
,
592 GLsizei height
, GLsizei depth
,
593 GLsizei dataSize
, const void* data
) {
594 const GLint border
= 0;
596 gl::GLContext::LocalErrorScope
errorScope(*gl
);
598 if (IsTarget3D(target
)) {
599 gl
->fCompressedTexImage3D(target
.get(), level
, internalFormat
, width
,
600 height
, depth
, border
, dataSize
, data
);
602 MOZ_ASSERT(depth
== 1);
603 gl
->fCompressedTexImage2D(target
.get(), level
, internalFormat
, width
,
604 height
, border
, dataSize
, data
);
607 return errorScope
.GetError();
610 GLenum
DoCompressedTexSubImage(gl::GLContext
* gl
, TexImageTarget target
,
611 GLint level
, GLint xOffset
, GLint yOffset
,
612 GLint zOffset
, GLsizei width
, GLsizei height
,
613 GLsizei depth
, GLenum sizedUnpackFormat
,
614 GLsizei dataSize
, const void* data
) {
615 gl::GLContext::LocalErrorScope
errorScope(*gl
);
617 if (IsTarget3D(target
)) {
618 gl
->fCompressedTexSubImage3D(target
.get(), level
, xOffset
, yOffset
, zOffset
,
619 width
, height
, depth
, sizedUnpackFormat
,
622 MOZ_ASSERT(zOffset
== 0);
623 MOZ_ASSERT(depth
== 1);
624 gl
->fCompressedTexSubImage2D(target
.get(), level
, xOffset
, yOffset
, width
,
625 height
, sizedUnpackFormat
, dataSize
, data
);
628 return errorScope
.GetError();
631 static inline GLenum
DoCopyTexSubImage(gl::GLContext
* gl
, TexImageTarget target
,
632 GLint level
, GLint xOffset
,
633 GLint yOffset
, GLint zOffset
, GLint x
,
634 GLint y
, GLsizei width
, GLsizei height
) {
635 gl::GLContext::LocalErrorScope
errorScope(*gl
);
637 if (IsTarget3D(target
)) {
638 gl
->fCopyTexSubImage3D(target
.get(), level
, xOffset
, yOffset
, zOffset
, x
, y
,
641 MOZ_ASSERT(zOffset
== 0);
642 gl
->fCopyTexSubImage2D(target
.get(), level
, xOffset
, yOffset
, x
, y
, width
,
646 return errorScope
.GetError();
649 //////////////////////////////////////////////////////////////////////////////////////////
650 //////////////////////////////////////////////////////////////////////////////////////////
651 // Actual (mostly generic) function implementations
653 static bool ValidateCompressedTexImageRestrictions(
654 const WebGLContext
* webgl
, TexImageTarget target
, uint32_t level
,
655 const webgl::FormatInfo
* format
, const uvec3
& size
) {
656 const auto fnIsDimValid_S3TC
= [level
](uint32_t size
, uint32_t blockSize
) {
657 if (size
% blockSize
== 0) return true;
659 if (level
== 0) return false;
661 return (size
== 0 || size
== 1 || size
== 2);
664 switch (format
->compression
->family
) {
665 case webgl::CompressionFamily::ASTC
:
666 if (target
== LOCAL_GL_TEXTURE_3D
&&
667 !webgl
->gl
->IsExtensionSupported(
668 gl::GLContext::KHR_texture_compression_astc_hdr
)) {
669 webgl
->ErrorInvalidOperation("TEXTURE_3D requires ASTC's hdr profile.");
674 case webgl::CompressionFamily::PVRTC
:
675 if (!IsPowerOfTwo(size
.x
) || !IsPowerOfTwo(size
.y
)) {
676 webgl
->ErrorInvalidValue("%s requires power-of-two width and height.",
683 case webgl::CompressionFamily::S3TC
:
684 if (!fnIsDimValid_S3TC(size
.x
, format
->compression
->blockWidth
) ||
685 !fnIsDimValid_S3TC(size
.y
, format
->compression
->blockHeight
)) {
686 webgl
->ErrorInvalidOperation(
687 "%s requires that width and height are"
688 " block-aligned, or, if level>0, equal to 0, 1,"
696 // Default: There are no restrictions on CompressedTexImage.
697 default: // ETC1, ES3
704 static bool ValidateTargetForFormat(const WebGLContext
* webgl
,
705 TexImageTarget target
,
706 const webgl::FormatInfo
* format
) {
708 // "Textures with a base internal format of DEPTH_COMPONENT or DEPTH_STENCIL
709 // are supported by texture image specification commands only if `target` is
710 // TEXTURE_2D, TEXTURE_2D_ARRAY, or TEXTURE_CUBE_MAP. Using these formats in
711 // conjunction with any other `target` will result in an INVALID_OPERATION
713 const bool ok
= [&]() {
714 if (bool(format
->d
) & (target
== LOCAL_GL_TEXTURE_3D
)) return false;
716 if (format
->compression
) {
717 switch (format
->compression
->family
) {
718 case webgl::CompressionFamily::ES3
:
719 case webgl::CompressionFamily::S3TC
:
720 if (target
== LOCAL_GL_TEXTURE_3D
) return false;
723 case webgl::CompressionFamily::ETC1
:
724 case webgl::CompressionFamily::PVRTC
:
725 case webgl::CompressionFamily::RGTC
:
726 if (target
== LOCAL_GL_TEXTURE_3D
||
727 target
== LOCAL_GL_TEXTURE_2D_ARRAY
) {
738 webgl
->ErrorInvalidOperation("Format %s cannot be used with target %s.",
739 format
->name
, GetEnumName(target
.get()));
746 void WebGLTexture::TexStorage(TexTarget target
, uint32_t levels
,
747 GLenum sizedFormat
, const uvec3
& size
) {
750 mContext
->ErrorInvalidValue("`levels` must be >= 1.");
754 if (!size
.x
|| !size
.y
|| !size
.z
) {
755 mContext
->ErrorInvalidValue("Dimensions must be non-zero.");
759 const TexImageTarget testTarget
=
760 IsCubeMap() ? LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X
: target
.get();
761 webgl::ImageInfo
* baseImageInfo
;
762 if (!ValidateTexImageSpecification(testTarget
, 0, size
, &baseImageInfo
)) {
765 MOZ_ALWAYS_TRUE(baseImageInfo
);
767 auto dstUsage
= mContext
->mFormatUsage
->GetSizedTexUsage(sizedFormat
);
769 mContext
->ErrorInvalidEnumInfo("internalformat", sizedFormat
);
772 auto dstFormat
= dstUsage
->format
;
774 if (!ValidateTargetForFormat(mContext
, testTarget
, dstFormat
)) return;
776 if (dstFormat
->compression
) {
777 if (!ValidateCompressedTexImageRestrictions(mContext
, testTarget
, 0,
783 ////////////////////////////////////
785 const bool levelsOk
= [&]() {
786 // Right-shift is only defined for bits-1, which is too large anyways.
787 const auto lastLevel
= uint32_t(levels
- 1);
788 if (lastLevel
> 31) return false;
790 const auto lastLevelWidth
= uint32_t(size
.x
) >> lastLevel
;
791 const auto lastLevelHeight
= uint32_t(size
.y
) >> lastLevel
;
793 // If these are all zero, then some earlier level was the final 1x1(x1)
795 bool ok
= lastLevelWidth
|| lastLevelHeight
;
796 if (target
== LOCAL_GL_TEXTURE_3D
) {
797 const auto lastLevelDepth
= uint32_t(size
.z
) >> lastLevel
;
798 ok
|= bool(lastLevelDepth
);
803 mContext
->ErrorInvalidOperation(
804 "Too many levels requested for the given"
805 " dimensions. (levels: %u, width: %u, height: %u,"
807 levels
, size
.x
, size
.y
, size
.z
);
811 ////////////////////////////////////
814 GLenum error
= DoTexStorage(mContext
->gl
, target
.get(), levels
, sizedFormat
,
815 size
.x
, size
.y
, size
.z
);
817 mContext
->OnDataAllocCall();
819 if (error
== LOCAL_GL_OUT_OF_MEMORY
) {
820 mContext
->ErrorOutOfMemory("Ran out of memory during texture allocation.");
825 mContext
->GenerateError(error
, "Unexpected error from driver.");
826 const nsPrintfCString
call(
827 "DoTexStorage(0x%04x, %i, 0x%04x, %i,%i,%i) -> 0x%04x", target
.get(),
828 levels
, sizedFormat
, size
.x
, size
.y
, size
.z
, error
);
829 gfxCriticalError() << "Unexpected error from driver: "
830 << call
.BeginReading();
834 ////////////////////////////////////
835 // Update our specification data.
837 auto uninitializedSlices
= Some(std::vector
<bool>(size
.z
, true));
838 const webgl::ImageInfo newInfo
{dstUsage
, size
.x
, size
.y
, size
.z
,
839 std::move(uninitializedSlices
)};
842 const auto base_level
= mBaseMipmapLevel
;
843 mBaseMipmapLevel
= 0;
845 ImageInfoAtFace(0, 0) = newInfo
;
846 PopulateMipChain(levels
- 1);
848 mBaseMipmapLevel
= base_level
;
852 mImmutableLevelCount
= AutoAssertCast(levels
);
853 ClampLevelBaseAndMax();
856 ////////////////////////////////////////
859 // TexSubImage iff `!respectFormat`
860 void WebGLTexture::TexImage(uint32_t level
, GLenum respecFormat
,
861 const uvec3
& offset
, const webgl::PackingInfo
& pi
,
862 const webgl::TexUnpackBlobDesc
& src
) {
863 Maybe
<RawBuffer
<>> cpuDataView
;
865 cpuDataView
= Some(RawBuffer
<>{src
.cpuData
->Data()});
867 const auto srcViewDesc
= webgl::TexUnpackBlobDesc
{
868 src
.imageTarget
, src
.size
, src
.srcAlphaType
, std::move(cpuDataView
),
869 src
.pboOffset
, src
.image
, src
.surf
, src
.unpacking
};
870 const auto blob
= webgl::TexUnpackBlob::Create(srcViewDesc
);
876 const auto imageTarget
= blob
->mDesc
.imageTarget
;
877 auto size
= blob
->mDesc
.size
;
879 if (!IsTarget3D(imageTarget
)) {
883 ////////////////////////////////////
886 const auto& fua
= mContext
->mFormatUsage
;
887 const auto fnValidateUnpackEnums
= [&]() {
888 if (!fua
->AreUnpackEnumsValid(pi
.format
, pi
.type
)) {
889 mContext
->ErrorInvalidEnum("Invalid unpack format/type: %s/%s",
890 EnumString(pi
.format
).c_str(),
891 EnumString(pi
.type
).c_str());
897 webgl::ImageInfo
* imageInfo
;
898 const webgl::FormatUsageInfo
* dstUsage
;
900 if (!ValidateTexImageSpecification(imageTarget
, level
, size
, &imageInfo
))
902 MOZ_ASSERT(imageInfo
);
904 if (!fua
->IsInternalFormatEnumValid(respecFormat
)) {
905 mContext
->ErrorInvalidValue("Invalid internalformat: 0x%04x",
910 dstUsage
= fua
->GetSizedTexUsage(respecFormat
);
912 if (respecFormat
!= pi
.format
) {
913 /* GL ES Version 3.0.4 - 3.8.3 Texture Image Specification
914 * "Specifying a combination of values for format, type, and
915 * internalformat that is not listed as a valid combination
916 * in tables 3.2 or 3.3 generates the error INVALID_OPERATION."
918 if (!fnValidateUnpackEnums()) return;
919 mContext
->ErrorInvalidOperation(
920 "Unsized internalFormat must match"
925 dstUsage
= fua
->GetUnsizedTexUsage(pi
);
929 if (!fnValidateUnpackEnums()) return;
930 mContext
->ErrorInvalidOperation(
931 "Invalid internalformat/format/type:"
932 " 0x%04x/0x%04x/0x%04x",
933 respecFormat
, pi
.format
, pi
.type
);
937 const auto& dstFormat
= dstUsage
->format
;
938 if (!ValidateTargetForFormat(mContext
, imageTarget
, dstFormat
)) return;
940 if (!mContext
->IsWebGL2() && dstFormat
->d
) {
941 if (imageTarget
!= LOCAL_GL_TEXTURE_2D
|| blob
->HasData() || level
!= 0) {
942 mContext
->ErrorInvalidOperation(
943 "With format %s, this function may only"
944 " be called with target=TEXTURE_2D,"
945 " data=null, and level=0.",
951 if (!ValidateTexImageSelection(imageTarget
, level
, offset
, size
,
955 MOZ_ASSERT(imageInfo
);
956 dstUsage
= imageInfo
->mFormat
;
958 const auto& dstFormat
= dstUsage
->format
;
959 if (!mContext
->IsWebGL2() && dstFormat
->d
) {
960 mContext
->ErrorInvalidOperation(
961 "Function may not be called on a texture of"
968 ////////////////////////////////////
971 const webgl::DriverUnpackInfo
* driverUnpackInfo
;
972 if (!dstUsage
->IsUnpackValid(pi
, &driverUnpackInfo
)) {
973 if (!fnValidateUnpackEnums()) return;
974 mContext
->ErrorInvalidOperation(
975 "Mismatched internalFormat and format/type:"
976 " 0x%04x and 0x%04x/0x%04x",
977 respecFormat
, pi
.format
, pi
.type
);
981 if (!blob
->Validate(mContext
, pi
)) return;
983 ////////////////////////////////////
986 Maybe
<webgl::ImageInfo
> newImageInfo
;
987 bool isRespec
= false;
989 // It's tempting to do allocation first, and TexSubImage second, but this is
991 newImageInfo
= Some(webgl::ImageInfo
{dstUsage
, size
.x
, size
.y
, size
.z
});
992 if (!blob
->HasData()) {
993 newImageInfo
->mUninitializedSlices
=
994 Some(std::vector
<bool>(size
.z
, true));
997 isRespec
= (imageInfo
->mWidth
!= newImageInfo
->mWidth
||
998 imageInfo
->mHeight
!= newImageInfo
->mHeight
||
999 imageInfo
->mDepth
!= newImageInfo
->mDepth
||
1000 imageInfo
->mFormat
!= newImageInfo
->mFormat
);
1002 if (!blob
->HasData()) {
1003 mContext
->ErrorInvalidValue("`source` cannot be null.");
1006 if (!EnsureImageDataInitializedForUpload(this, imageTarget
, level
, offset
,
1012 WebGLPixelStore::AssertDefault(*mContext
->gl
, mContext
->IsWebGL2());
1014 blob
->mDesc
.unpacking
.Apply(*mContext
->gl
, mContext
->IsWebGL2(), size
);
1015 const auto revertUnpacking
= MakeScopeExit([&]() {
1016 const WebGLPixelStore defaultUnpacking
;
1017 defaultUnpacking
.Apply(*mContext
->gl
, mContext
->IsWebGL2(), size
);
1020 const bool isSubImage
= !respecFormat
;
1022 if (!blob
->TexOrSubImage(isSubImage
, isRespec
, this, level
, driverUnpackInfo
,
1023 offset
.x
, offset
.y
, offset
.z
, pi
, &glError
)) {
1027 if (glError
== LOCAL_GL_OUT_OF_MEMORY
) {
1028 mContext
->ErrorOutOfMemory("Driver ran out of memory during upload.");
1034 const auto enumStr
= EnumString(glError
);
1035 const nsPrintfCString
dui(
1036 "Unexpected error %s during upload. (dui: %x/%x/%x)", enumStr
.c_str(),
1037 driverUnpackInfo
->internalFormat
, driverUnpackInfo
->unpackFormat
,
1038 driverUnpackInfo
->unpackType
);
1039 mContext
->ErrorInvalidOperation("%s", dui
.BeginReading());
1040 gfxCriticalError() << mContext
->FuncName() << ": " << dui
.BeginReading();
1044 ////////////////////////////////////
1045 // Update our specification data?
1048 mContext
->OnDataAllocCall();
1049 *imageInfo
= *newImageInfo
;
1054 ////////////////////////////////////////
1055 // CompressedTex(Sub)Image
1057 static inline bool IsSubImageBlockAligned(
1058 const webgl::CompressedFormatInfo
* compression
,
1059 const webgl::ImageInfo
* imageInfo
, GLint xOffset
, GLint yOffset
,
1060 uint32_t width
, uint32_t height
) {
1061 if (xOffset
% compression
->blockWidth
!= 0 ||
1062 yOffset
% compression
->blockHeight
!= 0) {
1066 if (width
% compression
->blockWidth
!= 0 &&
1067 xOffset
+ width
!= imageInfo
->mWidth
)
1070 if (height
% compression
->blockHeight
!= 0 &&
1071 yOffset
+ height
!= imageInfo
->mHeight
)
1077 // CompressedTexSubImage iff `sub`
1078 void WebGLTexture::CompressedTexImage(bool sub
, GLenum imageTarget
,
1079 uint32_t level
, GLenum formatEnum
,
1080 const uvec3
& offset
, const uvec3
& size
,
1081 const Range
<const uint8_t>& src
,
1082 const uint32_t pboImageSize
,
1083 const Maybe
<uint64_t>& pboOffset
) {
1084 auto imageSize
= pboImageSize
;
1086 const auto& buffer
=
1087 mContext
->ValidateBufferSelection(LOCAL_GL_PIXEL_UNPACK_BUFFER
);
1088 if (!buffer
) return;
1089 auto availBytes
= buffer
->ByteLength();
1090 if (*pboOffset
> availBytes
) {
1091 mContext
->GenerateError(
1092 LOCAL_GL_INVALID_OPERATION
,
1093 "`offset` (%llu) must be <= PIXEL_UNPACK_BUFFER size (%llu).",
1094 *pboOffset
, availBytes
);
1097 availBytes
-= *pboOffset
;
1098 if (availBytes
< pboImageSize
) {
1099 mContext
->GenerateError(
1100 LOCAL_GL_INVALID_OPERATION
,
1101 "PIXEL_UNPACK_BUFFER size minus `offset` (%llu) too small for"
1102 " `pboImageSize` (%u).",
1103 availBytes
, pboImageSize
);
1107 if (mContext
->mBoundPixelUnpackBuffer
) {
1108 mContext
->GenerateError(LOCAL_GL_INVALID_OPERATION
,
1109 "PIXEL_UNPACK_BUFFER is non-null.");
1112 imageSize
= src
.length();
1117 const auto usage
= mContext
->mFormatUsage
->GetSizedTexUsage(formatEnum
);
1118 if (!usage
|| !usage
->format
->compression
) {
1119 mContext
->ErrorInvalidEnumArg("format", formatEnum
);
1123 webgl::ImageInfo
* imageInfo
;
1125 if (!ValidateTexImageSpecification(imageTarget
, level
, size
, &imageInfo
)) {
1128 MOZ_ASSERT(imageInfo
);
1130 if (!ValidateTargetForFormat(mContext
, imageTarget
, usage
->format
)) return;
1131 if (!ValidateCompressedTexImageRestrictions(mContext
, imageTarget
, level
,
1132 usage
->format
, size
)) {
1136 if (!ValidateTexImageSelection(imageTarget
, level
, offset
, size
,
1139 MOZ_ASSERT(imageInfo
);
1141 const auto dstUsage
= imageInfo
->mFormat
;
1142 if (usage
!= dstUsage
) {
1143 mContext
->ErrorInvalidOperation(
1144 "`format` must match the format of the"
1145 " existing texture image.");
1149 const auto& format
= usage
->format
;
1150 switch (format
->compression
->family
) {
1152 case webgl::CompressionFamily::ETC1
:
1153 mContext
->ErrorInvalidOperation(
1154 "Format does not allow sub-image"
1159 case webgl::CompressionFamily::ES3
: // Yes, the ES3 formats don't match
1161 case webgl::CompressionFamily::S3TC
: // default behavior.
1162 case webgl::CompressionFamily::BPTC
:
1163 case webgl::CompressionFamily::RGTC
:
1164 if (!IsSubImageBlockAligned(format
->compression
, imageInfo
, offset
.x
,
1165 offset
.y
, size
.x
, size
.y
)) {
1166 mContext
->ErrorInvalidOperation(
1167 "Format requires block-aligned sub-image"
1173 // Full-only: (The ES3 default)
1175 if (offset
.x
|| offset
.y
|| size
.x
!= imageInfo
->mWidth
||
1176 size
.y
!= imageInfo
->mHeight
) {
1177 mContext
->ErrorInvalidOperation(
1178 "Format does not allow partial sub-image"
1186 if (!ValidateCompressedTexUnpack(mContext
, size
, usage
->format
, imageSize
))
1189 ////////////////////////////////////
1193 if (!EnsureImageDataInitializedForUpload(this, imageTarget
, level
, offset
,
1199 const ScopedLazyBind
bindPBO(mContext
->gl
, LOCAL_GL_PIXEL_UNPACK_BUFFER
,
1200 mContext
->mBoundPixelUnpackBuffer
);
1204 ptr
= reinterpret_cast<const void*>(*pboOffset
);
1206 ptr
= reinterpret_cast<const void*>(src
.begin().get());
1210 error
= DoCompressedTexImage(mContext
->gl
, imageTarget
, level
, formatEnum
,
1211 size
.x
, size
.y
, size
.z
, imageSize
, ptr
);
1213 error
= DoCompressedTexSubImage(mContext
->gl
, imageTarget
, level
, offset
.x
,
1214 offset
.y
, offset
.z
, size
.x
, size
.y
, size
.z
,
1215 formatEnum
, imageSize
, ptr
);
1217 if (error
== LOCAL_GL_OUT_OF_MEMORY
) {
1218 mContext
->ErrorOutOfMemory("Ran out of memory during upload.");
1223 mContext
->GenerateError(error
, "Unexpected error from driver.");
1226 call
= nsPrintfCString(
1227 "DoCompressedTexImage(0x%04x, %u, 0x%04x, %u,%u,%u, %u, %p)",
1228 imageTarget
, level
, formatEnum
, size
.x
, size
.y
, size
.z
, imageSize
,
1231 call
= nsPrintfCString(
1232 "DoCompressedTexSubImage(0x%04x, %u, %u,%u,%u, %u,%u,%u, 0x%04x, %u, "
1234 imageTarget
, level
, offset
.x
, offset
.y
, offset
.z
, size
.x
, size
.y
,
1235 size
.z
, formatEnum
, imageSize
, ptr
);
1237 gfxCriticalError() << "Unexpected error " << gfx::hexa(error
)
1238 << " from driver: " << call
.BeginReading();
1242 ////////////////////////////////////
1243 // Update our specification data?
1246 const auto uninitializedSlices
= Nothing();
1247 const webgl::ImageInfo newImageInfo
{usage
, size
.x
, size
.y
, size
.z
,
1248 uninitializedSlices
};
1249 *imageInfo
= newImageInfo
;
1254 ////////////////////////////////////////
1255 // CopyTex(Sub)Image
1257 static bool ValidateCopyTexImageFormats(WebGLContext
* webgl
,
1258 const webgl::FormatInfo
* srcFormat
,
1259 const webgl::FormatInfo
* dstFormat
) {
1260 MOZ_ASSERT(!srcFormat
->compression
);
1261 if (dstFormat
->compression
) {
1262 webgl
->ErrorInvalidEnum(
1263 "Specified destination must not have a compressed"
1268 if (dstFormat
->effectiveFormat
== webgl::EffectiveFormat::RGB9_E5
) {
1269 webgl
->ErrorInvalidOperation(
1270 "RGB9_E5 is an invalid destination for"
1271 " CopyTex(Sub)Image. (GLES 3.0.4 p145)");
1275 if (!DoChannelsMatchForCopyTexImage(srcFormat
, dstFormat
)) {
1276 webgl
->ErrorInvalidOperation(
1277 "Destination channels must be compatible with"
1278 " source channels. (GLES 3.0.4 p140 Table 3.16)");
1285 ////////////////////////////////////////////////////////////////////////////////
1287 class ScopedCopyTexImageSource
{
1288 WebGLContext
* const mWebGL
;
1293 ScopedCopyTexImageSource(WebGLContext
* webgl
, uint32_t srcWidth
,
1295 const webgl::FormatInfo
* srcFormat
,
1296 const webgl::FormatUsageInfo
* dstUsage
);
1297 ~ScopedCopyTexImageSource();
1300 ScopedCopyTexImageSource::ScopedCopyTexImageSource(
1301 WebGLContext
* webgl
, uint32_t srcWidth
, uint32_t srcHeight
,
1302 const webgl::FormatInfo
* srcFormat
, const webgl::FormatUsageInfo
* dstUsage
)
1303 : mWebGL(webgl
), mRB(0), mFB(0) {
1304 switch (dstUsage
->format
->unsizedFormat
) {
1305 case webgl::UnsizedFormat::L
:
1306 case webgl::UnsizedFormat::A
:
1307 case webgl::UnsizedFormat::LA
:
1308 webgl
->GenerateWarning(
1309 "Copying to a LUMINANCE, ALPHA, or LUMINANCE_ALPHA"
1310 " is deprecated, and has severely reduced performance"
1311 " on some platforms.");
1315 MOZ_ASSERT(!dstUsage
->textureSwizzleRGBA
);
1319 if (!dstUsage
->textureSwizzleRGBA
) return;
1321 gl::GLContext
* gl
= webgl
->gl
;
1325 switch (srcFormat
->componentType
) {
1326 case webgl::ComponentType::NormUInt
:
1327 sizedFormat
= LOCAL_GL_RGBA8
;
1330 case webgl::ComponentType::Float
:
1331 if (webgl
->IsExtensionEnabled(
1332 WebGLExtensionID::WEBGL_color_buffer_float
)) {
1333 sizedFormat
= LOCAL_GL_RGBA32F
;
1334 webgl
->WarnIfImplicit(WebGLExtensionID::WEBGL_color_buffer_float
);
1338 if (webgl
->IsExtensionEnabled(
1339 WebGLExtensionID::EXT_color_buffer_half_float
)) {
1340 sizedFormat
= LOCAL_GL_RGBA16F
;
1341 webgl
->WarnIfImplicit(WebGLExtensionID::EXT_color_buffer_half_float
);
1344 MOZ_CRASH("GFX: Should be able to request CopyTexImage from Float.");
1347 MOZ_CRASH("GFX: Should be able to request CopyTexImage from this type.");
1350 gl::ScopedTexture
scopedTex(gl
);
1351 gl::ScopedBindTexture
scopedBindTex(gl
, scopedTex
.Texture(),
1352 LOCAL_GL_TEXTURE_2D
);
1354 gl
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_MIN_FILTER
,
1356 gl
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_MAG_FILTER
,
1359 GLint blitSwizzle
[4] = {LOCAL_GL_ZERO
};
1360 switch (dstUsage
->format
->unsizedFormat
) {
1361 case webgl::UnsizedFormat::L
:
1362 blitSwizzle
[0] = LOCAL_GL_RED
;
1365 case webgl::UnsizedFormat::A
:
1366 blitSwizzle
[0] = LOCAL_GL_ALPHA
;
1369 case webgl::UnsizedFormat::LA
:
1370 blitSwizzle
[0] = LOCAL_GL_RED
;
1371 blitSwizzle
[1] = LOCAL_GL_ALPHA
;
1375 MOZ_CRASH("GFX: Unhandled unsizedFormat.");
1378 gl
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_SWIZZLE_R
,
1380 gl
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_SWIZZLE_G
,
1382 gl
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_SWIZZLE_B
,
1384 gl
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_SWIZZLE_A
,
1387 gl
->fCopyTexImage2D(LOCAL_GL_TEXTURE_2D
, 0, sizedFormat
, 0, 0, srcWidth
,
1390 // Now create the swizzled FB we'll be exposing.
1395 gl
->fGenRenderbuffers(1, &rgbaRB
);
1396 gl::ScopedBindRenderbuffer
scopedRB(gl
, rgbaRB
);
1397 gl
->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER
, sizedFormat
, srcWidth
,
1400 gl
->fGenFramebuffers(1, &rgbaFB
);
1401 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, rgbaFB
);
1402 gl
->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER
,
1403 LOCAL_GL_COLOR_ATTACHMENT0
,
1404 LOCAL_GL_RENDERBUFFER
, rgbaRB
);
1406 const GLenum status
= gl
->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER
);
1407 if (status
!= LOCAL_GL_FRAMEBUFFER_COMPLETE
) {
1408 MOZ_CRASH("GFX: Temp framebuffer is not complete.");
1412 // Draw-blit rgbaTex into rgbaFB.
1413 const gfx::IntSize
srcSize(srcWidth
, srcHeight
);
1415 const gl::ScopedBindFramebuffer
bindFB(gl
, rgbaFB
);
1416 gl
->BlitHelper()->DrawBlitTextureToFramebuffer(scopedTex
.Texture(), srcSize
,
1420 // Leave RB and FB alive, and FB bound.
1425 template <typename T
>
1426 static inline GLenum
ToGLHandle(const T
& obj
) {
1427 return (obj
? obj
->mGLName
: 0);
1430 ScopedCopyTexImageSource::~ScopedCopyTexImageSource() {
1437 gl::GLContext
* gl
= mWebGL
->gl
;
1439 // If we're swizzling, it's because we're on a GL core (3.2+) profile, which
1440 // has split framebuffer support.
1441 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
,
1442 ToGLHandle(mWebGL
->mBoundDrawFramebuffer
));
1443 gl
->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
,
1444 ToGLHandle(mWebGL
->mBoundReadFramebuffer
));
1446 gl
->fDeleteFramebuffers(1, &mFB
);
1447 gl
->fDeleteRenderbuffers(1, &mRB
);
1450 ////////////////////////////////////////////////////////////////////////////////
1452 static bool GetUnsizedFormatForCopy(GLenum internalFormat
,
1453 webgl::UnsizedFormat
* const out
) {
1454 switch (internalFormat
) {
1456 *out
= webgl::UnsizedFormat::R
;
1459 *out
= webgl::UnsizedFormat::RG
;
1462 *out
= webgl::UnsizedFormat::RGB
;
1465 *out
= webgl::UnsizedFormat::RGBA
;
1467 case LOCAL_GL_LUMINANCE
:
1468 *out
= webgl::UnsizedFormat::L
;
1470 case LOCAL_GL_ALPHA
:
1471 *out
= webgl::UnsizedFormat::A
;
1473 case LOCAL_GL_LUMINANCE_ALPHA
:
1474 *out
= webgl::UnsizedFormat::LA
;
1484 static const webgl::FormatUsageInfo
* ValidateCopyDestUsage(
1485 WebGLContext
* webgl
, const webgl::FormatInfo
* srcFormat
,
1486 GLenum internalFormat
) {
1487 const auto& fua
= webgl
->mFormatUsage
;
1489 switch (internalFormat
) {
1490 case LOCAL_GL_R8_SNORM
:
1491 case LOCAL_GL_RG8_SNORM
:
1492 case LOCAL_GL_RGB8_SNORM
:
1493 case LOCAL_GL_RGBA8_SNORM
:
1494 webgl
->ErrorInvalidEnum("SNORM formats are invalid for CopyTexImage.");
1498 auto dstUsage
= fua
->GetSizedTexUsage(internalFormat
);
1500 // Ok, maybe it's unsized.
1501 webgl::UnsizedFormat unsizedFormat
;
1502 if (!GetUnsizedFormatForCopy(internalFormat
, &unsizedFormat
)) {
1503 webgl
->ErrorInvalidEnumInfo("internalFormat", internalFormat
);
1507 const auto dstFormat
= srcFormat
->GetCopyDecayFormat(unsizedFormat
);
1509 dstUsage
= fua
->GetUsage(dstFormat
->effectiveFormat
);
1512 webgl
->ErrorInvalidOperation(
1513 "0x%04x is not a valid unsized format for"
1514 " source format %s.",
1515 internalFormat
, srcFormat
->name
);
1521 // Alright, it's sized.
1523 const auto dstFormat
= dstUsage
->format
;
1525 if (dstFormat
->componentType
!= srcFormat
->componentType
) {
1526 webgl
->ErrorInvalidOperation(
1527 "For sized internalFormats, source and dest"
1528 " component types must match. (source: %s, dest:"
1530 srcFormat
->name
, dstFormat
->name
);
1534 bool componentSizesMatch
= true;
1536 componentSizesMatch
&= (dstFormat
->r
== srcFormat
->r
);
1539 componentSizesMatch
&= (dstFormat
->g
== srcFormat
->g
);
1542 componentSizesMatch
&= (dstFormat
->b
== srcFormat
->b
);
1545 componentSizesMatch
&= (dstFormat
->a
== srcFormat
->a
);
1548 if (!componentSizesMatch
) {
1549 webgl
->ErrorInvalidOperation(
1550 "For sized internalFormats, source and dest"
1551 " component sizes must match exactly. (source: %s,"
1553 srcFormat
->name
, dstFormat
->name
);
1560 static bool ValidateCopyTexImageForFeedback(const WebGLContext
& webgl
,
1561 const WebGLTexture
& tex
,
1562 const uint32_t mipLevel
,
1563 const uint32_t zLayer
) {
1564 const auto& fb
= webgl
.BoundReadFb();
1566 MOZ_ASSERT(fb
->ColorReadBuffer());
1567 const auto& attach
= *fb
->ColorReadBuffer();
1568 MOZ_ASSERT(attach
.ZLayerCount() ==
1569 1); // Multiview invalid for copyTexImage.
1571 if (attach
.Texture() == &tex
&& attach
.Layer() == zLayer
&&
1572 attach
.MipLevel() == mipLevel
) {
1573 // Note that the TexImageTargets *don't* have to match for this to be
1574 // undefined per GLES 3.0.4 p211, thus an INVALID_OP in WebGL.
1575 webgl
.ErrorInvalidOperation(
1576 "Feedback loop detected, as this texture"
1577 " is already attached to READ_FRAMEBUFFER's"
1578 " READ_BUFFER-selected COLOR_ATTACHMENT%u.",
1579 attach
.mAttachmentPoint
);
1586 static bool DoCopyTexOrSubImage(WebGLContext
* webgl
, bool isSubImage
,
1587 bool needsInit
, WebGLTexture
* const tex
,
1588 const TexImageTarget target
, GLint level
,
1589 GLint xWithinSrc
, GLint yWithinSrc
,
1590 uint32_t srcTotalWidth
, uint32_t srcTotalHeight
,
1591 const webgl::FormatUsageInfo
* srcUsage
,
1592 GLint xOffset
, GLint yOffset
, GLint zOffset
,
1593 uint32_t dstWidth
, uint32_t dstHeight
,
1594 const webgl::FormatUsageInfo
* dstUsage
) {
1595 const auto& gl
= webgl
->gl
;
1599 int32_t readX
, readY
;
1600 int32_t writeX
, writeY
;
1601 int32_t rwWidth
, rwHeight
;
1602 if (!Intersect(srcTotalWidth
, xWithinSrc
, dstWidth
, &readX
, &writeX
,
1604 !Intersect(srcTotalHeight
, yWithinSrc
, dstHeight
, &readY
, &writeY
,
1606 webgl
->ErrorOutOfMemory("Bad subrect selection.");
1616 nsCString errorText
;
1618 const auto& idealUnpack
= dstUsage
->idealUnpack
;
1619 const auto& pi
= idealUnpack
->ToPacking();
1622 const bool fullOverwrite
=
1623 (uint32_t(rwWidth
) == dstWidth
&& uint32_t(rwHeight
) == dstHeight
);
1624 if (needsInit
&& !fullOverwrite
) {
1625 CheckedInt
<size_t> byteCount
= BytesPerPixel(pi
);
1626 byteCount
*= dstWidth
;
1627 byteCount
*= dstHeight
;
1629 if (byteCount
.isValid()) {
1630 zeros
= calloc(1u, byteCount
.value());
1634 webgl
->ErrorOutOfMemory("Ran out of memory allocating zeros.");
1639 if (!isSubImage
|| zeros
) {
1640 WebGLPixelStore::AssertDefault(*gl
, webgl
->IsWebGL2());
1642 gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 1);
1643 const auto revert
= MakeScopeExit(
1644 [&]() { gl
->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT
, 4); });
1646 error
= DoTexImage(gl
, target
, level
, idealUnpack
, dstWidth
, dstHeight
,
1649 errorText
= nsPrintfCString(
1650 "DoTexImage(0x%04x, %i, {0x%04x, 0x%04x, 0x%04x}, %u,%u,1) -> "
1652 target
.get(), level
, idealUnpack
->internalFormat
,
1653 idealUnpack
->unpackFormat
, idealUnpack
->unpackType
, dstWidth
,
1659 error
= DoTexSubImage(gl
, target
, level
, xOffset
, yOffset
, zOffset
,
1660 dstWidth
, dstHeight
, 1, pi
, zeros
.get());
1662 errorText
= nsPrintfCString(
1663 "DoTexSubImage(0x%04x, %i, %i,%i,%i, %u,%u,1, {0x%04x, 0x%04x}) "
1666 target
.get(), level
, xOffset
, yOffset
, zOffset
, dstWidth
,
1667 dstHeight
, idealUnpack
->unpackFormat
, idealUnpack
->unpackType
,
1674 if (!rwWidth
|| !rwHeight
) {
1675 // There aren't any pixels to copy, so we're 'done'.
1679 const auto& srcFormat
= srcUsage
->format
;
1680 ScopedCopyTexImageSource
maybeSwizzle(webgl
, srcTotalWidth
, srcTotalHeight
,
1681 srcFormat
, dstUsage
);
1683 error
= DoCopyTexSubImage(gl
, target
, level
, writeX
, writeY
, zOffset
, readX
,
1684 readY
, rwWidth
, rwHeight
);
1686 errorText
= nsPrintfCString(
1687 "DoCopyTexSubImage(0x%04x, %i, %i,%i,%i, %i,%i, %u,%u) -> 0x%04x",
1688 target
.get(), level
, writeX
, writeY
, zOffset
, readX
, readY
, rwWidth
,
1696 if (error
== LOCAL_GL_OUT_OF_MEMORY
) {
1697 webgl
->ErrorOutOfMemory("Ran out of memory during texture copy.");
1702 if (gl
->IsANGLE() && error
== LOCAL_GL_INVALID_OPERATION
) {
1703 webgl
->ErrorImplementationBug(
1704 "ANGLE is particular about CopyTexSubImage"
1705 " formats matching exactly.");
1709 webgl
->GenerateError(error
, "Unexpected error from driver.");
1710 gfxCriticalError() << "Unexpected error from driver: "
1711 << errorText
.BeginReading();
1715 // CopyTexSubImage if `!respecFormat`
1716 void WebGLTexture::CopyTexImage(GLenum imageTarget
, uint32_t level
,
1717 GLenum respecFormat
, const uvec3
& dstOffset
,
1718 const ivec2
& srcOffset
, const uvec2
& size2
) {
1719 ////////////////////////////////////
1722 const webgl::FormatUsageInfo
* srcUsage
;
1723 uint32_t srcTotalWidth
;
1724 uint32_t srcTotalHeight
;
1725 if (!mContext
->BindCurFBForColorRead(&srcUsage
, &srcTotalWidth
,
1729 const auto& srcFormat
= srcUsage
->format
;
1731 if (!ValidateCopyTexImageForFeedback(*mContext
, *this, level
, dstOffset
.z
))
1734 const auto size
= uvec3
{size2
.x
, size2
.y
, 1};
1736 ////////////////////////////////////
1739 webgl::ImageInfo
* imageInfo
;
1740 const webgl::FormatUsageInfo
* dstUsage
;
1742 if (!ValidateTexImageSpecification(imageTarget
, level
, size
, &imageInfo
))
1744 MOZ_ASSERT(imageInfo
);
1746 dstUsage
= ValidateCopyDestUsage(mContext
, srcFormat
, respecFormat
);
1747 if (!dstUsage
) return;
1749 if (!ValidateTargetForFormat(mContext
, imageTarget
, dstUsage
->format
))
1752 if (!ValidateTexImageSelection(imageTarget
, level
, dstOffset
, size
,
1756 MOZ_ASSERT(imageInfo
);
1758 dstUsage
= imageInfo
->mFormat
;
1759 MOZ_ASSERT(dstUsage
);
1762 const auto& dstFormat
= dstUsage
->format
;
1763 if (!mContext
->IsWebGL2() && dstFormat
->d
) {
1764 mContext
->ErrorInvalidOperation(
1765 "Function may not be called with format %s.", dstFormat
->name
);
1769 ////////////////////////////////////
1770 // Check that source and dest info are compatible
1772 if (!ValidateCopyTexImageFormats(mContext
, srcFormat
, dstFormat
)) return;
1774 ////////////////////////////////////
1777 const bool isSubImage
= !respecFormat
;
1778 bool expectsInit
= true;
1780 if (!EnsureImageDataInitializedForUpload(this, imageTarget
, level
,
1781 dstOffset
, size
, imageInfo
,
1787 if (!DoCopyTexOrSubImage(mContext
, isSubImage
, expectsInit
, this, imageTarget
,
1788 level
, srcOffset
.x
, srcOffset
.y
, srcTotalWidth
,
1789 srcTotalHeight
, srcUsage
, dstOffset
.x
, dstOffset
.y
,
1790 dstOffset
.z
, size
.x
, size
.y
, dstUsage
)) {
1794 mContext
->OnDataAllocCall();
1796 ////////////////////////////////////
1797 // Update our specification data?
1800 const auto uninitializedSlices
= Nothing();
1801 const webgl::ImageInfo newImageInfo
{dstUsage
, size
.x
, size
.y
, size
.z
,
1802 uninitializedSlices
};
1803 *imageInfo
= newImageInfo
;
1808 } // namespace mozilla