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 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE
:
356 isPNameValid
= webgl
->IsWebGL2();
359 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING
:
360 isPNameValid
= (webgl
->IsWebGL2() ||
361 webgl
->IsExtensionEnabled(WebGLExtensionID::EXT_sRGB
));
366 webgl
->ErrorInvalidEnum("Invalid pname: 0x%04x", pname
);
370 const auto& imageInfo
= *GetImageInfo();
371 const auto& usage
= imageInfo
.mFormat
;
373 if (pname
== LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING
)
374 return Some(LOCAL_GL_LINEAR
);
379 auto format
= usage
->format
;
383 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE
:
386 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE
:
389 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE
:
392 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE
:
395 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE
:
398 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE
:
402 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING
:
403 ret
= (format
->isSRGB
? LOCAL_GL_SRGB
: LOCAL_GL_LINEAR
);
406 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE
:
407 MOZ_ASSERT(attachment
!= LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
);
409 if (format
->unsizedFormat
== webgl::UnsizedFormat::DEPTH_STENCIL
) {
410 MOZ_ASSERT(attachment
== LOCAL_GL_DEPTH_ATTACHMENT
||
411 attachment
== LOCAL_GL_STENCIL_ATTACHMENT
);
413 if (attachment
== LOCAL_GL_DEPTH_ATTACHMENT
) {
414 switch (format
->effectiveFormat
) {
415 case webgl::EffectiveFormat::DEPTH24_STENCIL8
:
417 webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT24
);
419 case webgl::EffectiveFormat::DEPTH32F_STENCIL8
:
421 webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT32F
);
424 MOZ_ASSERT(false, "no matched DS format");
427 } else if (attachment
== LOCAL_GL_STENCIL_ATTACHMENT
) {
428 switch (format
->effectiveFormat
) {
429 case webgl::EffectiveFormat::DEPTH24_STENCIL8
:
430 case webgl::EffectiveFormat::DEPTH32F_STENCIL8
:
431 format
= webgl::GetFormat(webgl::EffectiveFormat::STENCIL_INDEX8
);
434 MOZ_ASSERT(false, "no matched DS format");
440 switch (format
->componentType
) {
441 case webgl::ComponentType::Int
:
444 case webgl::ComponentType::UInt
:
445 ret
= LOCAL_GL_UNSIGNED_INT
;
447 case webgl::ComponentType::NormInt
:
448 ret
= LOCAL_GL_SIGNED_NORMALIZED
;
450 case webgl::ComponentType::NormUInt
:
451 ret
= LOCAL_GL_UNSIGNED_NORMALIZED
;
453 case webgl::ComponentType::Float
:
454 ret
= LOCAL_GL_FLOAT
;
460 MOZ_ASSERT(false, "Missing case.");
467 ////////////////////////////////////////////////////////////////////////////////
468 ////////////////////////////////////////////////////////////////////////////////
471 WebGLFramebuffer::WebGLFramebuffer(WebGLContext
* webgl
, GLuint fbo
)
472 : WebGLContextBoundObject(webgl
),
474 mDepthAttachment(webgl
, LOCAL_GL_DEPTH_ATTACHMENT
),
475 mStencilAttachment(webgl
, LOCAL_GL_STENCIL_ATTACHMENT
),
476 mDepthStencilAttachment(webgl
, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
) {
477 mAttachments
.push_back(&mDepthAttachment
);
478 mAttachments
.push_back(&mStencilAttachment
);
480 if (!webgl
->IsWebGL2()) {
481 // Only WebGL1 has a separate depth+stencil attachment point.
482 mAttachments
.push_back(&mDepthStencilAttachment
);
486 for (auto& cur
: mColorAttachments
) {
487 new (&cur
) WebGLFBAttachPoint(webgl
, LOCAL_GL_COLOR_ATTACHMENT0
+ i
);
490 mAttachments
.push_back(&cur
);
493 mColorDrawBuffers
.push_back(&mColorAttachments
[0]);
494 mColorReadBuffer
= &mColorAttachments
[0];
497 WebGLFramebuffer::WebGLFramebuffer(WebGLContext
* webgl
,
498 UniquePtr
<gl::MozFramebuffer
> fbo
)
499 : WebGLContextBoundObject(webgl
),
501 mOpaque(std::move(fbo
)),
502 mColorReadBuffer(nullptr) {
503 // Opaque Framebuffer is guaranteed to be complete at this point.
504 // Cache the Completeness info.
505 CompletenessInfo info
;
506 info
.width
= mOpaque
->mSize
.width
;
507 info
.height
= mOpaque
->mSize
.height
;
508 info
.zLayerCount
= 1;
509 info
.isMultiview
= false;
511 mCompletenessInfo
= Some(std::move(info
));
514 WebGLFramebuffer::~WebGLFramebuffer() {
517 mDepthAttachment
.Clear();
518 mStencilAttachment
.Clear();
519 mDepthStencilAttachment
.Clear();
521 for (auto& cur
: mColorAttachments
) {
525 if (!mContext
) return;
526 // If opaque, fDeleteFramebuffers is called in the destructor of
529 mContext
->gl
->fDeleteFramebuffers(1, &mGLName
);
535 Maybe
<WebGLFBAttachPoint
*> WebGLFramebuffer::GetColorAttachPoint(
536 GLenum attachPoint
) {
537 if (attachPoint
== LOCAL_GL_NONE
) return Some
<WebGLFBAttachPoint
*>(nullptr);
539 if (attachPoint
< LOCAL_GL_COLOR_ATTACHMENT0
) return Nothing();
541 const size_t colorId
= attachPoint
- LOCAL_GL_COLOR_ATTACHMENT0
;
543 MOZ_ASSERT(mContext
->Limits().maxColorDrawBuffers
<= webgl::kMaxDrawBuffers
);
544 if (colorId
>= mContext
->MaxValidDrawBuffers()) return Nothing();
546 return Some(&mColorAttachments
[colorId
]);
549 Maybe
<WebGLFBAttachPoint
*> WebGLFramebuffer::GetAttachPoint(
550 GLenum attachPoint
) {
551 switch (attachPoint
) {
552 case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
:
553 return Some(&mDepthStencilAttachment
);
555 case LOCAL_GL_DEPTH_ATTACHMENT
:
556 return Some(&mDepthAttachment
);
558 case LOCAL_GL_STENCIL_ATTACHMENT
:
559 return Some(&mStencilAttachment
);
562 return GetColorAttachPoint(attachPoint
);
566 void WebGLFramebuffer::DetachTexture(const WebGLTexture
* tex
) {
567 for (const auto& attach
: mAttachments
) {
568 if (attach
->Texture() == tex
) {
575 void WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer
* rb
) {
576 for (const auto& attach
: mAttachments
) {
577 if (attach
->Renderbuffer() == rb
) {
584 ////////////////////////////////////////////////////////////////////////////////
587 bool WebGLFramebuffer::HasDuplicateAttachments() const {
588 std::set
<WebGLFBAttachPoint::Ordered
> uniqueAttachSet
;
590 for (const auto& attach
: mColorAttachments
) {
591 if (!attach
.HasAttachment()) continue;
593 const WebGLFBAttachPoint::Ordered
ordered(attach
);
595 const bool didInsert
= uniqueAttachSet
.insert(ordered
).second
;
596 if (!didInsert
) return true;
602 bool WebGLFramebuffer::HasDefinedAttachments() const {
603 bool hasAttachments
= false;
604 for (const auto& attach
: mAttachments
) {
605 hasAttachments
|= attach
->HasAttachment();
607 return hasAttachments
;
610 bool WebGLFramebuffer::HasIncompleteAttachments(
611 nsCString
* const out_info
) const {
612 bool hasIncomplete
= false;
613 for (const auto& cur
: mAttachments
) {
614 if (!cur
->HasAttachment())
615 continue; // Not defined, so can't count as incomplete.
617 hasIncomplete
|= !cur
->IsComplete(mContext
, out_info
);
619 return hasIncomplete
;
622 bool WebGLFramebuffer::AllImageRectsMatch() const {
623 MOZ_ASSERT(HasDefinedAttachments());
624 DebugOnly
<nsCString
> fbStatusInfo
;
625 MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo
));
627 bool needsInit
= true;
631 bool hasMismatch
= false;
632 for (const auto& attach
: mAttachments
) {
633 const auto& imageInfo
= attach
->GetImageInfo();
634 if (!imageInfo
) continue;
636 const auto& curWidth
= imageInfo
->mWidth
;
637 const auto& curHeight
= imageInfo
->mHeight
;
646 hasMismatch
|= (curWidth
!= width
|| curHeight
!= height
);
651 bool WebGLFramebuffer::AllImageSamplesMatch() const {
652 MOZ_ASSERT(HasDefinedAttachments());
653 DebugOnly
<nsCString
> fbStatusInfo
;
654 MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo
));
656 bool needsInit
= true;
657 uint32_t samples
= 0;
659 bool hasMismatch
= false;
660 for (const auto& attach
: mAttachments
) {
661 const auto& imageInfo
= attach
->GetImageInfo();
662 if (!imageInfo
) continue;
664 const auto& curSamples
= imageInfo
->mSamples
;
668 samples
= curSamples
;
672 hasMismatch
|= (curSamples
!= samples
);
677 FBStatus
WebGLFramebuffer::PrecheckFramebufferStatus(
678 nsCString
* const out_info
) const {
679 MOZ_ASSERT(mContext
->mBoundDrawFramebuffer
== this ||
680 mContext
->mBoundReadFramebuffer
== this);
681 if (!HasDefinedAttachments())
682 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
; // No
685 if (HasIncompleteAttachments(out_info
))
686 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
;
688 if (!AllImageRectsMatch())
689 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
; // Inconsistent sizes
691 if (!AllImageSamplesMatch())
692 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE
; // Inconsistent samples
694 if (HasDuplicateAttachments()) return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED
;
696 if (mContext
->IsWebGL2()) {
697 MOZ_ASSERT(!mDepthStencilAttachment
.HasAttachment());
698 if (mDepthAttachment
.HasAttachment() &&
699 mStencilAttachment
.HasAttachment()) {
700 if (!mDepthAttachment
.IsEquivalentForFeedback(mStencilAttachment
))
701 return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED
;
704 const auto depthOrStencilCount
=
705 int(mDepthAttachment
.HasAttachment()) +
706 int(mStencilAttachment
.HasAttachment()) +
707 int(mDepthStencilAttachment
.HasAttachment());
708 if (depthOrStencilCount
> 1) return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED
;
712 const WebGLFBAttachPoint
* example
= nullptr;
713 for (const auto& x
: mAttachments
) {
714 if (!x
->HasAttachment()) continue;
719 if (x
->ZLayerCount() != example
->ZLayerCount()) {
720 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR
;
725 return LOCAL_GL_FRAMEBUFFER_COMPLETE
;
728 ////////////////////////////////////////
731 bool WebGLFramebuffer::ValidateAndInitAttachments(
732 const GLenum incompleteFbError
) const {
733 MOZ_ASSERT(mContext
->mBoundDrawFramebuffer
== this ||
734 mContext
->mBoundReadFramebuffer
== this);
736 const auto fbStatus
= CheckFramebufferStatus();
737 if (fbStatus
== LOCAL_GL_FRAMEBUFFER_COMPLETE
) return true;
739 mContext
->GenerateError(incompleteFbError
, "Framebuffer must be complete.");
743 bool WebGLFramebuffer::ValidateClearBufferType(
744 GLenum buffer
, uint32_t drawBuffer
,
745 const webgl::AttribBaseType funcType
) const {
746 if (buffer
!= LOCAL_GL_COLOR
) return true;
748 const auto& attach
= mColorAttachments
[drawBuffer
];
749 const auto& imageInfo
= attach
.GetImageInfo();
750 if (!imageInfo
) return true;
752 if (!count(mColorDrawBuffers
.begin(), mColorDrawBuffers
.end(), &attach
))
753 return true; // DRAW_BUFFERi set to NONE.
755 auto attachType
= webgl::AttribBaseType::Float
;
756 switch (imageInfo
->mFormat
->format
->componentType
) {
757 case webgl::ComponentType::Int
:
758 attachType
= webgl::AttribBaseType::Int
;
760 case webgl::ComponentType::UInt
:
761 attachType
= webgl::AttribBaseType::Uint
;
767 if (attachType
!= funcType
) {
768 mContext
->ErrorInvalidOperation(
769 "This attachment is of type %s, but"
770 " this function is of type %s.",
771 ToString(attachType
), ToString(funcType
));
778 bool WebGLFramebuffer::ValidateForColorRead(
779 const webgl::FormatUsageInfo
** const out_format
, uint32_t* const out_width
,
780 uint32_t* const out_height
) const {
781 if (!mColorReadBuffer
) {
782 mContext
->ErrorInvalidOperation("READ_BUFFER must not be NONE.");
786 if (mColorReadBuffer
->ZLayerCount() > 1) {
787 mContext
->GenerateError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION
,
788 "The READ_BUFFER attachment has multiple views.");
792 const auto& imageInfo
= mColorReadBuffer
->GetImageInfo();
794 mContext
->ErrorInvalidOperation(
795 "The READ_BUFFER attachment is not defined.");
799 if (imageInfo
->mSamples
) {
800 mContext
->ErrorInvalidOperation(
801 "The READ_BUFFER attachment is multisampled.");
805 *out_format
= imageInfo
->mFormat
;
806 *out_width
= imageInfo
->mWidth
;
807 *out_height
= imageInfo
->mHeight
;
811 ////////////////////////////////////////////////////////////////////////////////
812 // Resolution and caching
814 void WebGLFramebuffer::DoDeferredAttachments() const {
815 if (mContext
->IsWebGL2()) return;
817 const auto& gl
= mContext
->gl
;
818 gl
->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER
, LOCAL_GL_DEPTH_ATTACHMENT
,
819 LOCAL_GL_RENDERBUFFER
, 0);
820 gl
->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER
,
821 LOCAL_GL_STENCIL_ATTACHMENT
,
822 LOCAL_GL_RENDERBUFFER
, 0);
824 const auto fn
= [&](const WebGLFBAttachPoint
& attach
) {
825 MOZ_ASSERT(attach
.mDeferAttachment
);
826 if (attach
.HasAttachment()) {
827 attach
.DoAttachment(gl
);
830 // Only one of these will have an attachment.
831 fn(mDepthAttachment
);
832 fn(mStencilAttachment
);
833 fn(mDepthStencilAttachment
);
836 void WebGLFramebuffer::ResolveAttachmentData() const {
838 // The result of clearing integer color buffers with `Clear` is undefined.
840 // Two different approaches:
841 // On WebGL 2, we have glClearBuffer, and *must* use it for integer buffers,
842 // so let's just use it for all the buffers. One WebGL 1, we might not have
845 // WebGL 1 is easier, because we can just call glClear, possibly with
848 const auto& gl
= mContext
->gl
;
850 const webgl::ScopedPrepForResourceClear
scopedPrep(*mContext
);
852 if (mContext
->IsWebGL2()) {
853 const uint32_t uiZeros
[4] = {};
854 const int32_t iZeros
[4] = {};
855 const float fZeros
[4] = {};
856 const float fOne
[] = {1.0f
};
858 for (const auto& cur
: mAttachments
) {
859 const auto& imageInfo
= cur
->GetImageInfo();
860 if (!imageInfo
|| !imageInfo
->mUninitializedSlices
)
861 continue; // Nothing attached, or already has data.
863 const auto fnClearBuffer
= [&]() {
864 const auto& format
= imageInfo
->mFormat
->format
;
865 MOZ_ASSERT(format
->estimatedBytesPerPixel
<= sizeof(uiZeros
));
866 MOZ_ASSERT(format
->estimatedBytesPerPixel
<= sizeof(iZeros
));
867 MOZ_ASSERT(format
->estimatedBytesPerPixel
<= sizeof(fZeros
));
869 switch (cur
->mAttachmentPoint
) {
870 case LOCAL_GL_DEPTH_ATTACHMENT
:
871 gl
->fClearBufferfv(LOCAL_GL_DEPTH
, 0, fOne
);
873 case LOCAL_GL_STENCIL_ATTACHMENT
:
874 gl
->fClearBufferiv(LOCAL_GL_STENCIL
, 0, iZeros
);
877 MOZ_ASSERT(cur
->mAttachmentPoint
!=
878 LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
);
879 const uint32_t drawBuffer
=
880 cur
->mAttachmentPoint
- LOCAL_GL_COLOR_ATTACHMENT0
;
881 MOZ_ASSERT(drawBuffer
<= 100);
882 switch (format
->componentType
) {
883 case webgl::ComponentType::Int
:
884 gl
->fClearBufferiv(LOCAL_GL_COLOR
, drawBuffer
, iZeros
);
886 case webgl::ComponentType::UInt
:
887 gl
->fClearBufferuiv(LOCAL_GL_COLOR
, drawBuffer
, uiZeros
);
890 gl
->fClearBufferfv(LOCAL_GL_COLOR
, drawBuffer
, fZeros
);
896 if (imageInfo
->mDepth
> 1) {
897 const auto& tex
= cur
->Texture();
898 const gl::ScopedFramebuffer
scopedFB(gl
);
899 const gl::ScopedBindFramebuffer
scopedBindFB(gl
, scopedFB
.FB());
900 for (const auto z
: IntegerRange(imageInfo
->mDepth
)) {
901 if ((*imageInfo
->mUninitializedSlices
)[z
]) {
902 gl
->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER
,
903 cur
->mAttachmentPoint
, tex
->mGLName
,
911 imageInfo
->mUninitializedSlices
= Nothing();
916 uint32_t clearBits
= 0;
917 std::vector
<GLenum
> drawBufferForClear
;
919 const auto fnGather
= [&](const WebGLFBAttachPoint
& attach
,
920 const uint32_t attachClearBits
) {
921 const auto& imageInfo
= attach
.GetImageInfo();
922 if (!imageInfo
|| !imageInfo
->mUninitializedSlices
) return false;
924 clearBits
|= attachClearBits
;
925 imageInfo
->mUninitializedSlices
= Nothing(); // Just mark it now.
931 for (const auto& cur
: mColorAttachments
) {
932 if (fnGather(cur
, LOCAL_GL_COLOR_BUFFER_BIT
)) {
933 const uint32_t id
= cur
.mAttachmentPoint
- LOCAL_GL_COLOR_ATTACHMENT0
;
934 MOZ_ASSERT(id
<= 100);
935 drawBufferForClear
.resize(id
+ 1); // Pads with zeros!
936 drawBufferForClear
[id
] = cur
.mAttachmentPoint
;
940 (void)fnGather(mDepthAttachment
, LOCAL_GL_DEPTH_BUFFER_BIT
);
941 (void)fnGather(mStencilAttachment
, LOCAL_GL_STENCIL_BUFFER_BIT
);
942 (void)fnGather(mDepthStencilAttachment
,
943 LOCAL_GL_DEPTH_BUFFER_BIT
| LOCAL_GL_STENCIL_BUFFER_BIT
);
947 if (!clearBits
) return;
949 if (gl
->IsSupported(gl::GLFeature::draw_buffers
)) {
950 gl
->fDrawBuffers(drawBufferForClear
.size(), drawBufferForClear
.data());
953 gl
->fClear(clearBits
);
955 RefreshDrawBuffers();
958 WebGLFramebuffer::CompletenessInfo::~CompletenessInfo() {
959 if (!this->fb
) return;
960 const auto& fb
= *this->fb
;
961 const auto& webgl
= fb
.mContext
;
962 fb
.mNumFBStatusInvals
++;
963 if (fb
.mNumFBStatusInvals
> webgl
->mMaxAcceptableFBStatusInvals
) {
964 webgl
->GeneratePerfWarning(
965 "FB was invalidated after being complete %u"
966 " times. [webgl.perf.max-acceptable-fb-status-invals]",
967 uint32_t(fb
.mNumFBStatusInvals
));
971 ////////////////////////////////////////////////////////////////////////////////
974 FBStatus
WebGLFramebuffer::CheckFramebufferStatus() const {
975 if (MOZ_UNLIKELY(mOpaque
&& !mInOpaqueRAF
)) {
976 // Opaque Framebuffers are considered incomplete outside of a RAF.
977 return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED
;
980 if (mCompletenessInfo
) return LOCAL_GL_FRAMEBUFFER_COMPLETE
;
982 // Ok, let's try to resolve it!
984 nsCString statusInfo
;
985 FBStatus ret
= PrecheckFramebufferStatus(&statusInfo
);
987 if (ret
!= LOCAL_GL_FRAMEBUFFER_COMPLETE
) break;
989 // Looks good on our end. Let's ask the driver.
990 gl::GLContext
* const gl
= mContext
->gl
;
992 const ScopedFBRebinder
autoFB(mContext
);
993 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, mGLName
);
997 DoDeferredAttachments();
998 RefreshDrawBuffers();
1001 ret
= gl
->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER
);
1005 if (ret
!= LOCAL_GL_FRAMEBUFFER_COMPLETE
) {
1006 const nsPrintfCString
text("Bad status according to the driver: 0x%04x",
1012 ResolveAttachmentData();
1014 // Sweet, let's cache that.
1015 auto info
= CompletenessInfo
{this};
1016 mCompletenessInfo
.ResetInvalidators({});
1017 mCompletenessInfo
.AddInvalidator(*this);
1019 const auto fnIsFloat32
= [](const webgl::FormatInfo
& info
) {
1020 if (info
.componentType
!= webgl::ComponentType::Float
) return false;
1021 return info
.r
== 32;
1024 for (const auto& cur
: mAttachments
) {
1025 const auto& tex
= cur
->Texture();
1026 const auto& rb
= cur
->Renderbuffer();
1028 mCompletenessInfo
.AddInvalidator(*tex
);
1029 info
.texAttachments
.push_back(cur
);
1031 mCompletenessInfo
.AddInvalidator(*rb
);
1035 const auto& imageInfo
= cur
->GetImageInfo();
1036 MOZ_ASSERT(imageInfo
);
1038 const auto maybeColorId
= cur
->ColorAttachmentId();
1040 const auto id
= *maybeColorId
;
1041 info
.hasAttachment
[id
] = true;
1042 info
.isAttachmentF32
[id
] = fnIsFloat32(*imageInfo
->mFormat
->format
);
1045 info
.width
= imageInfo
->mWidth
;
1046 info
.height
= imageInfo
->mHeight
;
1047 info
.zLayerCount
= cur
->ZLayerCount();
1048 info
.isMultiview
= cur
->IsMultiview();
1050 MOZ_ASSERT(info
.width
&& info
.height
);
1051 mCompletenessInfo
= Some(std::move(info
));
1052 info
.fb
= nullptr; // Don't trigger the invalidation warning.
1053 return LOCAL_GL_FRAMEBUFFER_COMPLETE
;
1056 MOZ_ASSERT(ret
!= LOCAL_GL_FRAMEBUFFER_COMPLETE
);
1057 mContext
->GenerateWarning("Framebuffer not complete. (status: 0x%04x) %s",
1058 ret
.get(), statusInfo
.BeginReading());
1064 void WebGLFramebuffer::RefreshDrawBuffers() const {
1065 const auto& gl
= mContext
->gl
;
1066 if (!gl
->IsSupported(gl::GLFeature::draw_buffers
)) return;
1068 // Prior to GL4.1, having a no-image FB attachment that's selected by
1069 // DrawBuffers yields a framebuffer status of
1070 // FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER. We could workaround this only on
1071 // affected versions, but it's easier be unconditional.
1072 std::vector
<GLenum
> driverBuffers(mContext
->Limits().maxColorDrawBuffers
,
1074 for (const auto& attach
: mColorDrawBuffers
) {
1075 if (attach
->HasAttachment()) {
1076 const uint32_t index
=
1077 attach
->mAttachmentPoint
- LOCAL_GL_COLOR_ATTACHMENT0
;
1078 driverBuffers
[index
] = attach
->mAttachmentPoint
;
1082 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, mGLName
);
1083 gl
->fDrawBuffers(driverBuffers
.size(), driverBuffers
.data());
1086 void WebGLFramebuffer::RefreshReadBuffer() const {
1087 const auto& gl
= mContext
->gl
;
1088 if (!gl
->IsSupported(gl::GLFeature::read_buffer
)) return;
1090 // Prior to GL4.1, having a no-image FB attachment that's selected by
1091 // ReadBuffer yields a framebuffer status of
1092 // FRAMEBUFFER_INCOMPLETE_READ_BUFFER. We could workaround this only on
1093 // affected versions, but it's easier be unconditional.
1094 GLenum driverBuffer
= LOCAL_GL_NONE
;
1095 if (mColorReadBuffer
&& mColorReadBuffer
->HasAttachment()) {
1096 driverBuffer
= mColorReadBuffer
->mAttachmentPoint
;
1099 gl
->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
, mGLName
);
1100 gl
->fReadBuffer(driverBuffer
);
1105 void WebGLFramebuffer::DrawBuffers(const std::vector
<GLenum
>& buffers
) {
1106 if (buffers
.size() > mContext
->MaxValidDrawBuffers()) {
1107 // "An INVALID_VALUE error is generated if `n` is greater than
1108 // MAX_DRAW_BUFFERS."
1109 mContext
->ErrorInvalidValue(
1110 "`buffers` must have a length <="
1111 " MAX_DRAW_BUFFERS.");
1115 std::vector
<const WebGLFBAttachPoint
*> newColorDrawBuffers
;
1116 newColorDrawBuffers
.reserve(buffers
.size());
1118 mDrawBufferEnabled
.reset();
1119 for (const auto i
: IntegerRange(buffers
.size())) {
1120 // "If the GL is bound to a draw framebuffer object, the `i`th buffer listed
1121 // in bufs must be COLOR_ATTACHMENTi or NONE. Specifying a buffer out of
1122 // order, BACK, or COLOR_ATTACHMENTm where `m` is greater than or equal to
1123 // the value of MAX_COLOR_ATTACHMENTS, will generate the error
1124 // INVALID_OPERATION.
1126 // WEBGL_draw_buffers:
1127 // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater
1128 // than or equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter." This
1129 // means that if buffers.Length() isn't larger than MaxDrawBuffers, it won't
1130 // be larger than MaxColorAttachments.
1131 const auto& cur
= buffers
[i
];
1132 if (cur
== LOCAL_GL_COLOR_ATTACHMENT0
+ i
) {
1133 const auto& attach
= mColorAttachments
[i
];
1134 newColorDrawBuffers
.push_back(&attach
);
1135 mDrawBufferEnabled
[i
] = true;
1136 } else if (cur
!= LOCAL_GL_NONE
) {
1137 const bool isColorEnum
= (cur
>= LOCAL_GL_COLOR_ATTACHMENT0
&&
1138 cur
< mContext
->LastColorAttachmentEnum());
1139 if (cur
!= LOCAL_GL_BACK
&& !isColorEnum
) {
1140 mContext
->ErrorInvalidEnum("Unexpected enum in buffers.");
1144 mContext
->ErrorInvalidOperation(
1145 "`buffers[i]` must be NONE or"
1146 " COLOR_ATTACHMENTi.");
1153 mColorDrawBuffers
= std::move(newColorDrawBuffers
);
1154 RefreshDrawBuffers(); // Calls glDrawBuffers.
1157 void WebGLFramebuffer::ReadBuffer(GLenum attachPoint
) {
1158 const auto& maybeAttach
= GetColorAttachPoint(attachPoint
);
1161 "`mode` must be a COLOR_ATTACHMENTi, for 0 <= i <"
1162 " MAX_DRAW_BUFFERS.";
1163 if (attachPoint
== LOCAL_GL_BACK
) {
1164 mContext
->ErrorInvalidOperation(text
);
1166 mContext
->ErrorInvalidEnum(text
);
1170 const auto& attach
= maybeAttach
.value(); // Might be nullptr.
1174 mColorReadBuffer
= attach
;
1175 RefreshReadBuffer(); // Calls glReadBuffer.
1180 bool WebGLFramebuffer::FramebufferAttach(const GLenum attachEnum
,
1181 const webgl::FbAttachInfo
& toAttach
) {
1182 MOZ_ASSERT(mContext
->mBoundDrawFramebuffer
== this ||
1183 mContext
->mBoundReadFramebuffer
== this);
1185 if (MOZ_UNLIKELY(mOpaque
)) {
1186 // An opaque framebuffer's attachments cannot be inspected or changed.
1191 const auto maybeAttach
= GetAttachPoint(attachEnum
);
1192 if (!maybeAttach
|| !maybeAttach
.value()) return false;
1193 const auto& attach
= maybeAttach
.value();
1195 const auto& gl
= mContext
->gl
;
1196 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, mGLName
);
1197 if (mContext
->IsWebGL2() && attachEnum
== LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
) {
1198 mDepthAttachment
.Set(gl
, toAttach
);
1199 mStencilAttachment
.Set(gl
, toAttach
);
1201 attach
->Set(gl
, toAttach
);
1207 Maybe
<double> WebGLFramebuffer::GetAttachmentParameter(GLenum attachEnum
,
1209 const auto maybeAttach
= GetAttachPoint(attachEnum
);
1210 if (!maybeAttach
|| attachEnum
== LOCAL_GL_NONE
) {
1211 mContext
->ErrorInvalidEnum(
1212 "Can only query COLOR_ATTACHMENTi,"
1213 " DEPTH_ATTACHMENT, DEPTH_STENCIL_ATTACHMENT, or"
1214 " STENCIL_ATTACHMENT for a framebuffer.");
1217 if (MOZ_UNLIKELY(mOpaque
)) {
1218 mContext
->ErrorInvalidOperation(
1219 "An opaque framebuffer's attachments cannot be inspected or changed.");
1222 auto attach
= maybeAttach
.value();
1224 if (mContext
->IsWebGL2() && attachEnum
== LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
) {
1225 // There are a couple special rules for this one.
1227 if (pname
== LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE
) {
1228 mContext
->ErrorInvalidOperation(
1230 " FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE"
1231 " against DEPTH_STENCIL_ATTACHMENT is an"
1236 if (mDepthAttachment
.Renderbuffer() != mStencilAttachment
.Renderbuffer() ||
1237 mDepthAttachment
.Texture() != mStencilAttachment
.Texture()) {
1238 mContext
->ErrorInvalidOperation(
1239 "DEPTH_ATTACHMENT and STENCIL_ATTACHMENT"
1240 " have different objects bound.");
1244 attach
= &mDepthAttachment
;
1247 return attach
->GetParameter(mContext
, attachEnum
, pname
);
1250 ////////////////////
1252 static void GetBackbufferFormats(const WebGLContext
* webgl
,
1253 const webgl::FormatInfo
** const out_color
,
1254 const webgl::FormatInfo
** const out_depth
,
1255 const webgl::FormatInfo
** const out_stencil
) {
1256 const auto& options
= webgl
->Options();
1258 const auto effFormat
= (options
.alpha
? webgl::EffectiveFormat::RGBA8
1259 : webgl::EffectiveFormat::RGB8
);
1260 *out_color
= webgl::GetFormat(effFormat
);
1262 *out_depth
= nullptr;
1263 *out_stencil
= nullptr;
1264 if (options
.depth
&& options
.stencil
) {
1265 *out_depth
= webgl::GetFormat(webgl::EffectiveFormat::DEPTH24_STENCIL8
);
1266 *out_stencil
= *out_depth
;
1268 if (options
.depth
) {
1269 *out_depth
= webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT16
);
1271 if (options
.stencil
) {
1272 *out_stencil
= webgl::GetFormat(webgl::EffectiveFormat::STENCIL_INDEX8
);
1278 void WebGLFramebuffer::BlitFramebuffer(WebGLContext
* webgl
, GLint _srcX0
,
1279 GLint _srcY0
, GLint _srcX1
, GLint _srcY1
,
1280 GLint _dstX0
, GLint _dstY0
, GLint _dstX1
,
1281 GLint _dstY1
, GLbitfield mask
,
1283 auto srcP0
= ivec2
{_srcX0
, _srcY0
};
1284 auto srcP1
= ivec2
{_srcX1
, _srcY1
};
1285 auto dstP0
= ivec2
{_dstX0
, _dstY0
};
1286 auto dstP1
= ivec2
{_dstX1
, _dstY1
};
1288 const GLbitfield depthAndStencilBits
=
1289 LOCAL_GL_DEPTH_BUFFER_BIT
| LOCAL_GL_STENCIL_BUFFER_BIT
;
1290 if (bool(mask
& depthAndStencilBits
) && filter
== LOCAL_GL_LINEAR
) {
1291 webgl
->ErrorInvalidOperation(
1292 "DEPTH_BUFFER_BIT and STENCIL_BUFFER_BIT can"
1293 " only be used with NEAREST filtering.");
1297 const auto& srcFB
= webgl
->mBoundReadFramebuffer
;
1298 const auto& dstFB
= webgl
->mBoundDrawFramebuffer
;
1303 const auto fnGetFormat
=
1304 [](const WebGLFBAttachPoint
& cur
,
1305 bool* const out_hasSamples
) -> const webgl::FormatInfo
* {
1306 const auto& imageInfo
= cur
.GetImageInfo();
1307 if (!imageInfo
) return nullptr; // No attachment.
1308 *out_hasSamples
= bool(imageInfo
->mSamples
);
1309 return imageInfo
->mFormat
->format
;
1312 bool srcHasSamples
= false;
1313 bool srcIsFilterable
= true;
1314 const webgl::FormatInfo
* srcColorFormat
;
1315 const webgl::FormatInfo
* srcDepthFormat
;
1316 const webgl::FormatInfo
* srcStencilFormat
;
1317 gfx::IntSize srcSize
;
1320 const auto& info
= *srcFB
->GetCompletenessInfo();
1321 if (info
.zLayerCount
!= 1) {
1322 webgl
->GenerateError(
1323 LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION
,
1324 "Source framebuffer cannot have more than one multiview layer.");
1327 srcColorFormat
= nullptr;
1328 if (srcFB
->mColorReadBuffer
) {
1329 const auto& imageInfo
= srcFB
->mColorReadBuffer
->GetImageInfo();
1331 srcIsFilterable
&= imageInfo
->mFormat
->isFilterable
;
1333 srcColorFormat
= fnGetFormat(*(srcFB
->mColorReadBuffer
), &srcHasSamples
);
1335 srcDepthFormat
= fnGetFormat(srcFB
->DepthAttachment(), &srcHasSamples
);
1336 srcStencilFormat
= fnGetFormat(srcFB
->StencilAttachment(), &srcHasSamples
);
1337 MOZ_ASSERT(!srcFB
->DepthStencilAttachment().HasAttachment());
1338 srcSize
= {info
.width
, info
.height
};
1340 srcHasSamples
= false; // Always false.
1342 GetBackbufferFormats(webgl
, &srcColorFormat
, &srcDepthFormat
,
1344 const auto& size
= webgl
->DrawingBufferSize();
1345 srcSize
= {size
.x
, size
.y
};
1350 bool dstHasSamples
= false;
1351 const webgl::FormatInfo
* dstDepthFormat
;
1352 const webgl::FormatInfo
* dstStencilFormat
;
1353 bool dstHasColor
= false;
1354 bool colorFormatsMatch
= true;
1355 bool colorTypesMatch
= true;
1356 bool colorSrgbMatches
= true;
1357 gfx::IntSize dstSize
;
1359 const auto fnCheckColorFormat
= [&](const webgl::FormatInfo
* dstFormat
) {
1360 MOZ_ASSERT(dstFormat
->r
|| dstFormat
->g
|| dstFormat
->b
|| dstFormat
->a
);
1362 colorFormatsMatch
&= (dstFormat
== srcColorFormat
);
1364 srcColorFormat
&& (dstFormat
->baseType
== srcColorFormat
->baseType
);
1366 srcColorFormat
&& (dstFormat
->isSRGB
== srcColorFormat
->isSRGB
);
1370 for (const auto& cur
: dstFB
->mColorDrawBuffers
) {
1371 const auto& format
= fnGetFormat(*cur
, &dstHasSamples
);
1372 if (!format
) continue;
1374 fnCheckColorFormat(format
);
1377 dstDepthFormat
= fnGetFormat(dstFB
->DepthAttachment(), &dstHasSamples
);
1378 dstStencilFormat
= fnGetFormat(dstFB
->StencilAttachment(), &dstHasSamples
);
1379 MOZ_ASSERT(!dstFB
->DepthStencilAttachment().HasAttachment());
1381 const auto& info
= *dstFB
->GetCompletenessInfo();
1382 if (info
.isMultiview
) {
1383 webgl
->GenerateError(
1384 LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION
,
1385 "Destination framebuffer cannot have multiview attachments.");
1388 dstSize
= {info
.width
, info
.height
};
1390 dstHasSamples
= webgl
->Options().antialias
;
1392 const webgl::FormatInfo
* dstColorFormat
;
1393 GetBackbufferFormats(webgl
, &dstColorFormat
, &dstDepthFormat
,
1396 fnCheckColorFormat(dstColorFormat
);
1398 const auto& size
= webgl
->DrawingBufferSize();
1399 dstSize
= {size
.x
, size
.y
};
1403 // Clear unused buffer bits
1405 if (mask
& LOCAL_GL_COLOR_BUFFER_BIT
&& !srcColorFormat
&& !dstHasColor
) {
1406 mask
^= LOCAL_GL_COLOR_BUFFER_BIT
;
1409 if (mask
& LOCAL_GL_DEPTH_BUFFER_BIT
&& !srcDepthFormat
&& !dstDepthFormat
) {
1410 mask
^= LOCAL_GL_DEPTH_BUFFER_BIT
;
1413 if (mask
& LOCAL_GL_STENCIL_BUFFER_BIT
&& !srcStencilFormat
&&
1414 !dstStencilFormat
) {
1415 mask
^= LOCAL_GL_STENCIL_BUFFER_BIT
;
1421 if (dstHasSamples
) {
1422 webgl
->ErrorInvalidOperation(
1423 "DRAW_FRAMEBUFFER may not have multiple"
1428 bool requireFilterable
= (filter
== LOCAL_GL_LINEAR
);
1429 if (srcHasSamples
) {
1430 requireFilterable
= false; // It picks one.
1432 if (mask
& LOCAL_GL_COLOR_BUFFER_BIT
&& dstHasColor
&& !colorFormatsMatch
) {
1433 webgl
->ErrorInvalidOperation(
1434 "Color buffer formats must match if"
1435 " selected, when reading from a multisampled"
1440 if (srcP0
!= dstP0
|| srcP1
!= dstP1
) {
1441 webgl
->ErrorInvalidOperation(
1442 "If the source is multisampled, then the"
1443 " source and dest regions must match exactly.");
1450 if (mask
& LOCAL_GL_COLOR_BUFFER_BIT
) {
1451 if (requireFilterable
&& !srcIsFilterable
) {
1452 webgl
->ErrorInvalidOperation(
1453 "`filter` is LINEAR and READ_BUFFER"
1454 " contains integer data.");
1458 if (!colorTypesMatch
) {
1459 webgl
->ErrorInvalidOperation(
1460 "Color component types (float/uint/"
1461 "int) must match.");
1466 /* GLES 3.0.4, p199:
1467 * Calling BlitFramebuffer will result in an INVALID_OPERATION error if
1468 * mask includes DEPTH_BUFFER_BIT or STENCIL_BUFFER_BIT, and the source
1469 * and destination depth and stencil buffer formats do not match.
1471 * jgilbert: The wording is such that if only DEPTH_BUFFER_BIT is specified,
1472 * the stencil formats must match. This seems wrong. It could be a spec bug,
1473 * or I could be missing an interaction in one of the earlier paragraphs.
1475 if (mask
& LOCAL_GL_DEPTH_BUFFER_BIT
&& dstDepthFormat
&&
1476 dstDepthFormat
!= srcDepthFormat
) {
1477 webgl
->ErrorInvalidOperation(
1478 "Depth buffer formats must match if selected.");
1482 if (mask
& LOCAL_GL_STENCIL_BUFFER_BIT
&& dstStencilFormat
&&
1483 dstStencilFormat
!= srcStencilFormat
) {
1484 webgl
->ErrorInvalidOperation(
1485 "Stencil buffer formats must match if selected.");
1490 // Check for feedback
1492 if (srcFB
&& dstFB
) {
1493 const WebGLFBAttachPoint
* feedback
= nullptr;
1495 if (mask
& LOCAL_GL_COLOR_BUFFER_BIT
) {
1496 MOZ_ASSERT(srcFB
->mColorReadBuffer
->HasAttachment());
1497 for (const auto& cur
: dstFB
->mColorDrawBuffers
) {
1498 if (srcFB
->mColorReadBuffer
->IsEquivalentForFeedback(*cur
)) {
1505 if (mask
& LOCAL_GL_DEPTH_BUFFER_BIT
&&
1506 srcFB
->DepthAttachment().IsEquivalentForFeedback(
1507 dstFB
->DepthAttachment())) {
1508 feedback
= &dstFB
->DepthAttachment();
1511 if (mask
& LOCAL_GL_STENCIL_BUFFER_BIT
&&
1512 srcFB
->StencilAttachment().IsEquivalentForFeedback(
1513 dstFB
->StencilAttachment())) {
1514 feedback
= &dstFB
->StencilAttachment();
1518 webgl
->ErrorInvalidOperation(
1519 "Feedback detected into DRAW_FRAMEBUFFER's"
1520 " 0x%04x attachment.",
1521 feedback
->mAttachmentPoint
);
1524 } else if (!srcFB
&& !dstFB
) {
1525 webgl
->ErrorInvalidOperation("Feedback with default framebuffer.");
1530 // Mutually constrain src and dst rects for eldritch blits.
1533 using fvec2
= avec2
<float>; // Switch to float, because there's no perfect
1536 const auto zero2f
= fvec2
{0, 0};
1537 const auto srcSizef
= AsVec(srcSize
).StaticCast
<fvec2
>();
1538 const auto dstSizef
= AsVec(dstSize
).StaticCast
<fvec2
>();
1540 const auto srcP0f
= srcP0
.StaticCast
<fvec2
>();
1541 const auto srcP1f
= srcP1
.StaticCast
<fvec2
>();
1542 const auto dstP0f
= dstP0
.StaticCast
<fvec2
>();
1543 const auto dstP1f
= dstP1
.StaticCast
<fvec2
>();
1545 const auto srcRectDiff
= srcP1f
- srcP0f
;
1546 const auto dstRectDiff
= dstP1f
- dstP0f
;
1548 // Skip if zero-sized.
1549 if (!srcRectDiff
.x
|| !srcRectDiff
.y
|| !dstRectDiff
.x
|| !dstRectDiff
.y
) {
1550 srcP0
= srcP1
= dstP0
= dstP1
= {0, 0};
1554 // Clamp the rect points
1555 const auto srcQ0
= srcP0f
.ClampMinMax(zero2f
, srcSizef
);
1556 const auto srcQ1
= srcP1f
.ClampMinMax(zero2f
, srcSizef
);
1558 // Normalized to the [0,1] abstact copy rect
1559 const auto srcQ0Norm
= (srcQ0
- srcP0f
) / srcRectDiff
;
1560 const auto srcQ1Norm
= (srcQ1
- srcP0f
) / srcRectDiff
;
1563 const auto srcQ0InDst
= dstP0f
+ srcQ0Norm
* dstRectDiff
;
1564 const auto srcQ1InDst
= dstP0f
+ srcQ1Norm
* dstRectDiff
;
1566 // Clamp the rect points
1567 const auto dstQ0
= srcQ0InDst
.ClampMinMax(zero2f
, dstSizef
);
1568 const auto dstQ1
= srcQ1InDst
.ClampMinMax(zero2f
, dstSizef
);
1570 // Alright, time to go back to src!
1571 // Normalized to the [0,1] abstact copy rect
1572 const auto dstQ0Norm
= (dstQ0
- dstP0f
) / dstRectDiff
;
1573 const auto dstQ1Norm
= (dstQ1
- dstP0f
) / dstRectDiff
;
1576 const auto dstQ0InSrc
= srcP0f
+ dstQ0Norm
* srcRectDiff
;
1577 const auto dstQ1InSrc
= srcP0f
+ dstQ1Norm
* srcRectDiff
;
1579 const auto srcQ0Constrained
= dstQ0InSrc
.ClampMinMax(zero2f
, srcSizef
);
1580 const auto srcQ1Constrained
= dstQ1InSrc
.ClampMinMax(zero2f
, srcSizef
);
1582 // Round, don't floor:
1583 srcP0
= (srcQ0Constrained
+ 0.5).StaticCast
<ivec2
>();
1584 srcP1
= (srcQ1Constrained
+ 0.5).StaticCast
<ivec2
>();
1585 dstP0
= (dstQ0
+ 0.5).StaticCast
<ivec2
>();
1586 dstP1
= (dstQ1
+ 0.5).StaticCast
<ivec2
>();
1589 bool inBounds
= true;
1590 inBounds
&= (srcP0
== srcP0
.Clamp({0, 0}, AsVec(srcSize
)));
1591 inBounds
&= (srcP1
== srcP1
.Clamp({0, 0}, AsVec(srcSize
)));
1592 inBounds
&= (dstP0
== dstP0
.Clamp({0, 0}, AsVec(dstSize
)));
1593 inBounds
&= (dstP1
== dstP1
.Clamp({0, 0}, AsVec(dstSize
)));
1595 webgl
->ErrorImplementationBug(
1596 "Subrects still not within src and dst after constraining.");
1601 // Execute as constrained
1603 const auto& gl
= webgl
->gl
;
1604 const ScopedDrawCallWrapper
wrapper(*webgl
);
1606 gl
->fBlitFramebuffer(srcP0
.x
, srcP0
.y
, srcP1
.x
, srcP1
.y
, dstP0
.x
, dstP0
.y
,
1607 dstP1
.x
, dstP1
.y
, mask
, filter
);
1611 if (mask
& LOCAL_GL_COLOR_BUFFER_BIT
&& !colorSrgbMatches
&& !gl
->IsGLES() &&
1612 gl
->Version() < 440) {
1614 // Remember, we have to filter in the *linear* format blit.
1616 // src -Blit-> fbB -DrawBlit-> fbC -Blit-> dst
1618 const auto fbB
= gl::MozFramebuffer::Create(gl
, {1, 1}, 0, false);
1619 const auto fbC
= gl::MozFramebuffer::Create(gl
, {1, 1}, 0, false);
1623 auto sizeBC
= srcSize
;
1624 GLenum formatC
= LOCAL_GL_RGBA8
;
1625 if (srcColorFormat
->isSRGB
) {
1630 formatC
= LOCAL_GL_SRGB8_ALPHA8
;
1633 const auto fnSetTex
= [&](const gl::MozFramebuffer
& fb
,
1634 const GLenum format
) {
1635 const gl::ScopedBindTexture
bindTex(gl
, fb
.ColorTex());
1636 gl
->fTexStorage2D(LOCAL_GL_TEXTURE_2D
, 1, format
, sizeBC
.width
,
1639 fnSetTex(*fbB
, srcColorFormat
->sizedFormat
);
1640 fnSetTex(*fbC
, formatC
);
1645 const gl::ScopedBindFramebuffer
bindFb(gl
);
1646 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, fbB
->mFB
);
1648 if (srcColorFormat
->isSRGB
) {
1650 gl
->fBlitFramebuffer(srcP0
.x
, srcP0
.y
, srcP1
.x
, srcP1
.y
, srcP0
.x
,
1651 srcP0
.y
, srcP1
.x
, srcP1
.y
,
1652 LOCAL_GL_COLOR_BUFFER_BIT
, LOCAL_GL_NEAREST
);
1655 gl
->fBlitFramebuffer(srcP0
.x
, srcP0
.y
, srcP1
.x
, srcP1
.y
, dstP0
.x
,
1656 dstP0
.y
, dstP1
.x
, dstP1
.y
,
1657 LOCAL_GL_COLOR_BUFFER_BIT
, filter
);
1660 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, fbC
->mFB
);
1661 gl
->BlitHelper()->DrawBlitTextureToFramebuffer(fbB
->ColorTex(), sizeBC
,
1666 const gl::ScopedBindFramebuffer
bindFb(gl
);
1667 gl
->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
, fbC
->mFB
);
1669 if (srcColorFormat
->isSRGB
) {
1671 gl
->fBlitFramebuffer(srcP0
.x
, srcP0
.y
, srcP1
.x
, srcP1
.y
, dstP0
.x
,
1672 dstP0
.y
, dstP1
.x
, dstP1
.y
,
1673 LOCAL_GL_COLOR_BUFFER_BIT
, filter
);
1676 gl
->fBlitFramebuffer(dstP0
.x
, dstP0
.y
, dstP1
.x
, dstP1
.y
, dstP0
.x
,
1677 dstP0
.y
, dstP1
.x
, dstP1
.y
,
1678 LOCAL_GL_COLOR_BUFFER_BIT
, LOCAL_GL_NEAREST
);
1684 // glBlitFramebuffer ignores glColorMask!
1686 if (!webgl
->mBoundDrawFramebuffer
&& webgl
->mNeedsFakeNoAlpha
) {
1687 const auto dstRectMin
= MinExtents(dstP0
, dstP1
);
1688 const auto dstRectMax
= MaxExtents(dstP0
, dstP1
);
1689 const auto dstRectSize
= dstRectMax
- dstRectMin
;
1690 const WebGLContext::ScissorRect dstRect
= {dstRectMin
.x
, dstRectMin
.y
,
1691 dstRectSize
.x
, dstRectSize
.y
};
1694 const auto forClear
= webgl::ScopedPrepForResourceClear
{*webgl
};
1696 gl
->fClearColor(0, 0, 0, 1);
1697 webgl
->DoColorMask(Some(0), 0b1000); // Only alpha.
1698 gl
->fClear(LOCAL_GL_COLOR_BUFFER_BIT
);
1700 webgl
->mScissorRect
.Apply(*gl
);
1704 } // namespace mozilla