Bug 614867 - Fix WebGL FBOs (pass framebuffer-object-attachment.html) - r=vlad

This commit is contained in:
Benoit Jacob 2010-12-06 06:34:35 -05:00
parent f3d3a4229e
commit 27b8817c93
2 changed files with 153 additions and 107 deletions

View File

@ -750,7 +750,8 @@ public:
mMaxLevelWithCustomImages(0), mMaxLevelWithCustomImages(0),
mHaveGeneratedMipmap(PR_FALSE), mHaveGeneratedMipmap(PR_FALSE),
mFakeBlackStatus(DoNotNeedFakeBlack) mFakeBlackStatus(DoNotNeedFakeBlack)
{} {
}
void Delete() { void Delete() {
if (mDeleted) if (mDeleted)
@ -1352,6 +1353,66 @@ protected:
NS_DEFINE_STATIC_IID_ACCESSOR(WebGLRenderbuffer, WEBGLRENDERBUFFER_PRIVATE_IID) NS_DEFINE_STATIC_IID_ACCESSOR(WebGLRenderbuffer, WEBGLRENDERBUFFER_PRIVATE_IID)
class WebGLFramebufferAttachment
{
nsRefPtr<WebGLTexture> mTexturePtr;
nsRefPtr<WebGLRenderbuffer> mRenderbufferPtr;
WebGLenum mAttachmentPoint;
public:
WebGLFramebufferAttachment(WebGLenum aAttachmentPoint)
: mAttachmentPoint(aAttachmentPoint)
{}
PRBool IsNull() const {
return !mTexturePtr && !mRenderbufferPtr;
}
void SetTexture(WebGLTexture *tex) {
mTexturePtr = tex;
mRenderbufferPtr = nsnull;
}
void SetRenderbuffer(WebGLRenderbuffer *rb) {
mTexturePtr = nsnull;
mRenderbufferPtr = rb;
}
WebGLTexture *Texture() const {
return mTexturePtr.get();
}
WebGLRenderbuffer *Renderbuffer() const {
return mRenderbufferPtr.get();
}
PRBool IsIncompatibleWithAttachmentPoint() const
{
// textures can only be color textures in WebGL
if (mTexturePtr)
return mAttachmentPoint != LOCAL_GL_COLOR_ATTACHMENT0;
if (mRenderbufferPtr) {
WebGLenum format = mRenderbufferPtr->InternalFormat();
switch (mAttachmentPoint) {
case LOCAL_GL_COLOR_ATTACHMENT0:
return format != LOCAL_GL_RGB565 &&
format != LOCAL_GL_RGB5_A1 &&
format != LOCAL_GL_RGBA4;
case LOCAL_GL_DEPTH_ATTACHMENT:
return format != LOCAL_GL_DEPTH_COMPONENT16;
case LOCAL_GL_STENCIL_ATTACHMENT:
return format != LOCAL_GL_STENCIL_INDEX8;
case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
return format != LOCAL_GL_DEPTH_STENCIL;
}
}
return PR_FALSE; // no attachment at all, so no incompatibility
}
PRBool HasUninitializedRenderbuffer() const {
return mRenderbufferPtr && !mRenderbufferPtr->Initialized();
}
};
#define WEBGLFRAMEBUFFER_PRIVATE_IID \ #define WEBGLFRAMEBUFFER_PRIVATE_IID \
{0x0052a16f, 0x4bc9, 0x4a55, {0x9d, 0xa3, 0x54, 0x95, 0xaa, 0x4e, 0x80, 0xb9}} {0x0052a16f, 0x4bc9, 0x4a55, {0x9d, 0xa3, 0x54, 0x95, 0xaa, 0x4e, 0x80, 0xb9}}
class WebGLFramebuffer : class WebGLFramebuffer :
@ -1366,10 +1427,11 @@ public:
WebGLFramebuffer(WebGLContext *context, WebGLuint name) : WebGLFramebuffer(WebGLContext *context, WebGLuint name) :
WebGLContextBoundObject(context), WebGLContextBoundObject(context),
mName(name), mDeleted(PR_FALSE), mName(name), mDeleted(PR_FALSE),
mColorAttachment0HasAlpha(PR_FALSE), mColorAttachmentHasAlpha(PR_FALSE),
mHasDepthAttachment(PR_FALSE), mColorAttachment(LOCAL_GL_COLOR_ATTACHMENT0),
mHasStencilAttachment(PR_FALSE), mDepthAttachment(LOCAL_GL_DEPTH_ATTACHMENT),
mHasDepthStencilAttachment(PR_FALSE) mStencilAttachment(LOCAL_GL_STENCIL_ATTACHMENT),
mDepthStencilAttachment(LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
{ } { }
void Delete() { void Delete() {
@ -1381,7 +1443,7 @@ public:
PRBool Deleted() { return mDeleted; } PRBool Deleted() { return mDeleted; }
WebGLuint GLName() { return mName; } WebGLuint GLName() { return mName; }
PRBool ColorAttachment0HasAlpha() { return mColorAttachment0HasAlpha; } PRBool ColorAttachmentHasAlpha() { return mColorAttachmentHasAlpha; }
nsresult FramebufferRenderbuffer(WebGLenum target, nsresult FramebufferRenderbuffer(WebGLenum target,
WebGLenum attachment, WebGLenum attachment,
@ -1404,59 +1466,27 @@ public:
if (rbtarget != LOCAL_GL_RENDERBUFFER) if (rbtarget != LOCAL_GL_RENDERBUFFER)
return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: renderbuffer target:", rbtarget); return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: renderbuffer target:", rbtarget);
const char *badAttachmentFormatMsg =
"framebufferRenderbuffer: this renderbuffer does not have a suitable format for this attachment point";
switch (attachment) { switch (attachment) {
case LOCAL_GL_DEPTH_ATTACHMENT: case LOCAL_GL_DEPTH_ATTACHMENT:
if (!isNull) { mDepthAttachment.SetRenderbuffer(wrb);
if (wrb->mInternalFormat != LOCAL_GL_DEPTH_COMPONENT16)
return mContext->ErrorInvalidOperation(badAttachmentFormatMsg);
}
mDepthOrStencilRenderbufferAttachment = wrb;
mHasDepthAttachment = !isNull;
break; break;
case LOCAL_GL_STENCIL_ATTACHMENT: case LOCAL_GL_STENCIL_ATTACHMENT:
if (!isNull) { mStencilAttachment.SetRenderbuffer(wrb);
if (wrb->mInternalFormat != LOCAL_GL_STENCIL_INDEX8)
return mContext->ErrorInvalidOperation(badAttachmentFormatMsg);
}
mDepthOrStencilRenderbufferAttachment = wrb;
mHasStencilAttachment = !isNull;
break; break;
case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
if (!isNull) { mDepthStencilAttachment.SetRenderbuffer(wrb);
if (wrb->mInternalFormat != LOCAL_GL_DEPTH_STENCIL)
return mContext->ErrorInvalidOperation(badAttachmentFormatMsg);
}
mDepthOrStencilRenderbufferAttachment = wrb;
mHasDepthStencilAttachment = !isNull;
break; break;
default: default:
// finish checking that the 'attachment' parameter is among the allowed values // finish checking that the 'attachment' parameter is among the allowed values
if ((attachment < LOCAL_GL_COLOR_ATTACHMENT0 || if (attachment != LOCAL_GL_COLOR_ATTACHMENT0)
attachment >= LOCAL_GL_COLOR_ATTACHMENT0 + mContext->mMaxFramebufferColorAttachments))
{
return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: attachment", attachment); return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: attachment", attachment);
}
if (!isNull) { if (!isNull) {
if (wrb->mInternalFormat != LOCAL_GL_RGBA4 &&
wrb->mInternalFormat != LOCAL_GL_RGB565 &&
wrb->mInternalFormat != LOCAL_GL_RGB5_A1)
{
return mContext->ErrorInvalidOperation(badAttachmentFormatMsg);
}
// ReadPixels needs alpha and size information, but only // ReadPixels needs alpha and size information, but only
// for COLOR_ATTACHMENT0 // for COLOR_ATTACHMENT0
if (attachment == LOCAL_GL_COLOR_ATTACHMENT0) { setDimensions(wrb);
setDimensions(wrb); mColorAttachmentHasAlpha = InternalFormatHasAlpha(wrb->mInternalFormat);
mColorAttachment0HasAlpha = InternalFormatHasAlpha(wrb->mInternalFormat);
} else {
mColorAttachment0HasAlpha = PR_FALSE;
}
} }
mColorRenderbufferAttachment = wrb; mColorAttachment.SetRenderbuffer(wrb);
break; break;
} }
@ -1495,34 +1525,32 @@ public:
switch (attachment) { switch (attachment) {
case LOCAL_GL_DEPTH_ATTACHMENT: case LOCAL_GL_DEPTH_ATTACHMENT:
mDepthAttachment.SetTexture(wtex);
break;
case LOCAL_GL_STENCIL_ATTACHMENT: case LOCAL_GL_STENCIL_ATTACHMENT:
mStencilAttachment.SetTexture(wtex);
break;
case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
return mContext->ErrorInvalidOperation("framebufferTexture2D: depth and stencil attachments can " mDepthStencilAttachment.SetTexture(wtex);
"only be renderbuffers, not textures, as there is no suitable texture format.");
break; break;
default: default:
if ((attachment < LOCAL_GL_COLOR_ATTACHMENT0 || if (attachment != LOCAL_GL_COLOR_ATTACHMENT0)
attachment >= LOCAL_GL_COLOR_ATTACHMENT0 + mContext->mMaxFramebufferColorAttachments))
{
return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: attachment", attachment); return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: attachment", attachment);
}
// keep data for readPixels, function only uses COLOR_ATTACHMENT0 // keep data for readPixels, function only uses COLOR_ATTACHMENT0
if (attachment == LOCAL_GL_COLOR_ATTACHMENT0) { setDimensions(wtex);
setDimensions(wtex);
if (wtex) { if (wtex) {
const WebGLTexture::ImageInfo& ia = wtex->ImageInfoAt const WebGLTexture::ImageInfo& ia = wtex->ImageInfoAt
(level, textarget == LOCAL_GL_TEXTURE_2D (level, textarget == LOCAL_GL_TEXTURE_2D
? 0 ? 0
: textarget - LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X); : textarget - LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X);
mColorAttachment0HasAlpha = InternalFormatHasAlpha(ia.mFormat); mColorAttachmentHasAlpha = InternalFormatHasAlpha(ia.mFormat);
} else { } else {
mColorAttachment0HasAlpha = PR_FALSE; mColorAttachmentHasAlpha = PR_FALSE;
}
} }
// nothing else to do for color buffers. all textures have a color-renderable format. mColorAttachment.SetTexture(wtex);
break; break;
} }
@ -1532,17 +1560,17 @@ public:
return NS_OK; return NS_OK;
} }
// implement inline, as it's performance critical (called by draw-functions). PRBool CheckAndInitializeRenderbuffers()
// the generic case for which we're optimizing is the case where there's nothing to initialize.
inline PRBool CheckAndInitializeRenderbuffers()
{ {
if (HasConflictingAttachments()) { if (HasBadAttachments()) {
mContext->SynthesizeGLError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION); mContext->SynthesizeGLError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION);
return PR_FALSE; return PR_FALSE;
} }
if ((mColorRenderbufferAttachment && !mColorRenderbufferAttachment->Initialized()) || if (mColorAttachment.HasUninitializedRenderbuffer() ||
(mDepthOrStencilRenderbufferAttachment && !mDepthOrStencilRenderbufferAttachment->Initialized())) mDepthAttachment.HasUninitializedRenderbuffer() ||
mStencilAttachment.HasUninitializedRenderbuffer() ||
mDepthStencilAttachment.HasUninitializedRenderbuffer())
{ {
InitializeRenderbuffers(); InitializeRenderbuffers();
} }
@ -1550,23 +1578,37 @@ public:
return PR_TRUE; return PR_TRUE;
} }
NS_DECL_ISUPPORTS PRBool HasBadAttachments() const {
NS_DECL_NSIWEBGLFRAMEBUFFER if (mColorAttachment.IsIncompatibleWithAttachmentPoint() ||
mDepthAttachment.IsIncompatibleWithAttachmentPoint() ||
PRBool HasConflictingAttachments() const { mStencilAttachment.IsIncompatibleWithAttachmentPoint() ||
return int(mHasDepthAttachment) + mDepthStencilAttachment.IsIncompatibleWithAttachmentPoint())
int(mHasStencilAttachment) + {
int(mHasDepthStencilAttachment) > 1; // some attachment is incompatible with its attachment point
return PR_TRUE;
}
else if (int(mDepthAttachment.IsNull()) +
int(mStencilAttachment.IsNull()) +
int(mDepthStencilAttachment.IsNull()) <= 1)
{
// has at least two among Depth, Stencil, DepthStencil
return PR_TRUE;
}
else return PR_FALSE;
} }
static PRBool InternalFormatHasAlpha(WebGLenum aInternalFormat) { static PRBool InternalFormatHasAlpha(WebGLenum aInternalFormat) {
return return
aInternalFormat == LOCAL_GL_RGBA || aInternalFormat == LOCAL_GL_RGBA ||
aInternalFormat == LOCAL_GL_LUMINANCE_ALPHA ||
aInternalFormat == LOCAL_GL_ALPHA || aInternalFormat == LOCAL_GL_ALPHA ||
aInternalFormat == LOCAL_GL_RGBA4 || aInternalFormat == LOCAL_GL_RGBA4 ||
aInternalFormat == LOCAL_GL_RGB5_A1; aInternalFormat == LOCAL_GL_RGB5_A1;
} }
NS_DECL_ISUPPORTS
NS_DECL_NSIWEBGLFRAMEBUFFER
protected: protected:
// protected because WebGLContext should only call InitializeRenderbuffers // protected because WebGLContext should only call InitializeRenderbuffers
@ -1577,12 +1619,11 @@ protected:
if (mContext->gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) != LOCAL_GL_FRAMEBUFFER_COMPLETE) if (mContext->gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) != LOCAL_GL_FRAMEBUFFER_COMPLETE)
return; return;
PRBool initializeColorBuffer = mColorRenderbufferAttachment && PRBool initializeColorBuffer = mColorAttachment.HasUninitializedRenderbuffer();
!mColorRenderbufferAttachment->Initialized(); PRBool initializeDepthBuffer = mDepthAttachment.HasUninitializedRenderbuffer() ||
PRBool initializeDepthOrStencilBuffer = mDepthOrStencilRenderbufferAttachment && mDepthStencilAttachment.HasUninitializedRenderbuffer();
!mDepthOrStencilRenderbufferAttachment->Initialized(); PRBool initializeStencilBuffer = mStencilAttachment.HasUninitializedRenderbuffer() ||
PRBool initializeDepthBuffer = initializeDepthOrStencilBuffer && HasDepthBuffer(); mDepthStencilAttachment.HasUninitializedRenderbuffer();
PRBool initializeStencilBuffer = initializeDepthOrStencilBuffer && HasStencilBuffer();
realGLboolean savedColorMask[] = {0}, savedDepthMask = 0; realGLboolean savedColorMask[] = {0}, savedDepthMask = 0;
GLuint savedStencilMask = 0; GLuint savedStencilMask = 0;
@ -1634,19 +1675,26 @@ protected:
savedColorClearValue[1], savedColorClearValue[1],
savedColorClearValue[2], savedColorClearValue[2],
savedColorClearValue[3]); savedColorClearValue[3]);
mColorRenderbufferAttachment->SetInitialized(PR_TRUE); mColorAttachment.Renderbuffer()->SetInitialized(PR_TRUE);
} }
if (initializeDepthBuffer) { if (initializeDepthBuffer) {
mContext->gl->fDepthMask(savedDepthMask); mContext->gl->fDepthMask(savedDepthMask);
mContext->gl->fClearDepth(savedDepthClearValue); mContext->gl->fClearDepth(savedDepthClearValue);
mDepthOrStencilRenderbufferAttachment->SetInitialized(PR_TRUE); if (mDepthAttachment.Renderbuffer())
mDepthAttachment.Renderbuffer()->SetInitialized(PR_TRUE);
} }
if (initializeStencilBuffer) { if (initializeStencilBuffer) {
mContext->gl->fStencilMask(savedStencilMask); mContext->gl->fStencilMask(savedStencilMask);
mContext->gl->fClearStencil(savedStencilClearValue); mContext->gl->fClearStencil(savedStencilClearValue);
mDepthOrStencilRenderbufferAttachment->SetInitialized(PR_TRUE); if (mStencilAttachment.Renderbuffer())
mStencilAttachment.Renderbuffer()->SetInitialized(PR_TRUE);
}
if (initializeDepthBuffer && initializeStencilBuffer) {
if (mDepthStencilAttachment.Renderbuffer())
mDepthStencilAttachment.Renderbuffer()->SetInitialized(PR_TRUE);
} }
mContext->gl->PopViewportRect(); mContext->gl->PopViewportRect();
@ -1662,28 +1710,17 @@ protected:
mContext->gl->fDisable(LOCAL_GL_SCISSOR_TEST); mContext->gl->fDisable(LOCAL_GL_SCISSOR_TEST);
} }
PRBool HasDepthBuffer() const {
return mHasDepthAttachment || mHasDepthStencilAttachment;
}
PRBool HasStencilBuffer() const {
return mHasStencilAttachment || mHasDepthStencilAttachment;
}
WebGLuint mName; WebGLuint mName;
PRPackedBool mDeleted; PRPackedBool mDeleted;
PRPackedBool mColorAttachment0HasAlpha;
PRBool mColorAttachmentHasAlpha;
// we only store pointers to attached renderbuffers, not to attached textures, because // we only store pointers to attached renderbuffers, not to attached textures, because
// we will only need to initialize renderbuffers. Textures are already initialized. // we will only need to initialize renderbuffers. Textures are already initialized.
nsRefPtr<WebGLRenderbuffer> mColorRenderbufferAttachment; WebGLFramebufferAttachment mColorAttachment,
nsRefPtr<WebGLRenderbuffer> mDepthOrStencilRenderbufferAttachment; mDepthAttachment,
mStencilAttachment,
// these boolean values keep track of all attachments: renderbuffers and textures. mDepthStencilAttachment;
// thus they are not at all redundant with the above member pointers.
PRBool mHasDepthAttachment;
PRBool mHasStencilAttachment;
PRBool mHasDepthStencilAttachment;
}; };
NS_DEFINE_STATIC_IID_ACCESSOR(WebGLFramebuffer, WEBGLFRAMEBUFFER_PRIVATE_IID) NS_DEFINE_STATIC_IID_ACCESSOR(WebGLFramebuffer, WEBGLFRAMEBUFFER_PRIVATE_IID)

View File

@ -523,7 +523,7 @@ WebGLContext::CheckFramebufferStatus(WebGLenum target, WebGLenum *retval)
if (target != LOCAL_GL_FRAMEBUFFER) if (target != LOCAL_GL_FRAMEBUFFER)
return ErrorInvalidEnum("checkFramebufferStatus: target must be FRAMEBUFFER"); return ErrorInvalidEnum("checkFramebufferStatus: target must be FRAMEBUFFER");
if (mBoundFramebuffer && mBoundFramebuffer->HasConflictingAttachments()) if (mBoundFramebuffer && mBoundFramebuffer->HasBadAttachments())
*retval = LOCAL_GL_FRAMEBUFFER_UNSUPPORTED; *retval = LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
else else
*retval = gl->fCheckFramebufferStatus(target); *retval = gl->fCheckFramebufferStatus(target);
@ -1794,7 +1794,6 @@ WebGLContext::GetRenderbufferParameter(WebGLenum target, WebGLenum pname, nsIVar
switch (pname) { switch (pname) {
case LOCAL_GL_RENDERBUFFER_WIDTH: case LOCAL_GL_RENDERBUFFER_WIDTH:
case LOCAL_GL_RENDERBUFFER_HEIGHT: case LOCAL_GL_RENDERBUFFER_HEIGHT:
case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT:
case LOCAL_GL_RENDERBUFFER_RED_SIZE: case LOCAL_GL_RENDERBUFFER_RED_SIZE:
case LOCAL_GL_RENDERBUFFER_GREEN_SIZE: case LOCAL_GL_RENDERBUFFER_GREEN_SIZE:
case LOCAL_GL_RENDERBUFFER_BLUE_SIZE: case LOCAL_GL_RENDERBUFFER_BLUE_SIZE:
@ -1807,7 +1806,17 @@ WebGLContext::GetRenderbufferParameter(WebGLenum target, WebGLenum pname, nsIVar
wrval->SetAsInt32(i); wrval->SetAsInt32(i);
} }
break; break;
case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT:
{
GLint i = 0;
gl->fGetRenderbufferParameteriv(target, pname, &i);
if (i == LOCAL_GL_DEPTH24_STENCIL8)
{
i = LOCAL_GL_DEPTH_STENCIL;
}
wrval->SetAsInt32(i);
}
break;
default: default:
return ErrorInvalidEnumInfo("GetRenderbufferParameter: parameter", pname); return ErrorInvalidEnumInfo("GetRenderbufferParameter: parameter", pname);
} }
@ -2590,7 +2599,7 @@ WebGLContext::ReadPixels_base(WebGLint x, WebGLint y, WebGLsizei width, WebGLsiz
{ {
PRBool needAlphaFixup; PRBool needAlphaFixup;
if (mBoundFramebuffer) { if (mBoundFramebuffer) {
needAlphaFixup = !mBoundFramebuffer->ColorAttachment0HasAlpha(); needAlphaFixup = !mBoundFramebuffer->ColorAttachmentHasAlpha();
} else { } else {
needAlphaFixup = gl->ActualFormat().alpha == 0; needAlphaFixup = gl->ActualFormat().alpha == 0;
} }
@ -2681,8 +2690,8 @@ WebGLContext::RenderbufferStorage(WebGLenum target, WebGLenum internalformat, We
if (!gl->IsGLES2()) internalformatForGL = LOCAL_GL_RGB8; if (!gl->IsGLES2()) internalformatForGL = LOCAL_GL_RGB8;
break; break;
case LOCAL_GL_DEPTH_COMPONENT16: case LOCAL_GL_DEPTH_COMPONENT16:
break;
case LOCAL_GL_STENCIL_INDEX8: case LOCAL_GL_STENCIL_INDEX8:
// nothing to do for these ones
break; break;
case LOCAL_GL_DEPTH_STENCIL: case LOCAL_GL_DEPTH_STENCIL:
// this one is available in newer OpenGL (at least since 3.1); will probably become available // this one is available in newer OpenGL (at least since 3.1); will probably become available