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 "WebGLFramebuffer.h"
8 // You know it's going to be fun when these two show up:
12 #include "GLBlitHelper.h"
13 #include "GLContext.h"
14 #include "GLScreenBuffer.h"
15 #include "MozFramebuffer.h"
16 #include "mozilla/dom/WebGLRenderingContextBinding.h"
17 #include "mozilla/IntegerRange.h"
18 #include "nsPrintfCString.h"
19 #include "WebGLContext.h"
20 #include "WebGLContextUtils.h"
21 #include "WebGLExtensions.h"
22 #include "WebGLFormats.h"
23 #include "WebGLRenderbuffer.h"
24 #include "WebGLTexture.h"
28 static bool ShouldDeferAttachment(const WebGLContext
* const webgl
,
29 const GLenum attachPoint
) {
30 if (webgl
->IsWebGL2()) return false;
32 switch (attachPoint
) {
33 case LOCAL_GL_DEPTH_ATTACHMENT
:
34 case LOCAL_GL_STENCIL_ATTACHMENT
:
35 case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
:
42 WebGLFBAttachPoint::WebGLFBAttachPoint() = default;
43 WebGLFBAttachPoint::WebGLFBAttachPoint(WebGLFBAttachPoint
&) = default;
45 WebGLFBAttachPoint::WebGLFBAttachPoint(const WebGLContext
* const webgl
,
46 const GLenum attachmentPoint
)
47 : mAttachmentPoint(attachmentPoint
),
48 mDeferAttachment(ShouldDeferAttachment(webgl
, mAttachmentPoint
)) {}
50 WebGLFBAttachPoint::~WebGLFBAttachPoint() {
51 MOZ_ASSERT(!mRenderbufferPtr
);
52 MOZ_ASSERT(!mTexturePtr
);
55 void WebGLFBAttachPoint::Clear() { Set(nullptr, {}); }
57 void WebGLFBAttachPoint::Set(gl::GLContext
* const gl
,
58 const webgl::FbAttachInfo
& toAttach
) {
59 mRenderbufferPtr
= toAttach
.rb
;
60 mTexturePtr
= toAttach
.tex
;
61 mTexImageLayer
= AssertedCast
<uint32_t>(toAttach
.zLayer
);
62 mTexImageZLayerCount
= AssertedCast
<uint8_t>(toAttach
.zLayerCount
);
63 mTexImageLevel
= AssertedCast
<uint8_t>(toAttach
.mipLevel
);
64 mIsMultiview
= toAttach
.isMultiview
;
66 if (gl
&& !mDeferAttachment
) {
71 const webgl::ImageInfo
* WebGLFBAttachPoint::GetImageInfo() const {
73 const auto target
= Texture()->Target();
75 if (target
== LOCAL_GL_TEXTURE_CUBE_MAP
) {
78 return &mTexturePtr
->ImageInfoAtFace(face
, mTexImageLevel
);
80 if (mRenderbufferPtr
) return &mRenderbufferPtr
->ImageInfo();
84 bool WebGLFBAttachPoint::IsComplete(WebGLContext
* webgl
,
85 nsCString
* const out_info
) const {
86 MOZ_ASSERT(HasAttachment());
88 const auto fnWriteErrorInfo
= [&](const char* const text
) {
89 WebGLContext::EnumName(mAttachmentPoint
, out_info
);
90 out_info
->AppendLiteral(": ");
91 out_info
->AppendASCII(text
);
94 const auto& imageInfo
= *GetImageInfo();
95 if (!imageInfo
.mWidth
|| !imageInfo
.mHeight
) {
96 fnWriteErrorInfo("Attachment has no width or height.");
99 MOZ_ASSERT(imageInfo
.IsDefined());
101 const auto& tex
= Texture();
103 // ES 3.0 spec, pg 213 has giant blocks of text that bake down to requiring
104 // that attached *non-immutable* tex images are within the valid mip-levels
105 // of the texture. We still need to check immutable textures though, because
106 // checking completeness is also when we zero invalidated/no-data tex
108 const auto attachedMipLevel
= MipLevel();
110 const bool withinValidMipLevels
= [&]() {
111 const bool ensureInit
= false;
112 const auto texCompleteness
= tex
->CalcCompletenessInfo(ensureInit
);
113 if (!texCompleteness
) return false; // OOM
115 if (tex
->Immutable()) {
116 // Immutable textures can attach a level that's not valid for sampling.
117 // It still has to exist though!
118 return attachedMipLevel
< tex
->ImmutableLevelCount();
121 // Base level must be complete.
122 if (!texCompleteness
->levels
) return false;
124 const auto baseLevel
= tex
->Es3_level_base();
125 if (attachedMipLevel
== baseLevel
) return true;
127 // If not base level, must be mip-complete and within mips.
128 if (!texCompleteness
->mipmapComplete
) return false;
129 const auto maxLevel
= baseLevel
+ texCompleteness
->levels
- 1;
130 return baseLevel
<= attachedMipLevel
&& attachedMipLevel
<= maxLevel
;
132 if (!withinValidMipLevels
) {
133 fnWriteErrorInfo("Attached mip level is invalid for texture.");
137 const auto& levelInfo
= tex
->ImageInfoAtFace(0, attachedMipLevel
);
138 const auto faceDepth
= levelInfo
.mDepth
* tex
->FaceCount();
139 const bool withinValidZLayers
= Layer() + ZLayerCount() - 1 < faceDepth
;
140 if (!withinValidZLayers
) {
141 fnWriteErrorInfo("Attached z layer is invalid for texture.");
146 const auto& formatUsage
= imageInfo
.mFormat
;
147 if (!formatUsage
->IsRenderable()) {
148 const auto info
= nsPrintfCString(
149 "Attachment has an effective format of %s,"
150 " which is not renderable.",
151 formatUsage
->format
->name
);
152 fnWriteErrorInfo(info
.BeginReading());
155 if (!formatUsage
->IsExplicitlyRenderable()) {
156 webgl
->WarnIfImplicit(formatUsage
->GetExtensionID());
159 const auto format
= formatUsage
->format
;
161 bool hasRequiredBits
;
163 switch (mAttachmentPoint
) {
164 case LOCAL_GL_DEPTH_ATTACHMENT
:
165 hasRequiredBits
= format
->d
;
168 case LOCAL_GL_STENCIL_ATTACHMENT
:
169 hasRequiredBits
= format
->s
;
172 case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
:
173 MOZ_ASSERT(!webgl
->IsWebGL2());
174 hasRequiredBits
= (format
->d
&& format
->s
);
178 MOZ_ASSERT(mAttachmentPoint
>= LOCAL_GL_COLOR_ATTACHMENT0
);
179 hasRequiredBits
= format
->IsColorFormat();
183 if (!hasRequiredBits
) {
185 "Attachment's format is missing required color/depth/stencil"
190 if (!webgl
->IsWebGL2()) {
191 bool hasSurplusPlanes
= false;
193 switch (mAttachmentPoint
) {
194 case LOCAL_GL_DEPTH_ATTACHMENT
:
195 hasSurplusPlanes
= format
->s
;
198 case LOCAL_GL_STENCIL_ATTACHMENT
:
199 hasSurplusPlanes
= format
->d
;
203 if (hasSurplusPlanes
) {
205 "Attachment has depth or stencil bits when it shouldn't.");
213 void WebGLFBAttachPoint::DoAttachment(gl::GLContext
* const gl
) const {
214 if (Renderbuffer()) {
215 Renderbuffer()->DoFramebufferRenderbuffer(mAttachmentPoint
);
220 MOZ_ASSERT(mAttachmentPoint
!= LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
);
221 // WebGL 2 doesn't have a real attachment for this, and WebGL 1 is defered
222 // and only DoAttachment if HasAttachment.
224 gl
->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER
, mAttachmentPoint
,
225 LOCAL_GL_RENDERBUFFER
, 0);
229 const auto& texName
= Texture()->mGLName
;
231 switch (Texture()->Target().get()) {
232 case LOCAL_GL_TEXTURE_2D
:
233 case LOCAL_GL_TEXTURE_CUBE_MAP
: {
234 TexImageTarget imageTarget
= LOCAL_GL_TEXTURE_2D
;
235 if (Texture()->Target() == LOCAL_GL_TEXTURE_CUBE_MAP
) {
236 imageTarget
= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X
+ Layer();
239 if (mAttachmentPoint
== LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
) {
240 gl
->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER
,
241 LOCAL_GL_DEPTH_ATTACHMENT
, imageTarget
.get(),
242 texName
, MipLevel());
243 gl
->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER
,
244 LOCAL_GL_STENCIL_ATTACHMENT
,
245 imageTarget
.get(), texName
, MipLevel());
247 gl
->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER
, mAttachmentPoint
,
248 imageTarget
.get(), texName
, MipLevel());
253 case LOCAL_GL_TEXTURE_2D_ARRAY
:
254 case LOCAL_GL_TEXTURE_3D
:
255 if (ZLayerCount() != 1) {
256 gl
->fFramebufferTextureMultiview(LOCAL_GL_FRAMEBUFFER
, mAttachmentPoint
,
257 texName
, MipLevel(), Layer(),
260 gl
->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER
, mAttachmentPoint
,
261 texName
, MipLevel(), Layer());
267 Maybe
<double> WebGLFBAttachPoint::GetParameter(WebGLContext
* webgl
,
269 GLenum pname
) const {
270 if (!HasAttachment()) {
271 // Divergent between GLES 3 and 2.
274 // "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is NONE, then
275 // querying any other pname will generate INVALID_ENUM."
278 // "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is NONE, no
279 // framebuffer is bound to target. In this case querying pname
280 // FRAMEBUFFER_ATTACHMENT_OBJECT_NAME will return zero, and all other
281 // queries will generate an INVALID_OPERATION error."
283 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE
:
284 return Some(LOCAL_GL_NONE
);
286 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME
:
287 if (webgl
->IsWebGL2()) return Nothing();
294 nsCString attachmentName
;
295 WebGLContext::EnumName(attachment
, &attachmentName
);
296 if (webgl
->IsWebGL2()) {
297 webgl
->ErrorInvalidOperation("No attachment at %s.",
298 attachmentName
.BeginReading());
300 webgl
->ErrorInvalidEnum("No attachment at %s.",
301 attachmentName
.BeginReading());
306 bool isPNameValid
= false;
308 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE
:
309 return Some(mTexturePtr
? LOCAL_GL_TEXTURE
: LOCAL_GL_RENDERBUFFER
);
313 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL
:
314 if (mTexturePtr
) return Some(AssertedCast
<uint32_t>(MipLevel()));
317 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE
:
320 if (mTexturePtr
->Target() == LOCAL_GL_TEXTURE_CUBE_MAP
) {
321 face
= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X
+ Layer();
329 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER
:
330 if (webgl
->IsWebGL2()) {
331 return Some(AssertedCast
<int32_t>(Layer()));
335 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR
:
336 if (webgl
->IsExtensionEnabled(WebGLExtensionID::OVR_multiview2
)) {
337 return Some(AssertedCast
<int32_t>(Layer()));
341 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR
:
342 if (webgl
->IsExtensionEnabled(WebGLExtensionID::OVR_multiview2
)) {
343 return Some(AssertedCast
<uint32_t>(ZLayerCount()));
349 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE
:
350 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE
:
351 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE
:
352 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE
:
353 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE
:
354 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE
:
355 isPNameValid
= webgl
->IsWebGL2();
358 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE
:
359 isPNameValid
= (webgl
->IsWebGL2() ||
360 webgl
->IsExtensionEnabled(
361 WebGLExtensionID::WEBGL_color_buffer_float
) ||
362 webgl
->IsExtensionEnabled(
363 WebGLExtensionID::EXT_color_buffer_half_float
));
366 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING
:
367 isPNameValid
= (webgl
->IsWebGL2() ||
368 webgl
->IsExtensionEnabled(WebGLExtensionID::EXT_sRGB
));
373 webgl
->ErrorInvalidEnum("Invalid pname: 0x%04x", pname
);
377 const auto& imageInfo
= *GetImageInfo();
378 const auto& usage
= imageInfo
.mFormat
;
380 if (pname
== LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING
)
381 return Some(LOCAL_GL_LINEAR
);
386 auto format
= usage
->format
;
390 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE
:
393 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE
:
396 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE
:
399 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE
:
402 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE
:
405 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE
:
409 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING
:
410 ret
= (format
->isSRGB
? LOCAL_GL_SRGB
: LOCAL_GL_LINEAR
);
413 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE
:
414 if (attachment
== LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
) {
415 webgl
->ErrorInvalidOperation(
417 " FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE"
418 " against DEPTH_STENCIL_ATTACHMENT is an"
423 if (format
->unsizedFormat
== webgl::UnsizedFormat::DEPTH_STENCIL
) {
424 MOZ_ASSERT(attachment
== LOCAL_GL_DEPTH_ATTACHMENT
||
425 attachment
== LOCAL_GL_STENCIL_ATTACHMENT
);
427 if (attachment
== LOCAL_GL_DEPTH_ATTACHMENT
) {
428 switch (format
->effectiveFormat
) {
429 case webgl::EffectiveFormat::DEPTH24_STENCIL8
:
431 webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT24
);
433 case webgl::EffectiveFormat::DEPTH32F_STENCIL8
:
435 webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT32F
);
438 MOZ_ASSERT(false, "no matched DS format");
441 } else if (attachment
== LOCAL_GL_STENCIL_ATTACHMENT
) {
442 switch (format
->effectiveFormat
) {
443 case webgl::EffectiveFormat::DEPTH24_STENCIL8
:
444 case webgl::EffectiveFormat::DEPTH32F_STENCIL8
:
445 format
= webgl::GetFormat(webgl::EffectiveFormat::STENCIL_INDEX8
);
448 MOZ_ASSERT(false, "no matched DS format");
454 switch (format
->componentType
) {
455 case webgl::ComponentType::Int
:
458 case webgl::ComponentType::UInt
:
459 ret
= LOCAL_GL_UNSIGNED_INT
;
461 case webgl::ComponentType::NormInt
:
462 ret
= LOCAL_GL_SIGNED_NORMALIZED
;
464 case webgl::ComponentType::NormUInt
:
465 ret
= LOCAL_GL_UNSIGNED_NORMALIZED
;
467 case webgl::ComponentType::Float
:
468 ret
= LOCAL_GL_FLOAT
;
474 MOZ_ASSERT(false, "Missing case.");
481 ////////////////////////////////////////////////////////////////////////////////
482 ////////////////////////////////////////////////////////////////////////////////
485 WebGLFramebuffer::WebGLFramebuffer(WebGLContext
* webgl
, GLuint fbo
)
486 : WebGLContextBoundObject(webgl
),
488 mDepthAttachment(webgl
, LOCAL_GL_DEPTH_ATTACHMENT
),
489 mStencilAttachment(webgl
, LOCAL_GL_STENCIL_ATTACHMENT
),
490 mDepthStencilAttachment(webgl
, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
) {
491 mAttachments
.push_back(&mDepthAttachment
);
492 mAttachments
.push_back(&mStencilAttachment
);
494 if (!webgl
->IsWebGL2()) {
495 // Only WebGL1 has a separate depth+stencil attachment point.
496 mAttachments
.push_back(&mDepthStencilAttachment
);
500 for (auto& cur
: mColorAttachments
) {
501 new (&cur
) WebGLFBAttachPoint(webgl
, LOCAL_GL_COLOR_ATTACHMENT0
+ i
);
504 mAttachments
.push_back(&cur
);
507 mColorDrawBuffers
.push_back(&mColorAttachments
[0]);
508 mColorReadBuffer
= &mColorAttachments
[0];
511 WebGLFramebuffer::WebGLFramebuffer(WebGLContext
* webgl
,
512 UniquePtr
<gl::MozFramebuffer
> fbo
)
513 : WebGLContextBoundObject(webgl
),
515 mOpaque(std::move(fbo
)),
516 mColorReadBuffer(nullptr) {
517 // Opaque Framebuffer is guaranteed to be complete at this point.
518 // Cache the Completeness info.
519 CompletenessInfo info
;
520 info
.width
= mOpaque
->mSize
.width
;
521 info
.height
= mOpaque
->mSize
.height
;
522 info
.zLayerCount
= 1;
523 info
.isMultiview
= false;
525 mCompletenessInfo
= Some(std::move(info
));
528 WebGLFramebuffer::~WebGLFramebuffer() {
531 mDepthAttachment
.Clear();
532 mStencilAttachment
.Clear();
533 mDepthStencilAttachment
.Clear();
535 for (auto& cur
: mColorAttachments
) {
539 if (!mContext
) return;
540 // If opaque, fDeleteFramebuffers is called in the destructor of
543 mContext
->gl
->fDeleteFramebuffers(1, &mGLName
);
549 Maybe
<WebGLFBAttachPoint
*> WebGLFramebuffer::GetColorAttachPoint(
550 GLenum attachPoint
) {
551 if (attachPoint
== LOCAL_GL_NONE
) return Some
<WebGLFBAttachPoint
*>(nullptr);
553 if (attachPoint
< LOCAL_GL_COLOR_ATTACHMENT0
) return Nothing();
555 const size_t colorId
= attachPoint
- LOCAL_GL_COLOR_ATTACHMENT0
;
557 MOZ_ASSERT(mContext
->Limits().maxColorDrawBuffers
<= webgl::kMaxDrawBuffers
);
558 if (colorId
>= mContext
->MaxValidDrawBuffers()) return Nothing();
560 return Some(&mColorAttachments
[colorId
]);
563 Maybe
<WebGLFBAttachPoint
*> WebGLFramebuffer::GetAttachPoint(
564 GLenum attachPoint
) {
565 switch (attachPoint
) {
566 case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
:
567 return Some(&mDepthStencilAttachment
);
569 case LOCAL_GL_DEPTH_ATTACHMENT
:
570 return Some(&mDepthAttachment
);
572 case LOCAL_GL_STENCIL_ATTACHMENT
:
573 return Some(&mStencilAttachment
);
576 return GetColorAttachPoint(attachPoint
);
580 void WebGLFramebuffer::DetachTexture(const WebGLTexture
* tex
) {
581 for (const auto& attach
: mAttachments
) {
582 if (attach
->Texture() == tex
) {
589 void WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer
* rb
) {
590 for (const auto& attach
: mAttachments
) {
591 if (attach
->Renderbuffer() == rb
) {
598 ////////////////////////////////////////////////////////////////////////////////
601 bool WebGLFramebuffer::HasDuplicateAttachments() const {
602 std::set
<WebGLFBAttachPoint::Ordered
> uniqueAttachSet
;
604 for (const auto& attach
: mColorAttachments
) {
605 if (!attach
.HasAttachment()) continue;
607 const WebGLFBAttachPoint::Ordered
ordered(attach
);
609 const bool didInsert
= uniqueAttachSet
.insert(ordered
).second
;
610 if (!didInsert
) return true;
616 bool WebGLFramebuffer::HasDefinedAttachments() const {
617 bool hasAttachments
= false;
618 for (const auto& attach
: mAttachments
) {
619 hasAttachments
|= attach
->HasAttachment();
621 return hasAttachments
;
624 bool WebGLFramebuffer::HasIncompleteAttachments(
625 nsCString
* const out_info
) const {
626 bool hasIncomplete
= false;
627 for (const auto& cur
: mAttachments
) {
628 if (!cur
->HasAttachment())
629 continue; // Not defined, so can't count as incomplete.
631 hasIncomplete
|= !cur
->IsComplete(mContext
, out_info
);
633 return hasIncomplete
;
636 bool WebGLFramebuffer::AllImageRectsMatch() const {
637 MOZ_ASSERT(HasDefinedAttachments());
638 DebugOnly
<nsCString
> fbStatusInfo
;
639 MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo
));
641 bool needsInit
= true;
645 bool hasMismatch
= false;
646 for (const auto& attach
: mAttachments
) {
647 const auto& imageInfo
= attach
->GetImageInfo();
648 if (!imageInfo
) continue;
650 const auto& curWidth
= imageInfo
->mWidth
;
651 const auto& curHeight
= imageInfo
->mHeight
;
660 hasMismatch
|= (curWidth
!= width
|| curHeight
!= height
);
665 bool WebGLFramebuffer::AllImageSamplesMatch() const {
666 MOZ_ASSERT(HasDefinedAttachments());
667 DebugOnly
<nsCString
> fbStatusInfo
;
668 MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo
));
670 bool needsInit
= true;
671 uint32_t samples
= 0;
673 bool hasMismatch
= false;
674 for (const auto& attach
: mAttachments
) {
675 const auto& imageInfo
= attach
->GetImageInfo();
676 if (!imageInfo
) continue;
678 const auto& curSamples
= imageInfo
->mSamples
;
682 samples
= curSamples
;
686 hasMismatch
|= (curSamples
!= samples
);
691 FBStatus
WebGLFramebuffer::PrecheckFramebufferStatus(
692 nsCString
* const out_info
) const {
693 MOZ_ASSERT(mContext
->mBoundDrawFramebuffer
== this ||
694 mContext
->mBoundReadFramebuffer
== this);
695 if (!HasDefinedAttachments())
696 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
; // No
699 if (HasIncompleteAttachments(out_info
))
700 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
;
702 if (!AllImageRectsMatch())
703 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
; // Inconsistent sizes
705 if (!AllImageSamplesMatch())
706 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE
; // Inconsistent samples
708 if (HasDuplicateAttachments()) return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED
;
710 if (mContext
->IsWebGL2()) {
711 MOZ_ASSERT(!mDepthStencilAttachment
.HasAttachment());
712 if (mDepthAttachment
.HasAttachment() &&
713 mStencilAttachment
.HasAttachment()) {
714 if (!mDepthAttachment
.IsEquivalentForFeedback(mStencilAttachment
))
715 return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED
;
718 const auto depthOrStencilCount
=
719 int(mDepthAttachment
.HasAttachment()) +
720 int(mStencilAttachment
.HasAttachment()) +
721 int(mDepthStencilAttachment
.HasAttachment());
722 if (depthOrStencilCount
> 1) return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED
;
726 const WebGLFBAttachPoint
* example
= nullptr;
727 for (const auto& x
: mAttachments
) {
728 if (!x
->HasAttachment()) continue;
733 if (x
->ZLayerCount() != example
->ZLayerCount()) {
734 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR
;
739 return LOCAL_GL_FRAMEBUFFER_COMPLETE
;
742 ////////////////////////////////////////
745 bool WebGLFramebuffer::ValidateAndInitAttachments(
746 const GLenum incompleteFbError
) const {
747 MOZ_ASSERT(mContext
->mBoundDrawFramebuffer
== this ||
748 mContext
->mBoundReadFramebuffer
== this);
750 const auto fbStatus
= CheckFramebufferStatus();
751 if (fbStatus
== LOCAL_GL_FRAMEBUFFER_COMPLETE
) return true;
753 mContext
->GenerateError(incompleteFbError
, "Framebuffer must be complete.");
757 bool WebGLFramebuffer::ValidateClearBufferType(
758 GLenum buffer
, uint32_t drawBuffer
,
759 const webgl::AttribBaseType funcType
) const {
760 if (buffer
!= LOCAL_GL_COLOR
) return true;
762 const auto& attach
= mColorAttachments
[drawBuffer
];
763 const auto& imageInfo
= attach
.GetImageInfo();
764 if (!imageInfo
) return true;
766 if (!count(mColorDrawBuffers
.begin(), mColorDrawBuffers
.end(), &attach
))
767 return true; // DRAW_BUFFERi set to NONE.
769 auto attachType
= webgl::AttribBaseType::Float
;
770 switch (imageInfo
->mFormat
->format
->componentType
) {
771 case webgl::ComponentType::Int
:
772 attachType
= webgl::AttribBaseType::Int
;
774 case webgl::ComponentType::UInt
:
775 attachType
= webgl::AttribBaseType::Uint
;
781 if (attachType
!= funcType
) {
782 mContext
->ErrorInvalidOperation(
783 "This attachment is of type %s, but"
784 " this function is of type %s.",
785 ToString(attachType
), ToString(funcType
));
792 bool WebGLFramebuffer::ValidateForColorRead(
793 const webgl::FormatUsageInfo
** const out_format
, uint32_t* const out_width
,
794 uint32_t* const out_height
) const {
795 if (!mColorReadBuffer
) {
796 mContext
->ErrorInvalidOperation("READ_BUFFER must not be NONE.");
800 if (mColorReadBuffer
->ZLayerCount() > 1) {
801 mContext
->GenerateError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION
,
802 "The READ_BUFFER attachment has multiple views.");
806 const auto& imageInfo
= mColorReadBuffer
->GetImageInfo();
808 mContext
->ErrorInvalidOperation(
809 "The READ_BUFFER attachment is not defined.");
813 if (imageInfo
->mSamples
) {
814 mContext
->ErrorInvalidOperation(
815 "The READ_BUFFER attachment is multisampled.");
819 *out_format
= imageInfo
->mFormat
;
820 *out_width
= imageInfo
->mWidth
;
821 *out_height
= imageInfo
->mHeight
;
825 ////////////////////////////////////////////////////////////////////////////////
826 // Resolution and caching
828 void WebGLFramebuffer::DoDeferredAttachments() const {
829 if (mContext
->IsWebGL2()) return;
831 const auto& gl
= mContext
->gl
;
832 gl
->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER
, LOCAL_GL_DEPTH_ATTACHMENT
,
833 LOCAL_GL_RENDERBUFFER
, 0);
834 gl
->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER
,
835 LOCAL_GL_STENCIL_ATTACHMENT
,
836 LOCAL_GL_RENDERBUFFER
, 0);
838 const auto fn
= [&](const WebGLFBAttachPoint
& attach
) {
839 MOZ_ASSERT(attach
.mDeferAttachment
);
840 if (attach
.HasAttachment()) {
841 attach
.DoAttachment(gl
);
844 // Only one of these will have an attachment.
845 fn(mDepthAttachment
);
846 fn(mStencilAttachment
);
847 fn(mDepthStencilAttachment
);
850 void WebGLFramebuffer::ResolveAttachmentData() const {
852 // The result of clearing integer color buffers with `Clear` is undefined.
854 // Two different approaches:
855 // On WebGL 2, we have glClearBuffer, and *must* use it for integer buffers,
856 // so let's just use it for all the buffers. One WebGL 1, we might not have
859 // WebGL 1 is easier, because we can just call glClear, possibly with
862 const auto& gl
= mContext
->gl
;
864 const webgl::ScopedPrepForResourceClear
scopedPrep(*mContext
);
866 if (mContext
->IsWebGL2()) {
867 const uint32_t uiZeros
[4] = {};
868 const int32_t iZeros
[4] = {};
869 const float fZeros
[4] = {};
870 const float fOne
[] = {1.0f
};
872 for (const auto& cur
: mAttachments
) {
873 const auto& imageInfo
= cur
->GetImageInfo();
874 if (!imageInfo
|| !imageInfo
->mUninitializedSlices
)
875 continue; // Nothing attached, or already has data.
877 const auto fnClearBuffer
= [&]() {
878 const auto& format
= imageInfo
->mFormat
->format
;
879 MOZ_ASSERT(format
->estimatedBytesPerPixel
<= sizeof(uiZeros
));
880 MOZ_ASSERT(format
->estimatedBytesPerPixel
<= sizeof(iZeros
));
881 MOZ_ASSERT(format
->estimatedBytesPerPixel
<= sizeof(fZeros
));
883 switch (cur
->mAttachmentPoint
) {
884 case LOCAL_GL_DEPTH_ATTACHMENT
:
885 gl
->fClearBufferfv(LOCAL_GL_DEPTH
, 0, fOne
);
887 case LOCAL_GL_STENCIL_ATTACHMENT
:
888 gl
->fClearBufferiv(LOCAL_GL_STENCIL
, 0, iZeros
);
891 MOZ_ASSERT(cur
->mAttachmentPoint
!=
892 LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
);
893 const uint32_t drawBuffer
=
894 cur
->mAttachmentPoint
- LOCAL_GL_COLOR_ATTACHMENT0
;
895 MOZ_ASSERT(drawBuffer
<= 100);
896 switch (format
->componentType
) {
897 case webgl::ComponentType::Int
:
898 gl
->fClearBufferiv(LOCAL_GL_COLOR
, drawBuffer
, iZeros
);
900 case webgl::ComponentType::UInt
:
901 gl
->fClearBufferuiv(LOCAL_GL_COLOR
, drawBuffer
, uiZeros
);
904 gl
->fClearBufferfv(LOCAL_GL_COLOR
, drawBuffer
, fZeros
);
910 if (imageInfo
->mDepth
> 1) {
911 const auto& tex
= cur
->Texture();
912 const gl::ScopedFramebuffer
scopedFB(gl
);
913 const gl::ScopedBindFramebuffer
scopedBindFB(gl
, scopedFB
.FB());
914 for (const auto z
: IntegerRange(imageInfo
->mDepth
)) {
915 if ((*imageInfo
->mUninitializedSlices
)[z
]) {
916 gl
->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER
,
917 cur
->mAttachmentPoint
, tex
->mGLName
,
925 imageInfo
->mUninitializedSlices
.reset();
930 uint32_t clearBits
= 0;
931 std::vector
<GLenum
> drawBufferForClear
;
933 const auto fnGather
= [&](const WebGLFBAttachPoint
& attach
,
934 const uint32_t attachClearBits
) {
935 const auto& imageInfo
= attach
.GetImageInfo();
936 if (!imageInfo
|| !imageInfo
->mUninitializedSlices
) return false;
938 clearBits
|= attachClearBits
;
939 imageInfo
->mUninitializedSlices
.reset(); // Just mark it now.
945 for (const auto& cur
: mColorAttachments
) {
946 if (fnGather(cur
, LOCAL_GL_COLOR_BUFFER_BIT
)) {
947 const uint32_t id
= cur
.mAttachmentPoint
- LOCAL_GL_COLOR_ATTACHMENT0
;
948 MOZ_ASSERT(id
<= 100);
949 drawBufferForClear
.resize(id
+ 1); // Pads with zeros!
950 drawBufferForClear
[id
] = cur
.mAttachmentPoint
;
954 (void)fnGather(mDepthAttachment
, LOCAL_GL_DEPTH_BUFFER_BIT
);
955 (void)fnGather(mStencilAttachment
, LOCAL_GL_STENCIL_BUFFER_BIT
);
956 (void)fnGather(mDepthStencilAttachment
,
957 LOCAL_GL_DEPTH_BUFFER_BIT
| LOCAL_GL_STENCIL_BUFFER_BIT
);
961 if (!clearBits
) return;
963 if (gl
->IsSupported(gl::GLFeature::draw_buffers
)) {
964 gl
->fDrawBuffers(drawBufferForClear
.size(), drawBufferForClear
.data());
967 gl
->fClear(clearBits
);
969 RefreshDrawBuffers();
972 WebGLFramebuffer::CompletenessInfo::~CompletenessInfo() {
973 if (!this->fb
) return;
974 const auto& fb
= *this->fb
;
975 const auto& webgl
= fb
.mContext
;
976 fb
.mNumFBStatusInvals
++;
977 if (fb
.mNumFBStatusInvals
> webgl
->mMaxAcceptableFBStatusInvals
) {
978 webgl
->GeneratePerfWarning(
979 "FB was invalidated after being complete %u"
980 " times. [webgl.perf.max-acceptable-fb-status-invals]",
981 uint32_t(fb
.mNumFBStatusInvals
));
985 ////////////////////////////////////////////////////////////////////////////////
988 FBStatus
WebGLFramebuffer::CheckFramebufferStatus() const {
989 if (MOZ_UNLIKELY(mOpaque
&& !mInOpaqueRAF
)) {
990 // Opaque Framebuffers are considered incomplete outside of a RAF.
991 return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED
;
994 if (mCompletenessInfo
) return LOCAL_GL_FRAMEBUFFER_COMPLETE
;
996 // Ok, let's try to resolve it!
998 nsCString statusInfo
;
999 FBStatus ret
= PrecheckFramebufferStatus(&statusInfo
);
1001 if (ret
!= LOCAL_GL_FRAMEBUFFER_COMPLETE
) break;
1003 // Looks good on our end. Let's ask the driver.
1004 gl::GLContext
* const gl
= mContext
->gl
;
1006 const ScopedFBRebinder
autoFB(mContext
);
1007 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, mGLName
);
1011 DoDeferredAttachments();
1012 RefreshDrawBuffers();
1013 RefreshReadBuffer();
1015 ret
= gl
->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER
);
1019 if (ret
!= LOCAL_GL_FRAMEBUFFER_COMPLETE
) {
1020 const nsPrintfCString
text("Bad status according to the driver: 0x%04x",
1026 ResolveAttachmentData();
1028 // Sweet, let's cache that.
1029 auto info
= CompletenessInfo
{this};
1030 mCompletenessInfo
.ResetInvalidators({});
1031 mCompletenessInfo
.AddInvalidator(*this);
1033 const auto fnIsFloat32
= [](const webgl::FormatInfo
& info
) {
1034 if (info
.componentType
!= webgl::ComponentType::Float
) return false;
1035 return info
.r
== 32;
1038 for (const auto& cur
: mAttachments
) {
1039 const auto& tex
= cur
->Texture();
1040 const auto& rb
= cur
->Renderbuffer();
1042 mCompletenessInfo
.AddInvalidator(*tex
);
1043 info
.texAttachments
.push_back(cur
);
1045 mCompletenessInfo
.AddInvalidator(*rb
);
1049 const auto& imageInfo
= cur
->GetImageInfo();
1050 MOZ_ASSERT(imageInfo
);
1052 const auto maybeColorId
= cur
->ColorAttachmentId();
1054 const auto id
= *maybeColorId
;
1055 info
.hasAttachment
[id
] = true;
1056 info
.isAttachmentF32
[id
] = fnIsFloat32(*imageInfo
->mFormat
->format
);
1059 info
.width
= imageInfo
->mWidth
;
1060 info
.height
= imageInfo
->mHeight
;
1061 info
.zLayerCount
= cur
->ZLayerCount();
1062 info
.isMultiview
= cur
->IsMultiview();
1064 MOZ_ASSERT(info
.width
&& info
.height
);
1065 mCompletenessInfo
= Some(std::move(info
));
1066 info
.fb
= nullptr; // Don't trigger the invalidation warning.
1067 return LOCAL_GL_FRAMEBUFFER_COMPLETE
;
1070 MOZ_ASSERT(ret
!= LOCAL_GL_FRAMEBUFFER_COMPLETE
);
1071 mContext
->GenerateWarning("Framebuffer not complete. (status: 0x%04x) %s",
1072 ret
.get(), statusInfo
.BeginReading());
1078 void WebGLFramebuffer::RefreshDrawBuffers() const {
1079 const auto& gl
= mContext
->gl
;
1080 if (!gl
->IsSupported(gl::GLFeature::draw_buffers
)) return;
1082 // Prior to GL4.1, having a no-image FB attachment that's selected by
1083 // DrawBuffers yields a framebuffer status of
1084 // FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER. We could workaround this only on
1085 // affected versions, but it's easier be unconditional.
1086 std::vector
<GLenum
> driverBuffers(mContext
->Limits().maxColorDrawBuffers
,
1088 for (const auto& attach
: mColorDrawBuffers
) {
1089 if (attach
->HasAttachment()) {
1090 const uint32_t index
=
1091 attach
->mAttachmentPoint
- LOCAL_GL_COLOR_ATTACHMENT0
;
1092 driverBuffers
[index
] = attach
->mAttachmentPoint
;
1096 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, mGLName
);
1097 gl
->fDrawBuffers(driverBuffers
.size(), driverBuffers
.data());
1100 void WebGLFramebuffer::RefreshReadBuffer() const {
1101 const auto& gl
= mContext
->gl
;
1102 if (!gl
->IsSupported(gl::GLFeature::read_buffer
)) return;
1104 // Prior to GL4.1, having a no-image FB attachment that's selected by
1105 // ReadBuffer yields a framebuffer status of
1106 // FRAMEBUFFER_INCOMPLETE_READ_BUFFER. We could workaround this only on
1107 // affected versions, but it's easier be unconditional.
1108 GLenum driverBuffer
= LOCAL_GL_NONE
;
1109 if (mColorReadBuffer
&& mColorReadBuffer
->HasAttachment()) {
1110 driverBuffer
= mColorReadBuffer
->mAttachmentPoint
;
1113 gl
->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
, mGLName
);
1114 gl
->fReadBuffer(driverBuffer
);
1119 void WebGLFramebuffer::DrawBuffers(const std::vector
<GLenum
>& buffers
) {
1120 if (buffers
.size() > mContext
->MaxValidDrawBuffers()) {
1121 // "An INVALID_VALUE error is generated if `n` is greater than
1122 // MAX_DRAW_BUFFERS."
1123 mContext
->ErrorInvalidValue(
1124 "`buffers` must have a length <="
1125 " MAX_DRAW_BUFFERS.");
1129 std::vector
<const WebGLFBAttachPoint
*> newColorDrawBuffers
;
1130 newColorDrawBuffers
.reserve(buffers
.size());
1132 mDrawBufferEnabled
.reset();
1133 for (const auto i
: IntegerRange(buffers
.size())) {
1134 // "If the GL is bound to a draw framebuffer object, the `i`th buffer listed
1135 // in bufs must be COLOR_ATTACHMENTi or NONE. Specifying a buffer out of
1136 // order, BACK, or COLOR_ATTACHMENTm where `m` is greater than or equal to
1137 // the value of MAX_COLOR_ATTACHMENTS, will generate the error
1138 // INVALID_OPERATION.
1140 // WEBGL_draw_buffers:
1141 // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater
1142 // than or equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter." This
1143 // means that if buffers.Length() isn't larger than MaxDrawBuffers, it won't
1144 // be larger than MaxColorAttachments.
1145 const auto& cur
= buffers
[i
];
1146 if (cur
== LOCAL_GL_COLOR_ATTACHMENT0
+ i
) {
1147 const auto& attach
= mColorAttachments
[i
];
1148 newColorDrawBuffers
.push_back(&attach
);
1149 mDrawBufferEnabled
[i
] = true;
1150 } else if (cur
!= LOCAL_GL_NONE
) {
1151 const bool isColorEnum
= (cur
>= LOCAL_GL_COLOR_ATTACHMENT0
&&
1152 cur
< mContext
->LastColorAttachmentEnum());
1153 if (cur
!= LOCAL_GL_BACK
&& !isColorEnum
) {
1154 mContext
->ErrorInvalidEnum("Unexpected enum in buffers.");
1158 mContext
->ErrorInvalidOperation(
1159 "`buffers[i]` must be NONE or"
1160 " COLOR_ATTACHMENTi.");
1167 mColorDrawBuffers
= std::move(newColorDrawBuffers
);
1168 RefreshDrawBuffers(); // Calls glDrawBuffers.
1171 void WebGLFramebuffer::ReadBuffer(GLenum attachPoint
) {
1172 const auto& maybeAttach
= GetColorAttachPoint(attachPoint
);
1175 "`mode` must be a COLOR_ATTACHMENTi, for 0 <= i <"
1176 " MAX_DRAW_BUFFERS.";
1177 if (attachPoint
== LOCAL_GL_BACK
) {
1178 mContext
->ErrorInvalidOperation(text
);
1180 mContext
->ErrorInvalidEnum(text
);
1184 const auto& attach
= maybeAttach
.value(); // Might be nullptr.
1188 mColorReadBuffer
= attach
;
1189 RefreshReadBuffer(); // Calls glReadBuffer.
1194 bool WebGLFramebuffer::FramebufferAttach(const GLenum attachEnum
,
1195 const webgl::FbAttachInfo
& toAttach
) {
1196 MOZ_ASSERT(mContext
->mBoundDrawFramebuffer
== this ||
1197 mContext
->mBoundReadFramebuffer
== this);
1199 if (MOZ_UNLIKELY(mOpaque
)) {
1200 // An opaque framebuffer's attachments cannot be inspected or changed.
1205 const auto maybeAttach
= GetAttachPoint(attachEnum
);
1206 if (!maybeAttach
|| !maybeAttach
.value()) return false;
1207 const auto& attach
= maybeAttach
.value();
1209 const auto& gl
= mContext
->gl
;
1210 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, mGLName
);
1211 if (mContext
->IsWebGL2() && attachEnum
== LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
) {
1212 mDepthAttachment
.Set(gl
, toAttach
);
1213 mStencilAttachment
.Set(gl
, toAttach
);
1215 attach
->Set(gl
, toAttach
);
1221 Maybe
<double> WebGLFramebuffer::GetAttachmentParameter(GLenum attachEnum
,
1223 const auto maybeAttach
= GetAttachPoint(attachEnum
);
1224 if (!maybeAttach
|| attachEnum
== LOCAL_GL_NONE
) {
1225 mContext
->ErrorInvalidEnum(
1226 "Can only query COLOR_ATTACHMENTi,"
1227 " DEPTH_ATTACHMENT, DEPTH_STENCIL_ATTACHMENT, or"
1228 " STENCIL_ATTACHMENT for a framebuffer.");
1231 if (MOZ_UNLIKELY(mOpaque
)) {
1232 mContext
->ErrorInvalidOperation(
1233 "An opaque framebuffer's attachments cannot be inspected or changed.");
1236 auto attach
= maybeAttach
.value();
1238 if (mContext
->IsWebGL2() && attachEnum
== LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
) {
1239 if (mDepthAttachment
.Renderbuffer() != mStencilAttachment
.Renderbuffer() ||
1240 mDepthAttachment
.Texture() != mStencilAttachment
.Texture()) {
1241 mContext
->ErrorInvalidOperation(
1242 "DEPTH_ATTACHMENT and STENCIL_ATTACHMENT"
1243 " have different objects bound.");
1247 attach
= &mDepthAttachment
;
1250 return attach
->GetParameter(mContext
, attachEnum
, pname
);
1253 ////////////////////
1255 static void GetBackbufferFormats(const WebGLContext
* webgl
,
1256 const webgl::FormatInfo
** const out_color
,
1257 const webgl::FormatInfo
** const out_depth
,
1258 const webgl::FormatInfo
** const out_stencil
) {
1259 const auto& options
= webgl
->Options();
1261 const auto effFormat
= (options
.alpha
? webgl::EffectiveFormat::RGBA8
1262 : webgl::EffectiveFormat::RGB8
);
1263 *out_color
= webgl::GetFormat(effFormat
);
1265 *out_depth
= nullptr;
1266 *out_stencil
= nullptr;
1267 if (options
.depth
&& options
.stencil
) {
1268 *out_depth
= webgl::GetFormat(webgl::EffectiveFormat::DEPTH24_STENCIL8
);
1269 *out_stencil
= *out_depth
;
1271 if (options
.depth
) {
1272 *out_depth
= webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT16
);
1274 if (options
.stencil
) {
1275 *out_stencil
= webgl::GetFormat(webgl::EffectiveFormat::STENCIL_INDEX8
);
1281 void WebGLFramebuffer::BlitFramebuffer(WebGLContext
* webgl
, GLint _srcX0
,
1282 GLint _srcY0
, GLint _srcX1
, GLint _srcY1
,
1283 GLint _dstX0
, GLint _dstY0
, GLint _dstX1
,
1284 GLint _dstY1
, GLbitfield mask
,
1286 auto srcP0
= ivec2
{_srcX0
, _srcY0
};
1287 auto srcP1
= ivec2
{_srcX1
, _srcY1
};
1288 auto dstP0
= ivec2
{_dstX0
, _dstY0
};
1289 auto dstP1
= ivec2
{_dstX1
, _dstY1
};
1291 const GLbitfield depthAndStencilBits
=
1292 LOCAL_GL_DEPTH_BUFFER_BIT
| LOCAL_GL_STENCIL_BUFFER_BIT
;
1293 if (bool(mask
& depthAndStencilBits
) && filter
== LOCAL_GL_LINEAR
) {
1294 webgl
->ErrorInvalidOperation(
1295 "DEPTH_BUFFER_BIT and STENCIL_BUFFER_BIT can"
1296 " only be used with NEAREST filtering.");
1300 const auto& srcFB
= webgl
->mBoundReadFramebuffer
;
1301 const auto& dstFB
= webgl
->mBoundDrawFramebuffer
;
1306 const auto fnGetFormat
=
1307 [](const WebGLFBAttachPoint
& cur
,
1308 bool* const out_hasSamples
) -> const webgl::FormatInfo
* {
1309 const auto& imageInfo
= cur
.GetImageInfo();
1310 if (!imageInfo
) return nullptr; // No attachment.
1311 *out_hasSamples
= bool(imageInfo
->mSamples
);
1312 return imageInfo
->mFormat
->format
;
1315 bool srcHasSamples
= false;
1316 bool srcIsFilterable
= true;
1317 const webgl::FormatInfo
* srcColorFormat
;
1318 const webgl::FormatInfo
* srcDepthFormat
;
1319 const webgl::FormatInfo
* srcStencilFormat
;
1320 gfx::IntSize srcSize
;
1323 const auto& info
= *srcFB
->GetCompletenessInfo();
1324 if (info
.zLayerCount
!= 1) {
1325 webgl
->GenerateError(
1326 LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION
,
1327 "Source framebuffer cannot have more than one multiview layer.");
1330 srcColorFormat
= nullptr;
1331 if (srcFB
->mColorReadBuffer
) {
1332 const auto& imageInfo
= srcFB
->mColorReadBuffer
->GetImageInfo();
1334 srcIsFilterable
&= imageInfo
->mFormat
->isFilterable
;
1336 srcColorFormat
= fnGetFormat(*(srcFB
->mColorReadBuffer
), &srcHasSamples
);
1338 srcDepthFormat
= fnGetFormat(srcFB
->DepthAttachment(), &srcHasSamples
);
1339 srcStencilFormat
= fnGetFormat(srcFB
->StencilAttachment(), &srcHasSamples
);
1340 MOZ_ASSERT(!srcFB
->DepthStencilAttachment().HasAttachment());
1341 srcSize
= {info
.width
, info
.height
};
1343 srcHasSamples
= false; // Always false.
1345 GetBackbufferFormats(webgl
, &srcColorFormat
, &srcDepthFormat
,
1347 const auto& size
= webgl
->DrawingBufferSize();
1348 srcSize
= {size
.x
, size
.y
};
1353 bool dstHasSamples
= false;
1354 const webgl::FormatInfo
* dstDepthFormat
;
1355 const webgl::FormatInfo
* dstStencilFormat
;
1356 bool dstHasColor
= false;
1357 bool colorFormatsMatch
= true;
1358 bool colorTypesMatch
= true;
1359 bool colorSrgbMatches
= true;
1360 gfx::IntSize dstSize
;
1362 const auto fnCheckColorFormat
= [&](const webgl::FormatInfo
* dstFormat
) {
1363 MOZ_ASSERT(dstFormat
->r
|| dstFormat
->g
|| dstFormat
->b
|| dstFormat
->a
);
1365 colorFormatsMatch
&= (dstFormat
== srcColorFormat
);
1367 srcColorFormat
&& (dstFormat
->baseType
== srcColorFormat
->baseType
);
1369 srcColorFormat
&& (dstFormat
->isSRGB
== srcColorFormat
->isSRGB
);
1373 for (const auto& cur
: dstFB
->mColorDrawBuffers
) {
1374 const auto& format
= fnGetFormat(*cur
, &dstHasSamples
);
1375 if (!format
) continue;
1377 fnCheckColorFormat(format
);
1380 dstDepthFormat
= fnGetFormat(dstFB
->DepthAttachment(), &dstHasSamples
);
1381 dstStencilFormat
= fnGetFormat(dstFB
->StencilAttachment(), &dstHasSamples
);
1382 MOZ_ASSERT(!dstFB
->DepthStencilAttachment().HasAttachment());
1384 const auto& info
= *dstFB
->GetCompletenessInfo();
1385 if (info
.isMultiview
) {
1386 webgl
->GenerateError(
1387 LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION
,
1388 "Destination framebuffer cannot have multiview attachments.");
1391 dstSize
= {info
.width
, info
.height
};
1393 dstHasSamples
= webgl
->Options().antialias
;
1395 const webgl::FormatInfo
* dstColorFormat
;
1396 GetBackbufferFormats(webgl
, &dstColorFormat
, &dstDepthFormat
,
1399 fnCheckColorFormat(dstColorFormat
);
1401 const auto& size
= webgl
->DrawingBufferSize();
1402 dstSize
= {size
.x
, size
.y
};
1406 // Clear unused buffer bits
1408 if (mask
& LOCAL_GL_COLOR_BUFFER_BIT
&& !srcColorFormat
&& !dstHasColor
) {
1409 mask
^= LOCAL_GL_COLOR_BUFFER_BIT
;
1412 if (mask
& LOCAL_GL_DEPTH_BUFFER_BIT
&& !srcDepthFormat
&& !dstDepthFormat
) {
1413 mask
^= LOCAL_GL_DEPTH_BUFFER_BIT
;
1416 if (mask
& LOCAL_GL_STENCIL_BUFFER_BIT
&& !srcStencilFormat
&&
1417 !dstStencilFormat
) {
1418 mask
^= LOCAL_GL_STENCIL_BUFFER_BIT
;
1424 if (dstHasSamples
) {
1425 webgl
->ErrorInvalidOperation(
1426 "DRAW_FRAMEBUFFER may not have multiple"
1431 bool requireFilterable
= (filter
== LOCAL_GL_LINEAR
);
1432 if (srcHasSamples
) {
1433 requireFilterable
= false; // It picks one.
1435 if (mask
& LOCAL_GL_COLOR_BUFFER_BIT
&& dstHasColor
&& !colorFormatsMatch
) {
1436 webgl
->ErrorInvalidOperation(
1437 "Color buffer formats must match if"
1438 " selected, when reading from a multisampled"
1443 if (srcP0
!= dstP0
|| srcP1
!= dstP1
) {
1444 webgl
->ErrorInvalidOperation(
1445 "If the source is multisampled, then the"
1446 " source and dest regions must match exactly.");
1453 if (mask
& LOCAL_GL_COLOR_BUFFER_BIT
) {
1454 if (requireFilterable
&& !srcIsFilterable
) {
1455 webgl
->ErrorInvalidOperation(
1456 "`filter` is LINEAR and READ_BUFFER"
1457 " contains integer data.");
1461 if (!colorTypesMatch
) {
1462 webgl
->ErrorInvalidOperation(
1463 "Color component types (float/uint/"
1464 "int) must match.");
1469 /* GLES 3.0.4, p199:
1470 * Calling BlitFramebuffer will result in an INVALID_OPERATION error if
1471 * mask includes DEPTH_BUFFER_BIT or STENCIL_BUFFER_BIT, and the source
1472 * and destination depth and stencil buffer formats do not match.
1474 * jgilbert: The wording is such that if only DEPTH_BUFFER_BIT is specified,
1475 * the stencil formats must match. This seems wrong. It could be a spec bug,
1476 * or I could be missing an interaction in one of the earlier paragraphs.
1478 if (mask
& LOCAL_GL_DEPTH_BUFFER_BIT
&& dstDepthFormat
&&
1479 dstDepthFormat
!= srcDepthFormat
) {
1480 webgl
->ErrorInvalidOperation(
1481 "Depth buffer formats must match if selected.");
1485 if (mask
& LOCAL_GL_STENCIL_BUFFER_BIT
&& dstStencilFormat
&&
1486 dstStencilFormat
!= srcStencilFormat
) {
1487 webgl
->ErrorInvalidOperation(
1488 "Stencil buffer formats must match if selected.");
1493 // Check for feedback
1495 if (srcFB
&& dstFB
) {
1496 const WebGLFBAttachPoint
* feedback
= nullptr;
1498 if (mask
& LOCAL_GL_COLOR_BUFFER_BIT
) {
1499 MOZ_ASSERT(srcFB
->mColorReadBuffer
->HasAttachment());
1500 for (const auto& cur
: dstFB
->mColorDrawBuffers
) {
1501 if (srcFB
->mColorReadBuffer
->IsEquivalentForFeedback(*cur
)) {
1508 if (mask
& LOCAL_GL_DEPTH_BUFFER_BIT
&&
1509 srcFB
->DepthAttachment().IsEquivalentForFeedback(
1510 dstFB
->DepthAttachment())) {
1511 feedback
= &dstFB
->DepthAttachment();
1514 if (mask
& LOCAL_GL_STENCIL_BUFFER_BIT
&&
1515 srcFB
->StencilAttachment().IsEquivalentForFeedback(
1516 dstFB
->StencilAttachment())) {
1517 feedback
= &dstFB
->StencilAttachment();
1521 webgl
->ErrorInvalidOperation(
1522 "Feedback detected into DRAW_FRAMEBUFFER's"
1523 " 0x%04x attachment.",
1524 feedback
->mAttachmentPoint
);
1527 } else if (!srcFB
&& !dstFB
) {
1528 webgl
->ErrorInvalidOperation("Feedback with default framebuffer.");
1533 // Mutually constrain src and dst rects for eldritch blits.
1536 using fvec2
= avec2
<float>; // Switch to float, because there's no perfect
1539 const auto zero2f
= fvec2
{0, 0};
1540 const auto srcSizef
= AsVec(srcSize
).StaticCast
<fvec2
>();
1541 const auto dstSizef
= AsVec(dstSize
).StaticCast
<fvec2
>();
1543 const auto srcP0f
= srcP0
.StaticCast
<fvec2
>();
1544 const auto srcP1f
= srcP1
.StaticCast
<fvec2
>();
1545 const auto dstP0f
= dstP0
.StaticCast
<fvec2
>();
1546 const auto dstP1f
= dstP1
.StaticCast
<fvec2
>();
1548 const auto srcRectDiff
= srcP1f
- srcP0f
;
1549 const auto dstRectDiff
= dstP1f
- dstP0f
;
1551 // Skip if zero-sized.
1552 if (!srcRectDiff
.x
|| !srcRectDiff
.y
|| !dstRectDiff
.x
|| !dstRectDiff
.y
) {
1553 srcP0
= srcP1
= dstP0
= dstP1
= {0, 0};
1557 // Clamp the rect points
1558 const auto srcQ0
= srcP0f
.Clamp(zero2f
, srcSizef
);
1559 const auto srcQ1
= srcP1f
.Clamp(zero2f
, srcSizef
);
1561 // Normalized to the [0,1] abstact copy rect
1562 const auto srcQ0Norm
= (srcQ0
- srcP0f
) / srcRectDiff
;
1563 const auto srcQ1Norm
= (srcQ1
- srcP0f
) / srcRectDiff
;
1566 const auto srcQ0InDst
= dstP0f
+ srcQ0Norm
* dstRectDiff
;
1567 const auto srcQ1InDst
= dstP0f
+ srcQ1Norm
* dstRectDiff
;
1569 // Clamp the rect points
1570 const auto dstQ0
= srcQ0InDst
.Clamp(zero2f
, dstSizef
);
1571 const auto dstQ1
= srcQ1InDst
.Clamp(zero2f
, dstSizef
);
1573 // Alright, time to go back to src!
1574 // Normalized to the [0,1] abstact copy rect
1575 const auto dstQ0Norm
= (dstQ0
- dstP0f
) / dstRectDiff
;
1576 const auto dstQ1Norm
= (dstQ1
- dstP0f
) / dstRectDiff
;
1579 const auto dstQ0InSrc
= srcP0f
+ dstQ0Norm
* srcRectDiff
;
1580 const auto dstQ1InSrc
= srcP0f
+ dstQ1Norm
* srcRectDiff
;
1582 const auto srcQ0Constrained
= dstQ0InSrc
.Clamp(zero2f
, srcSizef
);
1583 const auto srcQ1Constrained
= dstQ1InSrc
.Clamp(zero2f
, srcSizef
);
1585 // Round, don't floor:
1586 srcP0
= (srcQ0Constrained
+ 0.5).StaticCast
<ivec2
>();
1587 srcP1
= (srcQ1Constrained
+ 0.5).StaticCast
<ivec2
>();
1588 dstP0
= (dstQ0
+ 0.5).StaticCast
<ivec2
>();
1589 dstP1
= (dstQ1
+ 0.5).StaticCast
<ivec2
>();
1592 bool inBounds
= true;
1593 inBounds
&= (srcP0
== srcP0
.Clamp({0, 0}, AsVec(srcSize
)));
1594 inBounds
&= (srcP1
== srcP1
.Clamp({0, 0}, AsVec(srcSize
)));
1595 inBounds
&= (dstP0
== dstP0
.Clamp({0, 0}, AsVec(dstSize
)));
1596 inBounds
&= (dstP1
== dstP1
.Clamp({0, 0}, AsVec(dstSize
)));
1598 webgl
->ErrorImplementationBug(
1599 "Subrects still not within src and dst after constraining.");
1604 // Execute as constrained
1606 const auto& gl
= webgl
->gl
;
1607 const ScopedDrawCallWrapper
wrapper(*webgl
);
1609 gl
->fBlitFramebuffer(srcP0
.x
, srcP0
.y
, srcP1
.x
, srcP1
.y
, dstP0
.x
, dstP0
.y
,
1610 dstP1
.x
, dstP1
.y
, mask
, filter
);
1614 if (mask
& LOCAL_GL_COLOR_BUFFER_BIT
&& !colorSrgbMatches
&& !gl
->IsGLES() &&
1615 gl
->Version() < 440) {
1617 // Remember, we have to filter in the *linear* format blit.
1619 // src -Blit-> fbB -DrawBlit-> fbC -Blit-> dst
1621 const auto fbB
= gl::MozFramebuffer::Create(gl
, {1, 1}, 0, false);
1622 const auto fbC
= gl::MozFramebuffer::Create(gl
, {1, 1}, 0, false);
1626 auto sizeBC
= srcSize
;
1627 GLenum formatC
= LOCAL_GL_RGBA8
;
1628 if (srcColorFormat
->isSRGB
) {
1633 formatC
= LOCAL_GL_SRGB8_ALPHA8
;
1636 const auto fnSetTex
= [&](const gl::MozFramebuffer
& fb
,
1637 const GLenum format
) {
1638 const gl::ScopedBindTexture
bindTex(gl
, fb
.ColorTex());
1639 gl
->fTexStorage2D(LOCAL_GL_TEXTURE_2D
, 1, format
, sizeBC
.width
,
1642 fnSetTex(*fbB
, srcColorFormat
->sizedFormat
);
1643 fnSetTex(*fbC
, formatC
);
1648 const gl::ScopedBindFramebuffer
bindFb(gl
);
1649 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, fbB
->mFB
);
1651 if (srcColorFormat
->isSRGB
) {
1653 gl
->fBlitFramebuffer(srcP0
.x
, srcP0
.y
, srcP1
.x
, srcP1
.y
, srcP0
.x
,
1654 srcP0
.y
, srcP1
.x
, srcP1
.y
,
1655 LOCAL_GL_COLOR_BUFFER_BIT
, LOCAL_GL_NEAREST
);
1658 gl
->fBlitFramebuffer(srcP0
.x
, srcP0
.y
, srcP1
.x
, srcP1
.y
, dstP0
.x
,
1659 dstP0
.y
, dstP1
.x
, dstP1
.y
,
1660 LOCAL_GL_COLOR_BUFFER_BIT
, filter
);
1663 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, fbC
->mFB
);
1664 gl
->BlitHelper()->DrawBlitTextureToFramebuffer(fbB
->ColorTex(), sizeBC
,
1669 const gl::ScopedBindFramebuffer
bindFb(gl
);
1670 gl
->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
, fbC
->mFB
);
1672 if (srcColorFormat
->isSRGB
) {
1674 gl
->fBlitFramebuffer(srcP0
.x
, srcP0
.y
, srcP1
.x
, srcP1
.y
, dstP0
.x
,
1675 dstP0
.y
, dstP1
.x
, dstP1
.y
,
1676 LOCAL_GL_COLOR_BUFFER_BIT
, filter
);
1679 gl
->fBlitFramebuffer(dstP0
.x
, dstP0
.y
, dstP1
.x
, dstP1
.y
, dstP0
.x
,
1680 dstP0
.y
, dstP1
.x
, dstP1
.y
,
1681 LOCAL_GL_COLOR_BUFFER_BIT
, LOCAL_GL_NEAREST
);
1687 // glBlitFramebuffer ignores glColorMask!
1689 if (!webgl
->mBoundDrawFramebuffer
&& webgl
->mNeedsFakeNoAlpha
) {
1690 const auto dstRectMin
= MinExtents(dstP0
, dstP1
);
1691 const auto dstRectMax
= MaxExtents(dstP0
, dstP1
);
1692 const auto dstRectSize
= dstRectMax
- dstRectMin
;
1693 const WebGLContext::ScissorRect dstRect
= {dstRectMin
.x
, dstRectMin
.y
,
1694 dstRectSize
.x
, dstRectSize
.y
};
1697 const auto forClear
= webgl::ScopedPrepForResourceClear
{*webgl
};
1699 gl
->fClearColor(0, 0, 0, 1);
1700 webgl
->DoColorMask(Some(0), 0b1000); // Only alpha.
1701 gl
->fClear(LOCAL_GL_COLOR_BUFFER_BIT
);
1703 webgl
->mScissorRect
.Apply(*gl
);
1707 } // namespace mozilla