Improve canvas context caching

Improve https://github.com/lightpanda-io/browser/pull/2022 to also cache webgl
context and add tests.
This commit is contained in:
Karl Seguin
2026-03-30 12:14:32 +08:00
parent b4e3f246ca
commit 25889ff918
3 changed files with 40 additions and 24 deletions

View File

@@ -148,3 +148,13 @@
} }
</script> </script>
<script id=identity>
{
const element = document.createElement('canvas');
const ctx = element.getContext('2d');
testing.expectTrue(ctx === element.getContext('2d'));
testing.expectEqual(null, element.getContext('webgl'));
}
</script>

View File

@@ -85,3 +85,13 @@
loseContext.restoreContext(); loseContext.restoreContext();
} }
</script> </script>
<script id=identity>
{
const element = document.createElement('canvas');
const ctx = element.getContext('webgl');
testing.expectTrue(ctx === element.getContext('webgl'));
testing.expectEqual(null, element.getContext('2d'));
}
</script>

View File

@@ -29,11 +29,7 @@ const OffscreenCanvas = @import("../../canvas/OffscreenCanvas.zig");
const Canvas = @This(); const Canvas = @This();
_proto: *HtmlElement, _proto: *HtmlElement,
_cached: ?DrawingContext = null,
/// Cached context type. Once set, requesting a different type returns null (per spec).
_context_type: ContextType = .none,
/// Cached 2D rendering context (same object returned on repeated getContext("2d") calls).
_ctx_2d: ?*CanvasRenderingContext2D = null,
const ContextType = enum { none, @"2d", webgl }; const ContextType = enum { none, @"2d", webgl };
@@ -75,28 +71,28 @@ const DrawingContext = union(enum) {
}; };
pub fn getContext(self: *Canvas, context_type: []const u8, page: *Page) !?DrawingContext { pub fn getContext(self: *Canvas, context_type: []const u8, page: *Page) !?DrawingContext {
if (std.mem.eql(u8, context_type, "2d")) { if (self._cached) |cached| {
// Return cached context if available. const matches = switch (cached) {
if (self._ctx_2d) |cached| return .{ .@"2d" = cached }; .@"2d" => std.mem.eql(u8, context_type, "2d"),
// Per spec: return null if a different context type was already requested. .webgl => std.mem.eql(u8, context_type, "webgl") or std.mem.eql(u8, context_type, "experimental-webgl"),
if (self._context_type != .none) return null; };
return if (matches) cached else null;
const ctx = try page._factory.create(CanvasRenderingContext2D{ ._canvas = self });
self._ctx_2d = ctx;
self._context_type = .@"2d";
return .{ .@"2d" = ctx };
} }
if (std.mem.eql(u8, context_type, "webgl") or std.mem.eql(u8, context_type, "experimental-webgl")) { const drawing_context: DrawingContext = blk: {
// Per spec: return null if a different context type was already requested. if (std.mem.eql(u8, context_type, "2d")) {
if (self._context_type != .none and self._context_type != .webgl) return null; const ctx = try page._factory.create(CanvasRenderingContext2D{ ._canvas = self });
break :blk .{ .@"2d" = ctx };
}
const ctx = try page._factory.create(WebGLRenderingContext{}); if (std.mem.eql(u8, context_type, "webgl") or std.mem.eql(u8, context_type, "experimental-webgl")) {
self._context_type = .webgl; const ctx = try page._factory.create(WebGLRenderingContext{});
return .{ .webgl = ctx }; break :blk .{ .webgl = ctx };
} }
return null;
return null; };
self._cached = drawing_context;
return drawing_context;
} }
/// Transfers control of the canvas to an OffscreenCanvas. /// Transfers control of the canvas to an OffscreenCanvas.