--- a/dom/canvas/WebGL2Context.cpp
+++ b/dom/canvas/WebGL2Context.cpp
@@ -73,17 +73,18 @@ WebGLContext::InitWebGL2()
WebGLExtensionID::OES_texture_half_float,
WebGLExtensionID::OES_texture_half_float_linear,
WebGLExtensionID::OES_vertex_array_object,
WebGLExtensionID::WEBGL_depth_texture,
WebGLExtensionID::WEBGL_draw_buffers
};
const GLFeature sFeatureRequiredArr[] = {
GLFeature::instanced_non_arrays,
- GLFeature::transform_feedback2
+ GLFeature::transform_feedback2,
+ GLFeature::invalidate_framebuffer
};
// check WebGL extensions that are supposed to be natively supported
for (size_t i = 0; i < size_t(MOZ_ARRAY_LENGTH(sExtensionNativelySupportedArr)); i++)
{
WebGLExtensionID extension = sExtensionNativelySupportedArr[i];
if (!IsExtensionSupported(extension)) {
--- a/dom/canvas/WebGL2ContextFramebuffers.cpp
+++ b/dom/canvas/WebGL2ContextFramebuffers.cpp
@@ -27,27 +27,83 @@ WebGL2Context::FramebufferTextureLayer(G
}
void
WebGL2Context::GetInternalformatParameter(JSContext*, GLenum target, GLenum internalformat, GLenum pname, JS::MutableHandleValue retval)
{
MOZ_CRASH("Not Implemented.");
}
+// Map attachments intended for the default buffer, to attachments for a non-
+// default buffer.
+static void
+TranslateDefaultAttachments(const dom::Sequence<GLenum>& in, dom::Sequence<GLenum>* out)
+{
+ for (size_t i = 0; i < in.Length(); i++) {
+ switch (in[i]) {
+ case LOCAL_GL_COLOR:
+ out->AppendElement(LOCAL_GL_COLOR_ATTACHMENT0);
+ break;
+ case LOCAL_GL_DEPTH:
+ out->AppendElement(LOCAL_GL_DEPTH_ATTACHMENT);
+ break;
+ case LOCAL_GL_STENCIL:
+ out->AppendElement(LOCAL_GL_STENCIL_ATTACHMENT);
+ break;
+ }
+ }
+}
+
void
WebGL2Context::InvalidateFramebuffer(GLenum target, const dom::Sequence<GLenum>& attachments)
{
- MOZ_CRASH("Not Implemented.");
+ if (IsContextLost())
+ return;
+ MakeContextCurrent();
+
+ if (target != LOCAL_GL_FRAMEBUFFER)
+ return ErrorInvalidEnumInfo("invalidateFramebuffer: target", target);
+ for (size_t i = 0; i < attachments.Length(); i++) {
+ if (!ValidateFramebufferAttachment(attachments[i], "invalidateFramebuffer"))
+ return;
+ }
+
+ if (!mBoundFramebuffer && !gl->IsDrawingToDefaultFramebuffer()) {
+ dom::Sequence<GLenum> tmpAttachments;
+ TranslateDefaultAttachments(attachments, &tmpAttachments);
+ gl->fInvalidateFramebuffer(target, tmpAttachments.Length(), tmpAttachments.Elements());
+ } else {
+ gl->fInvalidateFramebuffer(target, attachments.Length(), attachments.Elements());
+ }
}
void
-WebGL2Context::InvalidateSubFramebuffer (GLenum target, const dom::Sequence<GLenum>& attachments,
- GLint x, GLint y, GLsizei width, GLsizei height)
+WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::Sequence<GLenum>& attachments,
+ GLint x, GLint y, GLsizei width, GLsizei height)
{
- MOZ_CRASH("Not Implemented.");
+ if (IsContextLost())
+ return;
+ MakeContextCurrent();
+
+ if (target != LOCAL_GL_FRAMEBUFFER)
+ return ErrorInvalidEnumInfo("invalidateFramebuffer: target", target);
+ for (size_t i = 0; i < attachments.Length(); i++) {
+ if (!ValidateFramebufferAttachment(attachments[i], "invalidateSubFramebuffer"))
+ return;
+ }
+
+ if (!mBoundFramebuffer && !gl->IsDrawingToDefaultFramebuffer()) {
+ dom::Sequence<GLenum> tmpAttachments;
+ TranslateDefaultAttachments(attachments, &tmpAttachments);
+ gl->fInvalidateSubFramebuffer(target, tmpAttachments.Length(), tmpAttachments.Elements(),
+ x, y, width, height);
+ } else {
+ gl->fInvalidateSubFramebuffer(target, attachments.Length(), attachments.Elements(),
+ x, y, width, height);
+ }
}
void
WebGL2Context::ReadBuffer(GLenum mode)
{
MOZ_CRASH("Not Implemented.");
}
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -334,16 +334,28 @@ bool WebGLContext::ValidateGLSLString(co
/**
* Return true if the framebuffer attachment is valid. Attachment must
* be one of depth/stencil/depth_stencil/color attachment.
*/
bool
WebGLContext::ValidateFramebufferAttachment(GLenum attachment, const char* funcName)
{
+ if (!mBoundFramebuffer) {
+ switch (attachment) {
+ case LOCAL_GL_COLOR:
+ case LOCAL_GL_DEPTH:
+ case LOCAL_GL_STENCIL:
+ return true;
+ default:
+ ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x.", funcName, attachment);
+ return false;
+ }
+ }
+
if (attachment == LOCAL_GL_DEPTH_ATTACHMENT ||
attachment == LOCAL_GL_STENCIL_ATTACHMENT ||
attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
{
return true;
}
GLenum colorAttachCount = 1;
--- a/dom/canvas/test/webgl-mochitest.ini
+++ b/dom/canvas/test/webgl-mochitest.ini
@@ -19,8 +19,10 @@ skip-if = toolkit == 'android' #bug 8654
[webgl-mochitest/test_webgl_conformance.html]
skip-if = buildapp == 'mulet' || toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
[webgl-mochitest/test_webgl_request_context.html]
skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
[webgl-mochitest/test_webgl_request_mismatch.html]
skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
[webgl-mochitest/test_webgl2_not_exposed.html]
skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
+[webgl-mochitest/test_webgl2_invalidate_framebuffer.html]
+skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/test_webgl2_invalidate_framebuffer.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+
+<title>WebGL2 test: Framebuffers</title>
+
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<script src="driver-info.js"></script>
+<script src="webgl-util.js"></script>
+<body>
+<canvas id="c" width="64" height="64"></canvas>
+<script>
+
+WebGLUtil.withWebGL2('c', function (gl) {
+ gl.invalidateFramebuffer(gl.FRAMEBUFFER, [gl.COLOR]);
+ ok(gl.getError() == 0, 'invalidateFramebuffer');
+ gl.invalidateSubFramebuffer(gl.FRAMEBUFFER, [gl.COLOR], 0, 0, 64, 64);
+ ok(gl.getError() == 0, 'invalidateSubFramebuffer');
+ gl.invalidateFramebuffer(gl.FRAMEBUFFER, [gl.GL_COLOR_ATTACHMENT0]);
+ ok(gl.getError() == gl.INVALID_ENUM, 'invalidateFrameBuffer should fail with GL_COLOR_ATTACHMENT on the default framebuffer');
+}, function () {
+ SimpleTest.finish();
+});
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
--- a/dom/canvas/test/webgl-mochitest/webgl-util.js
+++ b/dom/canvas/test/webgl-mochitest/webgl-util.js
@@ -53,16 +53,54 @@ WebGLUtil = (function() {
if (!gl) {
error('WebGL context could not be retrieved from \'' + canvasId + '\'.');
return null;
}
return gl;
}
+ function withWebGL2(canvasId, callback, onFinished) {
+ var prefArrArr = [
+ ['webgl.force-enabled', true],
+ ['webgl.disable-angle', true],
+ ['webgl.enable-prototype-webgl2', true],
+ ];
+ var prefEnv = {'set': prefArrArr};
+ SpecialPowers.pushPrefEnv(prefEnv, function() {
+ var canvas = document.getElementById(canvasId);
+
+ var gl = null;
+ try {
+ gl = canvas.getContext('webgl2');
+ } catch(e) {}
+
+ if (!gl) {
+ try {
+ gl = canvas.getContext('experimental-webgl2');
+ } catch(e) {}
+ }
+
+ if (!gl) {
+ todo(false, 'WebGL2 is not supported');
+ onFinished();
+ return;
+ }
+
+ function errorFunc(str) {
+ ok(false, 'Error: ' + str);
+ }
+ setErrorFunc(errorFunc);
+ setWarningFunc(errorFunc);
+
+ callback(gl);
+ onFinished();
+ });
+ }
+
function getContentFromElem(elem) {
var str = "";
var k = elem.firstChild;
while (k) {
if (k.nodeType == 3)
str += k.textContent;
k = k.nextSibling;
@@ -120,12 +158,13 @@ WebGLUtil = (function() {
return prog;
}
return {
setErrorFunc: setErrorFunc,
setWarningFunc: setWarningFunc,
getWebGL: getWebGL,
+ withWebGL2: withWebGL2,
createShaderById: createShaderById,
createProgramByIds: createProgramByIds,
};
})();
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -79,16 +79,17 @@ static const char *sExtensionNames[] = {
"GL_ARB_copy_buffer",
"GL_ARB_depth_texture",
"GL_ARB_draw_buffers",
"GL_ARB_draw_instanced",
"GL_ARB_framebuffer_object",
"GL_ARB_framebuffer_sRGB",
"GL_ARB_half_float_pixel",
"GL_ARB_instanced_arrays",
+ "GL_ARB_invalidate_subdata",
"GL_ARB_map_buffer_range",
"GL_ARB_occlusion_query2",
"GL_ARB_pixel_buffer_object",
"GL_ARB_robustness",
"GL_ARB_sampler_objects",
"GL_ARB_sync",
"GL_ARB_texture_compression",
"GL_ARB_texture_float",
@@ -1351,16 +1352,31 @@ GLContext::InitWithPrefix(const char *pr
if (!LoadSymbols(&umnSymbols[0], trygl, prefix)) {
NS_ERROR("GL supports uniform matrix with non-square dim without supplying its functions.");
MarkUnsupported(GLFeature::uniform_matrix_nonsquare);
ClearSymbols(umnSymbols);
}
}
+ if (IsSupported(GLFeature::invalidate_framebuffer)) {
+ SymLoadStruct invSymbols[] = {
+ { (PRFuncPtr *) &mSymbols.fInvalidateFramebuffer, { "InvalidateFramebuffer", nullptr } },
+ { (PRFuncPtr *) &mSymbols.fInvalidateSubFramebuffer, { "InvalidateSubFramebuffer", nullptr } },
+ END_SYMBOLS
+ };
+
+ if (!LoadSymbols(&invSymbols[0], trygl, prefix)) {
+ NS_ERROR("GL supports framebuffer invalidation without supplying its functions.");
+
+ MarkUnsupported(GLFeature::invalidate_framebuffer);
+ ClearSymbols(invSymbols);
+ }
+ }
+
if (IsExtensionSupported(KHR_debug)) {
SymLoadStruct extSymbols[] = {
{ (PRFuncPtr*) &mSymbols.fDebugMessageControl, { "DebugMessageControl", "DebugMessageControlKHR", nullptr } },
{ (PRFuncPtr*) &mSymbols.fDebugMessageInsert, { "DebugMessageInsert", "DebugMessageInsertKHR", nullptr } },
{ (PRFuncPtr*) &mSymbols.fDebugMessageCallback, { "DebugMessageCallback", "DebugMessageCallbackKHR", nullptr } },
{ (PRFuncPtr*) &mSymbols.fGetDebugMessageLog, { "GetDebugMessageLog", "GetDebugMessageLogKHR", nullptr } },
{ (PRFuncPtr*) &mSymbols.fGetPointerv, { "GetPointerv", "GetPointervKHR", nullptr } },
{ (PRFuncPtr*) &mSymbols.fPushDebugGroup, { "PushDebugGroup", "PushDebugGroupKHR", nullptr } },
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -95,16 +95,17 @@ MOZ_BEGIN_ENUM_CLASS(GLFeature)
framebuffer_multisample,
framebuffer_object,
get_integer_indexed,
get_integer64_indexed,
get_query_object_iv,
gpu_shader4,
instanced_arrays,
instanced_non_arrays,
+ invalidate_framebuffer,
map_buffer_range,
occlusion_query,
occlusion_query_boolean,
occlusion_query2,
packed_depth_stencil,
query_objects,
renderbuffer_color_float,
renderbuffer_color_half_float,
@@ -362,16 +363,17 @@ public:
ARB_copy_buffer,
ARB_depth_texture,
ARB_draw_buffers,
ARB_draw_instanced,
ARB_framebuffer_object,
ARB_framebuffer_sRGB,
ARB_half_float_pixel,
ARB_instanced_arrays,
+ ARB_invalidate_subdata,
ARB_map_buffer_range,
ARB_occlusion_query2,
ARB_pixel_buffer_object,
ARB_robustness,
ARB_sampler_objects,
ARB_sync,
ARB_texture_compression,
ARB_texture_float,
@@ -885,16 +887,30 @@ public:
default:
// Nothing we care about, likely an error.
break;
}
raw_fBindFramebuffer(target, framebuffer);
}
+ void fInvalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fInvalidateFramebuffer);
+ mSymbols.fInvalidateFramebuffer(target, numAttachments, attachments);
+ AFTER_GL_CALL;
+ }
+
+ void fInvalidateSubFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments, GLint x, GLint y, GLsizei width, GLsizei height) {
+ BEFORE_GL_CALL;
+ ASSERT_SYMBOL_PRESENT(fInvalidateSubFramebuffer);
+ mSymbols.fInvalidateSubFramebuffer(target, numAttachments, attachments, x, y, width, height);
+ AFTER_GL_CALL;
+ }
+
void fBindTexture(GLenum target, GLuint texture) {
BEFORE_GL_CALL;
mSymbols.fBindTexture(target, texture);
AFTER_GL_CALL;
}
void fBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) {
BEFORE_GL_CALL;
@@ -3532,16 +3548,20 @@ public:
/* Clear to transparent black, with 0 depth and stencil,
* while preserving current ClearColor etc. values.
* Useful for resizing offscreen buffers.
*/
void ClearSafely();
bool WorkAroundDriverBugs() const { return mWorkAroundDriverBugs; }
+ bool IsDrawingToDefaultFramebuffer() {
+ return Screen()->IsDrawFramebufferDefault();
+ }
+
protected:
nsRefPtr<TextureGarbageBin> mTexGarbageBin;
public:
TextureGarbageBin* TexGarbageBin() {
MOZ_ASSERT(mTexGarbageBin);
return mTexGarbageBin;
}
--- a/gfx/gl/GLContextFeatures.cpp
+++ b/gfx/gl/GLContextFeatures.cpp
@@ -284,16 +284,25 @@ static const FeatureInfo sFeatureInfoArr
}
/* This is an expanded version of `instanced_arrays` that allows for all
* enabled active attrib arrays to have non-zero divisors.
* ANGLE_instanced_arrays and NV_instanced_arrays forbid this, but GLES3
* has no such restriction.
*/
},
{
+ "invalidate_framebuffer",
+ 430, // OpenGL version
+ 300, // OpenGL ES version
+ GLContext::ARB_invalidate_subdata,
+ {
+ GLContext::Extensions_End
+ }
+ },
+ {
"map_buffer_range",
300, // OpenGL version
300, // OpenGL ES version
GLContext::ARB_map_buffer_range,
{
GLContext::Extensions_End
}
},
--- a/gfx/gl/GLContextSymbols.h
+++ b/gfx/gl/GLContextSymbols.h
@@ -329,16 +329,21 @@ struct GLContextSymbols
PFNGLISFRAMEBUFFER fIsFramebuffer;
typedef realGLboolean (GLAPIENTRY * PFNGLISRENDERBUFFER) (GLuint renderbuffer);
PFNGLISRENDERBUFFER fIsRenderbuffer;
typedef realGLboolean (GLAPIENTRY * PFNGLISVERTEXARRAY) (GLuint array);
PFNGLISVERTEXARRAY fIsVertexArray;
typedef void (GLAPIENTRY * PFNGLRENDERBUFFERSTORAGE) (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height);
PFNGLRENDERBUFFERSTORAGE fRenderbufferStorage;
+ typedef void (GLAPIENTRY * PFNINVALIDATEFRAMEBUFFER) (GLenum target, GLsizei numAttachments, const GLenum* attachments);
+ PFNINVALIDATEFRAMEBUFFER fInvalidateFramebuffer;
+ typedef void (GLAPIENTRY * PFNINVALIDATESUBFRAMEBUFFER) (GLenum target, GLsizei numAttachments, const GLenum* attachments, GLint x, GLint y, GLsizei width, GLsizei height);
+ PFNINVALIDATESUBFRAMEBUFFER fInvalidateSubFramebuffer;
+
// These functions are only used by Skia/GL in desktop mode.
// Other parts of Gecko should avoid using these
typedef void (GLAPIENTRY * PFNGLCLIENTACTIVETEXTURE) (GLenum texture);
PFNGLCLIENTACTIVETEXTURE fClientActiveTexture;
typedef void (GLAPIENTRY * PFNDISABLECLIENTSTATE) (GLenum capability);
PFNDISABLECLIENTSTATE fDisableClientState;
typedef void (GLAPIENTRY * PFNENABLECLIENTSTATE) (GLenum capability);
PFNENABLECLIENTSTATE fEnableClientState;
--- a/gfx/gl/GLScreenBuffer.cpp
+++ b/gfx/gl/GLScreenBuffer.cpp
@@ -524,16 +524,30 @@ GLScreenBuffer::Readback(SharedSurface*
}
if (needsSwap) {
src->UnlockProd();
SharedSurf()->LockProd();
}
}
+bool
+GLScreenBuffer::IsDrawFramebufferDefault() const
+{
+ if (!mDraw)
+ return IsReadFramebufferDefault();
+ return mDraw->mFB == 0;
+}
+
+bool
+GLScreenBuffer::IsReadFramebufferDefault() const
+{
+ return SharedSurf()->mAttachType == AttachmentType::Screen;
+}
+
////////////////////////////////////////////////////////////////////////
// DrawBuffer
bool
DrawBuffer::Create(GLContext* const gl,
const SurfaceCaps& caps,
const GLFormats& formats,
const gfx::IntSize& size,
--- a/gfx/gl/GLScreenBuffer.h
+++ b/gfx/gl/GLScreenBuffer.h
@@ -256,14 +256,17 @@ public:
GLuint GetDrawFB() const;
GLuint GetReadFB() const;
// Here `fb` is the actual framebuffer you want bound. Binding 0 will
// bind the (generally useless) default framebuffer.
void BindFB_Internal(GLuint fb);
void BindDrawFB_Internal(GLuint fb);
void BindReadFB_Internal(GLuint fb);
+
+ bool IsDrawFramebufferDefault() const;
+ bool IsReadFramebufferDefault() const;
};
} // namespace gl
} // namespace mozilla
#endif // SCREEN_BUFFER_H_