Преглед изворни кода

Merge 3fca836e15c61275b9d4bdd0123177ac168f9054 into 3eefa464eef2278f3f8c311aa5905bf6c0bb011e

Luke Wilde пре 6 месеци
родитељ
комит
18c6840c0f

+ 5 - 1
Libraries/LibWeb/WebGL/WebGL2RenderingContext.cpp

@@ -69,6 +69,7 @@ void WebGL2RenderingContext::initialize(JS::Realm& realm)
 void WebGL2RenderingContext::visit_edges(Cell::Visitor& visitor)
 {
     Base::visit_edges(visitor);
+    WebGL2RenderingContextImpl::visit_edges(visitor);
     visitor.visit(m_canvas_element);
 }
 
@@ -128,7 +129,10 @@ Optional<WebGLContextAttributes> WebGL2RenderingContext::get_context_attributes(
 
 void WebGL2RenderingContext::set_size(Gfx::IntSize const& size)
 {
-    context().set_size(size);
+    Gfx::IntSize final_size;
+    final_size.set_width(max(size.width(), 1));
+    final_size.set_height(max(size.height(), 1));
+    context().set_size(final_size);
 }
 
 void WebGL2RenderingContext::reset_to_default_state()

+ 15 - 15
Libraries/LibWeb/WebGL/WebGL2RenderingContextBase.idl

@@ -298,7 +298,7 @@ interface mixin WebGL2RenderingContextBase {
     // Framebuffer objects
     undefined blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
     [FIXME] undefined framebufferTextureLayer(GLenum target, GLenum attachment, WebGLTexture? texture, GLint level, GLint layer);
-    [FIXME] undefined invalidateFramebuffer(GLenum target, sequence<GLenum> attachments);
+    undefined invalidateFramebuffer(GLenum target, sequence<GLenum> attachments);
     [FIXME] undefined invalidateSubFramebuffer(GLenum target, sequence<GLenum> attachments, GLint x, GLint y, GLsizei width, GLsizei height);
     undefined readBuffer(GLenum src);
 
@@ -360,22 +360,22 @@ interface mixin WebGL2RenderingContextBase {
     [FIXME] undefined vertexAttribI4iv(GLuint index, Int32List values);
     [FIXME] undefined vertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w);
     [FIXME] undefined vertexAttribI4uiv(GLuint index, Uint32List values);
-    [FIXME] undefined vertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset);
+    undefined vertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset);
 
     // Writing to the drawing buffer
-    [FIXME] undefined vertexAttribDivisor(GLuint index, GLuint divisor);
+    undefined vertexAttribDivisor(GLuint index, GLuint divisor);
     [FIXME] undefined drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instanceCount);
-    [FIXME] undefined drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, GLintptr offset, GLsizei instanceCount);
+    undefined drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, GLintptr offset, GLsizei instanceCount);
     [FIXME] undefined drawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, GLintptr offset);
 
     // Multiple Render Targets
     undefined drawBuffers(sequence<GLenum> buffers);
 
-    [FIXME] undefined clearBufferfv(GLenum buffer, GLint drawbuffer, Float32List values, optional unsigned long long srcOffset = 0);
-    [FIXME] undefined clearBufferiv(GLenum buffer, GLint drawbuffer, Int32List values,  optional unsigned long long srcOffset = 0);
-    [FIXME] undefined clearBufferuiv(GLenum buffer, GLint drawbuffer, Uint32List values, optional unsigned long long srcOffset = 0);
+    undefined clearBufferfv(GLenum buffer, GLint drawbuffer, Float32List values, optional unsigned long long srcOffset = 0);
+    undefined clearBufferiv(GLenum buffer, GLint drawbuffer, Int32List values, optional unsigned long long srcOffset = 0);
+    undefined clearBufferuiv(GLenum buffer, GLint drawbuffer, Uint32List values, optional unsigned long long srcOffset = 0);
 
-    [FIXME] undefined clearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
+    undefined clearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
 
     // Query Objects
     [FIXME] WebGLQuery createQuery();
@@ -388,11 +388,11 @@ interface mixin WebGL2RenderingContextBase {
 
     // Sampler Objects
     WebGLSampler createSampler();
-    [FIXME] undefined deleteSampler(WebGLSampler? sampler);
+    undefined deleteSampler(WebGLSampler? sampler);
     [FIXME] GLboolean isSampler(WebGLSampler? sampler); // [WebGLHandlesContextLoss]
     undefined bindSampler(GLuint unit, WebGLSampler? sampler);
-    [FIXME] undefined samplerParameteri(WebGLSampler sampler, GLenum pname, GLint param);
-    [FIXME] undefined samplerParameterf(WebGLSampler sampler, GLenum pname, GLfloat param);
+    undefined samplerParameteri(WebGLSampler sampler, GLenum pname, GLint param);
+    undefined samplerParameterf(WebGLSampler sampler, GLenum pname, GLfloat param);
     [FIXME] any getSamplerParameter(WebGLSampler sampler, GLenum pname);
 
     // Sync objects
@@ -421,10 +421,10 @@ interface mixin WebGL2RenderingContextBase {
     [FIXME] any getIndexedParameter(GLenum target, GLuint index);
     [FIXME] sequence<GLuint>? getUniformIndices(WebGLProgram program, sequence<DOMString> uniformNames);
     [FIXME] any getActiveUniforms(WebGLProgram program, sequence<GLuint> uniformIndices, GLenum pname);
-    [FIXME] GLuint getUniformBlockIndex(WebGLProgram program, DOMString uniformBlockName);
-    [FIXME] any getActiveUniformBlockParameter(WebGLProgram program, GLuint uniformBlockIndex, GLenum pname);
-    [FIXME] DOMString? getActiveUniformBlockName(WebGLProgram program, GLuint uniformBlockIndex);
-    [FIXME] undefined uniformBlockBinding(WebGLProgram program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);
+    GLuint getUniformBlockIndex(WebGLProgram program, DOMString uniformBlockName);
+    any getActiveUniformBlockParameter(WebGLProgram program, GLuint uniformBlockIndex, GLenum pname);
+    DOMString? getActiveUniformBlockName(WebGLProgram program, GLuint uniformBlockIndex);
+    undefined uniformBlockBinding(WebGLProgram program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);
 
     // Vertex Array Objects
     WebGLVertexArrayObject createVertexArray();

+ 5 - 5
Libraries/LibWeb/WebGL/WebGL2RenderingContextOverloads.idl

@@ -10,8 +10,8 @@ interface mixin WebGL2RenderingContextOverloads {
     undefined bufferData(GLenum target, BufferSource? srcData, GLenum usage);
     undefined bufferSubData(GLenum target, GLintptr dstByteOffset, BufferSource srcData);
     // WebGL2:
-    [FIXME] undefined bufferData(GLenum target, [AllowShared] ArrayBufferView srcData, GLenum usage, unsigned long long srcOffset, optional GLuint length = 0);
-    [FIXME] undefined bufferSubData(GLenum target, GLintptr dstByteOffset, [AllowShared] ArrayBufferView srcData, unsigned long long srcOffset, optional GLuint length = 0);
+    undefined bufferData(GLenum target, [AllowShared] ArrayBufferView srcData, GLenum usage, unsigned long long srcOffset, optional GLuint length = 0);
+    undefined bufferSubData(GLenum target, GLintptr dstByteOffset, [AllowShared] ArrayBufferView srcData, unsigned long long srcOffset, optional GLuint length = 0);
 
     // WebGL1 legacy entrypoints:
     undefined texImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, [AllowShared] ArrayBufferView? pixels);
@@ -22,20 +22,20 @@ interface mixin WebGL2RenderingContextOverloads {
     undefined texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, [AllowShared] ArrayBufferView? pixels);
 
     // May throw DOMException
-    [FIXME] undefined texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLenum format, GLenum type, TexImageSource source);
+    undefined texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLenum format, GLenum type, TexImageSource source);
 
     // WebGL2 entrypoints:
     [FIXME] undefined texImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLintptr pboOffset);
 
     // May throw DOMException
-    [FIXME] undefined texImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, TexImageSource source);
+    undefined texImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, TexImageSource source);
 
     undefined texImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, [AllowShared] ArrayBufferView srcData, unsigned long long srcOffset);
 
     [FIXME] undefined texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLintptr pboOffset);
 
     // May throw DOMException
-    [FIXME] undefined texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, TexImageSource source);
+    undefined texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, TexImageSource source);
     undefined texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, [AllowShared] ArrayBufferView srcData, unsigned long long srcOffset);
 
     [FIXME] undefined compressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, GLintptr offset);

+ 5 - 1
Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp

@@ -86,6 +86,7 @@ void WebGLRenderingContext::initialize(JS::Realm& realm)
 void WebGLRenderingContext::visit_edges(Cell::Visitor& visitor)
 {
     Base::visit_edges(visitor);
+    WebGLRenderingContextImpl::visit_edges(visitor);
     visitor.visit(m_canvas_element);
 }
 
@@ -145,7 +146,10 @@ Optional<WebGLContextAttributes> WebGLRenderingContext::get_context_attributes()
 
 void WebGLRenderingContext::set_size(Gfx::IntSize const& size)
 {
-    context().set_size(size);
+    Gfx::IntSize final_size;
+    final_size.set_width(max(size.width(), 1));
+    final_size.set_height(max(size.height(), 1));
+    context().set_size(final_size);
 }
 
 void WebGLRenderingContext::reset_to_default_state()

+ 1 - 0
Libraries/LibWeb/WebGL/WebGLRenderingContextBase.h

@@ -13,6 +13,7 @@ namespace Web::WebGL {
 class WebGLRenderingContextBase {
 public:
     virtual GC::Cell const* gc_cell() const = 0;
+    virtual void visit_edges(JS::Cell::Visitor&) = 0;
 };
 
 }

+ 1 - 1
Libraries/LibWeb/WebGL/WebGLRenderingContextBase.idl

@@ -81,7 +81,7 @@ interface mixin WebGLRenderingContextBase {
     undefined deleteBuffer(WebGLBuffer? buffer);
     undefined deleteFramebuffer(WebGLFramebuffer? framebuffer);
     undefined deleteProgram(WebGLProgram? program);
-    [FIXME] undefined deleteRenderbuffer(WebGLRenderbuffer? renderbuffer);
+    undefined deleteRenderbuffer(WebGLRenderbuffer? renderbuffer);
     undefined deleteShader(WebGLShader? shader);
     undefined deleteTexture(WebGLTexture? texture);
 

+ 1 - 1
Libraries/LibWeb/WebGL/WebGLRenderingContextOverloads.idl

@@ -28,7 +28,7 @@ interface mixin WebGLRenderingContextOverloads {
     undefined texImage2D(GLenum target, GLint level, GLint internalformat, GLenum format, GLenum type, TexImageSource source); // May throw DOMException
 
     undefined texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, [AllowShared] ArrayBufferView? pixels);
-    [FIXME] undefined texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLenum format, GLenum type, TexImageSource source); // May throw DOMException
+    undefined texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLenum format, GLenum type, TexImageSource source); // May throw DOMException
 
     undefined uniform1fv(WebGLUniformLocation? location, Float32List v);
     undefined uniform2fv(WebGLUniformLocation? location, Float32List v);

+ 15 - 0
Libraries/LibWeb/WebIDL/Buffers.cpp

@@ -29,6 +29,21 @@ u32 BufferableObjectBase::byte_length() const
         [](GC::Ref<JS::ArrayBuffer> array_buffer) { return static_cast<u32>(array_buffer->byte_length()); });
 }
 
+u32 BufferableObjectBase::element_size() const
+{
+    return m_bufferable_object.visit(
+        [](GC::Ref<JS::TypedArrayBase> typed_array) -> u32 {
+            auto typed_array_record = JS::make_typed_array_with_buffer_witness_record(typed_array, JS::ArrayBuffer::Order::SeqCst);
+            return typed_array_record.object->element_size();
+        },
+        [](GC::Ref<JS::DataView>) -> u32 {
+            return 1;
+        },
+        [](GC::Ref<JS::ArrayBuffer>) -> u32 {
+            return 1;
+        });
+}
+
 GC::Ref<JS::Object> BufferableObjectBase::raw_object()
 {
     return m_bufferable_object.visit([](auto const& obj) -> GC::Ref<JS::Object> { return obj; });

+ 1 - 0
Libraries/LibWeb/WebIDL/Buffers.h

@@ -27,6 +27,7 @@ public:
     virtual ~BufferableObjectBase() override = default;
 
     u32 byte_length() const;
+    u32 element_size() const;
 
     GC::Ref<JS::Object> raw_object();
     GC::Ref<JS::Object const> raw_object() const { return const_cast<BufferableObjectBase&>(*this).raw_object(); }

+ 2 - 2
Libraries/LibWeb/WebIDL/OverloadResolution.cpp

@@ -210,7 +210,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
         //    then remove from S all other entries.
         else if (value.is_object() && is<JS::DataView>(value.as_object())
             && has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) {
-                   if (type.is_plain() && (type.name() == "DataView" || type.name() == "BufferSource"))
+                   if (type.is_plain() && (type.name() == "DataView" || type.name() == "BufferSource" || type.name() == "ArrayBufferView"))
                        return true;
                    if (type.is_object())
                        return true;
@@ -228,7 +228,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
         //    then remove from S all other entries.
         else if (value.is_object() && value.as_object().is_typed_array()
             && has_overload_with_argument_type_or_subtype_matching(overloads, i, [&](IDL::Type const& type) {
-                   if (type.is_plain() && (type.name() == static_cast<JS::TypedArrayBase const&>(value.as_object()).element_name() || type.name() == "BufferSource"))
+                   if (type.is_plain() && (type.name() == static_cast<JS::TypedArrayBase const&>(value.as_object()).element_name() || type.name() == "BufferSource" || type.name() == "ArrayBufferView"))
                        return true;
                    if (type.is_object())
                        return true;

+ 507 - 20
Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWebGLRenderingContext.cpp

@@ -29,7 +29,16 @@ static bool is_webgl_object_type(StringView type_name)
 
 static bool gl_function_modifies_framebuffer(StringView function_name)
 {
-    return function_name == "clearColor"sv || function_name == "drawArrays"sv || function_name == "drawElements"sv;
+    return function_name == "clear"sv
+        || function_name == "clearBufferfv"sv
+        || function_name == "clearBufferiv"sv
+        || function_name == "clearBufferuiv"sv
+        || function_name == "clearBufferfi"sv
+        || function_name == "drawArrays"sv
+        || function_name == "drawElements"sv
+        || function_name == "drawElementsInstanced"sv
+        || function_name == "blitFramebuffer"sv
+        || function_name == "invalidateFramebuffer"sv;
 }
 
 static ByteString to_cpp_type(const IDL::Type& type, const IDL::Interface& interface)
@@ -180,6 +189,13 @@ static void generate_get_parameter(SourceGenerator& generator, int webgl_version
         { "MAX_VERTEX_UNIFORM_BLOCKS"sv, { "GLint"sv }, 2 },
         { "MAX_FRAGMENT_INPUT_COMPONENTS"sv, { "GLint"sv }, 2 },
         { "MAX_COMBINED_UNIFORM_BLOCKS"sv, { "GLint"sv }, 2 },
+        { "UNIFORM_BUFFER_BINDING"sv, { "WebGLBuffer"sv }, 2 },
+        { "TEXTURE_BINDING_2D_ARRAY"sv, { "WebGLTexture"sv }, 2 },
+        { "COPY_READ_BUFFER_BINDING"sv, { "WebGLBuffer"sv }, 2 },
+        { "COPY_WRITE_BUFFER_BINDING"sv, { "WebGLBuffer"sv }, 2 },
+        { "MAX_ELEMENT_INDEX"sv, { "GLint64"sv }, 2 },
+        { "MAX_FRAGMENT_UNIFORM_BLOCKS"sv, { "GLint"sv }, 2 },
+        { "MAX_VARYING_COMPONENTS"sv, { "GLint"sv }, 2 },
     };
 
     auto is_primitive_type = [](StringView type) {
@@ -236,12 +252,11 @@ static void generate_get_parameter(SourceGenerator& generator, int webgl_version
         return JS::@type_name@::create(m_realm, @element_count@, array_buffer);
 )~~~");
         } else if (type_name == "WebGLProgram"sv || type_name == "WebGLBuffer"sv || type_name == "WebGLTexture"sv || type_name == "WebGLFramebuffer"sv || type_name == "WebGLRenderbuffer"sv) {
+            impl_generator.set("stored_name", name_and_type.name.to_lowercase_string());
             impl_generator.append(R"~~~(
-        GLint result;
-        glGetIntegerv(GL_@parameter_name@, &result);
-        if (!result)
+        if (!m_@stored_name@)
             return JS::js_null();
-        return @type_name@::create(m_realm, *this, result);
+        return JS::Value(m_@stored_name@);
 )~~~");
         } else {
             VERIFY_NOT_REACHED();
@@ -337,6 +352,40 @@ static void generate_webgl_object_handle_unwrap(SourceGenerator& generator, Stri
     generator.append(unwrap_generator.as_string_view());
 }
 
+static void generate_get_active_uniform_block_parameter(SourceGenerator& generator)
+{
+    generate_webgl_object_handle_unwrap(generator, "program"sv, "JS::js_null()"sv);
+    generator.append(R"~~~(
+    switch (pname) {
+    case GL_UNIFORM_BLOCK_BINDING:
+    case GL_UNIFORM_BLOCK_DATA_SIZE:
+    case GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS: {
+        GLint result = 0;
+        glGetActiveUniformBlockiv(program_handle, uniform_block_index, pname, &result);
+        return JS::Value(result);
+    }
+    case GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: {
+        GLint num_active_uniforms = 0;
+        glGetActiveUniformBlockiv(program_handle, uniform_block_index, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &num_active_uniforms);
+        auto active_uniform_indices_buffer = MUST(ByteBuffer::create_zeroed(num_active_uniforms * sizeof(GLint)));
+        glGetActiveUniformBlockiv(program_handle, uniform_block_index, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, reinterpret_cast<GLint*>(active_uniform_indices_buffer.data()));
+        auto array_buffer = JS::ArrayBuffer::create(m_realm, move(active_uniform_indices_buffer));
+        return JS::Uint32Array::create(m_realm, num_active_uniforms, array_buffer);
+    }
+    case GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
+    case GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER: {
+        GLint result = 0;
+        glGetActiveUniformBlockiv(program_handle, uniform_block_index, pname, &result);
+        return JS::Value(result == GL_TRUE);
+    }
+    default:
+        dbgln("Unknown WebGL active uniform block parameter name: {:x}", pname);
+        set_error(GL_INVALID_ENUM);
+        return JS::js_null();
+    }
+)~~~");
+}
+
 ErrorOr<int> serenity_main(Main::Arguments arguments)
 {
     StringView generated_header_path;
@@ -576,6 +625,14 @@ public:
             continue;
         }
 
+        if (function.name == "invalidateFramebuffer"sv) {
+            function_impl_generator.append(R"~~~(
+    glInvalidateFramebuffer(target, attachments.size(), attachments.data());
+    needs_to_present();
+)~~~");
+            continue;
+        }
+
         if (function.name == "createVertexArray"sv) {
             function_impl_generator.append(R"~~~(
     GLuint handle = 0;
@@ -680,7 +737,7 @@ public:
             continue;
         }
 
-        if (function.name == "texImage2D"sv && function.overload_index == 1) {
+        if (function.name == "texImage2D"sv && (function.overload_index == 1 || (webgl_version == 2 && function.overload_index == 2))) {
             // FIXME: If this function is called with an ImageData whose data attribute has been neutered,
             //        an INVALID_VALUE error is generated.
             // FIXME: If this function is called with an ImageBitmap that has been neutered, an INVALID_VALUE
@@ -716,14 +773,23 @@ public:
         return;
 
     void const* pixels_ptr = bitmap->bitmap()->begin();
+)~~~");
+
+            if (webgl_version == 2 && function.overload_index == 2) {
+                function_impl_generator.append(R"~~~(
+    glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels_ptr);
+)~~~");
+            } else {
+                function_impl_generator.append(R"~~~(
     int width = bitmap->width();
     int height = bitmap->height();
     glTexImage2D(target, level, internalformat, width, height, 0, format, type, pixels_ptr);
 )~~~");
+            }
             continue;
         }
 
-        if (webgl_version == 2 && function.name == "texImage2D"sv && function.overload_index == 2) {
+        if (webgl_version == 2 && function.name == "texImage2D"sv && function.overload_index == 3) {
             function_impl_generator.append(R"~~~(
     void const* pixels_ptr = nullptr;
     if (src_data) {
@@ -749,7 +815,58 @@ public:
             continue;
         }
 
-        if (webgl_version == 2 && function.name == "texSubImage2D"sv && function.overload_index == 1) {
+        if (function.name == "texSubImage2D" && (function.overload_index == 1 || (webgl_version == 2 && function.overload_index == 2))) {
+            // FIXME: If this function is called with an ImageData whose data attribute has been neutered,
+            //        an INVALID_VALUE error is generated.
+            // FIXME: If this function is called with an ImageBitmap that has been neutered, an INVALID_VALUE
+            //        error is generated.
+            // FIXME: If this function is called with an HTMLImageElement or HTMLVideoElement whose origin
+            //        differs from the origin of the containing Document, or with an HTMLCanvasElement,
+            //        ImageBitmap or OffscreenCanvas whose bitmap's origin-clean flag is set to false,
+            //        a SECURITY_ERR exception must be thrown. See Origin Restrictions.
+            // FIXME: If source is null then an INVALID_VALUE error is generated.
+            function_impl_generator.append(R"~~~(
+    auto bitmap = source.visit(
+        [](GC::Root<HTMLImageElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> {
+            return source->immutable_bitmap();
+        },
+        [](GC::Root<HTMLCanvasElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> {
+            auto surface = source->surface();
+            if (!surface)
+                return {};
+            auto bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA8888, Gfx::AlphaType::Premultiplied, surface->size()));
+            surface->read_into_bitmap(*bitmap);
+            return Gfx::ImmutableBitmap::create(*bitmap);
+        },
+        [](GC::Root<HTMLVideoElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> {
+            return Gfx::ImmutableBitmap::create(*source->bitmap());
+        },
+        [](GC::Root<ImageBitmap> const& source) -> RefPtr<Gfx::ImmutableBitmap> {
+            return Gfx::ImmutableBitmap::create(*source->bitmap());
+        },
+        [](GC::Root<ImageData> const& source) -> RefPtr<Gfx::ImmutableBitmap> {
+            return Gfx::ImmutableBitmap::create(source->bitmap());
+        });
+    if (!bitmap)
+        return;
+
+    void const* pixels_ptr = bitmap->bitmap()->begin();
+)~~~");
+            if (webgl_version == 2 && function.overload_index == 2) {
+                function_impl_generator.append(R"~~~(
+    glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels_ptr);
+)~~~");
+            } else {
+                function_impl_generator.append(R"~~~(
+    int width = bitmap->width();
+    int height = bitmap->height();
+    glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels_ptr);
+)~~~");
+            }
+            continue;
+        }
+
+        if (webgl_version == 2 && function.name == "texSubImage2D"sv && function.overload_index == 3) {
             function_impl_generator.append(R"~~~(
     void const* pixels_ptr = nullptr;
     if (src_data) {
@@ -795,6 +912,11 @@ public:
             continue;
         }
 
+        if (function.name == "getActiveUniformBlockParameter"sv) {
+            generate_get_active_uniform_block_parameter(function_impl_generator);
+            continue;
+        }
+
         if (function.name == "bufferData"sv && function.overload_index == 0) {
             function_impl_generator.append(R"~~~(
     glBufferData(target, size, 0, usage);
@@ -802,6 +924,62 @@ public:
             continue;
         }
 
+        if (webgl_version == 2 && function.name == "bufferData"sv && function.overload_index == 2) {
+            function_impl_generator.append(R"~~~(
+    VERIFY(src_data);
+    auto const& viewed_array_buffer = src_data->viewed_array_buffer();
+    auto const& byte_buffer = viewed_array_buffer->buffer();
+    auto src_data_length = src_data->byte_length();
+    auto src_data_element_size = src_data->element_size();
+    u8 const* buffer_ptr = byte_buffer.data();
+
+    u64 copy_length = length == 0 ? src_data_length - src_offset : length;
+    copy_length *= src_data_element_size;
+
+    if (src_offset > src_data_length) {
+        set_error(GL_INVALID_VALUE);
+        return;
+    }
+
+    if (src_offset + copy_length > src_data_length) {
+        set_error(GL_INVALID_VALUE);
+        return;
+    }
+
+    buffer_ptr += src_offset * src_data_element_size;
+    glBufferData(target, copy_length, buffer_ptr, usage);
+)~~~");
+            continue;
+        }
+
+        if (webgl_version == 2 && function.name == "bufferSubData"sv && function.overload_index == 1) {
+            function_impl_generator.append(R"~~~(
+    VERIFY(src_data);
+    auto const& viewed_array_buffer = src_data->viewed_array_buffer();
+    auto const& byte_buffer = viewed_array_buffer->buffer();
+    auto src_data_length = src_data->byte_length();
+    auto src_data_element_size = src_data->element_size();
+    u8 const* buffer_ptr = byte_buffer.data();
+
+    u64 copy_length = length == 0 ? src_data_length - src_offset : length;
+    copy_length *= src_data_element_size;
+
+    if (src_offset > src_data_length) {
+        set_error(GL_INVALID_VALUE);
+        return;
+    }
+
+    if (src_offset + copy_length > src_data_length) {
+        set_error(GL_INVALID_VALUE);
+        return;
+    }
+
+    buffer_ptr += src_offset * src_data_element_size;
+    glBufferSubData(target, dst_byte_offset, copy_length, buffer_ptr);
+)~~~");
+            continue;
+        }
+
         if (function.name == "readPixels"sv) {
             function_impl_generator.append(R"~~~(
     if (!pixels) {
@@ -832,6 +1010,14 @@ public:
             continue;
         }
 
+        if (function.name == "drawElementsInstanced"sv) {
+            function_impl_generator.append(R"~~~(
+    glDrawElementsInstanced(mode, count, type, reinterpret_cast<void*>(offset), instance_count);
+    needs_to_present();
+)~~~");
+            continue;
+        }
+
         if (function.name == "drawBuffers"sv) {
             function_impl_generator.append(R"~~~(
     glDrawBuffers(buffers.size(), buffers.data());
@@ -867,14 +1053,16 @@ public:
 
             if (webgl_version == 2) {
                 function_impl_generator.append(R"~~~(
+    if (src_offset + src_length > (count * matrix_size)) {
+        set_error(GL_INVALID_VALUE);
+        return;
+    }
+
     raw_data += src_offset;
     if (src_length == 0) {
         count -= src_offset;
-    }
-
-    if (src_offset + src_length <= count) {
-        set_error(GL_INVALID_VALUE);
-        return;
+    } else {
+        count = src_length;
     }
 )~~~");
             }
@@ -919,14 +1107,16 @@ public:
 
             if (webgl_version == 2) {
                 function_impl_generator.append(R"~~~(
+    if (src_offset + src_length > count) {
+        set_error(GL_INVALID_VALUE);
+        return;
+    }
+
     data += src_offset;
     if (src_length == 0) {
         count -= src_offset;
-    }
-
-    if (src_offset + src_length <= count) {
-        set_error(GL_INVALID_VALUE);
-        return;
+    } else {
+        count = src_length;
     }
 )~~~");
             }
@@ -955,6 +1145,13 @@ public:
             continue;
         }
 
+        if (function.name == "vertexAttribIPointer"sv) {
+            function_impl_generator.append(R"~~~(
+    glVertexAttribIPointer(index, size, type, stride, reinterpret_cast<void*>(offset));
+)~~~");
+            continue;
+        }
+
         if (function.name == "getParameter"sv) {
             generate_get_parameter(function_impl_generator, webgl_version);
             continue;
@@ -1040,6 +1237,21 @@ public:
             continue;
         }
 
+        if (function.name == "getActiveUniformBlockName"sv) {
+            generate_webgl_object_handle_unwrap(function_impl_generator, "program"sv, "OptionalNone {}"sv);
+            function_impl_generator.append(R"~~~(
+    GLint uniform_block_name_length = 0;
+    glGetActiveUniformBlockiv(program_handle, uniform_block_index, GL_UNIFORM_BLOCK_NAME_LENGTH, &uniform_block_name_length);
+    Vector<GLchar> uniform_block_name;
+    uniform_block_name.resize(uniform_block_name_length);
+    if (!uniform_block_name_length)
+        return String {};
+    glGetActiveUniformBlockName(program_handle, uniform_block_index, uniform_block_name_length, nullptr, uniform_block_name.data());
+    return String::from_utf8_without_validation(ReadonlyBytes { uniform_block_name.data(), static_cast<size_t>(uniform_block_name_length - 1) });
+)~~~");
+            continue;
+        }
+
         if (function.name == "deleteBuffer"sv) {
             generate_webgl_object_handle_unwrap(function_impl_generator, "buffer"sv, ""sv);
             function_impl_generator.append(R"~~~(
@@ -1056,6 +1268,14 @@ public:
             continue;
         }
 
+        if (function.name == "deleteRenderbuffer"sv) {
+            generate_webgl_object_handle_unwrap(function_impl_generator, "renderbuffer"sv, ""sv);
+            function_impl_generator.append(R"~~~(
+    glDeleteRenderbuffers(1, &renderbuffer_handle);
+)~~~");
+            continue;
+        }
+
         if (function.name == "deleteTexture"sv) {
             generate_webgl_object_handle_unwrap(function_impl_generator, "texture"sv, ""sv);
             function_impl_generator.append(R"~~~(
@@ -1072,6 +1292,225 @@ public:
             continue;
         }
 
+        if (function.name == "deleteSampler"sv) {
+            generate_webgl_object_handle_unwrap(function_impl_generator, "sampler"sv, ""sv);
+            function_impl_generator.append(R"~~~(
+    glDeleteSamplers(1, &sampler_handle);
+)~~~");
+            continue;
+        }
+
+        if (function.name == "bindBuffer"sv) {
+            // FIXME: Implement Buffer Object Binding restrictions.
+            generate_webgl_object_handle_unwrap(function_impl_generator, "buffer"sv, ""sv);
+            function_impl_generator.append(R"~~~(
+    switch (target) {
+    case GL_ELEMENT_ARRAY_BUFFER:
+        m_element_array_buffer_binding = buffer;
+        break;
+    case GL_ARRAY_BUFFER:
+        m_array_buffer_binding = buffer;
+        break;
+)~~~");
+
+            if (webgl_version == 2) {
+                function_impl_generator.append(R"~~~(
+    case GL_UNIFORM_BUFFER:
+        m_uniform_buffer_binding = buffer;
+        break;
+    case GL_COPY_READ_BUFFER:
+        m_copy_read_buffer_binding = buffer;
+        break;
+    case GL_COPY_WRITE_BUFFER:
+        m_copy_write_buffer_binding = buffer;
+        break;
+)~~~");
+            }
+
+            function_impl_generator.append(R"~~~(
+    default:
+        dbgln("Unknown WebGL buffer object binding target for storing current binding: 0x{:04x}", target);
+        break;
+    }
+
+    glBindBuffer(target, buffer_handle);
+)~~~");
+            continue;
+        }
+
+        if (function.name == "useProgram"sv) {
+            generate_webgl_object_handle_unwrap(function_impl_generator, "program"sv, ""sv);
+            function_impl_generator.append(R"~~~(
+    glUseProgram(program_handle);
+    m_current_program = program;
+)~~~");
+            continue;
+        }
+
+        if (function.name == "bindFramebuffer"sv) {
+            generate_webgl_object_handle_unwrap(function_impl_generator, "framebuffer"sv, ""sv);
+            function_impl_generator.append(R"~~~(
+    glBindFramebuffer(target, framebuffer_handle);
+    m_framebuffer_binding = framebuffer;
+)~~~");
+            continue;
+        }
+
+        if (function.name == "bindRenderbuffer"sv) {
+            generate_webgl_object_handle_unwrap(function_impl_generator, "renderbuffer"sv, ""sv);
+            function_impl_generator.append(R"~~~(
+    glBindRenderbuffer(target, renderbuffer_handle);
+    m_renderbuffer_binding = renderbuffer;
+)~~~");
+            continue;
+        }
+
+        if (function.name == "bindTexture"sv) {
+            generate_webgl_object_handle_unwrap(function_impl_generator, "texture"sv, ""sv);
+            function_impl_generator.append(R"~~~(
+    switch (target) {
+    case GL_TEXTURE_2D:
+        m_texture_binding_2d = texture;
+        break;
+    case GL_TEXTURE_CUBE_MAP:
+        m_texture_binding_cube_map = texture;
+        break;
+)~~~");
+
+            if (webgl_version == 2) {
+                function_impl_generator.append(R"~~~(
+    case GL_TEXTURE_2D_ARRAY:
+        m_texture_binding_2d_array = texture;
+        break;
+    case GL_TEXTURE_3D:
+        m_texture_binding_3d = texture;
+        break;
+)~~~");
+            }
+
+            function_impl_generator.append(R"~~~(
+    default:
+        dbgln("Unknown WebGL texture target for storing current binding: 0x{:04x}", target);
+        break;
+    }
+    glBindTexture(target, texture_handle);
+)~~~");
+            continue;
+        }
+
+        if (function.name == "renderbufferStorage"sv) {
+            // To be backward compatible with WebGL 1, also accepts internal format DEPTH_STENCIL, which should be
+            // mapped to DEPTH24_STENCIL8 by implementations.
+            if (webgl_version == 2) {
+                function_impl_generator.append(R"~~~(
+    if (internalformat == GL_DEPTH_STENCIL)
+        internalformat = GL_DEPTH24_STENCIL8;
+)~~~");
+            }
+
+            function_impl_generator.append(R"~~~(
+    glRenderbufferStorage(target, internalformat, width, height);
+)~~~");
+            continue;
+        }
+
+        if (function.name.starts_with("samplerParameter"sv)) {
+            generate_webgl_object_handle_unwrap(function_impl_generator, "sampler"sv, ""sv);
+            function_impl_generator.set("param_type", function.name.substring_view(16, 1));
+            // pname is given in the following table:
+            // - TEXTURE_COMPARE_FUNC
+            // - TEXTURE_COMPARE_MODE
+            // - TEXTURE_MAG_FILTER
+            // - TEXTURE_MAX_LOD
+            // - TEXTURE_MIN_FILTER
+            // - TEXTURE_MIN_LOD
+            // - TEXTURE_WRAP_R
+            // - TEXTURE_WRAP_S
+            // - TEXTURE_WRAP_T
+            // If pname is not in the table above, generates an INVALID_ENUM error.
+            // NOTE: We have to do this ourselves, as OpenGL does not.
+            function_impl_generator.append(R"~~~(
+    switch (pname) {
+    case GL_TEXTURE_COMPARE_FUNC:
+    case GL_TEXTURE_COMPARE_MODE:
+    case GL_TEXTURE_MAG_FILTER:
+    case GL_TEXTURE_MAX_LOD:
+    case GL_TEXTURE_MIN_FILTER:
+    case GL_TEXTURE_MIN_LOD:
+    case GL_TEXTURE_WRAP_R:
+    case GL_TEXTURE_WRAP_S:
+    case GL_TEXTURE_WRAP_T:
+        break;
+    default:
+        dbgln("Unknown WebGL sampler parameter name: 0x{:04x}", pname);
+        set_error(GL_INVALID_ENUM);
+        return;
+    }
+    glSamplerParameter@param_type@(sampler_handle, pname, param);
+)~~~");
+            continue;
+        }
+
+        if (function.name.starts_with("clearBuffer"sv) && function.name.ends_with('v')) {
+            auto element_type = function.name.substring_view(11, 2);
+            if (element_type == "fv"sv) {
+                function_impl_generator.set("cpp_element_type", "float"sv);
+                function_impl_generator.set("typed_array_type", "Float32Array"sv);
+                function_impl_generator.set("gl_postfix", "f"sv);
+            } else if (element_type == "iv"sv) {
+                function_impl_generator.set("cpp_element_type", "int"sv);
+                function_impl_generator.set("typed_array_type", "Int32Array"sv);
+                function_impl_generator.set("gl_postfix", "i"sv);
+            } else if (element_type == "ui"sv) {
+                function_impl_generator.set("cpp_element_type", "u32"sv);
+                function_impl_generator.set("typed_array_type", "Uint32Array"sv);
+                function_impl_generator.set("gl_postfix", "ui"sv);
+            } else {
+                VERIFY_NOT_REACHED();
+            }
+            function_impl_generator.append(R"~~~(
+    @cpp_element_type@ const* data = nullptr;
+    size_t count = 0;
+    if (values.has<Vector<@cpp_element_type@>>()) {
+        auto& vector = values.get<Vector<@cpp_element_type@>>();
+        data = vector.data();
+        count = vector.size();
+    } else if (values.has<GC::Root<WebIDL::BufferSource>>()) {
+        auto& typed_array_base = static_cast<JS::TypedArrayBase&>(*values.get<GC::Root<WebIDL::BufferSource>>()->raw_object());
+        auto& typed_array = verify_cast<JS::@typed_array_type@>(typed_array_base);
+        data = typed_array.data().data();
+        count = typed_array.array_length().length();
+    } else {
+        VERIFY_NOT_REACHED();
+    }
+
+    switch (buffer) {
+    case GL_COLOR:
+        if (src_offset + 4 > count) {
+            set_error(GL_INVALID_VALUE);
+            return;
+        }
+        break;
+    case GL_DEPTH:
+    case GL_STENCIL:
+        if (src_offset + 1 > count) {
+            set_error(GL_INVALID_VALUE);
+            return;
+        }
+        break;
+    default:
+        dbgln("Unknown WebGL buffer target for buffer clearing: 0x{:04x}", buffer);
+        set_error(GL_INVALID_ENUM);
+        return;
+    }
+
+    data += src_offset;
+    glClearBuffer@gl_postfix@v(buffer, drawbuffer, data);
+    needs_to_present();
+)~~~");
+            continue;
+        }
+
         Vector<ByteString> gl_call_arguments;
         for (size_t i = 0; i < function.parameters.size(); ++i) {
             auto const& parameter = function.parameters[i];
@@ -1129,8 +1568,8 @@ public:
     size_t byte_size = 0;
     if (@buffer_source_name@->is_typed_array_base()) {
         auto& typed_array_base = static_cast<JS::TypedArrayBase&>(*@buffer_source_name@->raw_object());
-        ptr = typed_array_base.viewed_array_buffer()->buffer().data();
-        byte_size = typed_array_base.viewed_array_buffer()->byte_length();
+        ptr = typed_array_base.viewed_array_buffer()->buffer().data() + typed_array_base.byte_offset();
+        byte_size = @buffer_source_name@->byte_length();
     } else if (@buffer_source_name@->is_data_view()) {
         auto& data_view = static_cast<JS::DataView&>(*@buffer_source_name@->raw_object());
         ptr = data_view.viewed_array_buffer()->buffer().data();
@@ -1175,8 +1614,31 @@ public:
     }
 
     header_file_generator.append(R"~~~(
+protected:
+    virtual void visit_edges(JS::Cell::Visitor&) override;
+
 private:
     GC::Ref<JS::Realm> m_realm;
+    GC::Ptr<WebGLBuffer> m_array_buffer_binding;
+    GC::Ptr<WebGLBuffer> m_element_array_buffer_binding;
+    GC::Ptr<WebGLProgram> m_current_program;
+    GC::Ptr<WebGLFramebuffer> m_framebuffer_binding;
+    GC::Ptr<WebGLRenderbuffer> m_renderbuffer_binding;
+    GC::Ptr<WebGLTexture> m_texture_binding_2d;
+    GC::Ptr<WebGLTexture> m_texture_binding_cube_map;
+)~~~");
+
+    if (webgl_version == 2) {
+        header_file_generator.append(R"~~~(
+    GC::Ptr<WebGLBuffer> m_uniform_buffer_binding;
+    GC::Ptr<WebGLBuffer> m_copy_read_buffer_binding;
+    GC::Ptr<WebGLBuffer> m_copy_write_buffer_binding;
+    GC::Ptr<WebGLTexture> m_texture_binding_2d_array;
+    GC::Ptr<WebGLTexture> m_texture_binding_3d;
+)~~~");
+    }
+
+    header_file_generator.append(R"~~~(
     NonnullOwnPtr<OpenGLContext> m_context;
 };
 
@@ -1184,6 +1646,31 @@ private:
 )~~~");
 
     implementation_file_generator.append(R"~~~(
+void @class_name@::visit_edges(JS::Cell::Visitor& visitor)
+{
+    visitor.visit(m_realm);
+    visitor.visit(m_array_buffer_binding);
+    visitor.visit(m_element_array_buffer_binding);
+    visitor.visit(m_current_program);
+    visitor.visit(m_framebuffer_binding);
+    visitor.visit(m_renderbuffer_binding);
+    visitor.visit(m_texture_binding_2d);
+    visitor.visit(m_texture_binding_cube_map);
+)~~~");
+
+    if (webgl_version == 2) {
+        implementation_file_generator.append(R"~~~(
+    visitor.visit(m_uniform_buffer_binding);
+    visitor.visit(m_copy_read_buffer_binding);
+    visitor.visit(m_copy_write_buffer_binding);
+    visitor.visit(m_texture_binding_2d_array);
+    visitor.visit(m_texture_binding_3d);
+)~~~");
+    }
+
+    implementation_file_generator.append(R"~~~(
+}
+
 }
 )~~~");