[WebGL2] Implement texStorage2D()
authormmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 19 Nov 2016 01:00:16 +0000 (01:00 +0000)
committermmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 19 Nov 2016 01:00:16 +0000 (01:00 +0000)
https://bugs.webkit.org/show_bug.cgi?id=164493

Reviewed by Dean Jackson.

Source/WebCore:

Create a new validation function which only accepts sized internalFormats.
After running texStorage2D(), we also texSubImage2D() to zero-fill it. This
is to compensate for potentially buggy drivers.

Because glTexStorage2D() was only added to OpenGL in version 4.2, not all
OpenGL 3.2+ contexts can implement this command. However, according to
https://developer.apple.com/opengl/capabilities/ all Apple GPUs have the
GL_ARB_texture_storage which implements this call. In the future, we could
implement texStorage2D() on top of texImage2D() if there are any ports which
want WebGL2 but don't have 4.2 and don't have the extension.

Also, when calling texStorage2D, callers specify an internalFormat but not a
type/format pair. This means that storing the texture's type is only valid
for WebGL 1 contexts. This patch surrounds all calls to reading the texture
type with guards and adds an ASSERT() at the read site to make sure the
right thing is happening.

Test: fast/canvas/webgl/webgl2-texStorage.html

* html/canvas/WebGL2RenderingContext.cpp:
(WebCore::WebGL2RenderingContext::validateTexStorageFuncParameters):
(WebCore::WebGL2RenderingContext::texStorage2D):
* html/canvas/WebGL2RenderingContext.h:
* html/canvas/WebGLRenderingContext.cpp:
(WebCore::WebGLRenderingContext::validateIndexArrayConservative):
* html/canvas/WebGLRenderingContextBase.cpp:
(WebCore::WebGLRenderingContextBase::create):
(WebCore::WebGLRenderingContextBase::copyTexSubImage2D):
(WebCore::WebGLRenderingContextBase::validateTexFunc):
(WebCore::WebGLRenderingContextBase::validateTexFuncData):
(WebCore::WebGLRenderingContextBase::texImage2D):
* html/canvas/WebGLTexture.cpp:
(WebCore::WebGLTexture::WebGLTexture):
(WebCore::WebGLTexture::getType):
(WebCore::WebGLTexture::needToUseBlackTexture):
(WebCore::WebGLTexture::canGenerateMipmaps):
(WebCore::internalFormatIsFloatType):
(WebCore::internalFormatIsHalfFloatType):
(WebCore::WebGLTexture::update):
* html/canvas/WebGLTexture.h:
* platform/graphics/GraphicsContext3D.cpp:
(WebCore::GraphicsContext3D::texImage2DResourceSafe):
(WebCore::GraphicsContext3D::packImageData):
(WebCore::GraphicsContext3D::extractImageData):
* platform/graphics/GraphicsContext3D.h:
* platform/graphics/opengl/Extensions3DOpenGLCommon.cpp:
(WebCore::Extensions3DOpenGLCommon::initializeAvailableExtensions):
* platform/graphics/opengl/GraphicsContext3DOpenGLCommon.cpp:
(WebCore::GraphicsContext3D::texStorage2D):
(WebCore::GraphicsContext3D::texStorage3D):

LayoutTests:

* fast/canvas/webgl/webgl2-texStorage-expected.txt: Added.
* fast/canvas/webgl/webgl2-texStorage.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@208910 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/canvas/webgl/webgl2-texStorage-expected.txt [new file with mode: 0644]
LayoutTests/fast/canvas/webgl/webgl2-texStorage.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/html/canvas/WebGL2RenderingContext.cpp
Source/WebCore/html/canvas/WebGL2RenderingContext.h
Source/WebCore/html/canvas/WebGLRenderingContext.cpp
Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp
Source/WebCore/html/canvas/WebGLTexture.cpp
Source/WebCore/html/canvas/WebGLTexture.h
Source/WebCore/platform/graphics/GraphicsContext3D.cpp
Source/WebCore/platform/graphics/GraphicsContext3D.h
Source/WebCore/platform/graphics/opengl/Extensions3DOpenGLCommon.cpp
Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGLCommon.cpp

index f38f94b..56ba818 100644 (file)
@@ -1,3 +1,13 @@
+2016-11-18  Myles C. Maxfield  <mmaxfield@apple.com>
+
+        [WebGL2] Implement texStorage2D()
+        https://bugs.webkit.org/show_bug.cgi?id=164493
+
+        Reviewed by Dean Jackson.
+
+        * fast/canvas/webgl/webgl2-texStorage-expected.txt: Added.
+        * fast/canvas/webgl/webgl2-texStorage.html: Added.
+
 2016-11-17  Alex Christensen  <achristensen@webkit.org>
 
         Support IDN2008 with UTS #46 instead of IDN2003
diff --git a/LayoutTests/fast/canvas/webgl/webgl2-texStorage-expected.txt b/LayoutTests/fast/canvas/webgl/webgl2-texStorage-expected.txt
new file mode 100644 (file)
index 0000000..c0a15d6
--- /dev/null
@@ -0,0 +1,159 @@
+CONSOLE MESSAGE: line 58: WebGL: INVALID_OPERATION: texStorage2D: texStorage2D already called on this texture
+CONSOLE MESSAGE: line 61: WebGL: INVALID_OPERATION: texImage2D: texStorage() called on this texture previously
+Test that texStorage2D() works.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS gl.getError() is gl.NO_ERROR
+PASS gl.getError() is gl.NO_ERROR
+PASS gl.getError() is gl.NO_ERROR
+PASS gl.getError() is gl.NO_ERROR
+PASS gl.getError() is gl.NO_ERROR
+PASS gl.getError() is gl.NO_ERROR
+PASS gl.getError() is gl.NO_ERROR
+asdf
+PASS gl.getError() is gl.NO_ERROR
+PASS gl.getError() is gl.NO_ERROR
+PASS gl.getError() is gl.NO_ERROR
+PASS gl.getError() is gl.NO_ERROR
+PASS gl.checkFramebufferStatus(gl.FRAMEBUFFER) is gl.FRAMEBUFFER_COMPLETE
+PASS gl.getError() is gl.NO_ERROR
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS receiver[i] is 0
+PASS gl.getError() is not gl.NO_ERROR
+PASS gl.getError() is not gl.NO_ERROR
+PASS gl.getError() is gl.NO_ERROR
+PASS gl.getError() is gl.NO_ERROR
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS receiver[i] is 1
+PASS gl.getError() is gl.NO_ERROR
+PASS gl.getError() is gl.NO_ERROR
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/canvas/webgl/webgl2-texStorage.html b/LayoutTests/fast/canvas/webgl/webgl2-texStorage.html
new file mode 100644 (file)
index 0000000..c930d1d
--- /dev/null
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<canvas id="canvas" width="40" height="40"></canvas>
+<script>
+description("Test that texStorage2D() works.");
+
+if (window.internals)
+    internals.setWebGL2Enabled(true);
+
+var canvas = document.getElementById("canvas");
+var width = canvas.width;
+var height = canvas.height;
+var gl = canvas.getContext("webgl2");
+shouldBe("gl.getError()", "gl.NO_ERROR");
+
+var texture = gl.createTexture();
+shouldBe("gl.getError()", "gl.NO_ERROR");
+gl.bindTexture(gl.TEXTURE_2D, texture);
+shouldBe("gl.getError()", "gl.NO_ERROR");
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+shouldBe("gl.getError()", "gl.NO_ERROR");
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+shouldBe("gl.getError()", "gl.NO_ERROR");
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+shouldBe("gl.getError()", "gl.NO_ERROR");
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+shouldBe("gl.getError()", "gl.NO_ERROR");
+
+var width = 4;
+var height = 4;
+debug("asdf");
+gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, width, height);
+shouldBe("gl.getError()", "gl.NO_ERROR");
+
+var framebuffer = gl.createFramebuffer();
+shouldBe("gl.getError()", "gl.NO_ERROR");
+gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
+shouldBe("gl.getError()", "gl.NO_ERROR");
+gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
+shouldBe("gl.getError()", "gl.NO_ERROR");
+shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE");
+
+var receiver = new Uint8Array(width * height * 4);
+for (var i = 0; i < width * height * 4; ++i) {
+    receiver[i] = 1;
+}
+
+gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, receiver);
+shouldBe("gl.getError()", "gl.NO_ERROR");
+for (var i = 0; i < width * height * 4; ++i) {
+    shouldBe("receiver[i]", "0");
+}
+
+gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, width, height);
+shouldNotBe("gl.getError()", "gl.NO_ERROR");
+
+gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, receiver);
+shouldNotBe("gl.getError()", "gl.NO_ERROR");
+
+for (var i = 0; i < width * height * 4; ++i) {
+    receiver[i] = 1;
+}
+gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, receiver);
+shouldBe("gl.getError()", "gl.NO_ERROR");
+
+for (var i = 0; i < width * height * 4; ++i) {
+    receiver[i] = 0;
+}
+gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, receiver);
+shouldBe("gl.getError()", "gl.NO_ERROR");
+
+for (var i = 0; i < width * height * 4; ++i) {
+    shouldBe("receiver[i]", "1");
+}
+
+gl.deleteFramebuffer(framebuffer);
+shouldBe("gl.getError()", "gl.NO_ERROR");
+
+gl.deleteTexture(texture);
+shouldBe("gl.getError()", "gl.NO_ERROR");
+</script>
+<script src="../../../resources/js-test-post.js"></script>
+</body>
+</html>
\ No newline at end of file
index 5dd569d..2dfb704 100644 (file)
@@ -1,3 +1,61 @@
+2016-11-18  Myles C. Maxfield  <mmaxfield@apple.com>
+
+        [WebGL2] Implement texStorage2D()
+        https://bugs.webkit.org/show_bug.cgi?id=164493
+
+        Reviewed by Dean Jackson.
+
+        Create a new validation function which only accepts sized internalFormats.
+        After running texStorage2D(), we also texSubImage2D() to zero-fill it. This
+        is to compensate for potentially buggy drivers.
+
+        Because glTexStorage2D() was only added to OpenGL in version 4.2, not all
+        OpenGL 3.2+ contexts can implement this command. However, according to
+        https://developer.apple.com/opengl/capabilities/ all Apple GPUs have the
+        GL_ARB_texture_storage which implements this call. In the future, we could
+        implement texStorage2D() on top of texImage2D() if there are any ports which
+        want WebGL2 but don't have 4.2 and don't have the extension.
+
+        Also, when calling texStorage2D, callers specify an internalFormat but not a
+        type/format pair. This means that storing the texture's type is only valid
+        for WebGL 1 contexts. This patch surrounds all calls to reading the texture
+        type with guards and adds an ASSERT() at the read site to make sure the
+        right thing is happening.
+
+        Test: fast/canvas/webgl/webgl2-texStorage.html
+
+        * html/canvas/WebGL2RenderingContext.cpp:
+        (WebCore::WebGL2RenderingContext::validateTexStorageFuncParameters):
+        (WebCore::WebGL2RenderingContext::texStorage2D):
+        * html/canvas/WebGL2RenderingContext.h:
+        * html/canvas/WebGLRenderingContext.cpp:
+        (WebCore::WebGLRenderingContext::validateIndexArrayConservative):
+        * html/canvas/WebGLRenderingContextBase.cpp:
+        (WebCore::WebGLRenderingContextBase::create):
+        (WebCore::WebGLRenderingContextBase::copyTexSubImage2D):
+        (WebCore::WebGLRenderingContextBase::validateTexFunc):
+        (WebCore::WebGLRenderingContextBase::validateTexFuncData):
+        (WebCore::WebGLRenderingContextBase::texImage2D):
+        * html/canvas/WebGLTexture.cpp:
+        (WebCore::WebGLTexture::WebGLTexture):
+        (WebCore::WebGLTexture::getType):
+        (WebCore::WebGLTexture::needToUseBlackTexture):
+        (WebCore::WebGLTexture::canGenerateMipmaps):
+        (WebCore::internalFormatIsFloatType):
+        (WebCore::internalFormatIsHalfFloatType):
+        (WebCore::WebGLTexture::update):
+        * html/canvas/WebGLTexture.h:
+        * platform/graphics/GraphicsContext3D.cpp:
+        (WebCore::GraphicsContext3D::texImage2DResourceSafe):
+        (WebCore::GraphicsContext3D::packImageData):
+        (WebCore::GraphicsContext3D::extractImageData):
+        * platform/graphics/GraphicsContext3D.h:
+        * platform/graphics/opengl/Extensions3DOpenGLCommon.cpp:
+        (WebCore::Extensions3DOpenGLCommon::initializeAvailableExtensions):
+        * platform/graphics/opengl/GraphicsContext3DOpenGLCommon.cpp:
+        (WebCore::GraphicsContext3D::texStorage2D):
+        (WebCore::GraphicsContext3D::texStorage3D):
+
 2016-11-18  Alex Christensen  <achristensen@webkit.org>
 
         TextDecoder constructor should not accept replacement encodings
index ce4f0ef..4ece377 100644 (file)
@@ -312,8 +312,167 @@ void WebGL2RenderingContext::renderbufferStorageMultisample(GC3Denum, GC3Dsizei,
 {
 }
 
-void WebGL2RenderingContext::texStorage2D(GC3Denum, GC3Dsizei, GC3Denum, GC3Dsizei, GC3Dsizei)
+bool WebGL2RenderingContext::validateTexStorageFuncParameters(GC3Denum target, GC3Dsizei levels, GC3Denum internalFormat, GC3Dsizei width, GC3Dsizei height, const char* functionName)
 {
+    if (width < 0 || height < 0) {
+        synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "width or height < 0");
+        return false;
+    }
+
+    if (width > m_maxTextureSize || height > m_maxTextureSize) {
+        synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "texture dimensions are larger than the maximum texture size");
+        return false;
+    }
+
+    if (target == GraphicsContext3D::TEXTURE_CUBE_MAP) {
+        if (width != height) {
+            synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "width != height for cube map");
+            return false;
+        }
+    } else if (target != GraphicsContext3D::TEXTURE_2D) {
+        synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid target");
+        return false;
+    }
+
+    if (levels < 0 || levels > m_maxTextureLevel) {
+        synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "number of levels is out of bounds");
+        return false;
+    }
+
+    switch (internalFormat) {
+    case GraphicsContext3D::R8:
+    case GraphicsContext3D::R8_SNORM:
+    case GraphicsContext3D::R16F:
+    case GraphicsContext3D::R32F:
+    case GraphicsContext3D::R8UI:
+    case GraphicsContext3D::R8I:
+    case GraphicsContext3D::R16UI:
+    case GraphicsContext3D::R16I:
+    case GraphicsContext3D::R32UI:
+    case GraphicsContext3D::R32I:
+    case GraphicsContext3D::RG8:
+    case GraphicsContext3D::RG8_SNORM:
+    case GraphicsContext3D::RG16F:
+    case GraphicsContext3D::RG32F:
+    case GraphicsContext3D::RG8UI:
+    case GraphicsContext3D::RG8I:
+    case GraphicsContext3D::RG16UI:
+    case GraphicsContext3D::RG16I:
+    case GraphicsContext3D::RG32UI:
+    case GraphicsContext3D::RG32I:
+    case GraphicsContext3D::RGB8:
+    case GraphicsContext3D::SRGB8:
+    case GraphicsContext3D::RGB565:
+    case GraphicsContext3D::RGB8_SNORM:
+    case GraphicsContext3D::R11F_G11F_B10F:
+    case GraphicsContext3D::RGB9_E5:
+    case GraphicsContext3D::RGB16F:
+    case GraphicsContext3D::RGB32F:
+    case GraphicsContext3D::RGB8UI:
+    case GraphicsContext3D::RGB8I:
+    case GraphicsContext3D::RGB16UI:
+    case GraphicsContext3D::RGB16I:
+    case GraphicsContext3D::RGB32UI:
+    case GraphicsContext3D::RGB32I:
+    case GraphicsContext3D::RGBA8:
+    case GraphicsContext3D::SRGB8_ALPHA8:
+    case GraphicsContext3D::RGBA8_SNORM:
+    case GraphicsContext3D::RGB5_A1:
+    case GraphicsContext3D::RGBA4:
+    case GraphicsContext3D::RGB10_A2:
+    case GraphicsContext3D::RGBA16F:
+    case GraphicsContext3D::RGBA32F:
+    case GraphicsContext3D::RGBA8UI:
+    case GraphicsContext3D::RGBA8I:
+    case GraphicsContext3D::RGB10_A2UI:
+    case GraphicsContext3D::RGBA16UI:
+    case GraphicsContext3D::RGBA16I:
+    case GraphicsContext3D::RGBA32I:
+    case GraphicsContext3D::RGBA32UI:
+    case GraphicsContext3D::DEPTH_COMPONENT16:
+    case GraphicsContext3D::DEPTH_COMPONENT24:
+    case GraphicsContext3D::DEPTH_COMPONENT32F:
+    case GraphicsContext3D::DEPTH24_STENCIL8:
+    case GraphicsContext3D::DEPTH32F_STENCIL8:
+        break;
+    default:
+        synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "Unknown internalFormat");
+        return false;
+    }
+
+    return true;
+}
+
+void WebGL2RenderingContext::texStorage2D(GC3Denum target, GC3Dsizei levels, GC3Denum internalFormat, GC3Dsizei width, GC3Dsizei height)
+{
+    if (isContextLostOrPending())
+        return;
+
+    WebGLTexture* texture = validateTextureBinding("texStorage2D", target, false);
+    if (!texture)
+        return;
+
+    if (!validateTexStorageFuncParameters(target, levels, internalFormat, width, height, "texStorage2D"))
+        return;
+
+    if (!validateNPOTTextureLevel(width, height, levels, "texStorage2D"))
+        return;
+
+    if (texture->immutable()) {
+        synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "texStorage2D", "texStorage2D already called on this texture");
+        return;
+    }
+    texture->setImmutable();
+
+    m_context->texStorage2D(target, levels, internalFormat, width, height);
+
+    {
+        GC3Denum format;
+        GC3Denum type;
+        if (!GraphicsContext3D::possibleFormatAndTypeForInternalFormat(internalFormat, format, type)) {
+            synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "texStorage2D", "Texture has unknown internal format");
+            return;
+        }
+
+        GC3Dsizei levelWidth = width;
+        GC3Dsizei levelHeight = height;
+
+        unsigned size;
+        GC3Denum error = m_context->computeImageSizeInBytes(format, type, width, height, m_unpackAlignment, &size, nullptr);
+        if (error != GraphicsContext3D::NO_ERROR) {
+            synthesizeGLError(error, "texStorage2D", "bad dimensions");
+            return;
+        }
+
+        Vector<char> data(size);
+        memset(data.data(), 0, size);
+
+        for (GC3Dsizei level = 0; level < levels; ++level) {
+            if (target == GraphicsContext3D::TEXTURE_CUBE_MAP) {
+                m_context->texSubImage2D(GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X, level, 0, 0, levelWidth, levelHeight, format, type, data.data());
+                m_context->texSubImage2D(GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X, level, 0, 0, levelWidth, levelHeight, format, type, data.data());
+                m_context->texSubImage2D(GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Y, level, 0, 0, levelWidth, levelHeight, format, type, data.data());
+                m_context->texSubImage2D(GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y, level, 0, 0, levelWidth, levelHeight, format, type, data.data());
+                m_context->texSubImage2D(GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z, level, 0, 0, levelWidth, levelHeight, format, type, data.data());
+                m_context->texSubImage2D(GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z, level, 0, 0, levelWidth, levelHeight, format, type, data.data());
+            } else
+                m_context->texSubImage2D(target, level, 0, 0, levelWidth, levelHeight, format, type, data.data());
+            levelWidth = std::max(1, levelWidth / 2);
+            levelHeight = std::max(1, levelHeight / 2);
+        }
+    }
+
+    for (GC3Dsizei level = 0; level < levels; ++level) {
+        if (target == GraphicsContext3D::TEXTURE_CUBE_MAP) {
+            texture->setLevelInfo(GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X, level, internalFormat, width, height, GraphicsContext3D::UNSIGNED_BYTE);
+            texture->setLevelInfo(GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X, level, internalFormat, width, height, GraphicsContext3D::UNSIGNED_BYTE);
+            texture->setLevelInfo(GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Y, level, internalFormat, width, height, GraphicsContext3D::UNSIGNED_BYTE);
+            texture->setLevelInfo(GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y, level, internalFormat, width, height, GraphicsContext3D::UNSIGNED_BYTE);
+            texture->setLevelInfo(GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z, level, internalFormat, width, height, GraphicsContext3D::UNSIGNED_BYTE);
+            texture->setLevelInfo(GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z, level, internalFormat, width, height, GraphicsContext3D::UNSIGNED_BYTE);
+        } else
+            texture->setLevelInfo(target, level, internalFormat, width, height, GraphicsContext3D::UNSIGNED_BYTE);
+    }
 }
 
 void WebGL2RenderingContext::texStorage3D(GC3Denum, GC3Dsizei, GC3Denum, GC3Dsizei, GC3Dsizei, GC3Dsizei)
index 1dc0ee4..7432644 100644 (file)
@@ -64,8 +64,8 @@ public:
     void renderbufferStorageMultisample(GC3Denum target, GC3Dsizei samples, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height);
     
     /* Texture objects */
-    void texStorage2D(GC3Denum target, GC3Dsizei levels, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height);
-    void texStorage3D(GC3Denum target, GC3Dsizei levels, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dsizei depth);
+    void texStorage2D(GC3Denum target, GC3Dsizei levels, GC3Denum internalFormat, GC3Dsizei width, GC3Dsizei height);
+    void texStorage3D(GC3Denum target, GC3Dsizei levels, GC3Denum internalFormat, GC3Dsizei width, GC3Dsizei height, GC3Dsizei depth);
     void texImage3D(GC3Denum target, GC3Dint level, GC3Dint internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dsizei depth, GC3Dint border, GC3Denum format, GC3Denum type, RefPtr<ArrayBufferView>&& pixels);
 
     void texSubImage3D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, GC3Dint zoffset, GC3Dsizei width, GC3Dsizei height, GC3Dsizei depth, GC3Denum format, GC3Denum type, RefPtr<ArrayBufferView>&& pixels);
@@ -191,6 +191,8 @@ private:
     GC3Denum baseInternalFormatFromInternalFormat(GC3Denum internalformat);
     bool isIntegerFormat(GC3Denum internalformat);
     void initializeShaderExtensions();
+
+    bool validateTexStorageFuncParameters(GC3Denum target, GC3Dsizei levels, GC3Denum internalFormat, GC3Dsizei width, GC3Dsizei height, const char* functionName);
 };
 
 } // namespace WebCore
index e688416..024c185 100644 (file)
@@ -772,8 +772,8 @@ bool WebGLRenderingContext::validateIndexArrayConservative(GC3Denum type, unsign
         }
         default:
             return false;
-    }
-    elementArrayBuffer->setCachedMaxIndex(type, maxIndex);
+        }
+        elementArrayBuffer->setCachedMaxIndex(type, maxIndex);
     }
     
     if (maxIndex >= 0) {
index 46ff5e5..3e9dcca 100644 (file)
@@ -434,6 +434,13 @@ std::unique_ptr<WebGLRenderingContextBase> WebGLRenderingContextBase::create(HTM
     if (extensions.supports("GL_EXT_debug_marker"))
         extensions.pushGroupMarkerEXT("WebGLRenderingContext");
 
+#if ENABLE(WEBGL2) && PLATFORM(MAC)
+    // glTexStorage() was only added to Core in OpenGL 4.2.
+    // However, according to https://developer.apple.com/opengl/capabilities/ all Apple GPUs support this extension.
+    if (attributes.useGLES3 && !extensions.supports("GL_ARB_texture_storage"))
+        return nullptr;
+#endif
+
     std::unique_ptr<WebGLRenderingContextBase> renderingContext = nullptr;
 #if ENABLE(WEBGL2)
     if (type == "webgl2")
@@ -1365,7 +1372,7 @@ void WebGLRenderingContextBase::copyTexSubImage2D(GC3Denum target, GC3Dint level
         std::unique_ptr<unsigned char[]> zero;
         if (width && height) {
             unsigned size;
-            GC3Denum error = m_context->computeImageSizeInBytes(format, type, width, height, m_unpackAlignment, &size, 0);
+            GC3Denum error = m_context->computeImageSizeInBytes(format, type, width, height, m_unpackAlignment, &size, nullptr);
             if (error != GraphicsContext3D::NO_ERROR) {
                 synthesizeGLError(error, "copyTexSubImage2D", "bad dimensions");
                 return;
@@ -3311,6 +3318,10 @@ bool WebGLRenderingContextBase::validateTexFunc(const char* functionName, TexFun
         return false;
 
     if (functionType != TexSubImage) {
+        if (functionType == TexImage && texture->immutable()) {
+            synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "texStorage() called on this texture previously");
+            return false;
+        }
         if (!validateNPOTTextureLevel(width, height, level, functionName))
             return false;
         // For SourceArrayBufferView, function validateTexFuncData() would handle whether to validate the SettableTexFormat
@@ -3333,7 +3344,7 @@ bool WebGLRenderingContextBase::validateTexFunc(const char* functionName, TexFun
             synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "dimensions out of range");
             return false;
         }
-        if (texture->getInternalFormat(target, level) != internalFormat || texture->getType(target, level) != type) {
+        if (texture->getInternalFormat(target, level) != internalFormat || (isWebGL1() && texture->getType(target, level) != type)) {
             synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "type and format do not match texture");
             return false;
         }
@@ -3629,7 +3640,7 @@ bool WebGLRenderingContextBase::validateTexFuncData(const char* functionName, GC
         return false;
     
     unsigned totalBytesRequired;
-    GC3Denum error = m_context->computeImageSizeInBytes(format, type, width, height, m_unpackAlignment, &totalBytesRequired, 0);
+    GC3Denum error = m_context->computeImageSizeInBytes(format, type, width, height, m_unpackAlignment, &totalBytesRequired, nullptr);
     if (error != GraphicsContext3D::NO_ERROR) {
         synthesizeGLError(error, functionName, "invalid texture dimensions");
         return false;
@@ -3963,6 +3974,14 @@ void WebGLRenderingContextBase::copyTexImage2D(GC3Denum target, GC3Dint level, G
     tex->setLevelInfo(target, level, internalFormat, width, height, GraphicsContext3D::UNSIGNED_BYTE);
 }
 
+static bool isRGBFormat(GC3Denum internalFormat)
+{
+    return internalFormat == GraphicsContext3D::RGB
+        || internalFormat == GraphicsContext3D::RGBA
+        || internalFormat == GraphicsContext3D::RGB8
+        || internalFormat == GraphicsContext3D::RGBA8;
+}
+
 ExceptionOr<void> WebGLRenderingContextBase::texImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Denum format, GC3Denum type, Optional<TexImageSource> source)
 {
     if (!source) {
@@ -4023,12 +4042,14 @@ ExceptionOr<void> WebGLRenderingContextBase::texImage2D(GC3Denum target, GC3Dint
         // ImageBuffer::copyToPlatformTexture implementations are fully functional.
         if (texture
             && (format == GraphicsContext3D::RGB || format == GraphicsContext3D::RGBA)
-            && type == GraphicsContext3D::UNSIGNED_BYTE
-            && (texture->getType(target, level) == GraphicsContext3D::UNSIGNED_BYTE || !texture->isValid(target, level))) {
-            ImageBuffer* buffer = canvas->buffer();
-            if (buffer && buffer->copyToPlatformTexture(*m_context.get(), target, texture->object(), internalformat, m_unpackPremultiplyAlpha, m_unpackFlipY)) {
-                texture->setLevelInfo(target, level, internalformat, canvas->width(), canvas->height(), type);
-                return { };
+            && type == GraphicsContext3D::UNSIGNED_BYTE) {
+            auto textureInternalFormat = texture->getInternalFormat(target, level);
+            if (isRGBFormat(textureInternalFormat) || !texture->isValid(target, level)) {
+                ImageBuffer* buffer = canvas->buffer();
+                if (buffer && buffer->copyToPlatformTexture(*m_context.get(), target, texture->object(), internalformat, m_unpackPremultiplyAlpha, m_unpackFlipY)) {
+                    texture->setLevelInfo(target, level, internalformat, canvas->width(), canvas->height(), type);
+                    return { };
+                }
             }
         }
 
@@ -4053,11 +4074,13 @@ ExceptionOr<void> WebGLRenderingContextBase::texImage2D(GC3Denum target, GC3Dint
         if (GraphicsContext3D::TEXTURE_2D == target && texture
             && (format == GraphicsContext3D::RGB || format == GraphicsContext3D::RGBA)
             && type == GraphicsContext3D::UNSIGNED_BYTE
-            && (texture->getType(target, level) == GraphicsContext3D::UNSIGNED_BYTE || !texture->isValid(target, level))
             && !level) {
-            if (video->copyVideoTextureToPlatformTexture(m_context.get(), texture->object(), target, level, internalformat, format, type, m_unpackPremultiplyAlpha, m_unpackFlipY)) {
-                texture->setLevelInfo(target, level, internalformat, video->videoWidth(), video->videoHeight(), type);
-                return { };
+            auto textureInternalFormat = texture->getInternalFormat(target, level);
+            if (isRGBFormat(textureInternalFormat) || !texture->isValid(target, level)) {
+                if (video->copyVideoTextureToPlatformTexture(m_context.get(), texture->object(), target, level, internalformat, format, type, m_unpackPremultiplyAlpha, m_unpackFlipY)) {
+                    texture->setLevelInfo(target, level, internalformat, video->videoWidth(), video->videoHeight(), type);
+                    return { };
+                }
             }
         }
 
index c4d5721..9eba972 100644 (file)
@@ -53,6 +53,7 @@ WebGLTexture::WebGLTexture(WebGLRenderingContextBase& ctx)
     , m_isCompressed(false)
     , m_isFloatType(false)
     , m_isHalfFloatType(false)
+    , m_isForWebGL1(ctx.isWebGL1())
 {
     setObject(ctx.graphicsContext3D()->createTexture());
 }
@@ -188,6 +189,7 @@ GC3Denum WebGLTexture::getInternalFormat(GC3Denum target, GC3Dint level) const
 
 GC3Denum WebGLTexture::getType(GC3Denum target, GC3Dint level) const
 {
+    ASSERT(m_isForWebGL1);
     const LevelInfo* info = getLevelInfo(target, level);
     if (!info)
         return 0;
@@ -250,10 +252,12 @@ bool WebGLTexture::needToUseBlackTexture(TextureExtensionFlag extensions) const
         return false;
     if (m_needToUseBlackTexture)
         return true;
-    if ((m_isFloatType && !(extensions & TextureExtensionFloatLinearEnabled)) || (m_isHalfFloatType && !(extensions & TextureExtensionHalfFloatLinearEnabled))) {
-        if (m_magFilter != GraphicsContext3D::NEAREST || (m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::NEAREST_MIPMAP_NEAREST))
-            return true;
-    }
+    if (m_magFilter == GraphicsContext3D::NEAREST && (m_minFilter == GraphicsContext3D::NEAREST || m_minFilter == GraphicsContext3D::NEAREST_MIPMAP_NEAREST))
+        return false;
+    if (m_isForWebGL1 && m_isHalfFloatType && !(extensions & TextureExtensionHalfFloatLinearEnabled))
+        return true;
+    if (m_isFloatType && !(extensions & TextureExtensionFloatLinearEnabled))
+        return true;
     return false;
 }
 
@@ -308,7 +312,7 @@ bool WebGLTexture::canGenerateMipmaps()
         const LevelInfo& info = m_info[ii][0];
         if (!info.valid
             || info.width != first.width || info.height != first.height
-            || info.internalFormat != first.internalFormat || info.type != first.type)
+            || info.internalFormat != first.internalFormat || (m_isForWebGL1 && info.type != first.type))
             return false;
     }
     return true;
@@ -334,6 +338,36 @@ GC3Dint WebGLTexture::computeLevelCount(GC3Dsizei width, GC3Dsizei height)
     return log + 1;
 }
 
+static bool internalFormatIsFloatType(GC3Denum internalFormat)
+{
+    switch (internalFormat) {
+    case GraphicsContext3D::R32F:
+    case GraphicsContext3D::RG32F:
+    case GraphicsContext3D::RGB32F:
+    case GraphicsContext3D::RGBA32F:
+    case GraphicsContext3D::DEPTH_COMPONENT32F:
+    case GraphicsContext3D::DEPTH32F_STENCIL8:
+        return true;
+    default:
+        return false;
+    }
+}
+
+static bool internalFormatIsHalfFloatType(GC3Denum internalFormat)
+{
+    switch (internalFormat) {
+    case GraphicsContext3D::R16F:
+    case GraphicsContext3D::RG16F:
+    case GraphicsContext3D::R11F_G11F_B10F:
+    case GraphicsContext3D::RGB9_E5:
+    case GraphicsContext3D::RGB16F:
+    case GraphicsContext3D::RGBA16F:
+        return true;
+    default:
+        return false;
+    }
+}
+
 void WebGLTexture::update()
 {
     m_isNPOT = false;
@@ -353,7 +387,7 @@ void WebGLTexture::update()
             const LevelInfo& info0 = m_info[ii][0];
             if (!info0.valid
                 || info0.width != first.width || info0.height != first.height
-                || info0.internalFormat != first.internalFormat || info0.type != first.type) {
+                || info0.internalFormat != first.internalFormat || (m_isForWebGL1 && info0.type != first.type)) {
                 m_isComplete = false;
                 break;
             }
@@ -365,7 +399,7 @@ void WebGLTexture::update()
                 const LevelInfo& info = m_info[ii][level];
                 if (!info.valid
                     || info.width != width || info.height != height
-                    || info.internalFormat != info0.internalFormat || info.type != info0.type) {
+                    || info.internalFormat != info0.internalFormat || (m_isForWebGL1 && info.type != info0.type)) {
                     m_isComplete = false;
                     break;
                 }
@@ -375,25 +409,37 @@ void WebGLTexture::update()
     }
 
     m_isFloatType = false;
-    if (m_isComplete)
-        m_isFloatType = m_info[0][0].type == GraphicsContext3D::FLOAT;
-    else {
-        for (size_t ii = 0; ii < m_info.size(); ++ii) {
-            if (m_info[ii][0].type == GraphicsContext3D::FLOAT) {
-                m_isFloatType = true;
-                break;
+    if (m_isForWebGL1) {
+        if (m_isComplete) {
+            if (m_isForWebGL1)
+                m_isFloatType = m_info[0][0].type == GraphicsContext3D::FLOAT;
+            else
+                m_isFloatType = internalFormatIsFloatType(m_info[0][0].internalFormat);
+        } else {
+            for (size_t ii = 0; ii < m_info.size(); ++ii) {
+                if ((m_isForWebGL1 && m_info[ii][0].type == GraphicsContext3D::FLOAT)
+                    || (!m_isForWebGL1 && internalFormatIsFloatType(m_info[ii][0].internalFormat))) {
+                    m_isFloatType = true;
+                    break;
+                }
             }
         }
     }
 
     m_isHalfFloatType = false;
-    if (m_isComplete)
-        m_isHalfFloatType = m_info[0][0].type == GraphicsContext3D::HALF_FLOAT_OES;
-    else {
-        for (size_t ii = 0; ii < m_info.size(); ++ii) {
-            if (m_info[ii][0].type == GraphicsContext3D::HALF_FLOAT_OES) {
-                m_isHalfFloatType = true;
-                break;
+    if (m_isForWebGL1) {
+        if (m_isComplete) {
+            if (m_isForWebGL1)
+                m_isHalfFloatType = internalFormatIsHalfFloatType(m_info[0][0].internalFormat);
+            else
+                m_isHalfFloatType = m_info[0][0].type == GraphicsContext3D::HALF_FLOAT_OES;
+        } else {
+            for (size_t ii = 0; ii < m_info.size(); ++ii) {
+                if ((m_isForWebGL1 && m_info[ii][0].type == GraphicsContext3D::HALF_FLOAT_OES)
+                    || (!m_isForWebGL1 && internalFormatIsHalfFloatType(m_info[ii][0].internalFormat))) {
+                    m_isHalfFloatType = true;
+                    break;
+                }
             }
         }
     }
index dd9864c..b266953 100644 (file)
@@ -78,6 +78,9 @@ public:
 
     static GC3Dint computeLevelCount(GC3Dsizei width, GC3Dsizei height);
 
+    bool immutable() const { return m_immutable; }
+    void setImmutable() { m_immutable = true; }
+
 private:
     WebGLTexture(WebGLRenderingContextBase&);
 
@@ -133,6 +136,8 @@ private:
     bool m_isCompressed;
     bool m_isFloatType;
     bool m_isHalfFloatType;
+    bool m_isForWebGL1;
+    bool m_immutable { false };
 };
 
 } // namespace WebCore
index 6e92761..789f063 100644 (file)
@@ -149,7 +149,7 @@ bool GraphicsContext3D::texImage2DResourceSafe(GC3Denum target, GC3Dint level, G
     std::unique_ptr<unsigned char[]> zero;
     if (width > 0 && height > 0) {
         unsigned int size;
-        GC3Denum error = computeImageSizeInBytes(format, type, width, height, unpackAlignment, &size, 0);
+        GC3Denum error = computeImageSizeInBytes(format, type, width, height, unpackAlignment, &size, nullptr);
         if (error != GraphicsContext3D::NO_ERROR) {
             synthesizeGLError(error);
             return false;
@@ -373,7 +373,7 @@ bool GraphicsContext3D::packImageData(Image* image, const void* pixels, GC3Denum
 
     unsigned packedSize;
     // Output data is tightly packed (alignment == 1).
-    if (computeImageSizeInBytes(format, type, width, height, 1, &packedSize, 0) != GraphicsContext3D::NO_ERROR)
+    if (computeImageSizeInBytes(format, type, width, height, 1, &packedSize, nullptr) != GraphicsContext3D::NO_ERROR)
         return false;
     data.resize(packedSize);
 
@@ -393,7 +393,7 @@ bool GraphicsContext3D::extractImageData(ImageData* imageData, GC3Denum format,
 
     unsigned int packedSize;
     // Output data is tightly packed (alignment == 1).
-    if (computeImageSizeInBytes(format, type, width, height, 1, &packedSize, 0) != GraphicsContext3D::NO_ERROR)
+    if (computeImageSizeInBytes(format, type, width, height, 1, &packedSize, nullptr) != GraphicsContext3D::NO_ERROR)
         return false;
     data.resize(packedSize);
 
index 6df891d..83a11e8 100644 (file)
@@ -984,6 +984,9 @@ public:
     GC3Dboolean unmapBuffer(GC3Denum target);
     void copyBufferSubData(GC3Denum readTarget, GC3Denum writeTarget, GC3Dintptr readOffset, GC3Dintptr writeOffset, GC3Dsizeiptr);
 
+    void texStorage2D(GC3Denum target, GC3Dsizei levels, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height);
+    void texStorage3D(GC3Denum target, GC3Dsizei levels, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dsizei depth);
+
     GC3Denum checkFramebufferStatus(GC3Denum target);
     void clear(GC3Dbitfield mask);
     void clearColor(GC3Dclampf red, GC3Dclampf green, GC3Dclampf blue, GC3Dclampf alpha);
index 3f2c0cb..a29ee2d 100644 (file)
@@ -215,6 +215,15 @@ void Extensions3DOpenGLCommon::initializeAvailableExtensions()
         ::glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
         for (GLint i = 0; i < numExtensions; ++i)
             m_availableExtensions.add(glGetStringi(GL_EXTENSIONS, i));
+
+        if (!m_availableExtensions.contains(ASCIILiteral("GL_ARB_texture_storage"))) {
+            GLint majorVersion;
+            glGetIntegerv(GL_MAJOR_VERSION, &majorVersion);
+            GLint minorVersion;
+            glGetIntegerv(GL_MINOR_VERSION, &minorVersion);
+            if (majorVersion > 4 || (majorVersion == 4 && minorVersion >= 2))
+                m_availableExtensions.add(ASCIILiteral("GL_ARB_texture_storage"));
+        }
     } else
 #endif
     {
index 0b2ad05..921f3c0 100644 (file)
@@ -70,6 +70,7 @@
 #define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED
 #include <OpenGL/gl.h>
 #include <OpenGL/gl3.h>
+#include <OpenGL/gl3ext.h>
 #undef GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED
 #elif PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(WIN)
 #include "OpenGLShims.h"
@@ -564,6 +565,18 @@ void GraphicsContext3D::copyBufferSubData(GC3Denum readTarget, GC3Denum writeTar
     makeContextCurrent();
     ::glCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size);
 }
+
+void GraphicsContext3D::texStorage2D(GC3Denum target, GC3Dsizei levels, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height)
+{
+    makeContextCurrent();
+    ::glTexStorage2D(target, levels, internalformat, width, height);
+}
+
+void GraphicsContext3D::texStorage3D(GC3Denum target, GC3Dsizei levels, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dsizei depth)
+{
+    makeContextCurrent();
+    ::glTexStorage3D(target, levels, internalformat, width, height, depth);
+}
 #endif
 
 GC3Denum GraphicsContext3D::checkFramebufferStatus(GC3Denum target)