Bug 1003607 - Sort out usage of GL format and type into WebGL and driver versions for texture checks.; r=jgilbert
authorDan Glastonbury <dglastonbury@mozilla.com>
Fri, 02 May 2014 13:15:58 +1000
changeset 181708 4774bb540acc1c680c501072306879538838b4d1
parent 181707 5fec1b48b68d08f8eb1cf379ad62863a58bac82f
child 181709 90b832821bf439b0fed519eeee0c293e0fc99b3c
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersjgilbert
bugs1003607
milestone32.0a1
Bug 1003607 - Sort out usage of GL format and type into WebGL and driver versions for texture checks.; r=jgilbert
content/canvas/src/WebGLContextDraw.cpp
content/canvas/src/WebGLContextGL.cpp
content/canvas/src/WebGLContextUtils.cpp
content/canvas/src/WebGLContextUtils.h
content/canvas/src/WebGLContextValidate.cpp
content/canvas/src/WebGLFramebuffer.cpp
content/canvas/src/WebGLTexture.cpp
content/canvas/src/WebGLTexture.h
content/canvas/test/webgl-mochitest/mochitest.ini
content/canvas/test/webgl-mochitest/test_texsubimage_float.html
--- a/content/canvas/src/WebGLContextDraw.cpp
+++ b/content/canvas/src/WebGLContextDraw.cpp
@@ -3,16 +3,17 @@
  * 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 "GLContext.h"
 #include "mozilla/CheckedInt.h"
 #include "WebGLBuffer.h"
+#include "WebGLContextUtils.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLProgram.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLShader.h"
 #include "WebGLTexture.h"
 #include "WebGLUniformInfo.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
@@ -625,17 +626,17 @@ WebGLContext::BindFakeBlackTexturesHelpe
         WebGLTextureFakeBlackStatus s = boundTexturesArray[i]->ResolvedFakeBlackStatus();
         MOZ_ASSERT(s != WebGLTextureFakeBlackStatus::Unknown);
 
         if (MOZ_LIKELY(s == WebGLTextureFakeBlackStatus::NotNeeded)) {
             continue;
         }
 
         bool alpha = s == WebGLTextureFakeBlackStatus::UninitializedImageData &&
-                     FormatHasAlpha(boundTexturesArray[i]->ImageInfoBase().InternalFormat());
+                     FormatHasAlpha(boundTexturesArray[i]->ImageInfoBase().WebGLFormat());
         ScopedDeletePtr<FakeBlackTexture>&
             blackTexturePtr = alpha
                               ? transparentTextureScopedPtr
                               : opaqueTextureScopedPtr;
 
         if (!blackTexturePtr) {
             GLenum format = alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
             blackTexturePtr
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -48,17 +48,16 @@
 #include "mozilla/Endian.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gl;
 using namespace mozilla::gfx;
 
 static bool BaseTypeAndSizeFromUniformType(GLenum uType, GLenum *baseType, GLint *unitSize);
-static GLenum InternalFormatForFormatAndType(GLenum format, GLenum type, bool isGLES2);
 
 const WebGLRectangleObject*
 WebGLContext::CurValidFBRectObject() const
 {
     const WebGLRectangleObject* rect = nullptr;
 
     if (mBoundFramebuffer) {
         // We don't really need to ask the driver.
@@ -448,22 +447,23 @@ WebGLContext::CopyTexImage2D(GLenum targ
                              GLsizei height,
                              GLint border)
 {
     if (IsContextLost())
         return;
 
     // copyTexImage2D only generates textures with type = UNSIGNED_BYTE
     const WebGLTexImageFunc func = WebGLTexImageFunc::CopyTexImage;
-    GLenum type = LOCAL_GL_UNSIGNED_BYTE;
-
-    if (!ValidateTexImage(2, target, level, internalformat,
+    const GLenum format = internalformat; // WebGL/ES Format
+    const GLenum type = LOCAL_GL_UNSIGNED_BYTE; // WebGL/ES Format
+
+    if (!ValidateTexImage(2, target, level, format,
                           0, 0, 0,
                           width, height, 0,
-                          border, internalformat, type,
+                          border, format, type,
                           func))
     {
         return;
     }
 
     if (mBoundFramebuffer) {
         if (!mBoundFramebuffer->CheckAndInitializeAttachments())
             return ErrorInvalidFramebufferOperation("copyTexImage2D: incomplete framebuffer");
@@ -472,51 +472,51 @@ WebGLContext::CopyTexImage2D(GLenum targ
         if (!mBoundFramebuffer->HasCompletePlanes(readPlaneBits)) {
             return ErrorInvalidOperation("copyTexImage2D: Read source attachment doesn't have the"
                                          " correct color/depth/stencil type.");
         }
     } else {
       ClearBackbufferIfNeeded();
     }
 
-    bool texFormatRequiresAlpha = internalformat == LOCAL_GL_RGBA ||
-                                  internalformat == LOCAL_GL_ALPHA ||
-                                  internalformat == LOCAL_GL_LUMINANCE_ALPHA;
+    bool texFormatRequiresAlpha = format == LOCAL_GL_RGBA ||
+                                  format == LOCAL_GL_ALPHA ||
+                                  format == LOCAL_GL_LUMINANCE_ALPHA;
     bool fboFormatHasAlpha = mBoundFramebuffer ? mBoundFramebuffer->ColorAttachment(0).HasAlpha()
                                                : bool(gl->GetPixelFormat().alpha > 0);
     if (texFormatRequiresAlpha && !fboFormatHasAlpha)
         return ErrorInvalidOperation("copyTexImage2D: texture format requires an alpha channel "
                                      "but the framebuffer doesn't have one");
 
     // check if the memory size of this texture may change with this call
     bool sizeMayChange = true;
     WebGLTexture* tex = activeBoundTextureForTarget(target);
     if (tex->HasImageInfoAt(target, level)) {
         const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(target, level);
 
         sizeMayChange = width != imageInfo.Width() ||
                         height != imageInfo.Height() ||
-                        internalformat != imageInfo.InternalFormat() ||
-                        type != imageInfo.Type();
+                        format != imageInfo.WebGLFormat() ||
+                        type != imageInfo.WebGLType();
     }
 
     if (sizeMayChange)
         GetAndFlushUnderlyingGLErrors();
 
-    CopyTexSubImage2D_base(target, level, internalformat, 0, 0, x, y, width, height, false);
+    CopyTexSubImage2D_base(target, level, format, 0, 0, x, y, width, height, false);
 
     if (sizeMayChange) {
         GLenum error = GetAndFlushUnderlyingGLErrors();
         if (error) {
             GenerateWarning("copyTexImage2D generated error %s", ErrorName(error));
             return;
         }
     }
 
-    tex->SetImageInfo(target, level, width, height, internalformat, type,
+    tex->SetImageInfo(target, level, width, height, format, type,
                       WebGLImageDataStatus::InitializedImageData);
 }
 
 void
 WebGLContext::CopyTexSubImage2D(GLenum target,
                                 GLint level,
                                 GLint xoffset,
                                 GLint yoffset,
@@ -566,51 +566,46 @@ WebGLContext::CopyTexSubImage2D(GLenum t
     GLsizei texHeight = imageInfo.Height();
 
     if (xoffset + width > texWidth || xoffset + width < 0)
       return ErrorInvalidValue("copyTexSubImage2D: xoffset+width is too large");
 
     if (yoffset + height > texHeight || yoffset + height < 0)
       return ErrorInvalidValue("copyTexSubImage2D: yoffset+height is too large");
 
-    GLenum internalFormat = imageInfo.InternalFormat();
-    if (IsGLDepthFormat(internalFormat) ||
-        IsGLDepthStencilFormat(internalFormat))
-    {
+    GLenum webGLFormat = imageInfo.WebGLFormat();
+    if (IsGLDepthFormat(webGLFormat) || IsGLDepthStencilFormat(webGLFormat))
         return ErrorInvalidOperation("copyTexSubImage2D: a base internal format of DEPTH_COMPONENT or DEPTH_STENCIL isn't supported");
-    }
 
     if (mBoundFramebuffer) {
         if (!mBoundFramebuffer->CheckAndInitializeAttachments())
             return ErrorInvalidFramebufferOperation("copyTexSubImage2D: incomplete framebuffer");
 
         GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT;
         if (!mBoundFramebuffer->HasCompletePlanes(readPlaneBits)) {
             return ErrorInvalidOperation("copyTexSubImage2D: Read source attachment doesn't have the"
                                          " correct color/depth/stencil type.");
         }
     } else {
         ClearBackbufferIfNeeded();
     }
 
-    bool texFormatRequiresAlpha = (internalFormat == LOCAL_GL_RGBA ||
-                                   internalFormat == LOCAL_GL_ALPHA ||
-                                   internalFormat == LOCAL_GL_LUMINANCE_ALPHA);
+    bool texFormatRequiresAlpha = FormatHasAlpha(webGLFormat);
     bool fboFormatHasAlpha = mBoundFramebuffer ? mBoundFramebuffer->ColorAttachment(0).HasAlpha()
                                                : bool(gl->GetPixelFormat().alpha > 0);
 
     if (texFormatRequiresAlpha && !fboFormatHasAlpha)
         return ErrorInvalidOperation("copyTexSubImage2D: texture format requires an alpha channel "
                                      "but the framebuffer doesn't have one");
 
     if (imageInfo.HasUninitializedImageData()) {
         tex->DoDeferredImageInitialization(target, level);
     }
 
-    return CopyTexSubImage2D_base(target, level, internalFormat, xoffset, yoffset, x, y, width, height, true);
+    return CopyTexSubImage2D_base(target, level, webGLFormat, xoffset, yoffset, x, y, width, height, true);
 }
 
 
 already_AddRefed<WebGLProgram>
 WebGLContext::CreateProgram()
 {
     if (IsContextLost())
         return nullptr;
@@ -895,22 +890,22 @@ WebGLContext::GenerateMipmap(GLenum targ
     if (!tex->HasImageInfoAt(imageTarget, 0))
     {
         return ErrorInvalidOperation("generateMipmap: Level zero of texture is not defined.");
     }
 
     if (!tex->IsFirstImagePowerOfTwo())
         return ErrorInvalidOperation("generateMipmap: Level zero of texture does not have power-of-two width and height.");
 
-    GLenum internalFormat = tex->ImageInfoAt(imageTarget, 0).InternalFormat();
-    if (IsTextureFormatCompressed(internalFormat))
+    GLenum webGLFormat = tex->ImageInfoAt(imageTarget, 0).WebGLFormat();
+    if (IsTextureFormatCompressed(webGLFormat))
         return ErrorInvalidOperation("generateMipmap: Texture data at level zero is compressed.");
 
     if (IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture) &&
-        (IsGLDepthFormat(internalFormat) || IsGLDepthStencilFormat(internalFormat)))
+        (IsGLDepthFormat(webGLFormat) || IsGLDepthStencilFormat(webGLFormat)))
     {
         return ErrorInvalidOperation("generateMipmap: "
                                      "A texture that has a base internal format of "
                                      "DEPTH_COMPONENT or DEPTH_STENCIL isn't supported");
     }
 
     if (!tex->AreAllLevel0ImageInfosEqual())
         return ErrorInvalidOperation("generateMipmap: The six faces of this cube map have different dimensions, format, or type.");
@@ -1172,21 +1167,21 @@ WebGLContext::GetFramebufferAttachmentPa
         }
 
         ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: pname", pname);
         return JS::NullValue();
     } else if (fba.Texture()) {
         switch (pname) {
              case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT:
                 if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) {
-                    const GLenum internalFormat =
-                        fba.Texture()->ImageInfoBase().InternalFormat();
-                    return (internalFormat == LOCAL_GL_SRGB_EXT ||
-                            internalFormat == LOCAL_GL_SRGB_ALPHA_EXT) ?
-                        JS::NumberValue(uint32_t(LOCAL_GL_SRGB_EXT)) :
+                    const GLenum webGLFormat =
+                        fba.Texture()->ImageInfoBase().WebGLFormat();
+                    return (webGLFormat == LOCAL_GL_SRGB ||
+                            webGLFormat == LOCAL_GL_SRGB_ALPHA) ?
+                        JS::NumberValue(uint32_t(LOCAL_GL_SRGB)) :
                         JS::NumberValue(uint32_t(LOCAL_GL_LINEAR));
                 }
                 break;
 
             case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
                 return JS::NumberValue(uint32_t(LOCAL_GL_TEXTURE));
 
             case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
@@ -1215,17 +1210,17 @@ WebGLContext::GetFramebufferAttachmentPa
                     return JS::NullValue();
                 }
 
                 if (!fba.IsComplete())
                     return JS::NumberValue(uint32_t(LOCAL_GL_NONE));
 
                 uint32_t ret = LOCAL_GL_NONE;
                 GLenum type = fba.Texture()->ImageInfoAt(fba.TexImageTarget(),
-                                                         fba.TexImageLevel()).Type();
+                                                         fba.TexImageLevel()).WebGLType();
                 switch (type) {
                 case LOCAL_GL_UNSIGNED_BYTE:
                 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
                 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
                 case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
                     ret = LOCAL_GL_UNSIGNED_NORMALIZED;
                     break;
                 case LOCAL_GL_FLOAT:
@@ -3518,55 +3513,56 @@ GLenum WebGLContext::CheckedTexImage2D(G
                                        GLenum internalFormat,
                                        GLsizei width,
                                        GLsizei height,
                                        GLint border,
                                        GLenum format,
                                        GLenum type,
                                        const GLvoid *data)
 {
+    MOZ_ASSERT(internalFormat == format);
     WebGLTexture *tex = activeBoundTextureForTarget(target);
     MOZ_ASSERT(tex != nullptr, "no texture bound");
 
     bool sizeMayChange = true;
 
     if (tex->HasImageInfoAt(target, level)) {
         const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(target, level);
         sizeMayChange = width != imageInfo.Width() ||
                         height != imageInfo.Height() ||
-                        format != imageInfo.InternalFormat() ||
-                        type != imageInfo.Type();
+                        format != imageInfo.WebGLFormat() ||
+                        type != imageInfo.WebGLType();
     }
 
-    // convert type for half float if not on GLES2
-    GLenum realType = type;
-    if (realType == LOCAL_GL_HALF_FLOAT_OES && !gl->IsGLES()) {
-        realType = LOCAL_GL_HALF_FLOAT;
-    }
+    // Convert to format and type required by OpenGL 'driver'.
+    GLenum driverType = DriverTypeFromType(gl, type);
+    GLenum driverInternalFormat = LOCAL_GL_NONE;
+    GLenum driverFormat = LOCAL_GL_NONE;
+    DriverFormatsFromFormatAndType(gl, format, type, &driverInternalFormat, &driverFormat);
 
     if (sizeMayChange) {
         GetAndFlushUnderlyingGLErrors();
-
-        gl->fTexImage2D(target, level, internalFormat, width, height, border, format, realType, data);
-
-        GLenum error = GetAndFlushUnderlyingGLErrors();
-        return error;
     }
 
-    gl->fTexImage2D(target, level, internalFormat, width, height, border, format, realType, data);
-
-    return LOCAL_GL_NO_ERROR;
+    gl->fTexImage2D(target, level, driverInternalFormat, width, height, border, driverFormat, driverType, data);
+
+    GLenum error = LOCAL_GL_NO_ERROR;
+    if (sizeMayChange) {
+        error = GetAndFlushUnderlyingGLErrors();
+    }
+
+    return error;
 }
 
 void
 WebGLContext::TexImage2D_base(GLenum target, GLint level, GLenum internalformat,
                               GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
                               GLint border,
                               GLenum format, GLenum type,
-                              void *data, uint32_t byteLength,
+                              void* data, uint32_t byteLength,
                               int jsArrayType, // a TypedArray format enum, or -1 if not relevant
                               WebGLTexelFormat srcFormat, bool srcPremultiplied)
 {
     const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage;
 
     if (!ValidateTexImage(2, target, level, internalformat,
                           0, 0, 0,
                           width, height, 0,
@@ -3593,17 +3589,16 @@ WebGLContext::TexImage2D_base(GLenum tar
     WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
 
     uint32_t srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(actualSrcFormat);
 
     CheckedUint32 checked_neededByteLength =
         GetImageSize(height, width, srcTexelSize, mPixelStoreUnpackAlignment);
 
     CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
-
     CheckedUint32 checked_alignedRowSize =
         RoundedToNextMultipleOf(checked_plainRowSize.value(), mPixelStoreUnpackAlignment);
 
     if (!checked_neededByteLength.isValid())
         return ErrorInvalidOperation("texImage2D: integer overflow computing the needed buffer size");
 
     uint32_t bytesNeeded = checked_neededByteLength.value();
 
@@ -3613,87 +3608,62 @@ WebGLContext::TexImage2D_base(GLenum tar
 
     WebGLTexture *tex = activeBoundTextureForTarget(target);
 
     if (!tex)
         return ErrorInvalidOperation("texImage2D: no texture is bound to this target");
 
     MakeContextCurrent();
 
-    // Handle ES2 and GL differences in floating point internal formats.  Note that
-    // format == internalformat, as checked above and as required by ES.
-    internalformat = InternalFormatForFormatAndType(format, type, gl->IsGLES());
-
-    // Handle ES2 and GL differences when supporting sRGB internal formats. GL ES
-    // requires that format == internalformat, but GL will fail in this case.
-    // GL requires:
-    //      format  ->  internalformat
-    //      GL_RGB      GL_SRGB_EXT
-    //      GL_RGBA     GL_SRGB_ALPHA_EXT
-    if (!gl->IsGLES()) {
-        switch (internalformat) {
-            case LOCAL_GL_SRGB_EXT:
-                format = LOCAL_GL_RGB;
-                break;
-            case LOCAL_GL_SRGB_ALPHA_EXT:
-                format = LOCAL_GL_RGBA;
-                break;
-        }
-    }
-
-    GLenum error = LOCAL_GL_NO_ERROR;
-
-    WebGLImageDataStatus imageInfoStatusIfSuccess = WebGLImageDataStatus::NoImageData;
+    nsAutoArrayPtr<uint8_t> convertedData;
+    void* pixels = nullptr;
+    WebGLImageDataStatus imageInfoStatusIfSuccess = WebGLImageDataStatus::UninitializedImageData;
 
     if (byteLength) {
-        size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
-
+        size_t   srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
         uint32_t dstTexelSize = GetBitsPerTexel(format, type) / 8;
-        size_t dstPlainRowSize = dstTexelSize * width;
-        size_t unpackAlignment = mPixelStoreUnpackAlignment;
-        size_t dstStride = ((dstPlainRowSize + unpackAlignment-1) / unpackAlignment) * unpackAlignment;
+        size_t   dstPlainRowSize = dstTexelSize * width;
+        size_t   unpackAlignment = mPixelStoreUnpackAlignment;
+        size_t   dstStride = ((dstPlainRowSize + unpackAlignment-1) / unpackAlignment) * unpackAlignment;
 
         if (actualSrcFormat == dstFormat &&
             srcPremultiplied == mPixelStorePremultiplyAlpha &&
             srcStride == dstStride &&
             !mPixelStoreFlipY)
         {
             // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
-            error = CheckedTexImage2D(target, level, internalformat,
-                                      width, height, border, format, type, data);
+            pixels = data;
         }
         else
         {
             size_t convertedDataSize = height * dstStride;
-            nsAutoArrayPtr<uint8_t> convertedData(new uint8_t[convertedDataSize]);
+            convertedData = new uint8_t[convertedDataSize];
             ConvertImage(width, height, srcStride, dstStride,
                         static_cast<uint8_t*>(data), convertedData,
                         actualSrcFormat, srcPremultiplied,
                         dstFormat, mPixelStorePremultiplyAlpha, dstTexelSize);
-            error = CheckedTexImage2D(target, level, internalformat,
-                                      width, height, border, format, type, convertedData);
+            pixels = reinterpret_cast<void*>(convertedData.get());
         }
         imageInfoStatusIfSuccess = WebGLImageDataStatus::InitializedImageData;
-    } else {
-        error = CheckedTexImage2D(target, level, internalformat,
-                                  width, height, border, format, type, nullptr);
-        imageInfoStatusIfSuccess = WebGLImageDataStatus::UninitializedImageData;
     }
 
+    GLenum error = CheckedTexImage2D(target, level, internalformat, width,
+                                     height, border, format, type, pixels);
+
     if (error) {
         GenerateWarning("texImage2D generated error %s", ErrorName(error));
         return;
     }
 
     // in all of the code paths above, we should have either initialized data,
     // or allocated data and left it uninitialized, but in any case we shouldn't
     // have NoImageData at this point.
     MOZ_ASSERT(imageInfoStatusIfSuccess != WebGLImageDataStatus::NoImageData);
 
-    tex->SetImageInfo(target, level, width, height, internalformat, type, imageInfoStatusIfSuccess);
+    tex->SetImageInfo(target, level, width, height, format, type, imageInfoStatusIfSuccess);
 }
 
 void
 WebGLContext::TexImage2D(GLenum target, GLint level,
                          GLenum internalformat, GLsizei width,
                          GLsizei height, GLint border, GLenum format,
                          GLenum type, const Nullable<ArrayBufferView> &pixels, ErrorResult& rv)
 {
@@ -3728,17 +3698,17 @@ WebGLContext::TexImage2D(GLenum target, 
 }
 
 
 void
 WebGLContext::TexSubImage2D_base(GLenum target, GLint level,
                                  GLint xoffset, GLint yoffset,
                                  GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
                                  GLenum format, GLenum type,
-                                 void *pixels, uint32_t byteLength,
+                                 void* data, uint32_t byteLength,
                                  int jsArrayType,
                                  WebGLTexelFormat srcFormat, bool srcPremultiplied)
 {
     const WebGLTexImageFunc func = WebGLTexImageFunc::TexSubImage;
 
     if (!ValidateTexImage(2, target, level, format,
                           xoffset, yoffset, 0,
                           width, height, 0,
@@ -3777,52 +3747,47 @@ WebGLContext::TexSubImage2D_base(GLenum 
     WebGLTexture *tex = activeBoundTextureForTarget(target);
     const WebGLTexture::ImageInfo &imageInfo = tex->ImageInfoAt(target, level);
 
     if (imageInfo.HasUninitializedImageData())
         tex->DoDeferredImageInitialization(target, level);
 
     MakeContextCurrent();
 
-    size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
-
+    size_t   srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
     uint32_t dstTexelSize = GetBitsPerTexel(format, type) / 8;
-    size_t dstPlainRowSize = dstTexelSize * width;
+    size_t   dstPlainRowSize = dstTexelSize * width;
     // There are checks above to ensure that this won't overflow.
-    size_t dstStride = RoundedToNextMultipleOf(dstPlainRowSize, mPixelStoreUnpackAlignment).value();
-
-    // convert type for half float if not on GLES2
-    GLenum realType = type;
-    if (realType == LOCAL_GL_HALF_FLOAT_OES) {
-        if (gl->IsSupported(gl::GLFeature::texture_half_float)) {
-            realType = LOCAL_GL_HALF_FLOAT;
-        } else {
-            MOZ_ASSERT(gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float));
-        }
-    }
-
-    if (actualSrcFormat == dstFormat &&
-        srcPremultiplied == mPixelStorePremultiplyAlpha &&
-        srcStride == dstStride &&
-        !mPixelStoreFlipY)
-    {
-        // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
-        gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, format, realType, pixels);
-    }
-    else
-    {
+    size_t   dstStride = RoundedToNextMultipleOf(dstPlainRowSize, mPixelStoreUnpackAlignment).value();
+
+    void* pixels = data;
+    nsAutoArrayPtr<uint8_t> convertedData;
+
+    // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
+    bool noConversion = (actualSrcFormat == dstFormat &&
+                         srcPremultiplied == mPixelStorePremultiplyAlpha &&
+                         srcStride == dstStride &&
+                         !mPixelStoreFlipY);
+
+    if (!noConversion) {
         size_t convertedDataSize = height * dstStride;
-        nsAutoArrayPtr<uint8_t> convertedData(new uint8_t[convertedDataSize]);
+        convertedData = new uint8_t[convertedDataSize];
         ConvertImage(width, height, srcStride, dstStride,
-                    static_cast<const uint8_t*>(pixels), convertedData,
+                    static_cast<const uint8_t*>(data), convertedData,
                     actualSrcFormat, srcPremultiplied,
                     dstFormat, mPixelStorePremultiplyAlpha, dstTexelSize);
-
-        gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, format, realType, convertedData);
+        pixels = reinterpret_cast<void*>(convertedData.get());
     }
+
+    GLenum driverType = DriverTypeFromType(gl, type);
+    GLenum driverInternalFormat = LOCAL_GL_NONE;
+    GLenum driverFormat = LOCAL_GL_NONE;
+    DriverFormatsFromFormatAndType(gl, format, type, &driverInternalFormat, &driverFormat);
+
+    gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, driverFormat, driverType, pixels);
 }
 
 void
 WebGLContext::TexSubImage2D(GLenum target, GLint level,
                             GLint xoffset, GLint yoffset,
                             GLsizei width, GLsizei height,
                             GLenum format, GLenum type,
                             const Nullable<ArrayBufferView> &pixels,
@@ -4066,82 +4031,16 @@ WebGLTexelFormat mozilla::GetWebGLTexelF
         default:
             MOZ_ASSERT(false, "Coding mistake?! Should never reach this point.");
             return WebGLTexelFormat::BadFormat;
     }
 
     MOZ_CRASH("Invalid WebGL texture format/type?");
 }
 
-GLenum
-InternalFormatForFormatAndType(GLenum format, GLenum type, bool isGLES2)
-{
-    // ES2 requires that format == internalformat; floating-point is
-    // indicated purely by the type that's loaded.  For desktop GL, we
-    // have to specify a floating point internal format.
-    if (isGLES2)
-        return format;
-
-    if (format == LOCAL_GL_DEPTH_COMPONENT) {
-        if (type == LOCAL_GL_UNSIGNED_SHORT)
-            return LOCAL_GL_DEPTH_COMPONENT16;
-        else if (type == LOCAL_GL_UNSIGNED_INT)
-            return LOCAL_GL_DEPTH_COMPONENT32;
-    }
-
-    if (format == LOCAL_GL_DEPTH_STENCIL) {
-        if (type == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
-            return LOCAL_GL_DEPTH24_STENCIL8;
-    }
-
-    switch (type) {
-    case LOCAL_GL_UNSIGNED_BYTE:
-    case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
-    case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
-    case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
-        return format;
-
-    case LOCAL_GL_FLOAT:
-        switch (format) {
-        case LOCAL_GL_RGBA:
-            return LOCAL_GL_RGBA32F_ARB;
-        case LOCAL_GL_RGB:
-            return LOCAL_GL_RGB32F_ARB;
-        case LOCAL_GL_ALPHA:
-            return LOCAL_GL_ALPHA32F_ARB;
-        case LOCAL_GL_LUMINANCE:
-            return LOCAL_GL_LUMINANCE32F_ARB;
-        case LOCAL_GL_LUMINANCE_ALPHA:
-            return LOCAL_GL_LUMINANCE_ALPHA32F_ARB;
-        }
-        break;
-
-    case LOCAL_GL_HALF_FLOAT_OES:
-        switch (format) {
-        case LOCAL_GL_RGBA:
-            return LOCAL_GL_RGBA16F;
-        case LOCAL_GL_RGB:
-            return LOCAL_GL_RGB16F;
-        case LOCAL_GL_ALPHA:
-            return LOCAL_GL_ALPHA16F_ARB;
-        case LOCAL_GL_LUMINANCE:
-            return LOCAL_GL_LUMINANCE16F_ARB;
-        case LOCAL_GL_LUMINANCE_ALPHA:
-            return LOCAL_GL_LUMINANCE_ALPHA16F_ARB;
-        }
-        break;
-
-    default:
-        break;
-    }
-
-    NS_ASSERTION(false, "Coding mistake -- bad format/type passed?");
-    return 0;
-}
-
 void
 WebGLContext::BlendColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a) {
     if (IsContextLost())
         return;
     MakeContextCurrent();
     gl->fBlendColor(r, g, b, a);
 }
 
--- a/content/canvas/src/WebGLContextUtils.cpp
+++ b/content/canvas/src/WebGLContextUtils.cpp
@@ -20,29 +20,182 @@
 #include "nsIDOMDataContainerEvent.h"
 
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 
 namespace mozilla {
 
+using namespace gl;
+
 bool
-IsGLDepthFormat(GLenum internalFormat)
+IsGLDepthFormat(GLenum webGLFormat)
 {
-    return (internalFormat == LOCAL_GL_DEPTH_COMPONENT ||
-            internalFormat == LOCAL_GL_DEPTH_COMPONENT16 ||
-            internalFormat == LOCAL_GL_DEPTH_COMPONENT32);
+    return (webGLFormat == LOCAL_GL_DEPTH_COMPONENT ||
+            webGLFormat == LOCAL_GL_DEPTH_COMPONENT16 ||
+            webGLFormat == LOCAL_GL_DEPTH_COMPONENT32);
+}
+
+bool
+IsGLDepthStencilFormat(GLenum webGLFormat)
+{
+    return (webGLFormat == LOCAL_GL_DEPTH_STENCIL ||
+            webGLFormat == LOCAL_GL_DEPTH24_STENCIL8);
 }
 
 bool
-IsGLDepthStencilFormat(GLenum internalFormat)
+FormatHasAlpha(GLenum webGLFormat)
+{
+    return webGLFormat == LOCAL_GL_RGBA ||
+           webGLFormat == LOCAL_GL_LUMINANCE_ALPHA ||
+           webGLFormat == LOCAL_GL_ALPHA ||
+           webGLFormat == LOCAL_GL_RGBA4 ||
+           webGLFormat == LOCAL_GL_RGB5_A1 ||
+           webGLFormat == LOCAL_GL_SRGB_ALPHA;
+}
+
+/**
+ * Convert WebGL/ES format and type into GL format and GL internal
+ * format valid for underlying driver.
+ */
+void
+DriverFormatsFromFormatAndType(GLContext* gl, GLenum webGLFormat, GLenum webGLType,
+                               GLenum* out_driverInternalFormat, GLenum* out_driverFormat)
 {
-    return (internalFormat == LOCAL_GL_DEPTH_STENCIL ||
-            internalFormat == LOCAL_GL_DEPTH24_STENCIL8);
+    MOZ_ASSERT(out_driverInternalFormat, "out_driverInternalFormat can't be nullptr.");
+    MOZ_ASSERT(out_driverFormat, "out_driverFormat can't be nullptr.");
+    if (!out_driverInternalFormat || !out_driverFormat)
+        return;
+
+    // ES2 requires that format == internalformat; floating-point is
+    // indicated purely by the type that's loaded.  For desktop GL, we
+    // have to specify a floating point internal format.
+    if (gl->IsGLES()) {
+        *out_driverInternalFormat = webGLFormat;
+        *out_driverFormat = webGLFormat;
+
+        return;
+    }
+
+    GLenum format = webGLFormat;
+    GLenum internalFormat = LOCAL_GL_NONE;
+
+    if (format == LOCAL_GL_DEPTH_COMPONENT) {
+        if (webGLType == LOCAL_GL_UNSIGNED_SHORT)
+            internalFormat = LOCAL_GL_DEPTH_COMPONENT16;
+        else if (webGLType == LOCAL_GL_UNSIGNED_INT)
+            internalFormat = LOCAL_GL_DEPTH_COMPONENT32;
+    } else if (format == LOCAL_GL_DEPTH_STENCIL) {
+        if (webGLType == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
+            internalFormat = LOCAL_GL_DEPTH24_STENCIL8;
+    } else {
+        switch (webGLType) {
+        case LOCAL_GL_UNSIGNED_BYTE:
+        case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
+        case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
+        case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
+            internalFormat = format;
+            break;
+
+        case LOCAL_GL_FLOAT:
+            switch (format) {
+            case LOCAL_GL_RGBA:
+                internalFormat = LOCAL_GL_RGBA32F;
+                break;
+
+            case LOCAL_GL_RGB:
+                internalFormat = LOCAL_GL_RGB32F;
+                break;
+
+            case LOCAL_GL_ALPHA:
+                internalFormat = LOCAL_GL_ALPHA32F_ARB;
+                break;
+
+            case LOCAL_GL_LUMINANCE:
+                internalFormat = LOCAL_GL_LUMINANCE32F_ARB;
+                break;
+
+            case LOCAL_GL_LUMINANCE_ALPHA:
+                internalFormat = LOCAL_GL_LUMINANCE_ALPHA32F_ARB;
+                break;
+            }
+            break;
+
+        case LOCAL_GL_HALF_FLOAT_OES:
+            switch (format) {
+            case LOCAL_GL_RGBA:
+                internalFormat = LOCAL_GL_RGBA16F;
+                break;
+
+            case LOCAL_GL_RGB:
+                internalFormat = LOCAL_GL_RGB16F;
+                break;
+
+            case LOCAL_GL_ALPHA:
+                internalFormat = LOCAL_GL_ALPHA16F_ARB;
+                break;
+
+            case LOCAL_GL_LUMINANCE:
+                internalFormat = LOCAL_GL_LUMINANCE16F_ARB;
+                break;
+
+            case LOCAL_GL_LUMINANCE_ALPHA:
+                internalFormat = LOCAL_GL_LUMINANCE_ALPHA16F_ARB;
+                break;
+            }
+            break;
+
+        default:
+            break;
+        }
+
+        // Handle ES2 and GL differences when supporting sRGB internal formats. GL ES
+        // requires that format == internalformat, but GL will fail in this case.
+        // GL requires:
+        //      format  ->  internalformat
+        //      GL_RGB      GL_SRGB_EXT
+        //      GL_RGBA     GL_SRGB_ALPHA_EXT
+        switch (format) {
+        case LOCAL_GL_SRGB:
+            internalFormat = format;
+            format = LOCAL_GL_RGB;
+            break;
+
+        case LOCAL_GL_SRGB_ALPHA:
+            internalFormat = format;
+            format = LOCAL_GL_RGBA;
+            break;
+        }
+    }
+
+    MOZ_ASSERT(format != LOCAL_GL_NONE && internalFormat != LOCAL_GL_NONE,
+               "Coding mistake -- bad format/type passed?");
+
+    *out_driverInternalFormat = internalFormat;
+    *out_driverFormat = format;
+}
+
+GLenum
+DriverTypeFromType(GLContext* gl, GLenum webGLType)
+{
+    if (gl->IsGLES())
+        return webGLType;
+
+    // convert type for half float if not on GLES2
+    GLenum type = webGLType;
+    if (type == LOCAL_GL_HALF_FLOAT_OES) {
+        if (gl->IsSupported(gl::GLFeature::texture_half_float)) {
+            return LOCAL_GL_HALF_FLOAT;
+        } else {
+            MOZ_ASSERT(gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float));
+        }
+    }
+
+    return webGLType;
 }
 
 } // namespace mozilla
 
 void
 WebGLContext::GenerateWarning(const char *fmt, ...)
 {
     va_list ap;
--- a/content/canvas/src/WebGLContextUtils.h
+++ b/content/canvas/src/WebGLContextUtils.h
@@ -7,18 +7,22 @@
 #define WEBGLCONTEXTUTILS_H_
 
 #include "WebGLContext.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/BindingUtils.h"
 
 namespace mozilla {
 
-bool IsGLDepthFormat(GLenum internalFormat);
-bool IsGLDepthStencilFormat(GLenum internalFormat);
+bool IsGLDepthFormat(GLenum webGLFormat);
+bool IsGLDepthStencilFormat(GLenum webGLFormat);
+bool FormatHasAlpha(GLenum webGLFormat);
+void DriverFormatsFromFormatAndType(gl::GLContext* gl, GLenum webGLFormat, GLenum webGLType,
+                                    GLenum* out_driverInternalFormat, GLenum* out_driverFormat);
+GLenum DriverTypeFromType(gl::GLContext* gl, GLenum webGLType);
 
 template <typename WebGLObjectType>
 JS::Value
 WebGLContext::WebGLObjectAsJSValue(JSContext *cx, const WebGLObjectType *object, ErrorResult& rv) const
 {
     if (!object) {
         return JS::NullValue();
     }
--- a/content/canvas/src/WebGLContextValidate.cpp
+++ b/content/canvas/src/WebGLContextValidate.cpp
@@ -1384,18 +1384,18 @@ WebGLContext::ValidateTexImage(GLuint di
                                      func))
         {
             return false;
         }
 
         /* Require the format and type to match that of the existing
          * texture as created
          */
-        if (imageInfo.InternalFormat() != format ||
-            imageInfo.Type() != type)
+        if (imageInfo.WebGLFormat() != format ||
+            imageInfo.WebGLType() != type)
         {
             ErrorInvalidOperation("%s: format or type doesn't match the existing texture",
                                   info);
             return false;
         }
     }
 
     /* Additional checks for depth textures */
--- a/content/canvas/src/WebGLFramebuffer.cpp
+++ b/content/canvas/src/WebGLFramebuffer.cpp
@@ -50,27 +50,27 @@ WebGLFramebuffer::Attachment::IsDeleteRe
 
 bool
 WebGLFramebuffer::Attachment::HasAlpha() const
 {
     MOZ_ASSERT(HasImage());
 
     GLenum format = 0;
     if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel))
-        format = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).InternalFormat();
+        format = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).WebGLFormat();
     else if (Renderbuffer())
         format = Renderbuffer()->InternalFormat();
     return FormatHasAlpha(format);
 }
 
 bool
 WebGLFramebuffer::Attachment::IsReadableFloat() const
 {
     if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) {
-        GLenum type = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).Type();
+        GLenum type = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).WebGLType();
         switch (type) {
         case LOCAL_GL_FLOAT:
         case LOCAL_GL_HALF_FLOAT_OES:
             return true;
         }
         return false;
     }
 
@@ -277,29 +277,29 @@ WebGLFramebuffer::Attachment::IsComplete
     {
         return false;
     }
 
     if (Texture()) {
         MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel));
         const WebGLTexture::ImageInfo& imageInfo =
             Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel);
-        GLenum internalFormat = imageInfo.InternalFormat();
+        GLenum webGLFormat = imageInfo.WebGLFormat();
 
         if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT)
-            return IsValidFBOTextureDepthFormat(internalFormat);
+            return IsValidFBOTextureDepthFormat(webGLFormat);
 
         if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
-            return IsValidFBOTextureDepthStencilFormat(internalFormat);
+            return IsValidFBOTextureDepthStencilFormat(webGLFormat);
 
         if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 &&
             mAttachmentPoint < GLenum(LOCAL_GL_COLOR_ATTACHMENT0 +
                                       WebGLContext::kMaxColorAttachments))
         {
-            return IsValidFBOTextureColorFormat(internalFormat);
+            return IsValidFBOTextureColorFormat(webGLFormat);
         }
         MOZ_ASSERT(false, "Invalid WebGL attachment point?");
         return false;
     }
 
     if (Renderbuffer()) {
         GLenum internalFormat = Renderbuffer()->InternalFormat();
 
--- a/content/canvas/src/WebGLTexture.cpp
+++ b/content/canvas/src/WebGLTexture.cpp
@@ -5,16 +5,17 @@
 
 #include "WebGLTexture.h"
 
 #include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "mozilla/Scoped.h"
 #include "ScopedGLHelpers.h"
 #include "WebGLContext.h"
+#include "WebGLContextUtils.h"
 #include "WebGLTexelConversions.h"
 
 #include <algorithm>
 
 using namespace mozilla;
 
 JSObject*
 WebGLTexture::WrapObject(JSContext *cx) {
@@ -47,17 +48,17 @@ WebGLTexture::Delete() {
     mContext->gl->fDeleteTextures(1, &mGLName);
     LinkedListElement<WebGLTexture>::removeFrom(mContext->mTextures);
 }
 
 int64_t
 WebGLTexture::ImageInfo::MemoryUsage() const {
     if (mImageDataStatus == WebGLImageDataStatus::NoImageData)
         return 0;
-    int64_t bitsPerTexel = WebGLContext::GetBitsPerTexel(mInternalFormat, mType);
+    int64_t bitsPerTexel = WebGLContext::GetBitsPerTexel(mWebGLFormat, mWebGLType);
     return int64_t(mWidth) * int64_t(mHeight) * bitsPerTexel/8;
 }
 
 int64_t
 WebGLTexture::MemoryUsage() const {
     if (IsDeleted())
         return 0;
     int64_t result = 0;
@@ -331,17 +332,17 @@ WebGLTexture::ResolvedFakeBlackStatus() 
                 mContext->GenerateWarning("%s is a cube map texture, with a minification filter not requiring a mipmap, "
                             "with some level 0 image having width or height not a power of two, and with a wrap mode "
                             "different from CLAMP_TO_EDGE.", msg_rendering_as_black);
                 mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
             }
         }
     }
 
-    if (ImageInfoBase().mType == LOCAL_GL_FLOAT &&
+    if (ImageInfoBase().mWebGLType == LOCAL_GL_FLOAT &&
         !Context()->IsExtensionEnabled(WebGLExtensionID::OES_texture_float_linear))
     {
         if (mMinFilter == LOCAL_GL_LINEAR ||
             mMinFilter == LOCAL_GL_LINEAR_MIPMAP_LINEAR ||
             mMinFilter == LOCAL_GL_LINEAR_MIPMAP_NEAREST ||
             mMinFilter == LOCAL_GL_NEAREST_MIPMAP_LINEAR)
         {
             mContext->GenerateWarning("%s is a texture with a linear minification filter, "
@@ -351,17 +352,17 @@ WebGLTexture::ResolvedFakeBlackStatus() 
         }
         else if (mMagFilter == LOCAL_GL_LINEAR)
         {
             mContext->GenerateWarning("%s is a texture with a linear magnification filter, "
                                       "which is not compatible with gl.FLOAT by default. "
                                       "Try enabling the OES_texture_float_linear extension if supported.", msg_rendering_as_black);
             mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
         }
-    } else if (ImageInfoBase().mType == LOCAL_GL_HALF_FLOAT_OES &&
+    } else if (ImageInfoBase().mWebGLType == LOCAL_GL_HALF_FLOAT_OES &&
                !Context()->IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float_linear))
     {
         if (mMinFilter == LOCAL_GL_LINEAR ||
             mMinFilter == LOCAL_GL_LINEAR_MIPMAP_LINEAR ||
             mMinFilter == LOCAL_GL_LINEAR_MIPMAP_NEAREST ||
             mMinFilter == LOCAL_GL_NEAREST_MIPMAP_LINEAR)
         {
             mContext->GenerateWarning("%s is a texture with a linear minification filter, "
@@ -538,18 +539,19 @@ void
 WebGLTexture::DoDeferredImageInitialization(GLenum imageTarget, GLint level)
 {
     const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level);
     MOZ_ASSERT(imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData);
 
     mContext->MakeContextCurrent();
 
     // Try to clear with glCLear.
-    WebGLTexelFormat texelformat = GetWebGLTexelFormat(imageInfo.mInternalFormat, imageInfo.mType);
-    GLenum format = WebGLTexelConversions::GLFormatForTexelFormat(texelformat);
+    GLenum format = imageInfo.mWebGLFormat;
+    GLenum type = imageInfo.mWebGLType;
+    WebGLTexelFormat texelformat = GetWebGLTexelFormat(format, type);
 
     bool cleared = ClearWithTempFB(mContext, GLName(),
                                    imageTarget, level,
                                    format, imageInfo.mHeight, imageInfo.mWidth);
     if (cleared) {
         SetImageDataStatus(imageTarget, level, WebGLImageDataStatus::InitializedImageData);
         return;
     }
@@ -563,21 +565,27 @@ WebGLTexture::DoDeferredImageInitializat
                         imageInfo.mHeight,
                         imageInfo.mWidth,
                         texelsize,
                         mContext->mPixelStoreUnpackAlignment);
     MOZ_ASSERT(checked_byteLength.isValid()); // should have been checked earlier
     ScopedFreePtr<void> zeros;
     zeros = calloc(1, checked_byteLength.value());
 
+    gl::GLContext* gl = mContext->gl;
+    GLenum driverType = DriverTypeFromType(gl, type);
+    GLenum driverInternalFormat = LOCAL_GL_NONE;
+    GLenum driverFormat = LOCAL_GL_NONE;
+    DriverFormatsFromFormatAndType(gl, format, type, &driverInternalFormat, &driverFormat);
+
     mContext->GetAndFlushUnderlyingGLErrors();
-    mContext->gl->fTexImage2D(imageTarget, level, imageInfo.mInternalFormat,
-                              imageInfo.mWidth, imageInfo.mHeight,
-                              0, format, imageInfo.mType,
-                              zeros);
+    gl->fTexImage2D(imageTarget, level, driverInternalFormat,
+                    imageInfo.mWidth, imageInfo.mHeight,
+                    0, driverFormat, driverType,
+                    zeros);
     GLenum error = mContext->GetAndFlushUnderlyingGLErrors();
     if (error) {
         // Should only be OUT_OF_MEMORY. Anyway, there's no good way to recover from this here.
         printf_stderr("Error: 0x%4x\n", error);
         MOZ_CRASH(); // errors on texture upload have been related to video memory exposure in the past.
     }
 
     SetImageDataStatus(imageTarget, level, WebGLImageDataStatus::InitializedImageData);
--- a/content/canvas/src/WebGLTexture.h
+++ b/content/canvas/src/WebGLTexture.h
@@ -18,25 +18,16 @@
 namespace mozilla {
 
 // Zero is not an integer power of two.
 inline bool is_pot_assuming_nonnegative(GLsizei x)
 {
     return x && (x & (x-1)) == 0;
 }
 
-inline bool FormatHasAlpha(GLenum format)
-{
-    return format == LOCAL_GL_RGBA ||
-           format == LOCAL_GL_LUMINANCE_ALPHA ||
-           format == LOCAL_GL_ALPHA ||
-           format == LOCAL_GL_RGBA4 ||
-           format == LOCAL_GL_RGB5_A1;
-}
-
 // NOTE: When this class is switched to new DOM bindings, update the (then-slow)
 // WrapObject calls in GetParameter and GetFramebufferAttachmentParameter.
 class WebGLTexture MOZ_FINAL
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLTexture>
     , public LinkedListElement<WebGLTexture>
     , public WebGLContextBoundObject
     , public WebGLFramebufferAttachable
@@ -77,38 +68,41 @@ protected:
 
 public:
 
     class ImageInfo
         : public WebGLRectangleObject
     {
     public:
         ImageInfo()
-            : mInternalFormat(0)
-            , mType(0)
+            : mWebGLFormat(LOCAL_GL_NONE)
+            , mWebGLType(LOCAL_GL_NONE)
             , mImageDataStatus(WebGLImageDataStatus::NoImageData)
         {}
 
-        ImageInfo(GLsizei width, GLsizei height,
-                  GLenum format, GLenum type, WebGLImageDataStatus status)
+        ImageInfo(GLsizei width,
+                  GLsizei height,
+                  GLenum webGLFormat,
+                  GLenum webGLType,
+                  WebGLImageDataStatus status)
             : WebGLRectangleObject(width, height)
-            , mInternalFormat(format)
-            , mType(type)
+            , mWebGLFormat(webGLFormat)
+            , mWebGLType(webGLType)
             , mImageDataStatus(status)
         {
             // shouldn't use this constructor to construct a null ImageInfo
             MOZ_ASSERT(status != WebGLImageDataStatus::NoImageData);
         }
 
         bool operator==(const ImageInfo& a) const {
             return mImageDataStatus == a.mImageDataStatus &&
-                   mWidth  == a.mWidth &&
+                   mWidth == a.mWidth &&
                    mHeight == a.mHeight &&
-                   mInternalFormat == a.mInternalFormat &&
-                   mType   == a.mType;
+                   mWebGLFormat == a.mWebGLFormat &&
+                   mWebGLType == a.mWebGLType;
         }
         bool operator!=(const ImageInfo& a) const {
             return !(*this == a);
         }
         bool IsSquare() const {
             return mWidth == mHeight;
         }
         bool IsPositive() const {
@@ -117,20 +111,30 @@ public:
         bool IsPowerOfTwo() const {
             return is_pot_assuming_nonnegative(mWidth) &&
                    is_pot_assuming_nonnegative(mHeight); // negative sizes should never happen (caught in texImage2D...)
         }
         bool HasUninitializedImageData() const {
             return mImageDataStatus == WebGLImageDataStatus::UninitializedImageData;
         }
         int64_t MemoryUsage() const;
-        GLenum InternalFormat() const { return mInternalFormat; }
-        GLenum Type() const { return mType; }
+        /*! This is the format passed from JS to WebGL.
+         * It can be converted to a value to be passed to driver with
+         * DriverFormatsFromFormatAndType().
+         */
+        GLenum WebGLFormat() const { return mWebGLFormat; }
+        /*! This is the type passed from JS to WebGL.
+         * It can be converted to a value to be passed to driver with
+         * DriverTypeFromType().
+         */
+        GLenum WebGLType() const { return mWebGLType; }
+
     protected:
-        GLenum mInternalFormat, mType;
+        GLenum mWebGLFormat; //!< This is the WebGL/GLES format
+        GLenum mWebGLType;   //!< This is the WebGL/GLES type
         WebGLImageDataStatus mImageDataStatus;
 
         friend class WebGLTexture;
     };
 
 private:
     static size_t FaceForTarget(GLenum target) {
         // Call this out explicitly:
--- a/content/canvas/test/webgl-mochitest/mochitest.ini
+++ b/content/canvas/test/webgl-mochitest/mochitest.ini
@@ -6,16 +6,17 @@ support-files =
 [test_depth_readpixels.html]
 [test_draw.html]
 [test_fb_param.html]
 [test_fb_param_crash.html]
 [test_highp_fs.html]
 [test_no_arr_points.html]
 [test_noprog_draw.html]
 [test_privileged_exts.html]
+[test_texsubimage_float.html]
 [test_webgl_available.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 [test_webgl_conformance.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 [test_webgl_request_context.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 [test_webgl_request_mismatch.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/content/canvas/test/webgl-mochitest/test_texsubimage_float.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<title>WebGL test: bug 1003607</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"></canvas>
+<script>
+
+// Give ourselves a scope to return early from:
+(function() {
+  var gl = WebGLUtil.getWebGL('c');
+  if (!gl) {
+    todo(false, 'WebGL is unavailable.');
+    return;
+  }
+
+  // Catch actual WebGLUtil errors, not GL errors.
+  function errorFunc(str) {
+    ok(false, 'Error: ' + str);
+  }
+  WebGLUtil.setErrorFunc(errorFunc);
+
+  function checkGLError(func, info, reference) {
+    var error = gl.getError();
+    var prefix = info ? ('[' + info + '] ') : '';
+    var text = 'gl.getError should be 0x' + reference.toString(16) +
+               ', was 0x' + error.toString(16) + '.';
+    func(error == reference, prefix + text);
+  }
+
+  // Begin test:
+  if (!gl.getExtension('OES_texture_float')) {
+    todo(false, 'Not having this extension is fine.');
+    return;
+  }
+  var tex = gl.createTexture();
+  gl.bindTexture(gl.TEXTURE_2D, tex);
+  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+  checkGLError(ok, 'texture parameter setup should succeed', gl.NO_ERROR);
+
+  // Generate data
+  var width = 2;
+  var height = 2;
+  var numChannels = 4;
+  var data = new Float32Array(width * height * numChannels);
+  for (var ii = 0; ii < data.length; ++ii) {
+    data[ii] = 10000;
+  }
+  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.FLOAT, data);
+  checkGLError(ok, 'floating-point texture allocation should succeed', gl.NO_ERROR);
+
+  // Try respecifying data
+  gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, gl.RGBA, gl.FLOAT, data);
+  checkGLError(ok, 'floating-point texture sub image should succeed', gl.NO_ERROR);
+})();
+
+</script>