gecko-dev/dom/canvas/WebGLContextFramebufferOperations.cpp
Jamie Nicol d27a81657e Bug 1443149 - Ensure WebGLFBAttachPoint::HasImage() before dereferencing Format(). r=jrmuizel
Previously we were checking IsDefined() rather than HasImage(), but
were hitting crashes. This was because Format() can return null if the
attach point is defined but the attached texture level or renderbuffer
aren't themselves defined. HasImage() checks for this case.

MozReview-Commit-ID: 8KY3zPXXAFv

--HG--
extra : rebase_source : 50706ae0903e15e8b299e7417e7e5d829b11aa79
2018-03-14 16:42:37 +00:00

228 lines
5.8 KiB
C++

/* -*- Mode: C++; tab-width: 4; 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 "WebGLContext.h"
#include "WebGLTexture.h"
#include "WebGLRenderbuffer.h"
#include "WebGLFramebuffer.h"
#include "GLContext.h"
#include "GLScreenBuffer.h"
namespace mozilla {
void
WebGLContext::Clear(GLbitfield mask)
{
const char funcName[] = "clear";
if (IsContextLost())
return;
uint32_t m = mask & (LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT);
if (mask != m)
return ErrorInvalidValue("%s: invalid mask bits", funcName);
if (mask == 0) {
GenerateWarning("Calling gl.clear(0) has no effect.");
} else if (mRasterizerDiscardEnabled) {
GenerateWarning("Calling gl.clear() with RASTERIZER_DISCARD enabled has no effects.");
}
if (mask & LOCAL_GL_COLOR_BUFFER_BIT && mBoundDrawFramebuffer) {
if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
for (const auto& cur : mBoundDrawFramebuffer->ColorDrawBuffers()) {
if (!cur->HasImage())
continue;
switch (cur->Format()->format->componentType) {
case webgl::ComponentType::Float:
case webgl::ComponentType::NormInt:
case webgl::ComponentType::NormUInt:
break;
default:
ErrorInvalidOperation("%s: Color draw buffers must be floating-point"
" or fixed-point. (normalized (u)ints)",
funcName);
return;
}
}
}
}
if (!BindCurFBForDraw(funcName))
return;
auto driverMask = mask;
if (!mBoundDrawFramebuffer) {
if (mNeedsFakeNoDepth) {
driverMask &= ~LOCAL_GL_DEPTH_BUFFER_BIT;
}
if (mNeedsFakeNoStencil) {
driverMask &= ~LOCAL_GL_STENCIL_BUFFER_BIT;
}
}
const ScopedDrawCallWrapper wrapper(*this);
gl->fClear(driverMask);
}
static GLfloat
GLClampFloat(GLfloat val)
{
if (val < 0.0)
return 0.0;
if (val > 1.0)
return 1.0;
return val;
}
void
WebGLContext::ClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
{
if (IsContextLost())
return;
const bool supportsFloatColorBuffers = (IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_float) ||
IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float) ||
IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float));
if (!supportsFloatColorBuffers) {
r = GLClampFloat(r);
g = GLClampFloat(g);
b = GLClampFloat(b);
a = GLClampFloat(a);
}
gl->fClearColor(r, g, b, a);
mColorClearValue[0] = r;
mColorClearValue[1] = g;
mColorClearValue[2] = b;
mColorClearValue[3] = a;
}
void
WebGLContext::ClearDepth(GLclampf v)
{
if (IsContextLost())
return;
mDepthClearValue = GLClampFloat(v);
gl->fClearDepth(mDepthClearValue);
}
void
WebGLContext::ClearStencil(GLint v)
{
if (IsContextLost())
return;
mStencilClearValue = v;
gl->fClearStencil(v);
}
void
WebGLContext::ColorMask(WebGLboolean r, WebGLboolean g, WebGLboolean b, WebGLboolean a)
{
if (IsContextLost())
return;
mColorWriteMask = uint8_t(bool(r)) << 0 |
uint8_t(bool(g)) << 1 |
uint8_t(bool(b)) << 2 |
uint8_t(bool(a)) << 3;
}
void
WebGLContext::DepthMask(WebGLboolean b)
{
if (IsContextLost())
return;
mDepthWriteMask = b;
gl->fDepthMask(b);
}
void
WebGLContext::DrawBuffers(const dom::Sequence<GLenum>& buffers)
{
const char funcName[] = "drawBuffers";
if (IsContextLost())
return;
if (mBoundDrawFramebuffer) {
mBoundDrawFramebuffer->DrawBuffers(funcName, buffers);
return;
}
// 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];
// Don't actually set it.
}
void
WebGLContext::StencilMask(GLuint mask)
{
if (IsContextLost())
return;
mStencilWriteMaskFront = mask;
mStencilWriteMaskBack = mask;
gl->fStencilMask(mask);
}
void
WebGLContext::StencilMaskSeparate(GLenum face, GLuint mask)
{
if (IsContextLost())
return;
if (!ValidateFaceEnum(face, "stencilMaskSeparate: face"))
return;
switch (face) {
case LOCAL_GL_FRONT_AND_BACK:
mStencilWriteMaskFront = mask;
mStencilWriteMaskBack = mask;
break;
case LOCAL_GL_FRONT:
mStencilWriteMaskFront = mask;
break;
case LOCAL_GL_BACK:
mStencilWriteMaskBack = mask;
break;
}
gl->fStencilMaskSeparate(face, mask);
}
} // namespace mozilla