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
.hasFloat32
= false;
509 info
.zLayerCount
= 1;
510 info
.isMultiview
= false;
512 mCompletenessInfo
= Some(std::move(info
));
515 WebGLFramebuffer::~WebGLFramebuffer() {
518 mDepthAttachment
.Clear();
519 mStencilAttachment
.Clear();
520 mDepthStencilAttachment
.Clear();
522 for (auto& cur
: mColorAttachments
) {
526 if (!mContext
) return;
527 // If opaque, fDeleteFramebuffers is called in the destructor of
530 mContext
->gl
->fDeleteFramebuffers(1, &mGLName
);
536 Maybe
<WebGLFBAttachPoint
*> WebGLFramebuffer::GetColorAttachPoint(
537 GLenum attachPoint
) {
538 if (attachPoint
== LOCAL_GL_NONE
) return Some
<WebGLFBAttachPoint
*>(nullptr);
540 if (attachPoint
< LOCAL_GL_COLOR_ATTACHMENT0
) return Nothing();
542 const size_t colorId
= attachPoint
- LOCAL_GL_COLOR_ATTACHMENT0
;
544 MOZ_ASSERT(mContext
->Limits().maxColorDrawBuffers
<= kMaxColorAttachments
);
545 if (colorId
>= mContext
->MaxValidDrawBuffers()) return Nothing();
547 return Some(&mColorAttachments
[colorId
]);
550 Maybe
<WebGLFBAttachPoint
*> WebGLFramebuffer::GetAttachPoint(
551 GLenum attachPoint
) {
552 switch (attachPoint
) {
553 case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
:
554 return Some(&mDepthStencilAttachment
);
556 case LOCAL_GL_DEPTH_ATTACHMENT
:
557 return Some(&mDepthAttachment
);
559 case LOCAL_GL_STENCIL_ATTACHMENT
:
560 return Some(&mStencilAttachment
);
563 return GetColorAttachPoint(attachPoint
);
567 void WebGLFramebuffer::DetachTexture(const WebGLTexture
* tex
) {
568 for (const auto& attach
: mAttachments
) {
569 if (attach
->Texture() == tex
) {
576 void WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer
* rb
) {
577 for (const auto& attach
: mAttachments
) {
578 if (attach
->Renderbuffer() == rb
) {
585 ////////////////////////////////////////////////////////////////////////////////
588 bool WebGLFramebuffer::HasDuplicateAttachments() const {
589 std::set
<WebGLFBAttachPoint::Ordered
> uniqueAttachSet
;
591 for (const auto& attach
: mColorAttachments
) {
592 if (!attach
.HasAttachment()) continue;
594 const WebGLFBAttachPoint::Ordered
ordered(attach
);
596 const bool didInsert
= uniqueAttachSet
.insert(ordered
).second
;
597 if (!didInsert
) return true;
603 bool WebGLFramebuffer::HasDefinedAttachments() const {
604 bool hasAttachments
= false;
605 for (const auto& attach
: mAttachments
) {
606 hasAttachments
|= attach
->HasAttachment();
608 return hasAttachments
;
611 bool WebGLFramebuffer::HasIncompleteAttachments(
612 nsCString
* const out_info
) const {
613 bool hasIncomplete
= false;
614 for (const auto& cur
: mAttachments
) {
615 if (!cur
->HasAttachment())
616 continue; // Not defined, so can't count as incomplete.
618 hasIncomplete
|= !cur
->IsComplete(mContext
, out_info
);
620 return hasIncomplete
;
623 bool WebGLFramebuffer::AllImageRectsMatch() const {
624 MOZ_ASSERT(HasDefinedAttachments());
625 DebugOnly
<nsCString
> fbStatusInfo
;
626 MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo
));
628 bool needsInit
= true;
632 bool hasMismatch
= false;
633 for (const auto& attach
: mAttachments
) {
634 const auto& imageInfo
= attach
->GetImageInfo();
635 if (!imageInfo
) continue;
637 const auto& curWidth
= imageInfo
->mWidth
;
638 const auto& curHeight
= imageInfo
->mHeight
;
647 hasMismatch
|= (curWidth
!= width
|| curHeight
!= height
);
652 bool WebGLFramebuffer::AllImageSamplesMatch() const {
653 MOZ_ASSERT(HasDefinedAttachments());
654 DebugOnly
<nsCString
> fbStatusInfo
;
655 MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo
));
657 bool needsInit
= true;
658 uint32_t samples
= 0;
660 bool hasMismatch
= false;
661 for (const auto& attach
: mAttachments
) {
662 const auto& imageInfo
= attach
->GetImageInfo();
663 if (!imageInfo
) continue;
665 const auto& curSamples
= imageInfo
->mSamples
;
669 samples
= curSamples
;
673 hasMismatch
|= (curSamples
!= samples
);
678 FBStatus
WebGLFramebuffer::PrecheckFramebufferStatus(
679 nsCString
* const out_info
) const {
680 MOZ_ASSERT(mContext
->mBoundDrawFramebuffer
== this ||
681 mContext
->mBoundReadFramebuffer
== this);
682 if (!HasDefinedAttachments())
683 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
; // No
686 if (HasIncompleteAttachments(out_info
))
687 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
;
689 if (!AllImageRectsMatch())
690 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
; // Inconsistent sizes
692 if (!AllImageSamplesMatch())
693 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE
; // Inconsistent samples
695 if (HasDuplicateAttachments()) return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED
;
697 if (mContext
->IsWebGL2()) {
698 MOZ_ASSERT(!mDepthStencilAttachment
.HasAttachment());
699 if (mDepthAttachment
.HasAttachment() &&
700 mStencilAttachment
.HasAttachment()) {
701 if (!mDepthAttachment
.IsEquivalentForFeedback(mStencilAttachment
))
702 return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED
;
705 const auto depthOrStencilCount
=
706 int(mDepthAttachment
.HasAttachment()) +
707 int(mStencilAttachment
.HasAttachment()) +
708 int(mDepthStencilAttachment
.HasAttachment());
709 if (depthOrStencilCount
> 1) return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED
;
713 const WebGLFBAttachPoint
* example
= nullptr;
714 for (const auto& x
: mAttachments
) {
715 if (!x
->HasAttachment()) continue;
720 if (x
->ZLayerCount() != example
->ZLayerCount()) {
721 return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR
;
726 return LOCAL_GL_FRAMEBUFFER_COMPLETE
;
729 ////////////////////////////////////////
732 bool WebGLFramebuffer::ValidateAndInitAttachments(
733 const GLenum incompleteFbError
) const {
734 MOZ_ASSERT(mContext
->mBoundDrawFramebuffer
== this ||
735 mContext
->mBoundReadFramebuffer
== this);
737 const auto fbStatus
= CheckFramebufferStatus();
738 if (fbStatus
== LOCAL_GL_FRAMEBUFFER_COMPLETE
) return true;
740 mContext
->GenerateError(incompleteFbError
, "Framebuffer must be complete.");
744 bool WebGLFramebuffer::ValidateClearBufferType(
745 GLenum buffer
, uint32_t drawBuffer
,
746 const webgl::AttribBaseType funcType
) const {
747 if (buffer
!= LOCAL_GL_COLOR
) return true;
749 const auto& attach
= mColorAttachments
[drawBuffer
];
750 const auto& imageInfo
= attach
.GetImageInfo();
751 if (!imageInfo
) return true;
753 if (!count(mColorDrawBuffers
.begin(), mColorDrawBuffers
.end(), &attach
))
754 return true; // DRAW_BUFFERi set to NONE.
756 auto attachType
= webgl::AttribBaseType::Float
;
757 switch (imageInfo
->mFormat
->format
->componentType
) {
758 case webgl::ComponentType::Int
:
759 attachType
= webgl::AttribBaseType::Int
;
761 case webgl::ComponentType::UInt
:
762 attachType
= webgl::AttribBaseType::Uint
;
768 if (attachType
!= funcType
) {
769 mContext
->ErrorInvalidOperation(
770 "This attachment is of type %s, but"
771 " this function is of type %s.",
772 ToString(attachType
), ToString(funcType
));
779 bool WebGLFramebuffer::ValidateForColorRead(
780 const webgl::FormatUsageInfo
** const out_format
, uint32_t* const out_width
,
781 uint32_t* const out_height
) const {
782 if (!mColorReadBuffer
) {
783 mContext
->ErrorInvalidOperation("READ_BUFFER must not be NONE.");
787 if (mColorReadBuffer
->ZLayerCount() > 1) {
788 mContext
->GenerateError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION
,
789 "The READ_BUFFER attachment has multiple views.");
793 const auto& imageInfo
= mColorReadBuffer
->GetImageInfo();
795 mContext
->ErrorInvalidOperation(
796 "The READ_BUFFER attachment is not defined.");
800 if (imageInfo
->mSamples
) {
801 mContext
->ErrorInvalidOperation(
802 "The READ_BUFFER attachment is multisampled.");
806 *out_format
= imageInfo
->mFormat
;
807 *out_width
= imageInfo
->mWidth
;
808 *out_height
= imageInfo
->mHeight
;
812 ////////////////////////////////////////////////////////////////////////////////
813 // Resolution and caching
815 void WebGLFramebuffer::DoDeferredAttachments() const {
816 if (mContext
->IsWebGL2()) return;
818 const auto& gl
= mContext
->gl
;
819 gl
->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER
, LOCAL_GL_DEPTH_ATTACHMENT
,
820 LOCAL_GL_RENDERBUFFER
, 0);
821 gl
->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER
,
822 LOCAL_GL_STENCIL_ATTACHMENT
,
823 LOCAL_GL_RENDERBUFFER
, 0);
825 const auto fn
= [&](const WebGLFBAttachPoint
& attach
) {
826 MOZ_ASSERT(attach
.mDeferAttachment
);
827 if (attach
.HasAttachment()) {
828 attach
.DoAttachment(gl
);
831 // Only one of these will have an attachment.
832 fn(mDepthAttachment
);
833 fn(mStencilAttachment
);
834 fn(mDepthStencilAttachment
);
837 void WebGLFramebuffer::ResolveAttachmentData() const {
839 // The result of clearing integer color buffers with `Clear` is undefined.
841 // Two different approaches:
842 // On WebGL 2, we have glClearBuffer, and *must* use it for integer buffers,
843 // so let's just use it for all the buffers. One WebGL 1, we might not have
846 // WebGL 1 is easier, because we can just call glClear, possibly with
849 const auto& gl
= mContext
->gl
;
851 const webgl::ScopedPrepForResourceClear
scopedPrep(*mContext
);
853 if (mContext
->IsWebGL2()) {
854 const uint32_t uiZeros
[4] = {};
855 const int32_t iZeros
[4] = {};
856 const float fZeros
[4] = {};
857 const float fOne
[] = {1.0f
};
859 for (const auto& cur
: mAttachments
) {
860 const auto& imageInfo
= cur
->GetImageInfo();
861 if (!imageInfo
|| !imageInfo
->mUninitializedSlices
)
862 continue; // Nothing attached, or already has data.
864 const auto fnClearBuffer
= [&]() {
865 const auto& format
= imageInfo
->mFormat
->format
;
866 MOZ_ASSERT(format
->estimatedBytesPerPixel
<= sizeof(uiZeros
));
867 MOZ_ASSERT(format
->estimatedBytesPerPixel
<= sizeof(iZeros
));
868 MOZ_ASSERT(format
->estimatedBytesPerPixel
<= sizeof(fZeros
));
870 switch (cur
->mAttachmentPoint
) {
871 case LOCAL_GL_DEPTH_ATTACHMENT
:
872 gl
->fClearBufferfv(LOCAL_GL_DEPTH
, 0, fOne
);
874 case LOCAL_GL_STENCIL_ATTACHMENT
:
875 gl
->fClearBufferiv(LOCAL_GL_STENCIL
, 0, iZeros
);
878 MOZ_ASSERT(cur
->mAttachmentPoint
!=
879 LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
);
880 const uint32_t drawBuffer
=
881 cur
->mAttachmentPoint
- LOCAL_GL_COLOR_ATTACHMENT0
;
882 MOZ_ASSERT(drawBuffer
<= 100);
883 switch (format
->componentType
) {
884 case webgl::ComponentType::Int
:
885 gl
->fClearBufferiv(LOCAL_GL_COLOR
, drawBuffer
, iZeros
);
887 case webgl::ComponentType::UInt
:
888 gl
->fClearBufferuiv(LOCAL_GL_COLOR
, drawBuffer
, uiZeros
);
891 gl
->fClearBufferfv(LOCAL_GL_COLOR
, drawBuffer
, fZeros
);
897 if (imageInfo
->mDepth
> 1) {
898 const auto& tex
= cur
->Texture();
899 const gl::ScopedFramebuffer
scopedFB(gl
);
900 const gl::ScopedBindFramebuffer
scopedBindFB(gl
, scopedFB
.FB());
901 for (const auto z
: IntegerRange(imageInfo
->mDepth
)) {
902 if ((*imageInfo
->mUninitializedSlices
)[z
]) {
903 gl
->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER
,
904 cur
->mAttachmentPoint
, tex
->mGLName
,
912 imageInfo
->mUninitializedSlices
= Nothing();
917 uint32_t clearBits
= 0;
918 std::vector
<GLenum
> drawBufferForClear
;
920 const auto fnGather
= [&](const WebGLFBAttachPoint
& attach
,
921 const uint32_t attachClearBits
) {
922 const auto& imageInfo
= attach
.GetImageInfo();
923 if (!imageInfo
|| !imageInfo
->mUninitializedSlices
) return false;
925 clearBits
|= attachClearBits
;
926 imageInfo
->mUninitializedSlices
= Nothing(); // Just mark it now.
932 for (const auto& cur
: mColorAttachments
) {
933 if (fnGather(cur
, LOCAL_GL_COLOR_BUFFER_BIT
)) {
934 const uint32_t id
= cur
.mAttachmentPoint
- LOCAL_GL_COLOR_ATTACHMENT0
;
935 MOZ_ASSERT(id
<= 100);
936 drawBufferForClear
.resize(id
+ 1); // Pads with zeros!
937 drawBufferForClear
[id
] = cur
.mAttachmentPoint
;
941 (void)fnGather(mDepthAttachment
, LOCAL_GL_DEPTH_BUFFER_BIT
);
942 (void)fnGather(mStencilAttachment
, LOCAL_GL_STENCIL_BUFFER_BIT
);
943 (void)fnGather(mDepthStencilAttachment
,
944 LOCAL_GL_DEPTH_BUFFER_BIT
| LOCAL_GL_STENCIL_BUFFER_BIT
);
948 if (!clearBits
) return;
950 if (gl
->IsSupported(gl::GLFeature::draw_buffers
)) {
951 gl
->fDrawBuffers(drawBufferForClear
.size(), drawBufferForClear
.data());
954 gl
->fClear(clearBits
);
956 RefreshDrawBuffers();
959 WebGLFramebuffer::CompletenessInfo::~CompletenessInfo() {
960 if (!this->fb
) return;
961 const auto& fb
= *this->fb
;
962 const auto& webgl
= fb
.mContext
;
963 fb
.mNumFBStatusInvals
++;
964 if (fb
.mNumFBStatusInvals
> webgl
->mMaxAcceptableFBStatusInvals
) {
965 webgl
->GeneratePerfWarning(
966 "FB was invalidated after being complete %u"
967 " times. [webgl.perf.max-acceptable-fb-status-invals]",
968 uint32_t(fb
.mNumFBStatusInvals
));
972 ////////////////////////////////////////////////////////////////////////////////
975 FBStatus
WebGLFramebuffer::CheckFramebufferStatus() const {
976 if (MOZ_UNLIKELY(mOpaque
&& !mInOpaqueRAF
)) {
977 // Opaque Framebuffers are considered incomplete outside of a RAF.
978 return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED
;
981 if (mCompletenessInfo
) return LOCAL_GL_FRAMEBUFFER_COMPLETE
;
983 // Ok, let's try to resolve it!
985 nsCString statusInfo
;
986 FBStatus ret
= PrecheckFramebufferStatus(&statusInfo
);
988 if (ret
!= LOCAL_GL_FRAMEBUFFER_COMPLETE
) break;
990 // Looks good on our end. Let's ask the driver.
991 gl::GLContext
* const gl
= mContext
->gl
;
993 const ScopedFBRebinder
autoFB(mContext
);
994 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, mGLName
);
998 DoDeferredAttachments();
999 RefreshDrawBuffers();
1000 RefreshReadBuffer();
1002 ret
= gl
->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER
);
1006 if (ret
!= LOCAL_GL_FRAMEBUFFER_COMPLETE
) {
1007 const nsPrintfCString
text("Bad status according to the driver: 0x%04x",
1013 ResolveAttachmentData();
1015 // Sweet, let's cache that.
1016 auto info
= CompletenessInfo
{this, UINT32_MAX
, UINT32_MAX
};
1017 mCompletenessInfo
.ResetInvalidators({});
1018 mCompletenessInfo
.AddInvalidator(*this);
1020 const auto fnIsFloat32
= [](const webgl::FormatInfo
& info
) {
1021 if (info
.componentType
!= webgl::ComponentType::Float
) return false;
1022 return info
.r
== 32;
1025 for (const auto& cur
: mAttachments
) {
1026 const auto& tex
= cur
->Texture();
1027 const auto& rb
= cur
->Renderbuffer();
1029 mCompletenessInfo
.AddInvalidator(*tex
);
1030 info
.texAttachments
.push_back(cur
);
1032 mCompletenessInfo
.AddInvalidator(*rb
);
1036 const auto& imageInfo
= cur
->GetImageInfo();
1037 MOZ_ASSERT(imageInfo
);
1038 info
.width
= std::min(info
.width
, imageInfo
->mWidth
);
1039 info
.height
= std::min(info
.height
, imageInfo
->mHeight
);
1040 info
.hasFloat32
|= fnIsFloat32(*imageInfo
->mFormat
->format
);
1041 info
.zLayerCount
= cur
->ZLayerCount();
1042 info
.isMultiview
= cur
->IsMultiview();
1044 mCompletenessInfo
= Some(std::move(info
));
1045 info
.fb
= nullptr; // Don't trigger the invalidation warning.
1046 return LOCAL_GL_FRAMEBUFFER_COMPLETE
;
1049 MOZ_ASSERT(ret
!= LOCAL_GL_FRAMEBUFFER_COMPLETE
);
1050 mContext
->GenerateWarning("Framebuffer not complete. (status: 0x%04x) %s",
1051 ret
.get(), statusInfo
.BeginReading());
1057 void WebGLFramebuffer::RefreshDrawBuffers() const {
1058 const auto& gl
= mContext
->gl
;
1059 if (!gl
->IsSupported(gl::GLFeature::draw_buffers
)) return;
1061 // Prior to GL4.1, having a no-image FB attachment that's selected by
1062 // DrawBuffers yields a framebuffer status of
1063 // FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER. We could workaround this only on
1064 // affected versions, but it's easier be unconditional.
1065 std::vector
<GLenum
> driverBuffers(mContext
->Limits().maxColorDrawBuffers
,
1067 for (const auto& attach
: mColorDrawBuffers
) {
1068 if (attach
->HasAttachment()) {
1069 const uint32_t index
=
1070 attach
->mAttachmentPoint
- LOCAL_GL_COLOR_ATTACHMENT0
;
1071 driverBuffers
[index
] = attach
->mAttachmentPoint
;
1075 gl
->fDrawBuffers(driverBuffers
.size(), driverBuffers
.data());
1078 void WebGLFramebuffer::RefreshReadBuffer() const {
1079 const auto& gl
= mContext
->gl
;
1080 if (!gl
->IsSupported(gl::GLFeature::read_buffer
)) return;
1082 // Prior to GL4.1, having a no-image FB attachment that's selected by
1083 // ReadBuffer yields a framebuffer status of
1084 // FRAMEBUFFER_INCOMPLETE_READ_BUFFER. We could workaround this only on
1085 // affected versions, but it's easier be unconditional.
1086 GLenum driverBuffer
= LOCAL_GL_NONE
;
1087 if (mColorReadBuffer
&& mColorReadBuffer
->HasAttachment()) {
1088 driverBuffer
= mColorReadBuffer
->mAttachmentPoint
;
1091 gl
->fReadBuffer(driverBuffer
);
1096 void WebGLFramebuffer::DrawBuffers(const std::vector
<GLenum
>& buffers
) {
1097 if (buffers
.size() > mContext
->MaxValidDrawBuffers()) {
1098 // "An INVALID_VALUE error is generated if `n` is greater than
1099 // MAX_DRAW_BUFFERS."
1100 mContext
->ErrorInvalidValue(
1101 "`buffers` must have a length <="
1102 " MAX_DRAW_BUFFERS.");
1106 std::vector
<const WebGLFBAttachPoint
*> newColorDrawBuffers
;
1107 newColorDrawBuffers
.reserve(buffers
.size());
1109 for (const auto i
: IntegerRange(buffers
.size())) {
1110 // "If the GL is bound to a draw framebuffer object, the `i`th buffer listed
1111 // in bufs must be COLOR_ATTACHMENTi or NONE. Specifying a buffer out of
1112 // order, BACK, or COLOR_ATTACHMENTm where `m` is greater than or equal to
1113 // the value of MAX_COLOR_ATTACHMENTS, will generate the error
1114 // INVALID_OPERATION.
1116 // WEBGL_draw_buffers:
1117 // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater
1118 // than or equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter." This
1119 // means that if buffers.Length() isn't larger than MaxDrawBuffers, it won't
1120 // be larger than MaxColorAttachments.
1121 const auto& cur
= buffers
[i
];
1122 if (cur
== LOCAL_GL_COLOR_ATTACHMENT0
+ i
) {
1123 const auto& attach
= mColorAttachments
[i
];
1124 newColorDrawBuffers
.push_back(&attach
);
1125 } else if (cur
!= LOCAL_GL_NONE
) {
1126 const bool isColorEnum
= (cur
>= LOCAL_GL_COLOR_ATTACHMENT0
&&
1127 cur
< mContext
->LastColorAttachmentEnum());
1128 if (cur
!= LOCAL_GL_BACK
&& !isColorEnum
) {
1129 mContext
->ErrorInvalidEnum("Unexpected enum in buffers.");
1133 mContext
->ErrorInvalidOperation(
1134 "`buffers[i]` must be NONE or"
1135 " COLOR_ATTACHMENTi.");
1142 mColorDrawBuffers
= std::move(newColorDrawBuffers
);
1143 RefreshDrawBuffers(); // Calls glDrawBuffers.
1146 bool WebGLFramebuffer::IsDrawBufferEnabled(const uint32_t slotId
) const {
1147 const auto attachEnum
= LOCAL_GL_COLOR_ATTACHMENT0
+ slotId
;
1148 for (const auto& cur
: mColorDrawBuffers
) {
1149 if (cur
->mAttachmentPoint
== attachEnum
) {
1156 void WebGLFramebuffer::ReadBuffer(GLenum attachPoint
) {
1157 const auto& maybeAttach
= GetColorAttachPoint(attachPoint
);
1160 "`mode` must be a COLOR_ATTACHMENTi, for 0 <= i <"
1161 " MAX_DRAW_BUFFERS.";
1162 if (attachPoint
== LOCAL_GL_BACK
) {
1163 mContext
->ErrorInvalidOperation(text
);
1165 mContext
->ErrorInvalidEnum(text
);
1169 const auto& attach
= maybeAttach
.value(); // Might be nullptr.
1173 mColorReadBuffer
= attach
;
1174 RefreshReadBuffer(); // Calls glReadBuffer.
1179 bool WebGLFramebuffer::FramebufferAttach(const GLenum attachEnum
,
1180 const webgl::FbAttachInfo
& toAttach
) {
1181 MOZ_ASSERT(mContext
->mBoundDrawFramebuffer
== this ||
1182 mContext
->mBoundReadFramebuffer
== this);
1184 if (MOZ_UNLIKELY(mOpaque
)) {
1185 // An opaque framebuffer's attachments cannot be inspected or changed.
1190 const auto maybeAttach
= GetAttachPoint(attachEnum
);
1191 if (!maybeAttach
|| !maybeAttach
.value()) return false;
1192 const auto& attach
= maybeAttach
.value();
1194 const auto& gl
= mContext
->gl
;
1195 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, mGLName
);
1196 if (mContext
->IsWebGL2() && attachEnum
== LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
) {
1197 mDepthAttachment
.Set(gl
, toAttach
);
1198 mStencilAttachment
.Set(gl
, toAttach
);
1200 attach
->Set(gl
, toAttach
);
1206 Maybe
<double> WebGLFramebuffer::GetAttachmentParameter(GLenum attachEnum
,
1208 const auto maybeAttach
= GetAttachPoint(attachEnum
);
1209 if (!maybeAttach
|| attachEnum
== LOCAL_GL_NONE
) {
1210 mContext
->ErrorInvalidEnum(
1211 "Can only query COLOR_ATTACHMENTi,"
1212 " DEPTH_ATTACHMENT, DEPTH_STENCIL_ATTACHMENT, or"
1213 " STENCIL_ATTACHMENT for a framebuffer.");
1216 if (MOZ_UNLIKELY(mOpaque
)) {
1217 mContext
->ErrorInvalidOperation(
1218 "An opaque framebuffer's attachments cannot be inspected or changed.");
1221 auto attach
= maybeAttach
.value();
1223 if (mContext
->IsWebGL2() && attachEnum
== LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
) {
1224 // There are a couple special rules for this one.
1226 if (pname
== LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE
) {
1227 mContext
->ErrorInvalidOperation(
1229 " FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE"
1230 " against DEPTH_STENCIL_ATTACHMENT is an"
1235 if (mDepthAttachment
.Renderbuffer() != mStencilAttachment
.Renderbuffer() ||
1236 mDepthAttachment
.Texture() != mStencilAttachment
.Texture()) {
1237 mContext
->ErrorInvalidOperation(
1238 "DEPTH_ATTACHMENT and STENCIL_ATTACHMENT"
1239 " have different objects bound.");
1243 attach
= &mDepthAttachment
;
1246 return attach
->GetParameter(mContext
, attachEnum
, pname
);
1249 ////////////////////
1251 static void GetBackbufferFormats(const WebGLContext
* webgl
,
1252 const webgl::FormatInfo
** const out_color
,
1253 const webgl::FormatInfo
** const out_depth
,
1254 const webgl::FormatInfo
** const out_stencil
) {
1255 const auto& options
= webgl
->Options();
1257 const auto effFormat
= (options
.alpha
? webgl::EffectiveFormat::RGBA8
1258 : webgl::EffectiveFormat::RGB8
);
1259 *out_color
= webgl::GetFormat(effFormat
);
1261 *out_depth
= nullptr;
1262 *out_stencil
= nullptr;
1263 if (options
.depth
&& options
.stencil
) {
1264 *out_depth
= webgl::GetFormat(webgl::EffectiveFormat::DEPTH24_STENCIL8
);
1265 *out_stencil
= *out_depth
;
1267 if (options
.depth
) {
1268 *out_depth
= webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT16
);
1270 if (options
.stencil
) {
1271 *out_stencil
= webgl::GetFormat(webgl::EffectiveFormat::STENCIL_INDEX8
);
1277 void WebGLFramebuffer::BlitFramebuffer(WebGLContext
* webgl
, GLint _srcX0
,
1278 GLint _srcY0
, GLint _srcX1
, GLint _srcY1
,
1279 GLint _dstX0
, GLint _dstY0
, GLint _dstX1
,
1280 GLint _dstY1
, GLbitfield mask
,
1282 auto srcP0
= ivec2
{_srcX0
, _srcY0
};
1283 auto srcP1
= ivec2
{_srcX1
, _srcY1
};
1284 auto dstP0
= ivec2
{_dstX0
, _dstY0
};
1285 auto dstP1
= ivec2
{_dstX1
, _dstY1
};
1287 const GLbitfield depthAndStencilBits
=
1288 LOCAL_GL_DEPTH_BUFFER_BIT
| LOCAL_GL_STENCIL_BUFFER_BIT
;
1289 if (bool(mask
& depthAndStencilBits
) && filter
== LOCAL_GL_LINEAR
) {
1290 webgl
->ErrorInvalidOperation(
1291 "DEPTH_BUFFER_BIT and STENCIL_BUFFER_BIT can"
1292 " only be used with NEAREST filtering.");
1296 const auto& srcFB
= webgl
->mBoundReadFramebuffer
;
1297 const auto& dstFB
= webgl
->mBoundDrawFramebuffer
;
1302 const auto fnGetFormat
=
1303 [](const WebGLFBAttachPoint
& cur
,
1304 bool* const out_hasSamples
) -> const webgl::FormatInfo
* {
1305 const auto& imageInfo
= cur
.GetImageInfo();
1306 if (!imageInfo
) return nullptr; // No attachment.
1307 *out_hasSamples
= bool(imageInfo
->mSamples
);
1308 return imageInfo
->mFormat
->format
;
1311 bool srcHasSamples
= false;
1312 bool srcIsFilterable
= true;
1313 const webgl::FormatInfo
* srcColorFormat
;
1314 const webgl::FormatInfo
* srcDepthFormat
;
1315 const webgl::FormatInfo
* srcStencilFormat
;
1316 gfx::IntSize srcSize
;
1319 const auto& info
= *srcFB
->GetCompletenessInfo();
1320 if (info
.zLayerCount
!= 1) {
1321 webgl
->GenerateError(
1322 LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION
,
1323 "Source framebuffer cannot have more than one multiview layer.");
1326 srcColorFormat
= nullptr;
1327 if (srcFB
->mColorReadBuffer
) {
1328 const auto& imageInfo
= srcFB
->mColorReadBuffer
->GetImageInfo();
1330 srcIsFilterable
&= imageInfo
->mFormat
->isFilterable
;
1332 srcColorFormat
= fnGetFormat(*(srcFB
->mColorReadBuffer
), &srcHasSamples
);
1334 srcDepthFormat
= fnGetFormat(srcFB
->DepthAttachment(), &srcHasSamples
);
1335 srcStencilFormat
= fnGetFormat(srcFB
->StencilAttachment(), &srcHasSamples
);
1336 MOZ_ASSERT(!srcFB
->DepthStencilAttachment().HasAttachment());
1337 srcSize
= {info
.width
, info
.height
};
1339 srcHasSamples
= false; // Always false.
1341 GetBackbufferFormats(webgl
, &srcColorFormat
, &srcDepthFormat
,
1343 const auto& size
= webgl
->DrawingBufferSize();
1344 srcSize
= {size
.x
, size
.y
};
1349 bool dstHasSamples
= false;
1350 const webgl::FormatInfo
* dstDepthFormat
;
1351 const webgl::FormatInfo
* dstStencilFormat
;
1352 bool dstHasColor
= false;
1353 bool colorFormatsMatch
= true;
1354 bool colorTypesMatch
= true;
1355 bool colorSrgbMatches
= true;
1356 gfx::IntSize dstSize
;
1358 const auto fnCheckColorFormat
= [&](const webgl::FormatInfo
* dstFormat
) {
1359 MOZ_ASSERT(dstFormat
->r
|| dstFormat
->g
|| dstFormat
->b
|| dstFormat
->a
);
1361 colorFormatsMatch
&= (dstFormat
== srcColorFormat
);
1363 srcColorFormat
&& (dstFormat
->baseType
== srcColorFormat
->baseType
);
1365 srcColorFormat
&& (dstFormat
->isSRGB
== srcColorFormat
->isSRGB
);
1369 for (const auto& cur
: dstFB
->mColorDrawBuffers
) {
1370 const auto& format
= fnGetFormat(*cur
, &dstHasSamples
);
1371 if (!format
) continue;
1373 fnCheckColorFormat(format
);
1376 dstDepthFormat
= fnGetFormat(dstFB
->DepthAttachment(), &dstHasSamples
);
1377 dstStencilFormat
= fnGetFormat(dstFB
->StencilAttachment(), &dstHasSamples
);
1378 MOZ_ASSERT(!dstFB
->DepthStencilAttachment().HasAttachment());
1380 const auto& info
= *dstFB
->GetCompletenessInfo();
1381 if (info
.isMultiview
) {
1382 webgl
->GenerateError(
1383 LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION
,
1384 "Destination framebuffer cannot have multiview attachments.");
1387 dstSize
= {info
.width
, info
.height
};
1389 dstHasSamples
= webgl
->Options().antialias
;
1391 const webgl::FormatInfo
* dstColorFormat
;
1392 GetBackbufferFormats(webgl
, &dstColorFormat
, &dstDepthFormat
,
1395 fnCheckColorFormat(dstColorFormat
);
1397 const auto& size
= webgl
->DrawingBufferSize();
1398 dstSize
= {size
.x
, size
.y
};
1402 // Clear unused buffer bits
1404 if (mask
& LOCAL_GL_COLOR_BUFFER_BIT
&& !srcColorFormat
&& !dstHasColor
) {
1405 mask
^= LOCAL_GL_COLOR_BUFFER_BIT
;
1408 if (mask
& LOCAL_GL_DEPTH_BUFFER_BIT
&& !srcDepthFormat
&& !dstDepthFormat
) {
1409 mask
^= LOCAL_GL_DEPTH_BUFFER_BIT
;
1412 if (mask
& LOCAL_GL_STENCIL_BUFFER_BIT
&& !srcStencilFormat
&&
1413 !dstStencilFormat
) {
1414 mask
^= LOCAL_GL_STENCIL_BUFFER_BIT
;
1420 if (dstHasSamples
) {
1421 webgl
->ErrorInvalidOperation(
1422 "DRAW_FRAMEBUFFER may not have multiple"
1427 bool requireFilterable
= (filter
== LOCAL_GL_LINEAR
);
1428 if (srcHasSamples
) {
1429 requireFilterable
= false; // It picks one.
1431 if (mask
& LOCAL_GL_COLOR_BUFFER_BIT
&& dstHasColor
&& !colorFormatsMatch
) {
1432 webgl
->ErrorInvalidOperation(
1433 "Color buffer formats must match if"
1434 " selected, when reading from a multisampled"
1439 if (srcP0
!= dstP0
|| srcP1
!= dstP1
) {
1440 webgl
->ErrorInvalidOperation(
1441 "If the source is multisampled, then the"
1442 " source and dest regions must match exactly.");
1449 if (mask
& LOCAL_GL_COLOR_BUFFER_BIT
) {
1450 if (requireFilterable
&& !srcIsFilterable
) {
1451 webgl
->ErrorInvalidOperation(
1452 "`filter` is LINEAR and READ_BUFFER"
1453 " contains integer data.");
1457 if (!colorTypesMatch
) {
1458 webgl
->ErrorInvalidOperation(
1459 "Color component types (float/uint/"
1460 "int) must match.");
1465 /* GLES 3.0.4, p199:
1466 * Calling BlitFramebuffer will result in an INVALID_OPERATION error if
1467 * mask includes DEPTH_BUFFER_BIT or STENCIL_BUFFER_BIT, and the source
1468 * and destination depth and stencil buffer formats do not match.
1470 * jgilbert: The wording is such that if only DEPTH_BUFFER_BIT is specified,
1471 * the stencil formats must match. This seems wrong. It could be a spec bug,
1472 * or I could be missing an interaction in one of the earlier paragraphs.
1474 if (mask
& LOCAL_GL_DEPTH_BUFFER_BIT
&& dstDepthFormat
&&
1475 dstDepthFormat
!= srcDepthFormat
) {
1476 webgl
->ErrorInvalidOperation(
1477 "Depth buffer formats must match if selected.");
1481 if (mask
& LOCAL_GL_STENCIL_BUFFER_BIT
&& dstStencilFormat
&&
1482 dstStencilFormat
!= srcStencilFormat
) {
1483 webgl
->ErrorInvalidOperation(
1484 "Stencil buffer formats must match if selected.");
1489 // Check for feedback
1491 if (srcFB
&& dstFB
) {
1492 const WebGLFBAttachPoint
* feedback
= nullptr;
1494 if (mask
& LOCAL_GL_COLOR_BUFFER_BIT
) {
1495 MOZ_ASSERT(srcFB
->mColorReadBuffer
->HasAttachment());
1496 for (const auto& cur
: dstFB
->mColorDrawBuffers
) {
1497 if (srcFB
->mColorReadBuffer
->IsEquivalentForFeedback(*cur
)) {
1504 if (mask
& LOCAL_GL_DEPTH_BUFFER_BIT
&&
1505 srcFB
->DepthAttachment().IsEquivalentForFeedback(
1506 dstFB
->DepthAttachment())) {
1507 feedback
= &dstFB
->DepthAttachment();
1510 if (mask
& LOCAL_GL_STENCIL_BUFFER_BIT
&&
1511 srcFB
->StencilAttachment().IsEquivalentForFeedback(
1512 dstFB
->StencilAttachment())) {
1513 feedback
= &dstFB
->StencilAttachment();
1517 webgl
->ErrorInvalidOperation(
1518 "Feedback detected into DRAW_FRAMEBUFFER's"
1519 " 0x%04x attachment.",
1520 feedback
->mAttachmentPoint
);
1523 } else if (!srcFB
&& !dstFB
) {
1524 webgl
->ErrorInvalidOperation("Feedback with default framebuffer.");
1529 // Mutually constrain src and dst rects for eldritch blits.
1532 using fvec2
= avec2
<float>; // Switch to float, because there's no perfect
1535 const auto zero2f
= fvec2
{0, 0};
1536 const auto srcSizef
= AsVec(srcSize
).StaticCast
<fvec2
>();
1537 const auto dstSizef
= AsVec(dstSize
).StaticCast
<fvec2
>();
1539 const auto srcP0f
= srcP0
.StaticCast
<fvec2
>();
1540 const auto srcP1f
= srcP1
.StaticCast
<fvec2
>();
1541 const auto dstP0f
= dstP0
.StaticCast
<fvec2
>();
1542 const auto dstP1f
= dstP1
.StaticCast
<fvec2
>();
1544 const auto srcRectDiff
= srcP1f
- srcP0f
;
1545 const auto dstRectDiff
= dstP1f
- dstP0f
;
1547 // Skip if zero-sized.
1548 if (!srcRectDiff
.x
|| !srcRectDiff
.y
|| !dstRectDiff
.x
|| !dstRectDiff
.y
) {
1549 srcP0
= srcP1
= dstP0
= dstP1
= {0, 0};
1553 // Clamp the rect points
1554 const auto srcQ0
= srcP0f
.ClampMinMax(zero2f
, srcSizef
);
1555 const auto srcQ1
= srcP1f
.ClampMinMax(zero2f
, srcSizef
);
1557 // Normalized to the [0,1] abstact copy rect
1558 const auto srcQ0Norm
= (srcQ0
- srcP0f
) / srcRectDiff
;
1559 const auto srcQ1Norm
= (srcQ1
- srcP0f
) / srcRectDiff
;
1562 const auto srcQ0InDst
= dstP0f
+ srcQ0Norm
* dstRectDiff
;
1563 const auto srcQ1InDst
= dstP0f
+ srcQ1Norm
* dstRectDiff
;
1565 // Clamp the rect points
1566 const auto dstQ0
= srcQ0InDst
.ClampMinMax(zero2f
, dstSizef
);
1567 const auto dstQ1
= srcQ1InDst
.ClampMinMax(zero2f
, dstSizef
);
1569 // Alright, time to go back to src!
1570 // Normalized to the [0,1] abstact copy rect
1571 const auto dstQ0Norm
= (dstQ0
- dstP0f
) / dstRectDiff
;
1572 const auto dstQ1Norm
= (dstQ1
- dstP0f
) / dstRectDiff
;
1575 const auto dstQ0InSrc
= srcP0f
+ dstQ0Norm
* srcRectDiff
;
1576 const auto dstQ1InSrc
= srcP0f
+ dstQ1Norm
* srcRectDiff
;
1578 const auto srcQ0Constrained
= dstQ0InSrc
.ClampMinMax(zero2f
, srcSizef
);
1579 const auto srcQ1Constrained
= dstQ1InSrc
.ClampMinMax(zero2f
, srcSizef
);
1581 // Round, don't floor:
1582 srcP0
= (srcQ0Constrained
+ 0.5).StaticCast
<ivec2
>();
1583 srcP1
= (srcQ1Constrained
+ 0.5).StaticCast
<ivec2
>();
1584 dstP0
= (dstQ0
+ 0.5).StaticCast
<ivec2
>();
1585 dstP1
= (dstQ1
+ 0.5).StaticCast
<ivec2
>();
1588 bool inBounds
= true;
1589 inBounds
&= (srcP0
== srcP0
.Clamp({0, 0}, AsVec(srcSize
)));
1590 inBounds
&= (srcP1
== srcP1
.Clamp({0, 0}, AsVec(srcSize
)));
1591 inBounds
&= (dstP0
== dstP0
.Clamp({0, 0}, AsVec(dstSize
)));
1592 inBounds
&= (dstP1
== dstP1
.Clamp({0, 0}, AsVec(dstSize
)));
1594 webgl
->ErrorImplementationBug(
1595 "Subrects still not within src and dst after constraining.");
1600 // Execute as constrained
1602 const auto& gl
= webgl
->gl
;
1603 const ScopedDrawCallWrapper
wrapper(*webgl
);
1605 gl
->fBlitFramebuffer(srcP0
.x
, srcP0
.y
, srcP1
.x
, srcP1
.y
, dstP0
.x
, dstP0
.y
,
1606 dstP1
.x
, dstP1
.y
, mask
, filter
);
1610 if (mask
& LOCAL_GL_COLOR_BUFFER_BIT
&& !colorSrgbMatches
&& !gl
->IsGLES() &&
1611 gl
->Version() < 440) {
1613 // Remember, we have to filter in the *linear* format blit.
1615 // src -Blit-> fbB -DrawBlit-> fbC -Blit-> dst
1617 const auto fbB
= gl::MozFramebuffer::Create(gl
, {1, 1}, 0, false);
1618 const auto fbC
= gl::MozFramebuffer::Create(gl
, {1, 1}, 0, false);
1622 auto sizeBC
= srcSize
;
1623 GLenum formatC
= LOCAL_GL_RGBA8
;
1624 if (srcColorFormat
->isSRGB
) {
1629 formatC
= LOCAL_GL_SRGB8_ALPHA8
;
1632 const auto fnSetTex
= [&](const gl::MozFramebuffer
& fb
,
1633 const GLenum format
) {
1634 const gl::ScopedBindTexture
bindTex(gl
, fb
.ColorTex());
1635 gl
->fTexStorage2D(LOCAL_GL_TEXTURE_2D
, 1, format
, sizeBC
.width
,
1638 fnSetTex(*fbB
, srcColorFormat
->sizedFormat
);
1639 fnSetTex(*fbC
, formatC
);
1644 const gl::ScopedBindFramebuffer
bindFb(gl
);
1645 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, fbB
->mFB
);
1647 if (srcColorFormat
->isSRGB
) {
1649 gl
->fBlitFramebuffer(srcP0
.x
, srcP0
.y
, srcP1
.x
, srcP1
.y
, srcP0
.x
,
1650 srcP0
.y
, srcP1
.x
, srcP1
.y
,
1651 LOCAL_GL_COLOR_BUFFER_BIT
, LOCAL_GL_NEAREST
);
1654 gl
->fBlitFramebuffer(srcP0
.x
, srcP0
.y
, srcP1
.x
, srcP1
.y
, dstP0
.x
,
1655 dstP0
.y
, dstP1
.x
, dstP1
.y
,
1656 LOCAL_GL_COLOR_BUFFER_BIT
, filter
);
1659 const auto& blitHelper
= *gl
->BlitHelper();
1660 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, fbC
->mFB
);
1661 blitHelper
.DrawBlitTextureToFramebuffer(fbB
->ColorTex(), sizeBC
, sizeBC
);
1665 const gl::ScopedBindFramebuffer
bindFb(gl
);
1666 gl
->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER
, fbC
->mFB
);
1668 if (srcColorFormat
->isSRGB
) {
1670 gl
->fBlitFramebuffer(srcP0
.x
, srcP0
.y
, srcP1
.x
, srcP1
.y
, dstP0
.x
,
1671 dstP0
.y
, dstP1
.x
, dstP1
.y
,
1672 LOCAL_GL_COLOR_BUFFER_BIT
, filter
);
1675 gl
->fBlitFramebuffer(dstP0
.x
, dstP0
.y
, dstP1
.x
, dstP1
.y
, dstP0
.x
,
1676 dstP0
.y
, dstP1
.x
, dstP1
.y
,
1677 LOCAL_GL_COLOR_BUFFER_BIT
, LOCAL_GL_NEAREST
);
1683 // glBlitFramebuffer ignores glColorMask!
1685 if (!webgl
->mBoundDrawFramebuffer
&& webgl
->mNeedsFakeNoAlpha
) {
1686 if (!webgl
->mScissorTestEnabled
) {
1687 gl
->fEnable(LOCAL_GL_SCISSOR_TEST
);
1689 if (webgl
->mRasterizerDiscardEnabled
) {
1690 gl
->fDisable(LOCAL_GL_RASTERIZER_DISCARD
);
1693 const auto dstRectMin
= MinExtents(dstP0
, dstP1
);
1694 const auto dstRectMax
= MaxExtents(dstP0
, dstP1
);
1695 const auto dstRectSize
= dstRectMax
- dstRectMin
;
1696 const WebGLContext::ScissorRect dstRect
= {dstRectMin
.x
, dstRectMin
.y
,
1697 dstRectSize
.x
, dstRectSize
.y
};
1700 gl
->fClearColor(0, 0, 0, 1);
1701 webgl
->DoColorMask(1 << 3);
1702 gl
->fClear(LOCAL_GL_COLOR_BUFFER_BIT
);
1704 if (!webgl
->mScissorTestEnabled
) {
1705 gl
->fDisable(LOCAL_GL_SCISSOR_TEST
);
1707 if (webgl
->mRasterizerDiscardEnabled
) {
1708 gl
->fEnable(LOCAL_GL_RASTERIZER_DISCARD
);
1710 webgl
->mScissorRect
.Apply(*gl
);
1711 gl
->fClearColor(webgl
->mColorClearValue
[0], webgl
->mColorClearValue
[1],
1712 webgl
->mColorClearValue
[2], webgl
->mColorClearValue
[3]);
1716 } // namespace mozilla