--- a/dom/canvas/WebGL2ContextFramebuffers.cpp
+++ b/dom/canvas/WebGL2ContextFramebuffers.cpp
@@ -8,81 +8,16 @@
#include "GLContext.h"
#include "GLScreenBuffer.h"
#include "WebGLContextUtils.h"
#include "WebGLFormats.h"
#include "WebGLFramebuffer.h"
namespace mozilla {
-static bool
-GetFBInfoForBlit(const WebGLFramebuffer* fb, const char* const fbInfo,
- GLsizei* const out_samples,
- const webgl::FormatInfo** const out_colorFormat,
- const webgl::FormatInfo** const out_depthFormat,
- const webgl::FormatInfo** const out_stencilFormat)
-{
- *out_samples = 0;
- *out_colorFormat = nullptr;
- *out_depthFormat = nullptr;
- *out_stencilFormat = nullptr;
-
- if (fb->ColorAttachment(0).IsDefined()) {
- const auto& attachment = fb->ColorAttachment(0);
- *out_samples = attachment.Samples();
- *out_colorFormat = attachment.Format()->format;
- }
-
- if (fb->DepthStencilAttachment().IsDefined()) {
- const auto& attachment = fb->DepthStencilAttachment();
- *out_samples = attachment.Samples();
-
- *out_depthFormat = attachment.Format()->format;
- *out_stencilFormat = *out_depthFormat;
- } else {
- if (fb->DepthAttachment().IsDefined()) {
- const auto& attachment = fb->DepthAttachment();
- *out_samples = attachment.Samples();
- *out_depthFormat = attachment.Format()->format;
- }
-
- if (fb->StencilAttachment().IsDefined()) {
- const auto& attachment = fb->StencilAttachment();
- *out_samples = attachment.Samples();
- *out_stencilFormat = attachment.Format()->format;
- }
- }
- return true;
-}
-
-static void
-GetBackbufferFormats(const WebGLContextOptions& options,
- const webgl::FormatInfo** const out_color,
- const webgl::FormatInfo** const out_depth,
- const webgl::FormatInfo** const out_stencil)
-{
- const auto effFormat = options.alpha ? webgl::EffectiveFormat::RGBA8
- : webgl::EffectiveFormat::RGB8;
- *out_color = webgl::GetFormat(effFormat);
-
- *out_depth = nullptr;
- *out_stencil = nullptr;
- if (options.depth && options.stencil) {
- *out_depth = webgl::GetFormat(webgl::EffectiveFormat::DEPTH24_STENCIL8);
- *out_stencil = *out_depth;
- } else {
- if (options.depth) {
- *out_depth = webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT16);
- }
- if (options.stencil) {
- *out_stencil = webgl::GetFormat(webgl::EffectiveFormat::STENCIL_INDEX8);
- }
- }
-}
-
void
WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
GLbitfield mask, GLenum filter)
{
if (IsContextLost())
return;
@@ -98,217 +33,75 @@ WebGL2Context::BlitFramebuffer(GLint src
case LOCAL_GL_NEAREST:
case LOCAL_GL_LINEAR:
break;
default:
ErrorInvalidEnumInfo("blitFramebuffer: Bad `filter`:", filter);
return;
}
- const GLbitfield depthAndStencilBits = LOCAL_GL_DEPTH_BUFFER_BIT |
- LOCAL_GL_STENCIL_BUFFER_BIT;
- if (mask & depthAndStencilBits &&
- filter != LOCAL_GL_NEAREST)
+ ////
+
+ const auto& readFB = mBoundReadFramebuffer;
+ if (readFB &&
+ !readFB->ValidateAndInitAttachments("blitFramebuffer's READ_FRAMEBUFFER"))
{
- ErrorInvalidOperation("blitFramebuffer: DEPTH_BUFFER_BIT and"
- " STENCIL_BUFFER_BIT can only be used with"
- " NEAREST filtering.");
- return;
- }
-
- if (mBoundReadFramebuffer == mBoundDrawFramebuffer) {
- // TODO: It's actually more complicated than this. We need to check that
- // the underlying buffers are not the same, not the framebuffers
- // themselves.
- ErrorInvalidOperation("blitFramebuffer: Source and destination must"
- " differ.");
return;
}
- GLsizei srcSamples;
- const webgl::FormatInfo* srcColorFormat = nullptr;
- const webgl::FormatInfo* srcDepthFormat = nullptr;
- const webgl::FormatInfo* srcStencilFormat = nullptr;
-
- if (mBoundReadFramebuffer) {
- if (!mBoundReadFramebuffer->ValidateAndInitAttachments("blitFramebuffer's READ_FRAMEBUFFER"))
- return;
-
- if (!GetFBInfoForBlit(mBoundReadFramebuffer, "READ_FRAMEBUFFER", &srcSamples,
- &srcColorFormat, &srcDepthFormat, &srcStencilFormat))
- {
- return;
- }
- } else {
- srcSamples = 0; // Always 0.
-
- GetBackbufferFormats(mOptions, &srcColorFormat, &srcDepthFormat,
- &srcStencilFormat);
- }
-
- GLsizei dstSamples;
- const webgl::FormatInfo* dstColorFormat = nullptr;
- const webgl::FormatInfo* dstDepthFormat = nullptr;
- const webgl::FormatInfo* dstStencilFormat = nullptr;
-
- if (mBoundDrawFramebuffer) {
- if (!mBoundDrawFramebuffer->ValidateAndInitAttachments("blitFramebuffer's DRAW_FRAMEBUFFER"))
- return;
-
- if (!GetFBInfoForBlit(mBoundDrawFramebuffer, "DRAW_FRAMEBUFFER", &dstSamples,
- &dstColorFormat, &dstDepthFormat, &dstStencilFormat))
- {
- return;
- }
- } else {
- dstSamples = gl->Screen()->Samples();
-
- GetBackbufferFormats(mOptions, &dstColorFormat, &dstDepthFormat,
- &dstStencilFormat);
- }
-
- if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
- const auto fnSignlessType = [](const webgl::FormatInfo* format)
- -> webgl::ComponentType
- {
- if (!format)
- return webgl::ComponentType::None;
-
- switch (format->componentType) {
- case webgl::ComponentType::UInt:
- return webgl::ComponentType::Int;
-
- case webgl::ComponentType::NormUInt:
- return webgl::ComponentType::NormInt;
-
- default:
- return format->componentType;
- }
- };
-
- const auto srcType = fnSignlessType(srcColorFormat);
- const auto dstType = fnSignlessType(dstColorFormat);
-
- if (srcType != dstType) {
- ErrorInvalidOperation("blitFramebuffer: Color buffer format component type"
- " mismatch.");
- return;
- }
-
- const bool srcIsInt = (srcType == webgl::ComponentType::Int);
- if (srcIsInt && filter != LOCAL_GL_NEAREST) {
- ErrorInvalidOperation("blitFramebuffer: Integer read buffers can only"
- " be filtered with NEAREST.");
- return;
- }
- }
-
- /* GLES 3.0.4, p199:
- * Calling BlitFramebuffer will result in an INVALID_OPERATION error if
- * mask includes DEPTH_BUFFER_BIT or STENCIL_BUFFER_BIT, and the source
- * and destination depth and stencil buffer formats do not match.
- *
- * jgilbert: The wording is such that if only DEPTH_BUFFER_BIT is specified,
- * the stencil formats must match. This seems wrong. It could be a spec bug,
- * or I could be missing an interaction in one of the earlier paragraphs.
- */
- if (mask & LOCAL_GL_DEPTH_BUFFER_BIT &&
- dstDepthFormat != srcDepthFormat)
+ const auto& drawFB = mBoundDrawFramebuffer;
+ if (drawFB &&
+ !drawFB->ValidateAndInitAttachments("blitFramebuffer's DRAW_FRAMEBUFFER"))
{
- ErrorInvalidOperation("blitFramebuffer: Depth buffer formats must match"
- " if selected.");
return;
}
- if (mask & LOCAL_GL_STENCIL_BUFFER_BIT &&
- dstStencilFormat != srcStencilFormat)
- {
- ErrorInvalidOperation("blitFramebuffer: Stencil buffer formats must"
- " match if selected.");
- return;
- }
-
- if (dstSamples != 0) {
- ErrorInvalidOperation("blitFramebuffer: DRAW_FRAMEBUFFER may not have"
- " multiple samples.");
- return;
- }
-
- if (srcSamples != 0) {
- if (mask & LOCAL_GL_COLOR_BUFFER_BIT &&
- dstColorFormat != srcColorFormat)
- {
- ErrorInvalidOperation("blitFramebuffer: Color buffer formats must"
- " match if selected, when reading from a"
- " multisampled source.");
- return;
- }
+ ////
- if (dstX0 != srcX0 ||
- dstX1 != srcX1 ||
- dstY0 != srcY0 ||
- dstY1 != srcY1)
- {
- ErrorInvalidOperation("blitFramebuffer: If the source is"
- " multisampled, then the source and dest"
- " regions must match exactly.");
- return;
- }
- }
-
- MakeContextCurrent();
- gl->fBlitFramebuffer(srcX0, srcY0, srcX1, srcY1,
- dstX0, dstY0, dstX1, dstY1,
- mask, filter);
-}
-
-static bool
-ValidateTextureLayerAttachment(GLenum attachment)
-{
- if (LOCAL_GL_COLOR_ATTACHMENT0 <= attachment &&
- attachment <= LOCAL_GL_COLOR_ATTACHMENT15)
- {
- return true;
- }
-
- switch (attachment) {
- case LOCAL_GL_DEPTH_ATTACHMENT:
- case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
- case LOCAL_GL_STENCIL_ATTACHMENT:
- return true;
- }
-
- return false;
+ WebGLFramebuffer::BlitFramebuffer(this,
+ readFB, srcX0, srcY0, srcX1, srcY1,
+ drawFB, dstX0, dstY0, dstX1, dstY1,
+ mask, filter);
}
void
WebGL2Context::FramebufferTextureLayer(GLenum target, GLenum attachment,
WebGLTexture* texture, GLint level, GLint layer)
{
+ const char funcName[] = "framebufferTextureLayer";
if (IsContextLost())
return;
- if (!ValidateFramebufferTarget(target, "framebufferTextureLayer"))
+ if (!ValidateFramebufferTarget(target, funcName))
return;
- if (!ValidateTextureLayerAttachment(attachment))
- return ErrorInvalidEnumInfo("framebufferTextureLayer: attachment:", attachment);
+ WebGLFramebuffer* fb;
+ switch (target) {
+ case LOCAL_GL_FRAMEBUFFER:
+ case LOCAL_GL_DRAW_FRAMEBUFFER:
+ fb = mBoundDrawFramebuffer;
+ break;
+
+ case LOCAL_GL_READ_FRAMEBUFFER:
+ fb = mBoundReadFramebuffer;
+ break;
+
+ default:
+ MOZ_CRASH("GFX: Bad target.");
+ }
+
+ if (!fb) {
+ return ErrorInvalidOperation("framebufferTextureLayer: cannot modify"
+ " framebuffer 0.");
+ }
+
+ if (!ValidateObjectAllowNull("framebufferTextureLayer: texture", texture))
+ return;
if (texture) {
- if (texture->IsDeleted()) {
- return ErrorInvalidValue("framebufferTextureLayer: texture must be a valid "
- "texture object.");
- }
-
- if (layer < 0)
- return ErrorInvalidValue("framebufferTextureLayer: layer must be >= 0.");
-
- if (level < 0)
- return ErrorInvalidValue("framebufferTextureLayer: level must be >= 0.");
-
switch (texture->Target().get()) {
case LOCAL_GL_TEXTURE_3D:
if (uint32_t(layer) >= mImplMax3DTextureSize) {
return ErrorInvalidValue("framebufferTextureLayer: layer must be < "
"MAX_3D_TEXTURE_SIZE");
}
if (uint32_t(level) > FloorLog2(mImplMax3DTextureSize)) {
@@ -330,37 +123,17 @@ WebGL2Context::FramebufferTextureLayer(G
break;
default:
return ErrorInvalidOperation("framebufferTextureLayer: texture must be an "
"existing 3D texture, or a 2D texture array.");
}
}
- WebGLFramebuffer* fb;
- switch (target) {
- case LOCAL_GL_FRAMEBUFFER:
- case LOCAL_GL_DRAW_FRAMEBUFFER:
- fb = mBoundDrawFramebuffer;
- break;
-
- case LOCAL_GL_READ_FRAMEBUFFER:
- fb = mBoundReadFramebuffer;
- break;
-
- default:
- MOZ_CRASH("GFX: Bad target.");
- }
-
- if (!fb) {
- return ErrorInvalidOperation("framebufferTextureLayer: cannot modify"
- " framebuffer 0.");
- }
-
- fb->FramebufferTextureLayer(attachment, texture, level, layer);
+ fb->FramebufferTextureLayer(funcName, attachment, texture, level, layer);
}
JS::Value
WebGL2Context::GetFramebufferAttachmentParameter(JSContext* cx,
GLenum target,
GLenum attachment,
GLenum pname,
ErrorResult& out_error)
@@ -527,51 +300,31 @@ WebGL2Context::InvalidateSubFramebuffer(
gl->fInvalidateSubFramebuffer(target, attachments.Length(),
attachments.Elements(), x, y, width, height);
}
}
void
WebGL2Context::ReadBuffer(GLenum mode)
{
+ const char funcName[] = "readBuffer";
if (IsContextLost())
return;
- const bool isColorAttachment = (mode >= LOCAL_GL_COLOR_ATTACHMENT0 &&
- mode <= LastColorAttachmentEnum());
-
- if (mode != LOCAL_GL_NONE && mode != LOCAL_GL_BACK && !isColorAttachment) {
- ErrorInvalidEnum("readBuffer: `mode` must be one of NONE, BACK, or "
- "COLOR_ATTACHMENTi. Was %s",
- EnumName(mode));
- return;
- }
-
if (mBoundReadFramebuffer) {
- if (mode != LOCAL_GL_NONE &&
- !isColorAttachment)
- {
- ErrorInvalidOperation("readBuffer: If READ_FRAMEBUFFER is non-null, `mode` "
- "must be COLOR_ATTACHMENTi or NONE. Was %s",
- EnumName(mode));
- return;
- }
-
- MakeContextCurrent();
- mBoundReadFramebuffer->SetReadBufferMode(mode);
- gl->fReadBuffer(mode);
+ mBoundReadFramebuffer->ReadBuffer(funcName, mode);
return;
}
// Operating on the default framebuffer.
if (mode != LOCAL_GL_NONE &&
mode != LOCAL_GL_BACK)
{
- ErrorInvalidOperation("readBuffer: If READ_FRAMEBUFFER is null, `mode`"
- " must be BACK or NONE. Was %s",
- EnumName(mode));
+ ErrorInvalidOperation("%s: If READ_FRAMEBUFFER is null, `mode` must be BACK or"
+ " NONE. Was %s",
+ funcName, EnumName(mode));
return;
}
gl->Screen()->SetReadBuffer(mode);
}
} // namespace mozilla
--- a/dom/canvas/WebGL2ContextState.cpp
+++ b/dom/canvas/WebGL2ContextState.cpp
@@ -38,20 +38,23 @@ WebGL2Context::GetParameter(JSContext* c
case LOCAL_GL_TRANSFORM_FEEDBACK_ACTIVE: {
realGLboolean b = 0;
gl->fGetBooleanv(pname, &b);
return JS::BooleanValue(bool(b));
}
/* GLenum */
case LOCAL_GL_READ_BUFFER: {
- if (mBoundReadFramebuffer)
- return JS::Int32Value(mBoundReadFramebuffer->ReadBufferMode());
+ if (!mBoundReadFramebuffer)
+ return JS::Int32Value(gl->Screen()->GetReadBufferMode());
- return JS::Int32Value(LOCAL_GL_BACK);
+ if (!mBoundReadFramebuffer->ColorReadBuffer())
+ return JS::Int32Value(LOCAL_GL_NONE);
+
+ return JS::Int32Value(mBoundReadFramebuffer->ColorReadBuffer()->mAttachmentPoint);
}
case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
/* fall through */
/* GLint */
case LOCAL_GL_MAX_COMBINED_UNIFORM_BLOCKS:
case LOCAL_GL_MAX_ELEMENTS_INDICES:
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -1632,31 +1632,21 @@ WebGLContext::EndComposition()
}
void
WebGLContext::DummyReadFramebufferOperation(const char* funcName)
{
if (!mBoundReadFramebuffer)
return; // Infallible.
- const auto target = (IsWebGL2() ? LOCAL_GL_READ_FRAMEBUFFER
- : LOCAL_GL_FRAMEBUFFER);
- nsCString fbStatusInfo;
- const auto status = mBoundReadFramebuffer->CheckFramebufferStatus(target,
- &fbStatusInfo);
+ const auto status = mBoundReadFramebuffer->CheckFramebufferStatus(funcName);
if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
- nsCString errorText("Incomplete framebuffer");
-
- if (fbStatusInfo.Length()) {
- errorText += ": ";
- errorText += fbStatusInfo;
- }
-
- ErrorInvalidFramebufferOperation("%s: %s.", funcName, errorText.BeginReading());
+ ErrorInvalidFramebufferOperation("%s: Framebuffer must be complete.",
+ funcName);
}
}
bool
WebGLContext::HasTimestampBits() const
{
// 'sync' provides glGetInteger64v either by supporting ARB_sync, GL3+, or GLES3+.
return gl->IsSupported(GLFeature::sync);
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1133,16 +1133,18 @@ protected:
uint32_t mImplMaxColorAttachments;
uint32_t mImplMaxDrawBuffers;
public:
GLenum LastColorAttachmentEnum() const {
return LOCAL_GL_COLOR_ATTACHMENT0 + mImplMaxColorAttachments - 1;
}
+ const decltype(mOptions)& Options() const { return mOptions; }
+
protected:
// Texture sizes are often not actually the GL values. Let's be explicit that these
// are implementation limits.
uint32_t mImplMaxTextureSize;
uint32_t mImplMaxCubeMapTextureSize;
uint32_t mImplMax3DTextureSize;
uint32_t mImplMaxArrayTextureLayers;
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -96,36 +96,38 @@ ScopedResolveTexturesForDraw::ScopedReso
MOZ_ASSERT(mWebGL->gl->IsCurrent());
if (!mWebGL->mActiveProgramLinkInfo) {
mWebGL->ErrorInvalidOperation("%s: The current program is not linked.", funcName);
*out_error = true;
return;
}
- std::vector<const WebGLFBAttachPoint*> fbAttachments;
- if (mWebGL->mBoundDrawFramebuffer) {
- const auto& fb = mWebGL->mBoundDrawFramebuffer;
- fb->GatherAttachments(&fbAttachments);
+ const std::vector<const WebGLFBAttachPoint*>* attachList = nullptr;
+ const auto& fb = mWebGL->mBoundDrawFramebuffer;
+ if (fb) {
+ attachList = &(fb->ResolvedCompleteData()->texDrawBuffers);
}
MOZ_ASSERT(mWebGL->mActiveProgramLinkInfo);
const auto& uniformSamplers = mWebGL->mActiveProgramLinkInfo->uniformSamplers;
for (const auto& uniform : uniformSamplers) {
const auto& texList = *(uniform->mSamplerTexList);
for (const auto& texUnit : uniform->mSamplerValues) {
if (texUnit >= texList.Length())
continue;
const auto& tex = texList[texUnit];
if (!tex)
continue;
- if (tex->IsFeedback(mWebGL, funcName, texUnit, fbAttachments)) {
+ if (attachList &&
+ tex->IsFeedback(mWebGL, funcName, texUnit, *attachList))
+ {
*out_error = true;
return;
}
FakeBlackType fakeBlack;
if (!tex->ResolveForDraw(funcName, texUnit, &fakeBlack)) {
mWebGL->ErrorOutOfMemory("%s: Failed to resolve textures for draw.",
funcName);
@@ -582,17 +584,18 @@ WebGLContext::DrawElementsInstanced(GLen
gl->fDrawElementsInstanced(mode, count, type,
reinterpret_cast<GLvoid*>(byteOffset),
primcount);
}
Draw_cleanup(funcName);
}
-void WebGLContext::Draw_cleanup(const char* funcName)
+void
+WebGLContext::Draw_cleanup(const char* funcName)
{
UndoFakeVertexAttrib0();
if (!mBoundDrawFramebuffer) {
Invalidate();
mShouldPresent = true;
MOZ_ASSERT(!mBackbufferNeedsClear);
}
@@ -609,19 +612,22 @@ void WebGLContext::Draw_cleanup(const ch
}
// Let's check for a really common error: Viewport is larger than the actual
// destination framebuffer.
uint32_t destWidth = mViewportWidth;
uint32_t destHeight = mViewportHeight;
if (mBoundDrawFramebuffer) {
- const auto& fba = mBoundDrawFramebuffer->ColorAttachment(0);
- if (fba.IsDefined()) {
- fba.Size(&destWidth, &destHeight);
+ const auto& drawBuffers = mBoundDrawFramebuffer->ColorDrawBuffers();
+ for (const auto& cur : drawBuffers) {
+ if (!cur->IsDefined())
+ continue;
+ cur->Size(&destWidth, &destHeight);
+ break;
}
} else {
destWidth = mWidth;
destHeight = mHeight;
}
if (mViewportWidth > int32_t(destWidth) ||
mViewportHeight > int32_t(destHeight))
--- a/dom/canvas/WebGLContextFramebufferOperations.cpp
+++ b/dom/canvas/WebGLContextFramebufferOperations.cpp
@@ -139,86 +139,47 @@ WebGLContext::DepthMask(WebGLboolean b)
void
WebGLContext::DrawBuffers(const dom::Sequence<GLenum>& buffers)
{
const char funcName[] = "drawBuffers";
if (IsContextLost())
return;
- if (!mBoundDrawFramebuffer) {
- // GLES 3.0.4 p186:
- // "If the GL is bound to the default framebuffer, then `n` must be 1 and the
- // constant must be BACK or NONE. [...] If DrawBuffers is supplied with a
- // constant other than BACK and NONE, or with a value of `n` other than 1, the
- // error INVALID_OPERATION is generated."
- if (buffers.Length() != 1) {
- ErrorInvalidOperation("%s: For the default framebuffer, `buffers` must have a"
- " length of 1.",
- funcName);
- return;
- }
-
- switch (buffers[0]) {
- case LOCAL_GL_NONE:
- case LOCAL_GL_BACK:
- break;
-
- default:
- ErrorInvalidOperation("%s: For the default framebuffer, `buffers[0]` must be"
- " BACK or NONE.",
- funcName);
- return;
- }
-
- mDefaultFB_DrawBuffer0 = buffers[0];
- gl->Screen()->SetDrawBuffer(buffers[0]);
+ if (mBoundDrawFramebuffer) {
+ mBoundDrawFramebuffer->DrawBuffers(funcName, buffers);
return;
}
- // Framebuffer object (not default framebuffer)
-
- if (buffers.Length() > mImplMaxDrawBuffers) {
- // "An INVALID_VALUE error is generated if `n` is greater than MAX_DRAW_BUFFERS."
- ErrorInvalidValue("%s: `buffers` must have a length <= MAX_DRAW_BUFFERS.",
- funcName);
+ // GLES 3.0.4 p186:
+ // "If the GL is bound to the default framebuffer, then `n` must be 1 and the
+ // constant must be BACK or NONE. [...] If DrawBuffers is supplied with a
+ // constant other than BACK and NONE, or with a value of `n` other than 1, the
+ // error INVALID_OPERATION is generated."
+ if (buffers.Length() != 1) {
+ ErrorInvalidOperation("%s: For the default framebuffer, `buffers` must have a"
+ " length of 1.",
+ funcName);
return;
}
- for (size_t i = 0; i < buffers.Length(); i++) {
- // "If the GL is bound to a draw framebuffer object, the `i`th buffer listed in
- // bufs must be COLOR_ATTACHMENTi or NONE. Specifying a buffer out of order,
- // BACK, or COLOR_ATTACHMENTm where `m` is greater than or equal to the value of
- // MAX_COLOR_ATTACHMENTS, will generate the error INVALID_OPERATION.
+ switch (buffers[0]) {
+ case LOCAL_GL_NONE:
+ case LOCAL_GL_BACK:
+ break;
- // WEBGL_draw_buffers:
- // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or
- // equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter."
- // This means that if buffers.Length() isn't larger than MaxDrawBuffers, it won't
- // be larger than MaxColorAttachments.
- if (buffers[i] != LOCAL_GL_NONE &&
- buffers[i] != LOCAL_GL_COLOR_ATTACHMENT0 + i)
- {
- ErrorInvalidOperation("%s: `buffers[i]` must be NONE or COLOR_ATTACHMENTi.",
- funcName);
- return;
- }
+ default:
+ ErrorInvalidOperation("%s: For the default framebuffer, `buffers[0]` must be"
+ " BACK or NONE.",
+ funcName);
+ return;
}
- MakeContextCurrent();
-
- const GLenum* ptr = nullptr;
- if (buffers.Length()) {
- ptr = buffers.Elements();
- }
-
- gl->fDrawBuffers(buffers.Length(), ptr);
-
- const auto end = ptr + buffers.Length();
- mBoundDrawFramebuffer->mDrawBuffers.assign(ptr, end);
+ mDefaultFB_DrawBuffer0 = buffers[0];
+ gl->Screen()->SetDrawBuffer(buffers[0]);
}
void
WebGLContext::StencilMask(GLuint mask)
{
if (IsContextLost())
return;
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -247,20 +247,21 @@ WebGLContext::BlendFuncSeparate(GLenum s
MakeContextCurrent();
gl->fBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
}
GLenum
WebGLContext::CheckFramebufferStatus(GLenum target)
{
+ const char funcName[] = "checkFramebufferStatus";
if (IsContextLost())
return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
- if (!ValidateFramebufferTarget(target, "invalidateFramebuffer"))
+ if (!ValidateFramebufferTarget(target, funcName))
return 0;
WebGLFramebuffer* fb;
switch (target) {
case LOCAL_GL_FRAMEBUFFER:
case LOCAL_GL_DRAW_FRAMEBUFFER:
fb = mBoundDrawFramebuffer;
break;
@@ -271,18 +272,17 @@ WebGLContext::CheckFramebufferStatus(GLe
default:
MOZ_CRASH("GFX: Bad target.");
}
if (!fb)
return LOCAL_GL_FRAMEBUFFER_COMPLETE;
- nsCString fbErrorInfo;
- return fb->CheckFramebufferStatus(target, &fbErrorInfo).get();
+ return fb->CheckFramebufferStatus(funcName).get();
}
already_AddRefed<WebGLProgram>
WebGLContext::CreateProgram()
{
if (IsContextLost())
return nullptr;
RefPtr<WebGLProgram> globj = new WebGLProgram(this);
@@ -479,20 +479,21 @@ WebGLContext::DepthRange(GLfloat zNear,
MakeContextCurrent();
gl->fDepthRange(zNear, zFar);
}
void
WebGLContext::FramebufferRenderbuffer(GLenum target, GLenum attachment,
GLenum rbtarget, WebGLRenderbuffer* wrb)
{
+ const char funcName[] = "framebufferRenderbuffer";
if (IsContextLost())
return;
- if (!ValidateFramebufferTarget(target, "framebufferRenderbuffer"))
+ if (!ValidateFramebufferTarget(target, funcName))
return;
WebGLFramebuffer* fb;
switch (target) {
case LOCAL_GL_FRAMEBUFFER:
case LOCAL_GL_DRAW_FRAMEBUFFER:
fb = mBoundDrawFramebuffer;
break;
@@ -500,114 +501,55 @@ WebGLContext::FramebufferRenderbuffer(GL
case LOCAL_GL_READ_FRAMEBUFFER:
fb = mBoundReadFramebuffer;
break;
default:
MOZ_CRASH("GFX: Bad target.");
}
- if (!fb) {
- return ErrorInvalidOperation("framebufferRenderbuffer: cannot modify"
- " framebuffer 0.");
- }
-
- if (rbtarget != LOCAL_GL_RENDERBUFFER) {
- return ErrorInvalidEnumInfo("framebufferRenderbuffer: rbtarget:",
- rbtarget);
- }
-
- if (!ValidateFramebufferAttachment(fb, attachment, "framebufferRenderbuffer"))
- return;
-
- fb->FramebufferRenderbuffer(attachment, rbtarget, wrb);
+ if (!fb)
+ return ErrorInvalidOperation("%s: Cannot modify framebuffer 0.", funcName);
+
+ fb->FramebufferRenderbuffer(funcName, attachment, rbtarget, wrb);
}
void
WebGLContext::FramebufferTexture2D(GLenum target,
GLenum attachment,
GLenum textarget,
WebGLTexture* tobj,
GLint level)
{
+ const char funcName[] = "framebufferTexture2D";
if (IsContextLost())
return;
- if (!ValidateFramebufferTarget(target, "framebufferTexture2D"))
- return;
-
- if (level < 0) {
- ErrorInvalidValue("framebufferTexture2D: level must not be negative.");
+ if (!ValidateFramebufferTarget(target, funcName))
return;
- }
-
- if (textarget != LOCAL_GL_TEXTURE_2D &&
- (textarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
- textarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))
- {
- return ErrorInvalidEnumInfo("framebufferTexture2D: textarget:",
- textarget);
- }
-
- if (IsWebGL2()) {
- /* GLES 3.0.4 p208:
- * If textarget is one of TEXTURE_CUBE_MAP_POSITIVE_X,
- * TEXTURE_CUBE_MAP_POSITIVE_Y, TEXTURE_CUBE_MAP_POSITIVE_Z,
- * TEXTURE_CUBE_MAP_NEGATIVE_X, TEXTURE_CUBE_MAP_NEGATIVE_Y,
- * or TEXTURE_CUBE_MAP_NEGATIVE_Z, then level must be greater
- * than or equal to zero and less than or equal to log2 of the
- * value of MAX_CUBE_MAP_TEXTURE_SIZE. If textarget is TEXTURE_2D,
- * level must be greater than or equal to zero and no larger than
- * log2 of the value of MAX_TEXTURE_SIZE. Otherwise, an
- * INVALID_VALUE error is generated.
- */
-
- if (textarget == LOCAL_GL_TEXTURE_2D) {
- if (uint32_t(level) > FloorLog2(mImplMaxTextureSize)) {
- ErrorInvalidValue("framebufferTexture2D: level is too large.");
- return;
- }
- } else {
- MOZ_ASSERT(textarget >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X &&
- textarget <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
-
- if (uint32_t(level) > FloorLog2(mImplMaxCubeMapTextureSize)) {
- ErrorInvalidValue("framebufferTexture2D: level is too large.");
- return;
- }
- }
- } else if (level != 0) {
- ErrorInvalidValue("framebufferTexture2D: level must be 0.");
- return;
- }
WebGLFramebuffer* fb;
switch (target) {
case LOCAL_GL_FRAMEBUFFER:
case LOCAL_GL_DRAW_FRAMEBUFFER:
fb = mBoundDrawFramebuffer;
break;
case LOCAL_GL_READ_FRAMEBUFFER:
fb = mBoundReadFramebuffer;
break;
default:
MOZ_CRASH("GFX: Bad target.");
}
- if (!fb) {
- return ErrorInvalidOperation("framebufferTexture2D: cannot modify"
- " framebuffer 0.");
- }
-
- if (!ValidateFramebufferAttachment(fb, attachment, "framebufferTexture2D"))
- return;
-
- fb->FramebufferTexture2D(attachment, textarget, tobj, level);
+ if (!fb)
+ return ErrorInvalidOperation("%s: Cannot modify framebuffer 0.", funcName);
+
+ fb->FramebufferTexture2D(funcName, attachment, textarget, tobj, level);
}
void
WebGLContext::FrontFace(GLenum mode)
{
if (IsContextLost())
return;
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -1,36 +1,50 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WebGLFramebuffer.h"
+// You know it's going to be fun when these two show up:
+#include <algorithm>
+#include <iterator>
+
#include "GLContext.h"
+#include "GLScreenBuffer.h"
#include "mozilla/dom/WebGLRenderingContextBinding.h"
#include "nsPrintfCString.h"
#include "WebGLContext.h"
#include "WebGLContextUtils.h"
#include "WebGLExtensions.h"
#include "WebGLRenderbuffer.h"
#include "WebGLTexture.h"
namespace mozilla {
+WebGLFBAttachPoint::WebGLFBAttachPoint()
+ : mFB(nullptr)
+ , mAttachmentPoint(0)
+ , mTexImageTarget(LOCAL_GL_NONE)
+ , mTexImageLayer(0)
+ , mTexImageLevel(0)
+{ }
+
WebGLFBAttachPoint::WebGLFBAttachPoint(WebGLFramebuffer* fb, GLenum attachmentPoint)
: mFB(fb)
, mAttachmentPoint(attachmentPoint)
, mTexImageTarget(LOCAL_GL_NONE)
, mTexImageLayer(0)
, mTexImageLevel(0)
{ }
WebGLFBAttachPoint::~WebGLFBAttachPoint()
{
+ MOZ_ASSERT(mFB, "Should have been Init'd.");
MOZ_ASSERT(!mRenderbufferPtr);
MOZ_ASSERT(!mTexturePtr);
}
void
WebGLFBAttachPoint::Unlink()
{
Clear();
@@ -81,25 +95,16 @@ WebGLFBAttachPoint::Samples() const
}
bool
WebGLFBAttachPoint::HasAlpha() const
{
return Format()->format->a;
}
-const webgl::FormatUsageInfo*
-WebGLFramebuffer::GetFormatForAttachment(const WebGLFBAttachPoint& attachment) const
-{
- MOZ_ASSERT(attachment.IsDefined());
- MOZ_ASSERT(attachment.Texture() || attachment.Renderbuffer());
-
- return attachment.Format();
-}
-
bool
WebGLFBAttachPoint::IsReadableFloat() const
{
auto formatUsage = Format();
MOZ_ASSERT(formatUsage);
auto format = formatUsage->format;
if (!format->IsColorFormat())
@@ -120,24 +125,18 @@ WebGLFBAttachPoint::Clear()
mTexturePtr = nullptr;
mRenderbufferPtr = nullptr;
OnBackingStoreRespecified();
}
void
-WebGLFBAttachPoint::SetTexImage(WebGLTexture* tex, TexImageTarget target, GLint level)
-{
- SetTexImageLayer(tex, target, level, 0);
-}
-
-void
-WebGLFBAttachPoint::SetTexImageLayer(WebGLTexture* tex, TexImageTarget target,
- GLint level, GLint layer)
+WebGLFBAttachPoint::SetTexImage(WebGLTexture* tex, TexImageTarget target, GLint level,
+ GLint layer)
{
Clear();
mTexturePtr = tex;
mTexImageTarget = target;
mTexImageLevel = level;
mTexImageLayer = layer;
@@ -171,17 +170,17 @@ WebGLFBAttachPoint::HasUninitializedImag
auto& imageInfo = mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel);
MOZ_ASSERT(imageInfo.IsDefined());
return !imageInfo.IsDataInitialized();
}
void
-WebGLFBAttachPoint::SetImageDataStatus(WebGLImageDataStatus newStatus)
+WebGLFBAttachPoint::SetImageDataStatus(WebGLImageDataStatus newStatus) const
{
if (!HasImage())
return;
if (mRenderbufferPtr) {
mRenderbufferPtr->mImageDataStatus = newStatus;
return;
}
@@ -338,94 +337,70 @@ WebGLFBAttachPoint::IsComplete(WebGLCont
return false;
}
}
return true;
}
void
-WebGLFBAttachPoint::FinalizeAttachment(gl::GLContext* gl, FBTarget target,
- GLenum attachment) const
+WebGLFBAttachPoint::Resolve(gl::GLContext* gl, FBTarget target) const
{
if (!HasImage()) {
- switch (attachment) {
+ switch (mAttachmentPoint) {
case LOCAL_GL_DEPTH_ATTACHMENT:
case LOCAL_GL_STENCIL_ATTACHMENT:
case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
break;
default:
- gl->fFramebufferRenderbuffer(target, attachment,
+ gl->fFramebufferRenderbuffer(target.get(), mAttachmentPoint,
LOCAL_GL_RENDERBUFFER, 0);
break;
}
return;
}
- MOZ_ASSERT(HasImage());
- if (Texture()) {
- MOZ_ASSERT(gl == Texture()->mContext->GL());
-
- const GLenum imageTarget = ImageTarget().get();
- const GLint mipLevel = MipLevel();
- const GLint layer = Layer();
- const GLuint glName = Texture()->mGLName;
-
- switch (imageTarget) {
- case LOCAL_GL_TEXTURE_2D:
- case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
- case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
- case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
- case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
- case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
- case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
- if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
- gl->fFramebufferTexture2D(target, LOCAL_GL_DEPTH_ATTACHMENT,
- imageTarget, glName, mipLevel);
- gl->fFramebufferTexture2D(target,
- LOCAL_GL_STENCIL_ATTACHMENT, imageTarget,
- glName, mipLevel);
- } else {
- gl->fFramebufferTexture2D(target, attachment, imageTarget,
- glName, mipLevel);
- }
- break;
-
- case LOCAL_GL_TEXTURE_2D_ARRAY:
- case LOCAL_GL_TEXTURE_3D:
- if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
- gl->fFramebufferTextureLayer(target,
- LOCAL_GL_DEPTH_ATTACHMENT, glName, mipLevel,
- layer);
- gl->fFramebufferTextureLayer(target,
- LOCAL_GL_STENCIL_ATTACHMENT, glName,
- mipLevel, layer);
- } else {
- gl->fFramebufferTextureLayer(target, attachment, glName,
- mipLevel, layer);
- }
- break;
- }
+ if (Renderbuffer()) {
+ Renderbuffer()->DoFramebufferRenderbuffer(target, mAttachmentPoint);
return;
}
+ MOZ_ASSERT(Texture());
- if (Renderbuffer()) {
- Renderbuffer()->DoFramebufferRenderbuffer(target, attachment);
- return;
+ MOZ_ASSERT(gl == Texture()->mContext->GL());
+
+ const auto& texName = Texture()->mGLName;
+
+ switch (mTexImageTarget.get()) {
+ case LOCAL_GL_TEXTURE_2D:
+ case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+ case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+ case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+ case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+ case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+ case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+ gl->fFramebufferTexture2D(target.get(), mAttachmentPoint, mTexImageTarget.get(),
+ texName, mTexImageLevel);
+ break;
+
+ case LOCAL_GL_TEXTURE_2D_ARRAY:
+ case LOCAL_GL_TEXTURE_3D:
+ // If we have fFramebufferTextureLayer, we can rely on having
+ // DEPTH_STENCIL_ATTACHMENT.
+ gl->fFramebufferTextureLayer(target.get(), mAttachmentPoint,
+ texName, mTexImageLevel, mTexImageLayer);
+ break;
}
-
- MOZ_CRASH("GFX: invalid render buffer");
}
JS::Value
WebGLFBAttachPoint::GetParameter(const char* funcName, WebGLContext* webgl, JSContext* cx,
GLenum target, GLenum attachment, GLenum pname,
- ErrorResult* const out_error)
+ ErrorResult* const out_error) const
{
const bool hasAttachment = (mTexturePtr || mRenderbufferPtr);
if (!hasAttachment) {
// Divergent between GLES 3 and 2.
// GLES 2.0.25 p127:
// "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is NONE, then querying any
// other pname will generate INVALID_ENUM."
@@ -620,367 +595,231 @@ WebGLFBAttachPoint::GetParameter(const c
MOZ_ASSERT(false, "Missing case.");
break;
}
return JS::Int32Value(ret);
}
////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
// WebGLFramebuffer
WebGLFramebuffer::WebGLFramebuffer(WebGLContext* webgl, GLuint fbo)
: WebGLContextBoundObject(webgl)
, mGLName(fbo)
- , mIsKnownFBComplete(false)
- , mReadBufferMode(LOCAL_GL_COLOR_ATTACHMENT0)
- , mColorAttachment0(this, LOCAL_GL_COLOR_ATTACHMENT0)
+#ifdef ANDROID
+ , mIsFB(false)
+#endif
, mDepthAttachment(this, LOCAL_GL_DEPTH_ATTACHMENT)
, mStencilAttachment(this, LOCAL_GL_STENCIL_ATTACHMENT)
, mDepthStencilAttachment(this, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
- , mMoreColorAttachments(webgl->mGLMaxColorAttachments)
- , mDrawBuffers(1, LOCAL_GL_COLOR_ATTACHMENT0)
-#ifdef ANDROID
- , mIsFB(false)
-#endif
{
mContext->mFramebuffers.insertBack(this);
+
+ size_t i = 0;
+ for (auto& cur : mColorAttachments) {
+ new (&cur) WebGLFBAttachPoint(this, LOCAL_GL_COLOR_ATTACHMENT0 + i);
+ i++;
+ }
+
+ mColorDrawBuffers.push_back(&mColorAttachments[0]);
+ mColorReadBuffer = &mColorAttachments[0];
}
void
WebGLFramebuffer::Delete()
{
- mColorAttachment0.Clear();
+ InvalidateFramebufferStatus();
+
mDepthAttachment.Clear();
mStencilAttachment.Clear();
mDepthStencilAttachment.Clear();
- for (auto& cur : mMoreColorAttachments) {
+ for (auto& cur : mColorAttachments) {
cur.Clear();
}
mContext->MakeContextCurrent();
mContext->gl->fDeleteFramebuffers(1, &mGLName);
LinkedListElement<WebGLFramebuffer>::removeFrom(mContext->mFramebuffers);
#ifdef ANDROID
mIsFB = false;
#endif
}
-void
-WebGLFramebuffer::FramebufferRenderbuffer(GLenum attachment, RBTarget rbtarget,
- WebGLRenderbuffer* rb)
+////
+
+Maybe<WebGLFBAttachPoint*>
+WebGLFramebuffer::GetColorAttachPoint(GLenum attachPoint)
{
- MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
- mContext->mBoundReadFramebuffer == this);
-
- if (!mContext->ValidateObjectAllowNull("framebufferRenderbuffer: renderbuffer", rb))
- return;
-
- // `attachPointEnum` is validated by ValidateFramebufferAttachment().
+ if (attachPoint == LOCAL_GL_NONE)
+ return Some<WebGLFBAttachPoint*>(nullptr);
- RefPtr<WebGLRenderbuffer> rb_ = rb; // Bug 1201275
- const auto fnAttach = [this, &rb_](GLenum attachment) {
- const auto attachPoint = this->GetAttachPoint(attachment);
- MOZ_ASSERT(attachPoint);
+ if (attachPoint < LOCAL_GL_COLOR_ATTACHMENT0)
+ return Nothing();
- attachPoint->SetRenderbuffer(rb_);
- };
+ const size_t colorId = attachPoint - LOCAL_GL_COLOR_ATTACHMENT0;
- if (mContext->IsWebGL2() && attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
- fnAttach(LOCAL_GL_DEPTH_ATTACHMENT);
- fnAttach(LOCAL_GL_STENCIL_ATTACHMENT);
- } else {
- fnAttach(attachment);
- }
+ MOZ_ASSERT(mContext->mImplMaxColorAttachments <= kMaxColorAttachments);
+ if (colorId >= mContext->mImplMaxColorAttachments)
+ return Nothing();
- InvalidateFramebufferStatus();
+ return Some(&mColorAttachments[colorId]);
}
-void
-WebGLFramebuffer::FramebufferTexture2D(GLenum attachment, TexImageTarget texImageTarget,
- WebGLTexture* tex, GLint level)
-{
- MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
- mContext->mBoundReadFramebuffer == this);
-
- if (!mContext->ValidateObjectAllowNull("framebufferTexture2D: texture", tex))
- return;
-
- if (tex) {
- if (!tex->HasEverBeenBound()) {
- mContext->ErrorInvalidOperation("framebufferTexture2D: the texture"
- " is not the name of a texture.");
- return;
- }
-
- const TexTarget destTexTarget = TexImageTargetToTexTarget(texImageTarget);
- if (tex->Target() != destTexTarget) {
- mContext->ErrorInvalidOperation("framebufferTexture2D: Mismatched"
- " texture and texture target.");
- return;
- }
- }
-
- RefPtr<WebGLTexture> tex_ = tex; // Bug 1201275
- const auto fnAttach = [this, &tex_, texImageTarget, level](GLenum attachment) {
- const auto attachPoint = this->GetAttachPoint(attachment);
- MOZ_ASSERT(attachPoint);
-
- attachPoint->SetTexImage(tex_, texImageTarget, level);
- };
-
- if (mContext->IsWebGL2() && attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
- fnAttach(LOCAL_GL_DEPTH_ATTACHMENT);
- fnAttach(LOCAL_GL_STENCIL_ATTACHMENT);
- } else {
- fnAttach(attachment);
- }
-
- InvalidateFramebufferStatus();
-}
-
-void
-WebGLFramebuffer::FramebufferTextureLayer(GLenum attachment, WebGLTexture* tex,
- GLint level, GLint layer)
-{
- MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
- mContext->mBoundReadFramebuffer == this);
-
- const TexImageTarget texImageTarget = (tex ? tex->Target().get()
- : LOCAL_GL_TEXTURE_2D);
-
- RefPtr<WebGLTexture> tex_ = tex; // Bug 1201275
- const auto fnAttach = [this, &tex_, texImageTarget, level, layer](GLenum attachment) {
- const auto attachPoint = this->GetAttachPoint(attachment);
- MOZ_ASSERT(attachPoint);
-
- attachPoint->SetTexImageLayer(tex_, texImageTarget, level, layer);
- };
-
- if (mContext->IsWebGL2() && attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
- fnAttach(LOCAL_GL_DEPTH_ATTACHMENT);
- fnAttach(LOCAL_GL_STENCIL_ATTACHMENT);
- } else {
- fnAttach(attachment);
- }
-
- InvalidateFramebufferStatus();
-}
-
-WebGLFBAttachPoint*
+Maybe<WebGLFBAttachPoint*>
WebGLFramebuffer::GetAttachPoint(GLenum attachPoint)
{
switch (attachPoint) {
- case LOCAL_GL_COLOR_ATTACHMENT0:
- return &mColorAttachment0;
-
case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
- return &mDepthStencilAttachment;
+ return Some(&mDepthStencilAttachment);
case LOCAL_GL_DEPTH_ATTACHMENT:
- return &mDepthAttachment;
+ return Some(&mDepthAttachment);
case LOCAL_GL_STENCIL_ATTACHMENT:
- return &mStencilAttachment;
+ return Some(&mStencilAttachment);
default:
- break;
+ return GetColorAttachPoint(attachPoint);
}
-
- const auto lastCAEnum = mContext->LastColorAttachmentEnum();
- if (attachPoint < LOCAL_GL_COLOR_ATTACHMENT1 ||
- attachPoint > lastCAEnum)
- {
- return nullptr;
- }
+}
- if (!mMoreColorAttachments.Size()) {
- for (GLenum cur = LOCAL_GL_COLOR_ATTACHMENT1; cur <= lastCAEnum; cur++) {
- mMoreColorAttachments.AppendNew(this, cur);
- }
+#define FOR_EACH_ATTACHMENT(X) \
+ X(mDepthAttachment); \
+ X(mStencilAttachment); \
+ X(mDepthStencilAttachment); \
+ \
+ for (auto& cur : mColorAttachments) { \
+ X(cur); \
}
- MOZ_ASSERT(LOCAL_GL_COLOR_ATTACHMENT0 + mMoreColorAttachments.Size() == lastCAEnum);
-
- const size_t offset = attachPoint - LOCAL_GL_COLOR_ATTACHMENT1;
- MOZ_ASSERT(offset <= mMoreColorAttachments.Size());
- return &mMoreColorAttachments[offset];
-}
void
WebGLFramebuffer::DetachTexture(const WebGLTexture* tex)
{
- if (mColorAttachment0.Texture() == tex)
- mColorAttachment0.Clear();
-
- if (mDepthAttachment.Texture() == tex)
- mDepthAttachment.Clear();
+ const auto fnDetach = [&](WebGLFBAttachPoint& attach) {
+ if (attach.Texture() == tex) {
+ attach.Clear();
+ }
+ };
- if (mStencilAttachment.Texture() == tex)
- mStencilAttachment.Clear();
-
- if (mDepthStencilAttachment.Texture() == tex)
- mDepthStencilAttachment.Clear();
-
- for (auto& cur : mMoreColorAttachments) {
- if (cur.Texture() == tex)
- cur.Clear();
- }
+ FOR_EACH_ATTACHMENT(fnDetach)
}
void
WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer* rb)
{
- if (mColorAttachment0.Renderbuffer() == rb)
- mColorAttachment0.Clear();
-
- if (mDepthAttachment.Renderbuffer() == rb)
- mDepthAttachment.Clear();
-
- if (mStencilAttachment.Renderbuffer() == rb)
- mStencilAttachment.Clear();
+ const auto fnDetach = [&](WebGLFBAttachPoint& attach) {
+ if (attach.Renderbuffer() == rb) {
+ attach.Clear();
+ }
+ };
- if (mDepthStencilAttachment.Renderbuffer() == rb)
- mDepthStencilAttachment.Clear();
+ FOR_EACH_ATTACHMENT(fnDetach)
+}
- for (auto& cur : mMoreColorAttachments) {
- if (cur.Renderbuffer() == rb)
- cur.Clear();
- }
-}
+////////////////////////////////////////////////////////////////////////////////
+// Completeness
bool
WebGLFramebuffer::HasDefinedAttachments() const
{
bool hasAttachments = false;
+ const auto func = [&](const WebGLFBAttachPoint& attach) {
+ hasAttachments |= attach.IsDefined();
+ };
- hasAttachments |= mColorAttachment0.IsDefined();
- hasAttachments |= mDepthAttachment.IsDefined();
- hasAttachments |= mStencilAttachment.IsDefined();
- hasAttachments |= mDepthStencilAttachment.IsDefined();
-
- for (const auto& cur : mMoreColorAttachments) {
- hasAttachments |= cur.IsDefined();
- }
-
+ FOR_EACH_ATTACHMENT(func)
return hasAttachments;
}
bool
WebGLFramebuffer::HasIncompleteAttachments(nsCString* const out_info) const
{
- const auto fnIsIncomplete = [this, out_info](const WebGLFBAttachPoint& cur) {
+ bool hasIncomplete = false;
+ const auto func = [&](const WebGLFBAttachPoint& cur) {
if (!cur.IsDefined())
- return false; // Not defined, so can't count as incomplete.
+ return; // Not defined, so can't count as incomplete.
- return !cur.IsComplete(this->mContext, out_info);
+ hasIncomplete |= !cur.IsComplete(mContext, out_info);
};
- bool hasIncomplete = false;
-
- hasIncomplete |= fnIsIncomplete(mColorAttachment0);
- hasIncomplete |= fnIsIncomplete(mDepthAttachment);
- hasIncomplete |= fnIsIncomplete(mStencilAttachment);
- hasIncomplete |= fnIsIncomplete(mDepthStencilAttachment);
-
- for (const auto& cur : mMoreColorAttachments) {
- hasIncomplete |= fnIsIncomplete(cur);
- }
-
+ FOR_EACH_ATTACHMENT(func)
return hasIncomplete;
}
bool
WebGLFramebuffer::AllImageRectsMatch() const
{
MOZ_ASSERT(HasDefinedAttachments());
DebugOnly<nsCString> fbStatusInfo;
MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo));
bool needsInit = true;
uint32_t width = 0;
uint32_t height = 0;
- const auto fnInitializeOrMatch = [&needsInit, &width,
- &height](const WebGLFBAttachPoint& attach)
- {
+ bool hasMismatch = false;
+ const auto func = [&](const WebGLFBAttachPoint& attach) {
if (!attach.HasImage())
- return true;
+ return;
uint32_t curWidth;
uint32_t curHeight;
attach.Size(&curWidth, &curHeight);
if (needsInit) {
needsInit = false;
width = curWidth;
height = curHeight;
- return true;
+ return;
}
- return (curWidth == width &&
- curHeight == height);
+ hasMismatch |= (curWidth != width ||
+ curHeight != height);
};
- bool matches = true;
-
- matches &= fnInitializeOrMatch(mColorAttachment0 );
- matches &= fnInitializeOrMatch(mDepthAttachment );
- matches &= fnInitializeOrMatch(mStencilAttachment );
- matches &= fnInitializeOrMatch(mDepthStencilAttachment);
-
- for (const auto& cur : mMoreColorAttachments) {
- matches &= fnInitializeOrMatch(cur);
- }
-
- return matches;
+ FOR_EACH_ATTACHMENT(func)
+ return !hasMismatch;
}
bool
WebGLFramebuffer::AllImageSamplesMatch() const
{
MOZ_ASSERT(HasDefinedAttachments());
DebugOnly<nsCString> fbStatusInfo;
MOZ_ASSERT(!HasIncompleteAttachments(&fbStatusInfo));
bool needsInit = true;
uint32_t samples = 0;
- const auto fnInitializeOrMatch = [&needsInit,
- &samples](const WebGLFBAttachPoint& attach)
- {
+ bool hasMismatch = false;
+ const auto func = [&](const WebGLFBAttachPoint& attach) {
if (!attach.HasImage())
- return true;
+ return;
const uint32_t curSamples = attach.Samples();
if (needsInit) {
needsInit = false;
samples = curSamples;
- return true;
+ return;
}
- return (curSamples == samples);
+ hasMismatch |= (curSamples != samples);
};
- bool matches = true;
-
- matches &= fnInitializeOrMatch(mColorAttachment0 );
- matches &= fnInitializeOrMatch(mDepthAttachment );
- matches &= fnInitializeOrMatch(mStencilAttachment );
- matches &= fnInitializeOrMatch(mDepthStencilAttachment);
+ FOR_EACH_ATTACHMENT(func)
+ return !hasMismatch;
+}
- for (const auto& cur : mMoreColorAttachments) {
- matches &= fnInitializeOrMatch(cur);
- }
-
- return matches;
-}
+#undef FOR_EACH_ATTACHMENT
FBStatus
WebGLFramebuffer::PrecheckFramebufferStatus(nsCString* const out_info) const
{
MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
mContext->mBoundReadFramebuffer == this);
if (!HasDefinedAttachments())
@@ -990,365 +829,948 @@ WebGLFramebuffer::PrecheckFramebufferSta
return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
if (!AllImageRectsMatch())
return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; // Inconsistent sizes
if (!AllImageSamplesMatch())
return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE; // Inconsistent samples
- if (!mContext->IsWebGL2()) {
- // INCOMPLETE_DIMENSIONS doesn't exist in GLES3.
+ if (mContext->IsWebGL2()) {
+ MOZ_ASSERT(!mDepthStencilAttachment.IsDefined());
+ } else {
const auto depthOrStencilCount = int(mDepthAttachment.IsDefined()) +
int(mStencilAttachment.IsDefined()) +
int(mDepthStencilAttachment.IsDefined());
if (depthOrStencilCount > 1)
return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
}
return LOCAL_GL_FRAMEBUFFER_COMPLETE;
}
-FBStatus
-WebGLFramebuffer::CheckFramebufferStatus(FBTarget target, nsCString* const out_info) const
-{
- if (mIsKnownFBComplete)
- return LOCAL_GL_FRAMEBUFFER_COMPLETE;
-
- FBStatus ret = PrecheckFramebufferStatus(out_info);
- if (ret != LOCAL_GL_FRAMEBUFFER_COMPLETE)
- return ret;
-
- // Looks good on our end. Let's ask the driver.
- mContext->MakeContextCurrent();
-
- // Ok, attach our chosen flavor of {DEPTH, STENCIL, DEPTH_STENCIL}.
- FinalizeAttachments();
-
- ret = mContext->gl->fCheckFramebufferStatus(target);
-
- if (ret == LOCAL_GL_FRAMEBUFFER_COMPLETE) {
- mIsKnownFBComplete = true;
- } else {
- out_info->AssignLiteral("Bad status according to the driver");
- }
-
- return ret;
-}
+////////////////////////////////////////
+// Validation
bool
WebGLFramebuffer::ValidateAndInitAttachments(const char* funcName)
{
MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
mContext->mBoundReadFramebuffer == this);
- nsCString fbStatusInfo;
- const auto fbStatus = CheckFramebufferStatus(&fbStatusInfo);
- if (fbStatus != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
- nsCString errorText = nsPrintfCString("Incomplete framebuffer: Status 0x%04x",
- fbStatus.get());
- if (fbStatusInfo.Length()) {
- errorText += ": ";
- errorText += fbStatusInfo;
- }
-
- mContext->ErrorInvalidFramebufferOperation("%s: %s.", funcName,
- errorText.BeginReading());
- return false;
- }
-
- // Cool! We've checked out ok. Just need to initialize.
-
- //////
- // Check if we need to initialize anything
-
- std::vector<WebGLFBAttachPoint*> tex3DToClear;
-
- const auto fnGatherIf3D = [&](WebGLFBAttachPoint& attach) {
- if (!attach.Texture())
- return false;
-
- const auto& info = attach.Texture()->ImageInfoAt(attach.ImageTarget(),
- attach.MipLevel());
- if (info.mDepth == 1)
- return false;
-
- tex3DToClear.push_back(&attach);
+ const auto fbStatus = CheckFramebufferStatus(funcName);
+ if (fbStatus == LOCAL_GL_FRAMEBUFFER_COMPLETE)
return true;
- };
-
- //////
-
- uint32_t clearBits = 0;
- std::vector<GLenum> drawBuffersForClear(1 + mMoreColorAttachments.Size(),
- LOCAL_GL_NONE);
-
- std::vector<WebGLFBAttachPoint*> attachmentsToClear;
- attachmentsToClear.reserve(1 + mMoreColorAttachments.Size() + 3);
-
- const auto fnGatherColor = [&](WebGLFBAttachPoint& attach, uint32_t colorAttachNum) {
- if (!IsDrawBuffer(colorAttachNum) || !attach.HasUninitializedImageData())
- return;
-
- if (fnGatherIf3D(attach))
- return;
-
- attachmentsToClear.push_back(&attach);
-
- clearBits |= LOCAL_GL_COLOR_BUFFER_BIT;
- drawBuffersForClear[colorAttachNum] = LOCAL_GL_COLOR_ATTACHMENT0 + colorAttachNum;
- };
-
- const auto fnGatherOther = [&](WebGLFBAttachPoint& attach, GLenum attachClearBits) {
- if (!attach.HasUninitializedImageData())
- return;
-
- if (fnGatherIf3D(attach))
- return;
-
- attachmentsToClear.push_back(&attach);
-
- clearBits |= attachClearBits;
- };
-
- //////
-
- fnGatherColor(mColorAttachment0, 0);
-
- size_t colorAttachNum = 1;
- for (auto& cur : mMoreColorAttachments) {
- fnGatherColor(cur, colorAttachNum);
- ++colorAttachNum;
- }
-
- fnGatherOther(mDepthAttachment, LOCAL_GL_DEPTH_BUFFER_BIT);
- fnGatherOther(mStencilAttachment, LOCAL_GL_STENCIL_BUFFER_BIT);
- fnGatherOther(mDepthStencilAttachment,
- LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT);
-
- //////
-
- mContext->MakeContextCurrent();
-
- if (clearBits) {
- const auto fnDrawBuffers = [this](const std::vector<GLenum>& list) {
- this->mContext->gl->fDrawBuffers(list.size(), list.data());
- };
-
- const auto drawBufferExt = WebGLExtensionID::WEBGL_draw_buffers;
- const bool hasDrawBuffers = (mContext->IsWebGL2() ||
- mContext->IsExtensionEnabled(drawBufferExt));
- if (hasDrawBuffers) {
- fnDrawBuffers(drawBuffersForClear);
- }
-
- ////////////
-
- // Clear!
- {
- gl::ScopedBindFramebuffer autoBind(mContext->gl, mGLName);
-
- mContext->ForceClearFramebufferWithDefaultValues(clearBits, false);
- }
-
- if (hasDrawBuffers) {
- fnDrawBuffers(mDrawBuffers);
- }
-
- for (auto* cur : attachmentsToClear) {
- cur->SetImageDataStatus(WebGLImageDataStatus::InitializedImageData);
- }
- }
-
- //////
-
- for (auto* attach : tex3DToClear) {
- auto* tex = attach->Texture();
- if (!tex->InitializeImageData(funcName, attach->ImageTarget(),
- attach->MipLevel()))
- {
- return false;
- }
- }
-
- return true;
-}
-
-static void
-FinalizeDrawAndReadBuffers(gl::GLContext* gl, bool isColorBufferDefined)
-{
- MOZ_ASSERT(gl, "Expected a valid GLContext ptr.");
- // GLES don't support DrawBuffer()/ReadBuffer.
- // According to http://www.opengl.org/wiki/Framebuffer_Object
- //
- // Each draw buffers must either specify color attachment points that have images
- // attached or must be GL_NONE​. (GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER​ when false).
- //
- // If the read buffer is set, then it must specify an attachment point that has an
- // image attached. (GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER​ when false).
- //
- // Note that this test is not performed if OpenGL 4.2 or ARB_ES2_compatibility is
- // available.
- if (gl->IsGLES() ||
- gl->IsSupported(gl::GLFeature::ES2_compatibility) ||
- gl->IsAtLeast(gl::ContextProfile::OpenGL, 420))
- {
- return;
- }
-
- // TODO(djg): Assert that fDrawBuffer/fReadBuffer is not NULL.
- GLenum colorBufferSource = isColorBufferDefined ? LOCAL_GL_COLOR_ATTACHMENT0
- : LOCAL_GL_NONE;
- gl->fDrawBuffer(colorBufferSource);
- gl->fReadBuffer(colorBufferSource);
-}
-
-void
-WebGLFramebuffer::FinalizeAttachments(FBTarget target) const
-{
- MOZ_ASSERT_IF(target == LOCAL_GL_READ_FRAMEBUFFER,
- mContext->mBoundReadFramebuffer == this);
- MOZ_ASSERT_IF(target != LOCAL_GL_READ_FRAMEBUFFER,
- mContext->mBoundDrawFramebuffer == this);
-
- gl::GLContext* gl = mContext->gl;
-
- ////
-
- mColorAttachment0.FinalizeAttachment(gl, target, LOCAL_GL_COLOR_ATTACHMENT0);
-
- for (size_t i = 0; i < mMoreColorAttachments.Size(); i++) {
- GLenum attachPoint = LOCAL_GL_COLOR_ATTACHMENT1 + i;
- mMoreColorAttachments[i].FinalizeAttachment(gl, target, attachPoint);
- }
-
- ////
-
- // Nuke the depth and stencil attachment points.
- gl->fFramebufferRenderbuffer(target, LOCAL_GL_DEPTH_ATTACHMENT,
- LOCAL_GL_RENDERBUFFER, 0);
- gl->fFramebufferRenderbuffer(target, LOCAL_GL_STENCIL_ATTACHMENT,
- LOCAL_GL_RENDERBUFFER, 0);
-
- mDepthAttachment.FinalizeAttachment(gl, target, LOCAL_GL_DEPTH_ATTACHMENT);
- mStencilAttachment.FinalizeAttachment(gl, target, LOCAL_GL_STENCIL_ATTACHMENT);
- mDepthStencilAttachment.FinalizeAttachment(gl, target, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT);
-
- ////
-
- FinalizeDrawAndReadBuffers(gl, mColorAttachment0.IsDefined());
+ mContext->ErrorInvalidFramebufferOperation("%s: Framebuffer must be"
+ " complete.",
+ funcName);
+ return false;
}
bool
WebGLFramebuffer::ValidateForRead(const char* funcName,
const webgl::FormatUsageInfo** const out_format,
uint32_t* const out_width, uint32_t* const out_height)
{
if (!ValidateAndInitAttachments(funcName))
return false;
- if (mReadBufferMode == LOCAL_GL_NONE) {
- mContext->ErrorInvalidOperation("%s: Read buffer mode must not be NONE.",
+ if (!mColorReadBuffer) {
+ mContext->ErrorInvalidOperation("%s: READ_BUFFER must not be NONE.", funcName);
+ return false;
+ }
+
+ if (!mColorReadBuffer->IsDefined()) {
+ mContext->ErrorInvalidOperation("%s: The READ_BUFFER attachment is not defined.",
funcName);
return false;
}
- const auto attachPoint = GetAttachPoint(mReadBufferMode);
- if (!attachPoint || !attachPoint->IsDefined()) {
- mContext->ErrorInvalidOperation("%s: The attachment specified for reading is"
- " null.", funcName);
- return false;
+ *out_format = mColorReadBuffer->Format();
+ mColorReadBuffer->Size(out_width, out_height);
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Resolution and caching
+
+void
+WebGLFramebuffer::ResolveAttachments(FBTarget target) const
+{
+ const auto& gl = mContext->gl;
+
+ ////
+
+ for (const auto& attach : mColorAttachments) {
+ attach.Resolve(gl, target);
}
- *out_format = attachPoint->Format();
- attachPoint->Size(out_width, out_height);
+ ////
+
+ // Nuke the depth and stencil attachment points.
+ gl->fFramebufferRenderbuffer(target.get(), LOCAL_GL_DEPTH_ATTACHMENT,
+ LOCAL_GL_RENDERBUFFER, 0);
+ gl->fFramebufferRenderbuffer(target.get(), LOCAL_GL_STENCIL_ATTACHMENT,
+ LOCAL_GL_RENDERBUFFER, 0);
+
+ mDepthAttachment.Resolve(gl, target);
+ mStencilAttachment.Resolve(gl, target);
+ mDepthStencilAttachment.Resolve(gl, target);
+}
+
+bool
+WebGLFramebuffer::ResolveAttachmentData(const char* funcName) const
+{
+ //////
+ // Check if we need to initialize anything
+
+ const auto fnIs3D = [&](const WebGLFBAttachPoint& attach) {
+ const auto& tex = attach.Texture();
+ if (!tex)
+ return false;
+
+ const auto& info = tex->ImageInfoAt(attach.ImageTarget(), attach.MipLevel());
+ if (info.mDepth == 1)
+ return false;
+
+ return true;
+ };
+
+ uint32_t clearBits = 0;
+ std::vector<const WebGLFBAttachPoint*> attachmentsToClear;
+ std::vector<const WebGLFBAttachPoint*> colorAttachmentsToClear;
+ std::vector<const WebGLFBAttachPoint*> tex3DAttachmentsToInit;
+
+ const auto fnGather = [&](const WebGLFBAttachPoint& attach, GLenum attachClearBits) {
+ if (!attach.HasUninitializedImageData())
+ return false;
+
+ if (fnIs3D(attach)) {
+ tex3DAttachmentsToInit.push_back(&attach);
+ return false;
+ }
+
+ clearBits |= attachClearBits;
+ attachmentsToClear.push_back(&attach);
+ return true;
+ };
+
+ //////
+
+ for (auto& cur : mColorDrawBuffers) {
+ if (fnGather(*cur, LOCAL_GL_COLOR_BUFFER_BIT)) {
+ colorAttachmentsToClear.push_back(cur);
+ }
+ }
+
+ fnGather(mDepthAttachment, LOCAL_GL_DEPTH_BUFFER_BIT);
+ fnGather(mStencilAttachment, LOCAL_GL_STENCIL_BUFFER_BIT);
+ fnGather(mDepthStencilAttachment, LOCAL_GL_DEPTH_BUFFER_BIT |
+ LOCAL_GL_STENCIL_BUFFER_BIT);
+
+ //////
+
+ for (const auto& attach : tex3DAttachmentsToInit) {
+ const auto& tex = attach->Texture();
+ if (!tex->InitializeImageData(funcName, attach->ImageTarget(),
+ attach->MipLevel()))
+ {
+ return false;
+ }
+ }
+
+ if (clearBits) {
+ const auto drawBufferExt = WebGLExtensionID::WEBGL_draw_buffers;
+ const bool hasDrawBuffers = (mContext->IsWebGL2() ||
+ mContext->IsExtensionEnabled(drawBufferExt));
+
+ const auto fnDrawBuffers = [&](const std::vector<const WebGLFBAttachPoint*>& src)
+ {
+ if (!hasDrawBuffers)
+ return;
+
+ std::vector<GLenum> enumList;
+
+ for (const auto& cur : src) {
+ const auto& attachEnum = cur->mAttachmentPoint;
+ const GLenum attachId = attachEnum - LOCAL_GL_COLOR_ATTACHMENT0;
+
+ while (enumList.size() < attachId) {
+ enumList.push_back(LOCAL_GL_NONE);
+ }
+ enumList.push_back(attachEnum);
+ }
+
+ mContext->gl->fDrawBuffers(enumList.size(), enumList.data());
+ };
+
+ ////
+ // Clear
+
+ mContext->MakeContextCurrent();
+
+ fnDrawBuffers(colorAttachmentsToClear);
+
+ {
+ gl::ScopedBindFramebuffer autoBind(mContext->gl, mGLName);
+
+ mContext->ForceClearFramebufferWithDefaultValues(clearBits, false);
+ }
+
+ fnDrawBuffers(mColorDrawBuffers);
+
+ // Mark initialized.
+ for (const auto& cur : attachmentsToClear) {
+ cur->SetImageDataStatus(WebGLImageDataStatus::InitializedImageData);
+ }
+ }
+
return true;
}
-static bool
-AttachmentsDontMatch(const WebGLFBAttachPoint& a, const WebGLFBAttachPoint& b)
+WebGLFramebuffer::ResolvedData::ResolvedData(const WebGLFramebuffer& parent)
+ : hasSampleBuffers(false)
+ , depthBuffer(nullptr)
+ , stencilBuffer(nullptr)
+{
+ if (parent.mDepthAttachment.IsDefined()) {
+ depthBuffer = &parent.mDepthAttachment;
+ }
+ if (parent.mStencilAttachment.IsDefined()) {
+ stencilBuffer = &parent.mDepthAttachment;
+ }
+ if (parent.mDepthStencilAttachment.IsDefined()) {
+ depthBuffer = &parent.mDepthStencilAttachment;
+ stencilBuffer = &parent.mDepthStencilAttachment;
+ }
+
+ ////
+
+ texDrawBuffers.reserve(parent.mColorDrawBuffers.size() + 2); // +2 for depth+stencil.
+
+ const auto fnCommon = [&](const WebGLFBAttachPoint& attach) {
+ if (!attach.IsDefined())
+ return false;
+
+ hasSampleBuffers |= bool(attach.Samples());
+
+ if (attach.Texture()) {
+ texDrawBuffers.push_back(&attach);
+ }
+ return true;
+ };
+
+ ////
+
+ const auto fnColor = [&](const WebGLFBAttachPoint& attach,
+ decltype(drawSet)* const out_destSet)
+ {
+ if (!fnCommon(attach))
+ return;
+
+ out_destSet->insert(attach);
+ };
+
+ const auto fnDepthStencil = [&](const WebGLFBAttachPoint& attach) {
+ if (!fnCommon(attach))
+ return;
+
+ drawSet.insert(attach);
+ readSet.insert(attach);
+ };
+
+ ////
+
+ fnDepthStencil(parent.mDepthAttachment);
+ fnDepthStencil(parent.mStencilAttachment);
+ fnDepthStencil(parent.mDepthStencilAttachment);
+
+ for (const auto& attach : parent.mColorDrawBuffers) {
+ fnColor(*attach, &drawSet);
+ }
+
+ if (parent.mColorReadBuffer) {
+ fnColor(*(parent.mColorReadBuffer), &readSet);
+ }
+}
+
+void
+WebGLFramebuffer::RecacheResolvedData()
+{
+ if (mResolvedCompleteData) {
+ mResolvedCompleteData.reset(new ResolvedData(*this));
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Entrypoints
+
+FBStatus
+WebGLFramebuffer::CheckFramebufferStatus(const char* funcName)
{
- if (a.Texture()) {
- return (a.Texture() != b.Texture());
+ if (IsResolvedComplete())
+ return LOCAL_GL_FRAMEBUFFER_COMPLETE;
+
+ // Ok, let's try to resolve it!
+
+ nsCString statusInfo;
+ FBStatus ret = PrecheckFramebufferStatus(&statusInfo);
+ do {
+ if (ret != LOCAL_GL_FRAMEBUFFER_COMPLETE)
+ break;
+
+ // Looks good on our end. Let's ask the driver.
+ gl::GLContext* const gl = mContext->gl;
+ gl->MakeCurrent();
+
+ ////
+
+ const FBTarget fbTarget = (mContext->IsWebGL2() ? LOCAL_GL_DRAW_FRAMEBUFFER
+ : LOCAL_GL_FRAMEBUFFER);
+ const bool needsFBRebind = (mContext->mBoundDrawFramebuffer != this);
+ if (needsFBRebind) {
+ gl->fBindFramebuffer(fbTarget.get(), mGLName);
+ }
+
+ ResolveAttachments(fbTarget); // OK, attach everything properly!
+ ret = gl->fCheckFramebufferStatus(fbTarget.get());
+
+ if (needsFBRebind) {
+ gl->fBindFramebuffer(fbTarget.get(),
+ mContext->mBoundDrawFramebuffer->mGLName);
+ }
+
+ ////
+
+ if (ret != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
+ statusInfo.AssignLiteral("Bad status according to the driver:");
+ break;
+ }
+
+ if (!ResolveAttachmentData(funcName)) {
+ ret = LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
+ statusInfo.AssignLiteral("Failed to lazily-initialize attachment data.");
+ break;
+ }
+
+ mResolvedCompleteData.reset(new ResolvedData(*this));
+ return LOCAL_GL_FRAMEBUFFER_COMPLETE;
+ } while (false);
+
+ MOZ_ASSERT(ret != LOCAL_GL_FRAMEBUFFER_COMPLETE);
+ mContext->GenerateWarning("%s: Framebuffer not complete. (status: 0x%04x) %s",
+ funcName, ret.get(), statusInfo.BeginReading());
+ return ret;
+}
+
+////
+
+void
+WebGLFramebuffer::DrawBuffers(const char* funcName, const dom::Sequence<GLenum>& buffers)
+{
+ if (buffers.Length() > mContext->mImplMaxDrawBuffers) {
+ // "An INVALID_VALUE error is generated if `n` is greater than MAX_DRAW_BUFFERS."
+ mContext->ErrorInvalidValue("%s: `buffers` must have a length <="
+ " MAX_DRAW_BUFFERS.", funcName);
+ return;
+ }
+
+ for (size_t i = 0; i < buffers.Length(); i++) {
+ // "If the GL is bound to a draw framebuffer object, the `i`th buffer listed in
+ // bufs must be COLOR_ATTACHMENTi or NONE. Specifying a buffer out of order,
+ // BACK, or COLOR_ATTACHMENTm where `m` is greater than or equal to the value of
+ // MAX_COLOR_ATTACHMENTS, will generate the error INVALID_OPERATION.
+
+ // WEBGL_draw_buffers:
+ // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or
+ // equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter."
+ // This means that if buffers.Length() isn't larger than MaxDrawBuffers, it won't
+ // be larger than MaxColorAttachments.
+ if (buffers[i] != LOCAL_GL_NONE &&
+ buffers[i] != LOCAL_GL_COLOR_ATTACHMENT0 + i)
+ {
+ mContext->ErrorInvalidOperation("%s: `buffers[i]` must be NONE or"
+ " COLOR_ATTACHMENTi.",
+ funcName);
+ return;
+ }
}
- if (a.Renderbuffer()) {
- return (a.Renderbuffer() != b.Renderbuffer());
+ ////
+ // Record it.
+
+ mColorDrawBuffers.clear();
+ for (size_t i = 0; i < buffers.Length(); i++) {
+ const auto& attachEnum = buffers[i];
+ if (attachEnum == LOCAL_GL_NONE)
+ continue;
+
+ const auto& attach = mColorAttachments[i];
+ MOZ_ASSERT(attach.mAttachmentPoint == attachEnum);
+
+ mColorDrawBuffers.push_back(&attach);
+ }
+ RecacheResolvedData();
+
+ ////
+
+ mContext->MakeContextCurrent();
+ mContext->gl->fDrawBuffers(buffers.Length(), buffers.Elements());
+}
+
+void
+WebGLFramebuffer::ReadBuffer(const char* funcName, GLenum attachPoint)
+{
+ const auto& maybeAttach = GetColorAttachPoint(attachPoint);
+ if (!maybeAttach) {
+ mContext->ErrorInvalidValue("%s: `mode` must be a COLOR_ATTACHMENTi, for "
+ " 0 <= i < MAX_DRAW_BUFFERS.", funcName);
+ return;
+ }
+ const auto& attach = maybeAttach.value();
+
+ // Record it.
+ mColorReadBuffer = attach;
+ RecacheResolvedData();
+
+ mContext->MakeContextCurrent();
+ mContext->gl->fReadBuffer(attachPoint);
+}
+
+////
+
+void
+WebGLFramebuffer::FramebufferRenderbuffer(const char* funcName, GLenum attachEnum,
+ GLenum rbtarget, WebGLRenderbuffer* rb)
+{
+ MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
+ mContext->mBoundReadFramebuffer == this);
+
+ // `attachment`
+ const auto maybeAttach = GetAttachPoint(attachEnum);
+ if (!maybeAttach) {
+ mContext->ErrorInvalidEnum("%s: Bad `attachment`: 0x%x.", funcName, attachEnum);
+ return;
+ }
+ const auto& attach = maybeAttach.value();
+
+ // `rbTarget`
+ if (rbtarget != LOCAL_GL_RENDERBUFFER) {
+ mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: rbtarget:", rbtarget);
+ return;
+ }
+
+ // `rb`
+ if (!mContext->ValidateObjectAllowNull("framebufferRenderbuffer: rb", rb))
+ return;
+
+ // End of validation.
+
+ if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
+ mDepthAttachment.SetRenderbuffer(rb);
+ mStencilAttachment.SetRenderbuffer(rb);
+ } else {
+ attach->SetRenderbuffer(rb);
+ }
+
+ InvalidateFramebufferStatus();
+}
+
+void
+WebGLFramebuffer::FramebufferTexture2D(const char* funcName, GLenum attachEnum,
+ GLenum texImageTarget, WebGLTexture* tex,
+ GLint level)
+{
+ MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
+ mContext->mBoundReadFramebuffer == this);
+
+ // `attachment`
+ const auto maybeAttach = GetAttachPoint(attachEnum);
+ if (!maybeAttach) {
+ mContext->ErrorInvalidEnum("%s: Bad `attachment`: 0x%x.", funcName, attachEnum);
+ return;
+ }
+ const auto& attach = maybeAttach.value();
+
+ // `texImageTarget`
+ if (texImageTarget != LOCAL_GL_TEXTURE_2D &&
+ (texImageTarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
+ texImageTarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))
+ {
+ mContext->ErrorInvalidEnumInfo("framebufferTexture2D: texImageTarget:",
+ texImageTarget);
+ return;
+ }
+
+ // `texture`
+ if (!mContext->ValidateObjectAllowNull("framebufferTexture2D: texture", tex))
+ return;
+
+ if (tex) {
+ if (!tex->HasEverBeenBound()) {
+ mContext->ErrorInvalidOperation("%s: `texture` has never been bound.",
+ funcName);
+ return;
+ }
+
+ const TexTarget destTexTarget = TexImageTargetToTexTarget(texImageTarget);
+ if (tex->Target() != destTexTarget) {
+ mContext->ErrorInvalidOperation("%s: Mismatched texture and texture target.",
+ funcName);
+ return;
+ }
}
- return false;
+ // `level`
+ if (level < 0)
+ return mContext->ErrorInvalidValue("%s: `level` must not be negative.", funcName);
+
+ if (mContext->IsWebGL2()) {
+ /* GLES 3.0.4 p208:
+ * If textarget is one of TEXTURE_CUBE_MAP_POSITIVE_X,
+ * TEXTURE_CUBE_MAP_POSITIVE_Y, TEXTURE_CUBE_MAP_POSITIVE_Z,
+ * TEXTURE_CUBE_MAP_NEGATIVE_X, TEXTURE_CUBE_MAP_NEGATIVE_Y,
+ * or TEXTURE_CUBE_MAP_NEGATIVE_Z, then level must be greater
+ * than or equal to zero and less than or equal to log2 of the
+ * value of MAX_CUBE_MAP_TEXTURE_SIZE. If textarget is TEXTURE_2D,
+ * level must be greater than or equal to zero and no larger than
+ * log2 of the value of MAX_TEXTURE_SIZE. Otherwise, an
+ * INVALID_VALUE error is generated.
+ */
+
+ if (texImageTarget == LOCAL_GL_TEXTURE_2D) {
+ if (uint32_t(level) > FloorLog2(mContext->mImplMaxTextureSize))
+ return mContext->ErrorInvalidValue("%s: `level` is too large.", funcName);
+ } else {
+ MOZ_ASSERT(texImageTarget >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X &&
+ texImageTarget <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
+
+ if (uint32_t(level) > FloorLog2(mContext->mImplMaxCubeMapTextureSize))
+ return mContext->ErrorInvalidValue("%s: `level` is too large.", funcName);
+ }
+ } else if (level != 0) {
+ return mContext->ErrorInvalidValue("%s: `level` must be 0.", funcName);
+ }
+
+ // End of validation.
+
+ if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
+ mDepthAttachment.SetTexImage(tex, texImageTarget, level);
+ mStencilAttachment.SetTexImage(tex, texImageTarget, level);
+ } else {
+ attach->SetTexImage(tex, texImageTarget, level);
+ }
+
+ InvalidateFramebufferStatus();
+}
+
+void
+WebGLFramebuffer::FramebufferTextureLayer(const char* funcName, GLenum attachEnum,
+ WebGLTexture* tex, GLint level, GLint layer)
+{
+ MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
+ mContext->mBoundReadFramebuffer == this);
+
+ // `attachment`
+ const auto maybeAttach = GetAttachPoint(attachEnum);
+ if (!maybeAttach) {
+ mContext->ErrorInvalidEnum("%s: Bad `attachment`: 0x%x.", funcName, attachEnum);
+ return;
+ }
+ const auto& attach = maybeAttach.value();
+
+ // `texture`
+ if (!mContext->ValidateObjectAllowNull("framebufferTextureLayer: texture", tex))
+ return;
+
+ if (tex && !tex->HasEverBeenBound()) {
+ mContext->ErrorInvalidOperation("%s: `texture` has never been bound.", funcName);
+ return;
+ }
+
+ // `level`, `layer`
+ if (layer < 0)
+ return mContext->ErrorInvalidValue("%s: `layer` must be >= 0.", funcName);
+
+ if (level < 0)
+ return mContext->ErrorInvalidValue("%s: `level` must be >= 0.", funcName);
+
+ TexImageTarget texImageTarget = LOCAL_GL_TEXTURE_3D;
+ if (tex) {
+ texImageTarget = tex->Target().get();
+ switch (texImageTarget.get()) {
+ case LOCAL_GL_TEXTURE_3D:
+ if (uint32_t(layer) >= mContext->mImplMax3DTextureSize) {
+ mContext->ErrorInvalidValue("%s: `layer` must be < %s.", funcName,
+ "MAX_3D_TEXTURE_SIZE");
+ return;
+ }
+
+ if (uint32_t(level) > FloorLog2(mContext->mImplMax3DTextureSize)) {
+ mContext->ErrorInvalidValue("%s: `level` must be <= log2(%s).", funcName,
+ "MAX_3D_TEXTURE_SIZE");
+ return;
+ }
+ break;
+
+ case LOCAL_GL_TEXTURE_2D_ARRAY:
+ if (uint32_t(layer) >= mContext->mImplMaxArrayTextureLayers) {
+ mContext->ErrorInvalidValue("%s: `layer` must be < %s.", funcName,
+ "MAX_ARRAY_TEXTURE_LAYERS");
+ return;
+ }
+
+ if (uint32_t(level) > FloorLog2(mContext->mImplMaxTextureSize)) {
+ mContext->ErrorInvalidValue("%s: `level` must be <= log2(%s).", funcName,
+ "MAX_TEXTURE_SIZE");
+ return;
+ }
+ break;
+
+ default:
+ mContext->ErrorInvalidOperation("%s: `texture` must be a TEXTURE_3D or"
+ " TEXTURE_2D_ARRAY.",
+ funcName);
+ return;
+ }
+ }
+
+ // End of validation.
+
+ if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
+ mDepthAttachment.SetTexImage(tex, texImageTarget, level, layer);
+ mStencilAttachment.SetTexImage(tex, texImageTarget, level, layer);
+ } else {
+ attach->SetTexImage(tex, texImageTarget, level, layer);
+ }
+
+ InvalidateFramebufferStatus();
}
JS::Value
WebGLFramebuffer::GetAttachmentParameter(const char* funcName, JSContext* cx,
- GLenum target, GLenum attachment,
- GLenum pname, ErrorResult* const out_error)
+ GLenum target, GLenum attachEnum, GLenum pname,
+ ErrorResult* const out_error)
{
- auto attachPoint = GetAttachPoint(attachment);
- if (!attachPoint) {
+ const auto maybeAttach = GetAttachPoint(attachEnum);
+ if (!maybeAttach) {
mContext->ErrorInvalidEnum("%s: Can only query COLOR_ATTACHMENTi,"
" DEPTH_ATTACHMENT, DEPTH_STENCIL_ATTACHMENT, or"
" STENCIL_ATTACHMENT for a framebuffer.",
funcName);
return JS::NullValue();
}
+ auto attach = maybeAttach.value();
- if (mContext->IsWebGL2() && attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
+ if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
// There are a couple special rules for this one.
if (pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE) {
mContext->ErrorInvalidOperation("%s: Querying"
" FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE"
" against DEPTH_STENCIL_ATTACHMENT is an"
" error.",
funcName);
return JS::NullValue();
}
- if (AttachmentsDontMatch(DepthAttachment(), StencilAttachment())) {
+ if (mDepthAttachment.Renderbuffer() != mStencilAttachment.Renderbuffer() ||
+ mDepthAttachment.Texture() != mStencilAttachment.Texture())
+ {
mContext->ErrorInvalidOperation("%s: DEPTH_ATTACHMENT and STENCIL_ATTACHMENT"
" have different objects bound.",
funcName);
return JS::NullValue();
}
- attachPoint = GetAttachPoint(LOCAL_GL_DEPTH_ATTACHMENT);
+ attach = &mDepthAttachment;
}
- return attachPoint->GetParameter(funcName, mContext, cx, target, attachment, pname,
- out_error);
+ return attach->GetParameter(funcName, mContext, cx, target, attachEnum, pname,
+ out_error);
}
+////////////////////
+
+static void
+GetBackbufferFormats(const WebGLContext* webgl,
+ const webgl::FormatInfo** const out_color,
+ const webgl::FormatInfo** const out_depth,
+ const webgl::FormatInfo** const out_stencil)
+{
+ const auto& options = webgl->Options();
+
+ const auto effFormat = (options.alpha ? webgl::EffectiveFormat::RGBA8
+ : webgl::EffectiveFormat::RGB8);
+ *out_color = webgl::GetFormat(effFormat);
+
+ *out_depth = nullptr;
+ *out_stencil = nullptr;
+ if (options.depth && options.stencil) {
+ *out_depth = webgl::GetFormat(webgl::EffectiveFormat::DEPTH24_STENCIL8);
+ *out_stencil = *out_depth;
+ } else {
+ if (options.depth) {
+ *out_depth = webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT16);
+ }
+ if (options.stencil) {
+ *out_stencil = webgl::GetFormat(webgl::EffectiveFormat::STENCIL_INDEX8);
+ }
+ }
+}
+
+/*static*/ void
+WebGLFramebuffer::BlitFramebuffer(WebGLContext* webgl,
+ const WebGLFramebuffer* srcFB, GLint srcX0, GLint srcY0,
+ GLint srcX1, GLint srcY1,
+ const WebGLFramebuffer* dstFB, GLint dstX0, GLint dstY0,
+ GLint dstX1, GLint dstY1,
+ GLbitfield mask, GLenum filter)
+{
+ const char funcName[] = "blitFramebuffer";
+ auto& gl = webgl->gl;
+
-void
-WebGLFramebuffer::GatherAttachments(std::vector<const WebGLFBAttachPoint*>* const out) const
-{
- auto itr = mDrawBuffers.cbegin();
- if (itr != mDrawBuffers.cend() &&
- *itr != LOCAL_GL_NONE)
+ ////
+ // Collect data
+
+ const auto fnGetFormat = [](const WebGLFBAttachPoint* cur) -> const webgl::FormatInfo*
{
- out->push_back(&mColorAttachment0);
- ++itr;
+ if (!cur)
+ return nullptr;
+
+ MOZ_ASSERT(cur->IsDefined());
+ return cur->Format()->format;
+ };
+
+ bool srcSampleBuffers;
+ const webgl::FormatInfo* srcColorFormat;
+ const webgl::FormatInfo* srcDepthFormat;
+ const webgl::FormatInfo* srcStencilFormat;
+
+ if (srcFB) {
+ srcSampleBuffers = srcFB->mResolvedCompleteData->hasSampleBuffers;
+
+ srcColorFormat = fnGetFormat(srcFB->mColorReadBuffer);
+ srcDepthFormat = fnGetFormat(srcFB->mResolvedCompleteData->depthBuffer);
+ srcStencilFormat = fnGetFormat(srcFB->mResolvedCompleteData->stencilBuffer);
+ } else {
+ srcSampleBuffers = false; // Always false.
+
+ GetBackbufferFormats(webgl, &srcColorFormat, &srcDepthFormat, &srcStencilFormat);
+ }
+
+ ////
+
+ bool dstSampleBuffers;
+ const webgl::FormatInfo* dstDepthFormat;
+ const webgl::FormatInfo* dstStencilFormat;
+ bool dstHasColor = false;
+ bool colorFormatsMatch = true;
+ bool colorTypesMatch = true;
+
+ const auto fnCheckColorFormat = [&](const webgl::FormatInfo* dstFormat) {
+ dstHasColor = true;
+ colorFormatsMatch &= (dstFormat == srcColorFormat);
+ colorTypesMatch &= (dstFormat->componentType == srcColorFormat->componentType);
+ };
+
+ if (dstFB) {
+ dstSampleBuffers = dstFB->mResolvedCompleteData->hasSampleBuffers;
+
+ dstDepthFormat = fnGetFormat(dstFB->mResolvedCompleteData->depthBuffer);
+ dstStencilFormat = fnGetFormat(dstFB->mResolvedCompleteData->stencilBuffer);
+
+ for (const auto& drawBuffer : dstFB->mColorDrawBuffers) {
+ fnCheckColorFormat(drawBuffer->Format()->format);
+ }
+ } else {
+ dstSampleBuffers = bool(gl->Screen()->Samples());
+
+ const webgl::FormatInfo* dstColorFormat;
+ GetBackbufferFormats(webgl, &dstColorFormat, &dstDepthFormat, &dstStencilFormat);
+
+ fnCheckColorFormat(dstColorFormat);
+ }
+
+ ////
+ // Clear unused buffer bits
+
+ if (mask & LOCAL_GL_COLOR_BUFFER_BIT &&
+ !srcColorFormat && !dstHasColor)
+ {
+
+ mask ^= LOCAL_GL_COLOR_BUFFER_BIT;
+ }
+
+ if (mask & LOCAL_GL_DEPTH_BUFFER_BIT &&
+ !srcDepthFormat && !dstDepthFormat)
+ {
+ mask ^= LOCAL_GL_DEPTH_BUFFER_BIT;
+ }
+
+ if (mask & LOCAL_GL_STENCIL_BUFFER_BIT &&
+ !srcStencilFormat && !dstStencilFormat)
+ {
+ mask ^= LOCAL_GL_STENCIL_BUFFER_BIT;
+ }
+
+ ////
+ // Validation
+
+ if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
+ if (srcColorFormat && filter == LOCAL_GL_LINEAR) {
+ const auto& type = srcColorFormat->componentType;
+ if (type == webgl::ComponentType::Int ||
+ type == webgl::ComponentType::UInt)
+ {
+ webgl->ErrorInvalidOperation("%s: `filter` is LINEAR and READ_BUFFER"
+ " contains integer data.",
+ funcName);
+ return;
+ }
+ }
+
+ if (!colorTypesMatch) {
+ webgl->ErrorInvalidOperation("%s: Color component types (fixed/float/uint/"
+ "int) must match.",
+ funcName);
+ return;
+ }
}
- size_t i = 0;
- for (; itr != mDrawBuffers.cend(); ++itr) {
- if (i >= mMoreColorAttachments.Size())
- break;
+ const GLbitfield depthAndStencilBits = LOCAL_GL_DEPTH_BUFFER_BIT |
+ LOCAL_GL_STENCIL_BUFFER_BIT;
+ if (bool(mask & depthAndStencilBits) &&
+ filter != LOCAL_GL_NEAREST)
+ {
+ webgl->ErrorInvalidOperation("%s: DEPTH_BUFFER_BIT and STENCIL_BUFFER_BIT can"
+ " only be used with NEAREST filtering.",
+ funcName);
+ return;
+ }
- if (*itr != LOCAL_GL_NONE) {
- out->push_back(&mMoreColorAttachments[i]);
- }
- ++i;
+ /* GLES 3.0.4, p199:
+ * Calling BlitFramebuffer will result in an INVALID_OPERATION error if
+ * mask includes DEPTH_BUFFER_BIT or STENCIL_BUFFER_BIT, and the source
+ * and destination depth and stencil buffer formats do not match.
+ *
+ * jgilbert: The wording is such that if only DEPTH_BUFFER_BIT is specified,
+ * the stencil formats must match. This seems wrong. It could be a spec bug,
+ * or I could be missing an interaction in one of the earlier paragraphs.
+ */
+ if (mask & LOCAL_GL_DEPTH_BUFFER_BIT &&
+ dstDepthFormat != srcDepthFormat)
+ {
+ webgl->ErrorInvalidOperation("%s: Depth buffer formats must match if selected.",
+ funcName);
+ return;
+ }
+
+ if (mask & LOCAL_GL_STENCIL_BUFFER_BIT &&
+ dstStencilFormat != srcStencilFormat)
+ {
+ webgl->ErrorInvalidOperation("%s: Stencil buffer formats must match if selected.",
+ funcName);
+ return;
+ }
+
+ ////
+
+ if (dstSampleBuffers) {
+ webgl->ErrorInvalidOperation("%s: DRAW_FRAMEBUFFER may not have multiple"
+ " samples.",
+ funcName);
+ return;
}
- out->push_back(&mDepthAttachment);
- out->push_back(&mStencilAttachment);
- out->push_back(&mDepthStencilAttachment);
+ if (srcSampleBuffers) {
+ if (mask & LOCAL_GL_COLOR_BUFFER_BIT &&
+ !colorFormatsMatch)
+ {
+ webgl->ErrorInvalidOperation("%s: Color buffer formats must match if"
+ " selected, when reading from a multisampled"
+ " source.",
+ funcName);
+ return;
+ }
+
+ if (dstX0 != srcX0 ||
+ dstX1 != srcX1 ||
+ dstY0 != srcY0 ||
+ dstY1 != srcY1)
+ {
+ webgl->ErrorInvalidOperation("%s: If the source is multisampled, then the"
+ " source and dest regions must match exactly.",
+ funcName);
+ return;
+ }
+ }
+
+ ////
+ // Check for feedback
+
+ if (srcFB && dstFB) {
+ const auto fnValidateBuffers = [&](GLenum bufferBit, const char* bufferBitName,
+ bool srcHas, bool dstHas)
+ {
+ if (mask & bufferBit &&
+ srcHas != dstHas)
+ {
+ webgl->ErrorInvalidOperation("%s: With %s, must have a corresponding draw"
+ " buffer iff there's a relevent read"
+ " buffer.",
+ funcName, bufferBitName);
+ return false;
+ }
+ return true;
+ };
+
+ if (!fnValidateBuffers( LOCAL_GL_COLOR_BUFFER_BIT, "COLOR_BUFFER_BIT",
+ bool(srcFB->mColorReadBuffer),
+ bool(dstFB->mColorDrawBuffers.size()) ) ||
+ !fnValidateBuffers( LOCAL_GL_DEPTH_BUFFER_BIT, "DEPTH_BUFFER_BIT",
+ bool(srcFB->mResolvedCompleteData->depthBuffer),
+ bool(dstFB->mResolvedCompleteData->depthBuffer) ) ||
+ !fnValidateBuffers( LOCAL_GL_STENCIL_BUFFER_BIT, "STENCIL_BUFFER_BIT",
+ bool(srcFB->mResolvedCompleteData->stencilBuffer),
+ bool(dstFB->mResolvedCompleteData->stencilBuffer) ))
+ {
+ return;
+ }
+
+ const auto& readSet = srcFB->mResolvedCompleteData->readSet;
+ const auto& drawSet = dstFB->mResolvedCompleteData->drawSet;
+
+ std::vector<WebGLFBAttachPoint::Ordered> intersection;
+ std::set_intersection(drawSet.begin(), drawSet.end(),
+ readSet.begin(), readSet.end(),
+ std::back_inserter(intersection));
+
+ if (intersection.size()) {
+ // set_intersection pulls from the first range, so it records conflicts on the
+ // DRAW_FRAMEBUFFER.
+ const auto& example = intersection.cbegin()->mRef;
+ webgl->ErrorInvalidOperation("%s: Feedback detected into DRAW_FRAMEBUFFER's"
+ " 0x%04x attachment.",
+ funcName, example.mAttachmentPoint);
+ return;
+ }
+ } else if (!srcFB && !dstFB) {
+ webgl->ErrorInvalidOperation("%s: Feedback with default framebuffer.", funcName);
+ return;
+ }
+
+ ////
+
+ gl->MakeCurrent();
+ gl->fBlitFramebuffer(srcX0, srcY0, srcX1, srcY1,
+ dstX0, dstY0, dstX1, dstY1,
+ mask, filter);
}
////////////////////////////////////////////////////////////////////////////////
// Goop.
JSObject*
WebGLFramebuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
{
@@ -1388,18 +1810,17 @@ ImplCycleCollectionTraverse(nsCycleColle
uint32_t flags = 0)
{
for (auto& cur : field) {
ImplCycleCollectionTraverse(callback, cur, name, flags);
}
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLFramebuffer,
- mColorAttachment0,
mDepthAttachment,
mStencilAttachment,
mDepthStencilAttachment,
- mMoreColorAttachments)
+ mColorAttachments)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLFramebuffer, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLFramebuffer, Release)
} // namespace mozilla
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -22,279 +22,263 @@ namespace mozilla {
class WebGLFramebuffer;
class WebGLRenderbuffer;
class WebGLTexture;
template<typename T>
class PlacementArray;
-class WebGLFBAttachPoint
+namespace gl {
+ class GLContext;
+} // namespace gl
+
+class WebGLFBAttachPoint final
{
+ friend class WebGLFramebuffer;
public:
WebGLFramebuffer* const mFB;
const GLenum mAttachmentPoint;
-private:
+
+protected:
WebGLRefPtr<WebGLTexture> mTexturePtr;
WebGLRefPtr<WebGLRenderbuffer> mRenderbufferPtr;
TexImageTarget mTexImageTarget;
GLint mTexImageLayer;
uint32_t mTexImageLevel;
- // PlacementArray needs a default constructor.
- template<typename T>
- friend class PlacementArray;
+ ////
- WebGLFBAttachPoint()
- : mFB(nullptr)
- , mAttachmentPoint(0)
- { }
+ WebGLFBAttachPoint();
+ WebGLFBAttachPoint(WebGLFramebuffer* fb, GLenum attachmentPoint);
public:
- WebGLFBAttachPoint(WebGLFramebuffer* fb, GLenum attachmentPoint);
~WebGLFBAttachPoint();
+ ////
+
void Unlink();
bool IsDefined() const;
bool IsDeleteRequested() const;
const webgl::FormatUsageInfo* Format() const;
uint32_t Samples() const;
bool HasAlpha() const;
bool IsReadableFloat() const;
void Clear();
- void SetTexImage(WebGLTexture* tex, TexImageTarget target, GLint level);
- void SetTexImageLayer(WebGLTexture* tex, TexImageTarget target, GLint level,
- GLint layer);
+ void SetTexImage(WebGLTexture* tex, TexImageTarget target, GLint level,
+ GLint layer = 0);
void SetRenderbuffer(WebGLRenderbuffer* rb);
- const WebGLTexture* Texture() const {
- return mTexturePtr;
- }
- WebGLTexture* Texture() {
- return mTexturePtr;
- }
- const WebGLRenderbuffer* Renderbuffer() const {
- return mRenderbufferPtr;
- }
- WebGLRenderbuffer* Renderbuffer() {
- return mRenderbufferPtr;
- }
+ WebGLTexture* Texture() const { return mTexturePtr; }
+ WebGLRenderbuffer* Renderbuffer() const { return mRenderbufferPtr; }
+
TexImageTarget ImageTarget() const {
return mTexImageTarget;
}
GLint Layer() const {
return mTexImageLayer;
}
uint32_t MipLevel() const {
return mTexImageLevel;
}
void AttachmentName(nsCString* out) const;
bool HasUninitializedImageData() const;
- void SetImageDataStatus(WebGLImageDataStatus x);
+ void SetImageDataStatus(WebGLImageDataStatus x) const;
void Size(uint32_t* const out_width, uint32_t* const out_height) const;
bool HasImage() const;
bool IsComplete(WebGLContext* webgl, nsCString* const out_info) const;
- void FinalizeAttachment(gl::GLContext* gl, FBTarget target,
- GLenum attachmentLoc) const;
+ void Resolve(gl::GLContext* gl, FBTarget target) const;
JS::Value GetParameter(const char* funcName, WebGLContext* webgl, JSContext* cx,
GLenum target, GLenum attachment, GLenum pname,
- ErrorResult* const out_error);
+ ErrorResult* const out_error) const;
void OnBackingStoreRespecified() const;
-};
+
+ ////
-template<typename T>
-class PlacementArray
-{
-public:
- const size_t mCapacity;
-protected:
- size_t mSize;
- T* const mArray;
+ struct Ordered {
+ const WebGLFBAttachPoint& mRef;
-public:
- explicit PlacementArray(size_t capacity)
- : mCapacity(capacity)
- , mSize(0)
- , mArray((T*)moz_xmalloc(sizeof(T) * capacity))
- { }
+ Ordered(const WebGLFBAttachPoint& ref)
+ : mRef(ref)
+ { }
- ~PlacementArray() {
- for (auto& cur : *this) {
- cur.~T();
- }
- free(mArray);
- }
+ bool operator<(const Ordered& other) const {
+ MOZ_ASSERT(mRef.IsDefined() && other.mRef.IsDefined());
- T* begin() const {
- return mArray;
- }
-
- T* end() const {
- return mArray + mSize;
- }
+#define ORDER_BY(X) if (X != other.X) return X < other.X;
- T& operator [](size_t offset) const {
- MOZ_ASSERT(offset < mSize);
- return mArray[offset];
- }
-
- const size_t& Size() const { return mSize; }
+ ORDER_BY(mRef.mRenderbufferPtr)
+ ORDER_BY(mRef.mTexturePtr)
+ ORDER_BY(mRef.mTexImageTarget.get())
+ ORDER_BY(mRef.mTexImageLevel)
+ ORDER_BY(mRef.mTexImageLayer)
- template<typename A, typename B>
- void AppendNew(A a, B b) {
- if (mSize == mCapacity)
- MOZ_CRASH("GFX: Bad EmplaceAppend.");
-
- // Placement `new`:
- new (&(mArray[mSize])) T(a, b);
- ++mSize;
- }
+#undef ORDER_BY
+ return false;
+ }
+ };
};
class WebGLFramebuffer final
: public nsWrapperCache
, public WebGLRefCountedObject<WebGLFramebuffer>
, public LinkedListElement<WebGLFramebuffer>
, public WebGLContextBoundObject
, public SupportsWeakPtr<WebGLFramebuffer>
{
friend class WebGLContext;
public:
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebGLFramebuffer)
const GLuint mGLName;
-private:
- mutable bool mIsKnownFBComplete;
-
- GLenum mReadBufferMode;
-
- // No need to chase pointers for the oft-used color0.
- WebGLFBAttachPoint mColorAttachment0;
- WebGLFBAttachPoint mDepthAttachment;
- WebGLFBAttachPoint mStencilAttachment;
- WebGLFBAttachPoint mDepthStencilAttachment;
-
- PlacementArray<WebGLFBAttachPoint> mMoreColorAttachments;
-
- std::vector<GLenum> mDrawBuffers;
-
- bool IsDrawBuffer(size_t n) const {
- if (n < mDrawBuffers.size())
- return bool(mDrawBuffers[n]);
-
- return false;
- }
-
+protected:
#ifdef ANDROID
// Bug 1140459: Some drivers (including our test slaves!) don't
// give reasonable answers for IsRenderbuffer, maybe others.
// This shows up on Android 2.3 emulator.
//
// So we track the `is a Framebuffer` state ourselves.
bool mIsFB;
#endif
+ ////
+
+ WebGLFBAttachPoint mDepthAttachment;
+ WebGLFBAttachPoint mStencilAttachment;
+ WebGLFBAttachPoint mDepthStencilAttachment;
+
+ // In theory, this number can be unbounded based on the driver. However, no driver
+ // appears to expose more than 8. We might as well stop there too, for now.
+ // (http://opengl.gpuinfo.org/gl_stats_caps_single.php?listreportsbycap=GL_MAX_COLOR_ATTACHMENTS)
+ static const size_t kMaxColorAttachments = 8; // jgilbert's MacBook Pro exposes 8.
+ WebGLFBAttachPoint mColorAttachments[kMaxColorAttachments];
+
+ ////
+
+ std::vector<const WebGLFBAttachPoint*> mColorDrawBuffers; // Non-null
+ const WebGLFBAttachPoint* mColorReadBuffer; // Null if NONE
+
+ ////
+
+ struct ResolvedData {
+ // BlitFramebuffer
+ bool hasSampleBuffers;
+ const WebGLFBAttachPoint* depthBuffer;
+ const WebGLFBAttachPoint* stencilBuffer;
+
+ // IsFeedback
+ std::vector<const WebGLFBAttachPoint*> texDrawBuffers; // Non-null
+ std::set<WebGLFBAttachPoint::Ordered> drawSet;
+ std::set<WebGLFBAttachPoint::Ordered> readSet;
+
+ ResolvedData(const WebGLFramebuffer& parent);
+ };
+
+ UniquePtr<const ResolvedData> mResolvedCompleteData;
+
+ ////
+
public:
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLFramebuffer)
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLFramebuffer)
+
WebGLFramebuffer(WebGLContext* webgl, GLuint fbo);
+ WebGLContext* GetParentObject() const { return mContext; }
+ virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
+
private:
~WebGLFramebuffer() {
DeleteOnce();
}
- const WebGLRectangleObject& GetAnyRectObject() const;
-
public:
void Delete();
- void FramebufferRenderbuffer(GLenum attachment, RBTarget rbtarget,
- WebGLRenderbuffer* rb);
- void FramebufferTexture2D(GLenum attachment, TexImageTarget texImageTarget,
- WebGLTexture* tex, GLint level);
- void FramebufferTextureLayer(GLenum attachment, WebGLTexture* tex, GLint level,
- GLint layer);
+ ////
bool HasDefinedAttachments() const;
bool HasIncompleteAttachments(nsCString* const out_info) const;
bool AllImageRectsMatch() const;
bool AllImageSamplesMatch() const;
FBStatus PrecheckFramebufferStatus(nsCString* const out_info) const;
- FBStatus CheckFramebufferStatus(FBTarget target, nsCString* const out_info) const;
-
- const webgl::FormatUsageInfo*
- GetFormatForAttachment(const WebGLFBAttachPoint& attachment) const;
-
- const WebGLFBAttachPoint& ColorAttachment(size_t colorAttachmentId) const {
- MOZ_ASSERT(colorAttachmentId < 1 + mMoreColorAttachments.Size());
- return colorAttachmentId ? mMoreColorAttachments[colorAttachmentId - 1]
- : mColorAttachment0;
- }
-
- const WebGLFBAttachPoint& DepthAttachment() const {
- return mDepthAttachment;
- }
-
- const WebGLFBAttachPoint& StencilAttachment() const {
- return mStencilAttachment;
- }
-
- const WebGLFBAttachPoint& DepthStencilAttachment() const {
- return mDepthStencilAttachment;
- }
-
- void SetReadBufferMode(GLenum readBufferMode) {
- mReadBufferMode = readBufferMode;
- }
-
- GLenum ReadBufferMode() const { return mReadBufferMode; }
-
- void GatherAttachments(std::vector<const WebGLFBAttachPoint*>* const out) const;
protected:
- WebGLFBAttachPoint* GetAttachPoint(GLenum attachment); // Fallible
+ Maybe<WebGLFBAttachPoint*> GetAttachPoint(GLenum attachment); // Fallible
+ Maybe<WebGLFBAttachPoint*> GetColorAttachPoint(GLenum attachment); // Fallible
+ void ResolveAttachments(FBTarget target) const;
+ bool ResolveAttachmentData(const char* funcName) const;
public:
void DetachTexture(const WebGLTexture* tex);
-
void DetachRenderbuffer(const WebGLRenderbuffer* rb);
-
- WebGLContext* GetParentObject() const {
- return mContext;
- }
-
- void FinalizeAttachments(FBTarget target) const;
-
- virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
-
- NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLFramebuffer)
- NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLFramebuffer)
-
bool ValidateAndInitAttachments(const char* funcName);
- void InvalidateFramebufferStatus() const {
- mIsKnownFBComplete = false;
- }
-
bool ValidateForRead(const char* info,
const webgl::FormatUsageInfo** const out_format,
uint32_t* const out_width, uint32_t* const out_height);
+ ////////////////
+ // Getters
+
+#define GETTER(X) const decltype(m##X)& X() const { return m##X; }
+
+ GETTER(DepthAttachment)
+ GETTER(StencilAttachment)
+ GETTER(DepthStencilAttachment)
+ GETTER(ColorDrawBuffers)
+ GETTER(ColorReadBuffer)
+ GETTER(ResolvedCompleteData)
+
+#undef GETTER
+
+ ////////////////
+ // Invalidation
+
+ bool IsResolvedComplete() const { return bool(mResolvedCompleteData); }
+
+ void InvalidateFramebufferStatus() {
+ mResolvedCompleteData = nullptr;
+ }
+
+ void RecacheResolvedData();
+
+ ////////////////
+ // WebGL funcs
+
+ FBStatus CheckFramebufferStatus(const char* funcName);
+ void FramebufferRenderbuffer(const char* funcName, GLenum attachment, GLenum rbtarget,
+ WebGLRenderbuffer* rb);
+ void FramebufferTexture2D(const char* funcName, GLenum attachment,
+ GLenum texImageTarget, WebGLTexture* tex, GLint level);
+ void FramebufferTextureLayer(const char* funcName, GLenum attachment,
+ WebGLTexture* tex, GLint level, GLint layer);
+ void DrawBuffers(const char* funcName, const dom::Sequence<GLenum>& buffers);
+ void ReadBuffer(const char* funcName, GLenum attachPoint);
+
JS::Value GetAttachmentParameter(const char* funcName, JSContext* cx, GLenum target,
GLenum attachment, GLenum pname,
ErrorResult* const out_error);
+
+ static void BlitFramebuffer(WebGLContext* webgl,
+ const WebGLFramebuffer* src, GLint srcX0, GLint srcY0,
+ GLint srcX1, GLint srcY1,
+ const WebGLFramebuffer* dst, GLint dstX0, GLint dstY0,
+ GLint dstX1, GLint dstY1,
+ GLbitfield mask, GLenum filter);
};
} // namespace mozilla
#endif // WEBGL_FRAMEBUFFER_H_
--- a/dom/canvas/WebGLRenderbuffer.cpp
+++ b/dom/canvas/WebGLRenderbuffer.cpp
@@ -229,24 +229,24 @@ WebGLRenderbuffer::RenderbufferStorage(c
void
WebGLRenderbuffer::DoFramebufferRenderbuffer(FBTarget target, GLenum attachment) const
{
gl::GLContext* gl = mContext->gl;
if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
const GLuint stencilRB = (mSecondaryRB ? mSecondaryRB : mPrimaryRB);
- gl->fFramebufferRenderbuffer(target, LOCAL_GL_DEPTH_ATTACHMENT,
+ gl->fFramebufferRenderbuffer(target.get(), LOCAL_GL_DEPTH_ATTACHMENT,
LOCAL_GL_RENDERBUFFER, mPrimaryRB);
- gl->fFramebufferRenderbuffer(target, LOCAL_GL_STENCIL_ATTACHMENT,
+ gl->fFramebufferRenderbuffer(target.get(), LOCAL_GL_STENCIL_ATTACHMENT,
LOCAL_GL_RENDERBUFFER, stencilRB);
return;
}
- gl->fFramebufferRenderbuffer(target, attachment,
+ gl->fFramebufferRenderbuffer(target.get(), attachment,
LOCAL_GL_RENDERBUFFER, mPrimaryRB);
}
GLint
WebGLRenderbuffer::GetRenderbufferParameter(RBTarget target,
RBParam pname) const
{
gl::GLContext* gl = mContext->gl;
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -1964,30 +1964,28 @@ ValidateCopyDestUsage(const char* funcNa
return dstUsage;
}
bool
WebGLTexture::ValidateCopyTexImageForFeedback(const char* funcName, uint32_t level) const
{
const auto& fb = mContext->mBoundReadFramebuffer;
if (fb) {
- const auto readBuffer = fb->ReadBufferMode();
- MOZ_ASSERT(readBuffer != LOCAL_GL_NONE);
- const uint32_t colorAttachment = readBuffer - LOCAL_GL_COLOR_ATTACHMENT0;
- const auto& attach = fb->ColorAttachment(colorAttachment);
+ const auto& attach = fb->ColorReadBuffer();
+ MOZ_ASSERT(attach);
- if (attach.Texture() == this &&
- uint32_t(attach.MipLevel()) == level)
+ if (attach->Texture() == this &&
+ uint32_t(attach->MipLevel()) == level)
{
// Note that the TexImageTargets *don't* have to match for this to be
// undefined per GLES 3.0.4 p211, thus an INVALID_OP in WebGL.
mContext->ErrorInvalidOperation("%s: Feedback loop detected, as this texture"
" is already attached to READ_FRAMEBUFFER's"
" READ_BUFFER-selected COLOR_ATTACHMENT%u.",
- funcName, colorAttachment);
+ funcName, attach->mAttachmentPoint);
return false;
}
}
return true;
}
// There is no CopyTexImage3D.
void