Bug 1193070 - Implement GetFramebufferAttachmentParameter. r=jgilbert
authorDan Glastonbury <dglastonbury@mozilla.com>
Thu, 06 Aug 2015 16:31:00 +1000
changeset 291938 81c18a4b672f8ac003f531294bf0f160c5420ce5
parent 291937 26e8885719b643380d55131391eea42f5d669736
child 291941 92eb00ee19771b8ea7027d10acec971430a69143
push idunknown
push userunknown
push dateunknown
reviewersjgilbert
bugs1193070
milestone44.0a1
Bug 1193070 - Implement GetFramebufferAttachmentParameter. r=jgilbert
dom/canvas/WebGL2Context.h
dom/canvas/WebGL2ContextFramebuffers.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLFormats.cpp
dom/canvas/WebGLFormats.h
dom/canvas/WebGLFramebuffer.cpp
dom/canvas/WebGLFramebuffer.h
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -66,16 +66,21 @@ public:
 
     // -------------------------------------------------------------------------
     // Framebuffer objects - WebGL2ContextFramebuffers.cpp
 
     void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
                          GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
                          GLbitfield mask, GLenum filter);
     void FramebufferTextureLayer(GLenum target, GLenum attachment, WebGLTexture* texture, GLint level, GLint layer);
+
+    virtual JS::Value GetFramebufferAttachmentParameter(JSContext* cx, GLenum target,
+                                                        GLenum attachment, GLenum pname,
+                                                        ErrorResult& rv) override;
+
     void InvalidateFramebuffer(GLenum target, const dom::Sequence<GLenum>& attachments,
                                ErrorResult& rv);
     void InvalidateSubFramebuffer (GLenum target, const dom::Sequence<GLenum>& attachments, GLint x, GLint y,
                                    GLsizei width, GLsizei height, ErrorResult& rv);
     void ReadBuffer(GLenum mode);
 
 
     // -------------------------------------------------------------------------
--- a/dom/canvas/WebGL2ContextFramebuffers.cpp
+++ b/dom/canvas/WebGL2ContextFramebuffers.cpp
@@ -3,20 +3,27 @@
  * 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 "WebGL2Context.h"
 
 #include "GLContext.h"
 #include "GLScreenBuffer.h"
 #include "WebGLContextUtils.h"
+#include "WebGLFormats.h"
 #include "WebGLFramebuffer.h"
 
 namespace mozilla {
 
+using gl::GLContext;
+using gl::GLFormats;
+using webgl::EffectiveFormat;
+using webgl::FormatInfo;
+using webgl::ComponentType;
+
 // Returns one of FLOAT, INT, UNSIGNED_INT.
 // Fixed-points (normalized ints) are considered FLOAT.
 static GLenum
 ValueTypeForFormat(GLenum internalFormat)
 {
     switch (internalFormat) {
     // Fixed-point
     case LOCAL_GL_R8:
@@ -413,16 +420,113 @@ WebGL2Context::FramebufferTextureLayer(G
     if (!fb) {
         return ErrorInvalidOperation("framebufferTextureLayer: cannot modify"
                                      " framebuffer 0.");
     }
 
     fb->FramebufferTextureLayer(attachment, texture, level, layer);
 }
 
+JS::Value
+WebGL2Context::GetFramebufferAttachmentParameter(JSContext* cx,
+                                                 GLenum target,
+                                                 GLenum attachment,
+                                                 GLenum pname,
+                                                 ErrorResult& rv)
+{
+    if (IsContextLost())
+        return JS::NullValue();
+
+    // OpenGL ES 3.0.4 (August 27, 2014) 6.1. QUERYING GL STATE 240
+    // "getFramebufferAttachmentParamter returns information about attachments of a bound
+    // framebuffer object. target must be DRAW_FRAMEBUFFER, READ_FRAMEBUFFER, or
+    // FRAMEBUFFER."
+
+    if (!ValidateFramebufferTarget(target, "getFramebufferAttachmentParameter"))
+        return JS::NullValue();
+
+    // FRAMEBUFFER is equivalent to DRAW_FRAMEBUFFER.
+    if (target == LOCAL_GL_FRAMEBUFFER)
+        target = LOCAL_GL_DRAW_FRAMEBUFFER;
+
+    WebGLFramebuffer* boundFB = nullptr;
+    switch (target) {
+    case LOCAL_GL_DRAW_FRAMEBUFFER: boundFB = mBoundDrawFramebuffer; break;
+    case LOCAL_GL_READ_FRAMEBUFFER: boundFB = mBoundReadFramebuffer; break;
+    }
+
+    if (boundFB) {
+        return boundFB->GetAttachmentParameter(cx, attachment, pname, rv);
+    }
+
+    // Handle default FB
+    const gl::GLFormats& formats = gl->GetGLFormats();
+    GLenum internalFormat = LOCAL_GL_NONE;
+
+    /* If the default framebuffer is bound to target, then attachment must be BACK,
+       identifying the color buffer; DEPTH, identifying the depth buffer; or STENCIL,
+       identifying the stencil buffer. */
+    switch (attachment) {
+    case LOCAL_GL_BACK:
+        internalFormat = formats.color_texInternalFormat;
+        break;
+
+    case LOCAL_GL_DEPTH:
+        internalFormat = formats.depth;
+        break;
+
+    case LOCAL_GL_STENCIL:
+        internalFormat = formats.stencil;
+        break;
+
+    default:
+        ErrorInvalidEnum("getFramebufferAttachmentParameter: Can only query "
+                         "attachment BACK, DEPTH, or STENCIL from default "
+                         "framebuffer");
+        return JS::NullValue();
+    }
+
+    const FormatInfo* info = webgl::GetInfoBySizedFormat(internalFormat);
+    MOZ_RELEASE_ASSERT(info);
+    EffectiveFormat effectiveFormat = info->effectiveFormat;
+
+    switch (pname) {
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
+        return JS::Int32Value(LOCAL_GL_FRAMEBUFFER_DEFAULT);
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
+        return JS::Int32Value(webgl::GetComponentSize(effectiveFormat, pname));
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
+        if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT &&
+            pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)
+        {
+            ErrorInvalidOperation("getFramebufferAttachmentParameter: Querying "
+                                  "FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE against "
+                                  "DEPTH_STENCIL_ATTACHMENT is an error.");
+            return JS::NullValue();
+        }
+
+        return JS::Int32Value(webgl::GetComponentType(effectiveFormat));
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
+        return JS::Int32Value(webgl::GetColorEncoding(effectiveFormat));
+    }
+
+    /* Any combinations of framebuffer type and pname not described above will generate an
+       INVALID_ENUM error. */
+    ErrorInvalidEnum("getFramebufferAttachmentParameter: Invalid combination of ");
+    return JS::NullValue();
+}
+
 // Map attachments intended for the default buffer, to attachments for a non-
 // default buffer.
 static bool
 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:
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -453,19 +453,19 @@ public:
 
     void GetBufferParameter(JSContext*, GLenum target, GLenum pname,
                             JS::MutableHandle<JS::Value> retval)
     {
         retval.set(GetBufferParameter(target, pname));
     }
 
     GLenum GetError();
-    JS::Value GetFramebufferAttachmentParameter(JSContext* cx, GLenum target,
-                                                GLenum attachment, GLenum pname,
-                                                ErrorResult& rv);
+    virtual JS::Value GetFramebufferAttachmentParameter(JSContext* cx, GLenum target,
+                                                        GLenum attachment, GLenum pname,
+                                                        ErrorResult& rv);
 
     void GetFramebufferAttachmentParameter(JSContext* cx, GLenum target,
                                            GLenum attachment, GLenum pname,
                                            JS::MutableHandle<JS::Value> retval,
                                            ErrorResult& rv)
     {
         retval.set(GetFramebufferAttachmentParameter(cx, target, attachment,
                                                      pname, rv));
--- a/dom/canvas/WebGLFormats.cpp
+++ b/dom/canvas/WebGLFormats.cpp
@@ -804,10 +804,324 @@ FormatUsageAuthority::AddUnpackOption(GL
 
     MOZ_RELEASE_ASSERT(usage->asTexture);
 
     auto res = usage->validUnpacks.insert(unpack);
     bool didInsert = res.second;
     MOZ_ALWAYS_TRUE(didInsert);
 }
 
+////////////////////////////////////////////////////////////////////////////////
+
+struct ComponentSizes
+{
+    GLubyte redSize;
+    GLubyte greenSize;
+    GLubyte blueSize;
+    GLubyte alphaSize;
+    GLubyte depthSize;
+    GLubyte stencilSize;
+};
+
+static ComponentSizes kComponentSizes[] = {
+    // GLES 3.0.4, p128-129, "Required Texture Formats"
+    // "Texture and renderbuffer color formats"
+    { 32, 32, 32, 32,  0,  0 }, // RGBA32I,
+    { 32, 32, 32, 32,  0,  0 }, // RGBA32UI,
+    { 16, 16, 16, 16,  0,  0 }, // RGBA16I,
+    { 16, 16, 16, 16,  0,  0 }, // RGBA16UI,
+    {  8,  8,  8,  8,  0,  0 }, // RGBA8,
+    {  8,  8,  8,  8,  0,  0 }, // RGBA8I,
+    {  8,  8,  8,  8,  0,  0 }, // RGBA8UI,
+    {  8,  8,  8,  8,  0,  0 }, // SRGB8_ALPHA8,
+    { 10, 10, 10,  2,  0,  0 }, // RGB10_A2,
+    { 10, 10, 10,  2,  0,  0 }, // RGB10_A2UI,
+    {  4,  4,  4,  4,  0,  0 }, // RGBA4,
+    {  5,  5,  5,  1,  0,  0 }, // RGB5_A1,
+
+    {  8,  8,  8,  0,  0,  0 }, // RGB8,
+    {  8,  8,  8,  0,  0,  0 }, // RGB565,
+
+    { 32, 32,  0,  0,  0,  0 }, // RG32I,
+    { 32, 32,  0,  0,  0,  0 }, // RG32UI,
+    { 16, 16,  0,  0,  0,  0 }, // RG16I,
+    { 16, 16,  0,  0,  0,  0 }, // RG16UI,
+    {  8,  8,  0,  0,  0,  0 }, // RG8,
+    {  8,  8,  0,  0,  0,  0 }, // RG8I,
+    {  8,  8,  0,  0,  0,  0 }, // RG8UI,
+
+    { 32,  0,  0,  0,  0,  0 }, // R32I,
+    { 32,  0,  0,  0,  0,  0 }, // R32UI,
+    { 16,  0,  0,  0,  0,  0 }, // R16I,
+    { 16,  0,  0,  0,  0,  0 }, // R16UI,
+    {  8,  0,  0,  0,  0,  0 }, // R8,
+    {  8,  0,  0,  0,  0,  0 }, // R8I,
+    {  8,  0,  0,  0,  0,  0 }, // R8UI,
+
+    // "Texture-only color formats"
+    { 32, 32, 32, 32,  0,  0 }, // RGBA32F,
+    { 16, 16, 16, 16,  0,  0 }, // RGBA16F,
+    {  8,  8,  8,  8,  0,  0 }, // RGBA8_SNORM,
+
+    { 32, 32, 32,  0,  0,  0 }, // RGB32F,
+    { 32, 32, 32,  0,  0,  0 }, // RGB32I,
+    { 32, 32, 32,  0,  0,  0 }, // RGB32UI,
+
+    { 16, 16, 16,  0,  0,  0 }, // RGB16F,
+    { 16, 16, 16,  0,  0,  0 }, // RGB16I,
+    { 16, 16, 16,  0,  0,  0 }, // RGB16UI,
+
+    {  8,  8,  8,  0,  0,  0 }, // RGB8_SNORM,
+    {  8,  8,  8,  0,  0,  0 }, // RGB8I,
+    {  8,  8,  8,  0,  0,  0 }, // RGB8UI,
+    {  8,  8,  8,  0,  0,  0 }, // SRGB8,
+
+    { 11, 11, 11,  0,  0,  0 }, // R11F_G11F_B10F,
+    {  9,  9,  9,  0,  0,  0 }, // RGB9_E5,
+
+    { 32, 32,  0,  0,  0,  0 }, // RG32F,
+    { 16, 16,  0,  0,  0,  0 }, // RG16F,
+    {  8,  8,  0,  0,  0,  0 }, // RG8_SNORM,
+
+    { 32,  0,  0,  0,  0,  0 }, // R32F,
+    { 16,  0,  0,  0,  0,  0 }, // R16F,
+    {  8,  0,  0,  0,  0,  0 }, // R8_SNORM,
+
+    // "Depth formats"
+    {  0,  0,  0,  0, 32,  0 }, // DEPTH_COMPONENT32F,
+    {  0,  0,  0,  0, 24,  0 }, // DEPTH_COMPONENT24,
+    {  0,  0,  0,  0, 16,  0 }, // DEPTH_COMPONENT16,
+
+    // "Combined depth+stencil formats"
+    {  0,  0,  0,  0, 32,  8 }, // DEPTH32F_STENCIL0,
+    {  0,  0,  0,  0, 24,  8 }, // DEPTH24_STENCIL8,
+
+    // GLES 3.0.4, p205-206, "Required Renderbuffer Formats"
+    {  0,  0,  0,  0,  0,  8 }, // STENCIL_INDEX8,
+
+    // GLES 3.0.4, p128, table 3.12.
+    {  8,  8,  8,  8,  0,  0 }, // Luminance8Alpha8,
+    {  8,  8,  8,  0,  0,  0 }, // Luminance8,
+    {  0,  0,  0,  8,  0,  0 }, // Alpha8,
+
+    // GLES 3.0.4, p147, table 3.19
+    // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats"
+    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_R11_EAC,
+    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_SIGNED_R11_EAC,
+    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_RG11_EAC,
+    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_SIGNED_RG11_EAC,
+    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_RGB8_ETC2,
+    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_SRGB8_ETC2,
+    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,
+    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,
+    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_RGBA8_ETC2_EAC,
+    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_SRGB8_ALPHA8_ETC2_EAC,
+
+    // AMD_compressed_ATC_texture
+    {  8,  8,  8,  0,  0,  0 }, // ATC_RGB_AMD,
+    {  8,  8,  8,  8,  0,  0 }, // ATC_RGBA_EXPLICIT_ALPHA_AMD,
+    {  8,  8,  8,  8,  0,  0 }, // ATC_RGBA_INTERPOLATED_ALPHA_AMD,
+
+    // EXT_texture_compression_s3tc
+    {  8,  8,  8,  0,  0,  0 }, // COMPRESSED_RGB_S3TC_DXT1,
+    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_RGBA_S3TC_DXT1,
+    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_RGBA_S3TC_DXT3,
+    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_RGBA_S3TC_DXT5,
+
+    // IMG_texture_compression_pvrtc
+    {  8,  8,  8,  0,  0,  0 }, // COMPRESSED_RGB_PVRTC_4BPPV1,
+    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_RGBA_PVRTC_4BPPV1,
+    {  8,  8,  8,  0,  0,  0 }, // COMPRESSED_RGB_PVRTC_2BPPV1,
+    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_RGBA_PVRTC_2BPPV1,
+
+    // OES_compressed_ETC1_RGB8_texture
+    {  8,  8,  8,  0,  0,  0 }, // ETC1_RGB8,
+
+    // OES_texture_float
+    { 32, 32, 32, 32,  0,  0 }, // Luminance32FAlpha32F,
+    { 32, 32, 32,  0,  0,  0 }, // Luminance32F,
+    {  0,  0,  0, 32,  0,  0 }, // Alpha32F,
+
+    // OES_texture_half_float
+    { 16, 16, 16, 16, 0, 0 }, // Luminance16FAlpha16F,
+    { 16, 16, 16,  0, 0, 0 }, // Luminance16F,
+    {  0,  0,  0, 16, 0, 0 }, // Alpha16F,
+
+    {  0, } // MAX
+};
+
+GLint
+GetComponentSize(EffectiveFormat format, GLenum component)
+{
+    ComponentSizes compSize = kComponentSizes[(int) format];
+    switch (component) {
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
+    case LOCAL_GL_RENDERBUFFER_RED_SIZE:
+    case LOCAL_GL_RED_BITS:
+        return compSize.redSize;
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
+    case LOCAL_GL_RENDERBUFFER_GREEN_SIZE:
+    case LOCAL_GL_GREEN_BITS:
+        return compSize.greenSize;
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
+    case LOCAL_GL_RENDERBUFFER_BLUE_SIZE:
+    case LOCAL_GL_BLUE_BITS:
+        return compSize.blueSize;
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
+    case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE:
+    case LOCAL_GL_ALPHA_BITS:
+        return compSize.alphaSize;
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
+    case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE:
+    case LOCAL_GL_DEPTH_BITS:
+        return compSize.depthSize;
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
+    case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE:
+    case LOCAL_GL_STENCIL_BITS:
+        return compSize.stencilSize;
+    }
+
+    return 0;
+}
+
+static GLenum kComponentTypes[] = {
+    // GLES 3.0.4, p128-129, "Required Texture Formats"
+    // "Texture and renderbuffer color formats"
+    LOCAL_GL_INT,                       // RGBA32I,
+    LOCAL_GL_UNSIGNED_INT,              // RGBA32UI,
+    LOCAL_GL_INT,                       // RGBA16I,
+    LOCAL_GL_UNSIGNED_INT,              // RGBA16UI,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // RGBA8,
+    LOCAL_GL_INT,                       // RGBA8I,
+    LOCAL_GL_UNSIGNED_INT,              // RGBA8UI,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // SRGB8_ALPHA8,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // RGB10_A2,
+    LOCAL_GL_UNSIGNED_INT,              // RGB10_A2UI,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // RGBA4,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // RGB5_A1,
+
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // RGB8,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // RGB565,
+
+    LOCAL_GL_INT,                       // RG32I,
+    LOCAL_GL_UNSIGNED_INT,              // RG32UI,
+    LOCAL_GL_INT,                       // RG16I,
+    LOCAL_GL_UNSIGNED_INT,              // RG16UI,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // RG8,
+    LOCAL_GL_INT,                       // RG8I,
+    LOCAL_GL_UNSIGNED_INT,              // RG8UI,
+
+    LOCAL_GL_INT,                       // R32I,
+    LOCAL_GL_UNSIGNED_INT,              // R32UI,
+    LOCAL_GL_INT,                       // R16I,
+    LOCAL_GL_UNSIGNED_INT,              // R16UI,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // R8,
+    LOCAL_GL_INT,                       // R8I,
+    LOCAL_GL_UNSIGNED_INT,              // R8UI,
+
+    // "Texture-only color formats"
+    LOCAL_GL_FLOAT,                     // RGBA32F,
+    LOCAL_GL_FLOAT,                     // RGBA16F,
+    LOCAL_GL_SIGNED_NORMALIZED,         // RGBA8_SNORM,
+
+    LOCAL_GL_FLOAT,                     // RGB32F,
+    LOCAL_GL_INT,                       // RGB32I,
+    LOCAL_GL_UNSIGNED_INT,              // RGB32UI,
+
+    LOCAL_GL_FLOAT,                     // RGB16F,
+    LOCAL_GL_INT,                       // RGB16I,
+    LOCAL_GL_UNSIGNED_INT,              // RGB16UI,
+
+    LOCAL_GL_SIGNED_NORMALIZED,         // RGB8_SNORM,
+    LOCAL_GL_INT,                       // RGB8I,
+    LOCAL_GL_UNSIGNED_INT,              // RGB8UI,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // SRGB8,
+
+    LOCAL_GL_FLOAT,                     // R11F_G11F_B10F,
+    LOCAL_GL_FLOAT,                     // RGB9_E5,
+
+    LOCAL_GL_FLOAT,                     // RG32F,
+    LOCAL_GL_FLOAT,                     // RG16F,
+    LOCAL_GL_SIGNED_NORMALIZED,         // RG8_SNORM,
+
+    LOCAL_GL_FLOAT,                     // R32F,
+    LOCAL_GL_FLOAT,                     // R16F,
+    LOCAL_GL_SIGNED_NORMALIZED,         // R8_SNORM,
+
+    // "Depth formats"
+    LOCAL_GL_FLOAT,                     // DEPTH_COMPONENT32F,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // DEPTH_COMPONENT24,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // DEPTH_COMPONENT16,
+
+    // "Combined depth+stencil formats"
+    LOCAL_GL_FLOAT,                     // DEPTH32F_STENCIL8,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // DEPTH24_STENCIL8,
+
+    // GLES 3.0.4, p205-206, "Required Renderbuffer Formats"
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // STENCIL_INDEX8,
+
+    // GLES 3.0.4, p128, table 3.12.
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // Luminance8Alpha8,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // Luminance8,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // Alpha8,
+
+    // GLES 3.0.4, p147, table 3.19
+    // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats"
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_R11_EAC,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_SIGNED_R11_EAC,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RG11_EAC,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_SIGNED_RG11_EAC,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGB8_ETC2,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_SRGB8_ETC2,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGBA8_ETC2_EAC,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_SRGB8_ALPHA8_ETC2_EAC,
+
+    // AMD_compressed_ATC_texture
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // ATC_RGB_AMD,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // ATC_RGBA_EXPLICIT_ALPHA_AMD,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // ATC_RGBA_INTERPOLATED_ALPHA_AMD,
+
+    // EXT_texture_compression_s3tc
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGB_S3TC_DXT1,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGBA_S3TC_DXT1,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGBA_S3TC_DXT3,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGBA_S3TC_DXT5,
+
+    // IMG_texture_compression_pvrtc
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGB_PVRTC_4BPPV1,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGBA_PVRTC_4BPPV1,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGB_PVRTC_2BPPV1,
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGBA_PVRTC_2BPPV1,
+
+    // OES_compressed_ETC1_RGB8_texture
+    LOCAL_GL_UNSIGNED_NORMALIZED,       // ETC1_RGB8,
+
+    // OES_texture_float
+    LOCAL_GL_FLOAT,                     // Luminance32FAlpha32F,
+    LOCAL_GL_FLOAT,                     // Luminance32F,
+    LOCAL_GL_FLOAT,                     // Alpha32F,
+
+    // OES_texture_half_float
+    LOCAL_GL_FLOAT,                     // Luminance16FAlpha16F,
+    LOCAL_GL_FLOAT,                     // Luminance16F,
+    LOCAL_GL_FLOAT,                     // Alpha16F,
+
+    LOCAL_GL_NONE // MAX
+};
+
+GLenum
+GetComponentType(EffectiveFormat format)
+{
+    return kComponentTypes[(int) format];
+}
+
+GLenum
+GetColorEncoding(EffectiveFormat format)
+{
+    const bool isSRGB = (GetFormatInfo(format)->colorComponentType ==
+                         ComponentType::NormUIntSRGB);
+    return (isSRGB) ? LOCAL_GL_SRGB : LOCAL_GL_LINEAR;
+}
+
 } // namespace webgl
 } // namespace mozilla
--- a/dom/canvas/WebGLFormats.h
+++ b/dom/canvas/WebGLFormats.h
@@ -247,19 +247,27 @@ private:
 public:
     void AddFormat(EffectiveFormat format, bool asRenderbuffer, bool isRenderable,
                    bool asTexture, bool isFilterable);
 
     void AddUnpackOption(GLenum unpackFormat, GLenum unpackType,
                          EffectiveFormat effectiveFormat);
 
     FormatUsageInfo* GetUsage(EffectiveFormat format);
-
     FormatUsageInfo* GetUsage(const FormatInfo* format)
     {
+        if (!format)
+            return nullptr;
+
         return GetUsage(format->effectiveFormat);
     }
 };
 
+////////////////////////////////////////////////////////////////////////////////
+
+GLint GetComponentSize(EffectiveFormat format, GLenum component);
+GLenum GetComponentType(EffectiveFormat format);
+GLenum GetColorEncoding(EffectiveFormat format);
+
 } // namespace webgl
 } // namespace mozilla
 
 #endif // WEBGL_FORMATS_H_
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -428,16 +428,76 @@ WebGLFBAttachPoint::FinalizeAttachment(g
     if (Renderbuffer()) {
         Renderbuffer()->FramebufferRenderbuffer(attachmentLoc);
         return;
     }
 
     MOZ_CRASH();
 }
 
+JS::Value
+WebGLFBAttachPoint::GetParameter(WebGLContext* context, GLenum pname)
+{
+    // TODO: WebGLTexture and WebGLRenderbuffer should store FormatInfo instead of doing
+    // this dance every time.
+    const GLenum internalFormat = EffectiveInternalFormat().get();
+    const webgl::FormatInfo* info = webgl::GetInfoBySizedFormat(internalFormat);
+    MOZ_ASSERT(info);
+
+    WebGLTexture* tex = Texture();
+
+    switch (pname) {
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
+        return JS::Int32Value(webgl::GetComponentSize(info->effectiveFormat, pname));
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
+        return JS::Int32Value(webgl::GetComponentType(info->effectiveFormat));
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
+        return JS::Int32Value(webgl::GetColorEncoding(info->effectiveFormat));
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:
+        if (tex) {
+            return JS::Int32Value(MipLevel());
+        }
+        break;
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE:
+        if (tex) {
+            int32_t face = 0;
+            if (tex->Target() == LOCAL_GL_TEXTURE_CUBE_MAP) {
+                face = ImageTarget().get();
+            }
+            return JS::Int32Value(face);
+        }
+        break;
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER:
+        if (tex) {
+            int32_t layer = 0;
+            if (tex->Target() == LOCAL_GL_TEXTURE_2D_ARRAY ||
+                tex->Target() == LOCAL_GL_TEXTURE_3D)
+            {
+                layer = Layer();
+            }
+            return JS::Int32Value(layer);
+        }
+        break;
+    }
+
+    context->ErrorInvalidEnum("getFramebufferParameter: Invalid combination of "
+                              "attachment and pname.");
+    return JS::NullValue();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // WebGLFramebuffer
 
 WebGLFramebuffer::WebGLFramebuffer(WebGLContext* webgl, GLuint fbo)
     : WebGLContextBoundObject(webgl)
     , mGLName(fbo)
     , mStatus(0)
     , mReadBufferMode(LOCAL_GL_COLOR_ATTACHMENT0)
@@ -982,16 +1042,120 @@ WebGLFramebuffer::ValidateForRead(const 
         mContext->ErrorInvalidOperation("readPixels: ");
         return false;
     }
 
     *out_format = attachPoint.EffectiveInternalFormat();
     return true;
 }
 
+static bool
+AttachmentsDontMatch(const WebGLFBAttachPoint& a, const WebGLFBAttachPoint& b)
+{
+    if (a.Texture()) {
+        return (a.Texture() != b.Texture());
+    }
+
+    if (a.Renderbuffer()) {
+        return (a.Renderbuffer() != b.Renderbuffer());
+    }
+
+    return false;
+}
+
+JS::Value
+WebGLFramebuffer::GetAttachmentParameter(JSContext* cx,
+                                         GLenum attachment,
+                                         GLenum pname,
+                                         ErrorResult& rv)
+{
+    // "If a framebuffer object is bound to target, then attachment must be one of the
+    // attachment points of the framebuffer listed in table 4.6."
+    switch (attachment) {
+    case LOCAL_GL_DEPTH_ATTACHMENT:
+    case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
+        break;
+
+    case LOCAL_GL_STENCIL_ATTACHMENT:
+        // "If attachment is DEPTH_STENCIL_ATTACHMENT, and different objects are bound to
+        //  the depth and stencil attachment points of target, the query will fail and
+        //  generate an INVALID_OPERATION error. If the same object is bound to both
+        //  attachment points, information about that object will be returned."
+
+        // Does this mean it has to be the same level or layer? Because the queries are
+        // independent of level or layer.
+        if (AttachmentsDontMatch(DepthAttachment(), StencilAttachment())) {
+            mContext->ErrorInvalidOperation("getFramebufferAttachmentParameter: "
+                                            "DEPTH_ATTACHMENT and STENCIL_ATTACHMENT "
+                                            "have different objects bound.");
+            return JS::NullValue();
+        }
+        break;
+
+    default:
+        if (attachment < LOCAL_GL_COLOR_ATTACHMENT0 ||
+            attachment > mContext->LastColorAttachment())
+        {
+            mContext->ErrorInvalidEnum("getFramebufferAttachmentParameter: Can only "
+                                       "query COLOR_ATTACHMENTi, DEPTH_ATTACHMENT, "
+                                       "DEPTH_STENCIL_ATTACHMENT, or STENCIL_ATTACHMENT "
+                                       "on framebuffer.");
+            return JS::NullValue();
+        }
+    }
+
+    if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT &&
+        pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)
+    {
+        mContext->ErrorInvalidOperation("getFramebufferAttachmentParameter: Querying "
+                                        "FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE against "
+                                        "DEPTH_STENCIL_ATTACHMENT is an error.");
+        return JS::NullValue();
+    }
+
+    GLenum objectType = LOCAL_GL_NONE;
+    auto& fba = GetAttachPoint(attachment);
+    if (fba.Texture()) {
+        objectType = LOCAL_GL_TEXTURE;
+    } else if (fba.Renderbuffer()) {
+        objectType = LOCAL_GL_RENDERBUFFER;
+    }
+
+    switch (pname) {
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
+        return JS::Int32Value(objectType);
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
+        if (objectType == LOCAL_GL_NONE) {
+            return JS::NullValue();
+        }
+
+        if (objectType == LOCAL_GL_RENDERBUFFER) {
+            const WebGLRenderbuffer* rb = fba.Renderbuffer();
+            return mContext->WebGLObjectAsJSValue(cx, rb, rv);
+        }
+
+        /* If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is TEXTURE, then */
+        if (objectType == LOCAL_GL_TEXTURE) {
+            const WebGLTexture* tex = fba.Texture();
+            return mContext->WebGLObjectAsJSValue(cx, tex, rv);
+        }
+        break;
+    }
+
+    if (objectType == LOCAL_GL_NONE) {
+        mContext->ErrorInvalidOperation("getFramebufferAttachmentParameter: No "
+                                        "attachment at %s",
+                                        mContext->EnumName(attachment));
+        return JS::NullValue();
+    }
+
+    return fba.GetParameter(mContext, pname);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Goop.
 
 JSObject*
 WebGLFramebuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLFramebufferBinding::Wrap(cx, this, givenProto);
 }
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -57,17 +57,17 @@ public:
     void Clear() {
         SetRenderbuffer(nullptr);
     }
 
     void SetTexImage(WebGLTexture* tex, TexImageTarget target, GLint level);
     void SetTexImageLayer(WebGLTexture* tex, TexImageTarget target, GLint level,
                           GLint layer);
     void SetRenderbuffer(WebGLRenderbuffer* rb);
-    
+
     const WebGLTexture* Texture() const {
         return mTexturePtr;
     }
     WebGLTexture* Texture() {
         return mTexturePtr;
     }
     const WebGLRenderbuffer* Renderbuffer() const {
         return mRenderbufferPtr;
@@ -90,16 +90,18 @@ public:
 
     const WebGLRectangleObject& RectangleObject() const;
 
     bool HasImage() const;
     bool IsComplete() const;
 
     void FinalizeAttachment(gl::GLContext* gl,
                             FBAttachment attachmentLoc) const;
+
+    JS::Value GetParameter(WebGLContext* context, GLenum pname);
 };
 
 class WebGLFramebuffer final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLFramebuffer>
     , public LinkedListElement<WebGLFramebuffer>
     , public WebGLContextBoundObject
     , public SupportsWeakPtr<WebGLFramebuffer>
@@ -220,13 +222,16 @@ public:
 
     void EnsureColorAttachPoints(size_t colorAttachmentId);
 
     void InvalidateFramebufferStatus() const {
         mStatus = 0;
     }
 
     bool ValidateForRead(const char* info, TexInternalFormat* const out_format);
+
+    JS::Value GetAttachmentParameter(JSContext* cx, GLenum attachment, GLenum pname,
+                                     ErrorResult& rv);
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_FRAMEBUFFER_H_