This page details the WebGL rendering context implementation in Ladybird, which exposes hardware-accelerated 3D graphics through OpenGL ES. The rendering context acts as the bridge between JavaScript WebGL API calls and the underlying OpenGL ES implementation provided by ANGLE.
This page covers:
For WebGL extensions, see other pages in the Web Platform APIs section. For canvas integration, see HTML Elements.
The WebGL rendering context implementation uses a multi-layered class hierarchy. The IDL layer defines WebGLRenderingContextBase and WebGL2RenderingContextBase as mixins. On the C++ side, this maps to a chain of classes that progressively add functionality.
Rendering Context Class Hierarchy
The WebGLRenderingContextOverloads and WebGL2RenderingContextOverloads layers exist to handle IDL overloaded methods (e.g., the multiple bufferData() and texImage2D() signatures) that require disambiguation beyond what the IDL generator handles uniformly. WebGL2RenderingContextImpl extends WebGLRenderingContextImpl directly, sharing the full WebGL1 method set.
Sources:
When JavaScript calls canvas.getContext("webgl") or canvas.getContext("webgl2"), the browser creates a rendering context through a multi-step initialization process.
Context Creation Sequence
The WebGLRenderingContext::create() method performs these steps:
convert_value_to_context_attributes_dictionary()TraversableNavigableOpenGLContext with WebGLVersion::WebGL1 or WebGL2; on failure, fires webglcontextcreationerror on the canvascanvas.bitmap_size_for_canvas(1, 1)make_current() callSources:
The WebGLContextAttributes dictionary configures rendering behavior during context creation. The implementation converts JavaScript options to C++ attributes in convert_value_to_context_attributes_dictionary().
| Attribute | Type | Default | WebIDL IDL Definition |
|---|---|---|---|
alpha | boolean | true | Controls alpha channel in drawing buffer |
depth | boolean | true | Enables depth buffer |
stencil | boolean | false | Enables stencil buffer |
antialias | boolean | true | Enables antialiasing |
premultipliedAlpha | boolean | true | Premultiplies alpha during blending |
preserveDrawingBuffer | boolean | false | Preserves buffer after compositing |
powerPreference | enum | "default" | Hint: "default", "high-performance", "low-power" |
failIfMajorPerformanceCaveat | boolean | false | Fails if hardware acceleration unavailable |
desynchronized | boolean | false | Allows desynchronized rendering |
The context stores both creation parameters (requested attributes) and actual context parameters (realized attributes after hardware negotiation) in separate WebGLContextAttributes members.
Sources:
WebGL rendering context methods directly map to OpenGL ES function calls. The implementation classes (WebGLRenderingContextImpl and WebGL2RenderingContextImpl) forward JavaScript calls to ANGLE-provided OpenGL ES functions.
API Method Dispatch Pattern
The bind_buffer() method demonstrates the typical pattern:
Sources:
WebGL2RenderingContextImpl extends the base implementation with OpenGL ES 3.0 functionality:
tex_image3d(), tex_sub_image3d() → glTexImage3DRobustANGLE(), glTexSubImage3DRobustANGLE()draw_arrays_instanced() → glDrawArraysInstanced()begin_transform_feedback() → glBeginTransformFeedback()bind_buffer_base() → glBindBufferBase()create_vertex_array() → glGenVertexArrays()Sources:
The rendering context maintains WebGL state that mirrors OpenGL ES state. State tracking enables proper cleanup, validation, and context loss handling.
Tracked State in WebGLRenderingContextImpl
State updates occur in two places:
bind_buffer(), bind_texture(), use_program() update tracked pointersSources:
ANGLE (Almost Native Graphics Layer Engine) translates OpenGL ES API calls to platform-native graphics APIs. The OpenGLContext class manages the EGL context and the Gfx::PaintingSurface that serves as the rendering target.
OpenGLContext Members and Roles
| Member | Type | Role |
|---|---|---|
m_impl | NonnullOwnPtr<Impl> | Holds EGL handles: display, config, context, surface, framebuffer, color_buffer, depth_buffer |
m_painting_surface | RefPtr<Gfx::PaintingSurface> | Skia-compatible surface wrapping the native texture |
m_skia_backend_context | NonnullRefPtr<Gfx::SkiaBackendContext> | Skia GPU context used when creating the painting surface |
m_webgl_version | WebGLVersion | WebGL1 or WebGL2 — affects EGL context version |
m_size | Gfx::IntSize | Current drawing buffer size; changing it invalidates m_painting_surface |
OpenGLContext Painting Surface Allocation
Every WebGL method begins with m_context->make_current(). This calls allocate_painting_surface_if_needed() (creating the surface on first use or after a resize), then activates the EGL context with no EGL surface (the framebuffer is managed manually):
Libraries/LibWeb/WebGL/OpenGLContext.cpp424-430
The internal framebuffer (m_impl->framebuffer) and color_buffer (m_impl->color_buffer) are OpenGL objects that back the PaintingSurface. On macOS the color buffer is bound to an IOSurface texture; on Vulkan it is bound to a VkImage via glEGLImageTargetTexture2DOES.
WebGL is only compiled when ENABLE_WEBGL is defined, which requires either macOS (Metal backend via ANGLE) or Vulkan image support (USE_VULKAN_IMAGES):
| Platform | EGL Platform Type | Color Buffer | Sync |
|---|---|---|---|
| macOS | EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE | IOSurface texture | eglWaitUntilWorkScheduledANGLE |
| Linux/Vulkan | EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE + EGL_PLATFORM_SURFACELESS_MESA | VkImage via DMA-BUF + EGLImage | glFinish() |
Sources:
The rendering contexts expose different API surfaces based on the WebGL version requested during creation.
| Feature | WebGL 1.0 | WebGL 2.0 |
|---|---|---|
| Based on | OpenGL ES 2.0 | OpenGL ES 3.0 |
| 3D Textures | No | Yes (texImage3D, texSubImage3D) |
| Uniform Buffer Objects | Extension only | Core (bindBufferBase, getUniformBlockIndex) |
| Vertex Array Objects | Extension (OES_vertex_array_object) | Core (createVertexArray, bindVertexArray) |
| Instanced Rendering | Extension (ANGLE_instanced_arrays) | Core (drawArraysInstanced, vertexAttribDivisor) |
| Transform Feedback | No | Yes (beginTransformFeedback, transformFeedbackVaryings) |
| Multiple Render Targets | Extension (WEBGL_draw_buffers) | Core (drawBuffers) |
| Sampler Objects | No | Yes (createSampler, bindSampler) |
| Sync Objects | No | Yes (fenceSync, clientWaitSync) |
| Integer Uniforms | No | Yes (uniform1ui, uniform2ui, etc.) |
| Framebuffer Blit | No | Yes (blitFramebuffer) |
OpenGLContext stores a WebGLVersion enum (WebGL1 or WebGL2) used in two ways:
EGL_CONTEXT_CLIENT_VERSION is set to 2 for WebGL1 or 3 for WebGL2.WebGLRenderingContextImpl: Methods like bind_buffer() and bind_texture() check m_context->webgl_version() to decide whether to track additional binding targets (e.g., GL_UNIFORM_BUFFER, GL_TEXTURE_3D) that only exist in WebGL2/OpenGL ES 3.0.Sources:
WebGL uses a deferred presentation model. Drawing commands (clear(), drawArrays(), drawElements(), etc.) mark the canvas dirty immediately; the actual GPU sync and buffer management happen when the compositor calls present().
Presentation Flow
The preserveDrawingBuffer context attribute (stored in m_context_creation_parameters) controls post-composite behavior:
false (default): clear_buffer_to_default_values() clears color to (0,0,0,0), depth to 1.0, stencil to 0 after each composite. This avoids stale state accumulating across frames.true: Buffer contents persist until the author explicitly clears or overwrites them.clear_buffer_to_default_values() saves and restores the application's current clear values around the implicit clear, so that the user-set state is unaffected.
Sources:
WebGL uses OpenGL ES error codes to report problems. The context tracks one error at a time, returning it via getError().
Error Tracking State Machine
The WebGLRenderingContextBase maintains an m_error member:
The implementation uses the SET_ERROR_VALUE_IF_ERROR macro for concise error checking:
Sources:
WebGL contexts can be lost due to various reasons (e.g., GPU reset). Ladybird implements the context loss mechanism:
Sources:
WebGL in Ladybird includes several performance optimizations:
Sources:
WebGL support in Ladybird is currently most complete on macOS, using ANGLE with Metal as the backend. Support for other platforms is in development.
Sources:
Refresh this wiki