Skip to content

Commit f436744

Browse files
committed
Delay requestAnimation
This is often called in a tight loop (the callback to requestAnimation typically calls requestAnimation). Instead, we can treat it like a setTimeout with a short delay (5ms ?). This has the added benefit of making it cancelable, FWIW.
1 parent fff0a8a commit f436744

File tree

1 file changed

+28
-30
lines changed

1 file changed

+28
-30
lines changed

src/browser/html/window.zig

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -163,50 +163,33 @@ pub const Window = struct {
163163
return &self.performance;
164164
}
165165

166-
// Tells the browser you wish to perform an animation. It requests the browser to call a user-supplied callback function before the next repaint.
167-
// fn callback(timestamp: f64)
168-
// Returns the request ID, that uniquely identifies the entry in the callback list.
169-
pub fn _requestAnimationFrame(
170-
self: *Window,
171-
callback: Function,
172-
) !u32 {
173-
// We immediately execute the callback, but this may not be correct TBD.
174-
// Since: When multiple callbacks queued by requestAnimationFrame() begin to fire in a single frame, each receives the same timestamp even though time has passed during the computation of every previous callback's workload.
175-
var result: Function.Result = undefined;
176-
callback.tryCall(void, .{self.performance._now()}, &result) catch {
177-
log.debug(.user_script, "callback error", .{
178-
.err = result.exception,
179-
.stack = result.stack,
180-
.source = "requestAnimationFrame",
181-
});
182-
};
183-
return 99; // not unique, but user cannot make assumptions about it. cancelAnimationFrame will be too late anyway.
166+
pub fn _requestAnimationFrame(self: *Window, cbk: Function, page: *Page) !u32 {
167+
return self.createTimeout(cbk, 5, page, .{.animation_frame = true});
184168
}
185169

186-
// Cancels an animation frame request previously scheduled through requestAnimationFrame().
187-
// This is a no-op since _requestAnimationFrame immediately executes the callback.
188-
pub fn _cancelAnimationFrame(_: *Window, request_id: u32) void {
189-
_ = request_id;
170+
pub fn _cancelAnimationFrame(self: *Window, id: u32, page: *Page) !void {
171+
const kv = self.timers.fetchRemove(id) orelse return;
172+
return page.loop.cancel(kv.value.loop_id);
190173
}
191174

192175
// TODO handle callback arguments.
193176
pub fn _setTimeout(self: *Window, cbk: Function, delay: ?u32, page: *Page) !u32 {
194-
return self.createTimeout(cbk, delay, page, false);
177+
return self.createTimeout(cbk, delay, page, .{});
195178
}
196179

197180
// TODO handle callback arguments.
198181
pub fn _setInterval(self: *Window, cbk: Function, delay: ?u32, page: *Page) !u32 {
199-
return self.createTimeout(cbk, delay, page, true);
182+
return self.createTimeout(cbk, delay, page, .{.repeat = true});
200183
}
201184

202185
pub fn _clearTimeout(self: *Window, id: u32, page: *Page) !void {
203186
const kv = self.timers.fetchRemove(id) orelse return;
204-
try page.loop.cancel(kv.value.loop_id);
187+
return page.loop.cancel(kv.value.loop_id);
205188
}
206189

207190
pub fn _clearInterval(self: *Window, id: u32, page: *Page) !void {
208191
const kv = self.timers.fetchRemove(id) orelse return;
209-
try page.loop.cancel(kv.value.loop_id);
192+
return page.loop.cancel(kv.value.loop_id);
210193
}
211194

212195
pub fn _matchMedia(_: *const Window, media: []const u8, page: *Page) !MediaQueryList {
@@ -216,10 +199,14 @@ pub const Window = struct {
216199
};
217200
}
218201

219-
fn createTimeout(self: *Window, cbk: Function, delay_: ?u32, page: *Page, comptime repeat: bool) !u32 {
202+
const CreateTimeoutOpts = struct {
203+
repeat: bool = false,
204+
animation_frame: bool = false,
205+
};
206+
fn createTimeout(self: *Window, cbk: Function, delay_: ?u32, page: *Page, comptime opts: CreateTimeoutOpts) !u32 {
220207
const delay = delay_ orelse 0;
221208
if (delay > 5000) {
222-
log.warn(.user_script, "long timeout ignored", .{ .delay = delay, .interval = repeat });
209+
log.warn(.user_script, "long timeout ignored", .{ .delay = delay, .interval = opts.repeat });
223210
// self.timer_id is u30, so the largest value we can generate is
224211
// 1_073_741_824. Returning 2_000_000_000 makes sure that clients
225212
// can call cancelTimer/cancelInterval without breaking anything.
@@ -250,7 +237,8 @@ pub const Window = struct {
250237
.window = self,
251238
.timer_id = timer_id,
252239
.node = .{ .func = TimerCallback.run },
253-
.repeat = if (repeat) delay_ms else null,
240+
.repeat = if (opts.repeat) delay_ms else null,
241+
.animation_frame = opts.animation_frame,
254242
};
255243
callback.loop_id = try page.loop.timeout(delay_ms, &callback.node);
256244

@@ -300,13 +288,23 @@ const TimerCallback = struct {
300288
// if the event should be repeated
301289
repeat: ?u63 = null,
302290

291+
animation_frame: bool = false,
292+
303293
window: *Window,
304294

305295
fn run(node: *Loop.CallbackNode, repeat_delay: *?u63) void {
306296
const self: *TimerCallback = @fieldParentPtr("node", node);
307297

308298
var result: Function.Result = undefined;
309-
self.cbk.tryCall(void, .{}, &result) catch {
299+
300+
var call: anyerror!void = undefined;
301+
if (self.animation_frame) {
302+
call = self.cbk.tryCall(void, .{self.window.performance._now()}, &result);
303+
} else {
304+
call = self.cbk.tryCall(void, .{}, &result);
305+
}
306+
307+
call catch {
310308
log.debug(.user_script, "callback error", .{
311309
.err = result.exception,
312310
.stack = result.stack,

0 commit comments

Comments
 (0)