Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion editor/src/messages/animation/animation_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ pub struct AnimationMessageHandler {
animation_state: AnimationState,
fps: f64,
animation_time_mode: AnimationTimeMode,
/// Seconds of animation time elapsed between the previous frame and the current one.
animation_delta_time: f64,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The new animation_delta_time field is updated in SetTime and reset in RestartAnimation, but SetFrameIndex triggers a graph render (SubmitActiveGraphRender) without resetting it. If the user scrubs to a specific frame while the animation is playing, the Animation Delta Time node will read a stale non-zero delta from the last playback frame instead of 0, since manual frame scrubbing involves no elapsed animation time.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At editor/src/messages/animation/animation_message_handler.rs, line 37:

<comment>The new `animation_delta_time` field is updated in `SetTime` and reset in `RestartAnimation`, but `SetFrameIndex` triggers a graph render (`SubmitActiveGraphRender`) without resetting it. If the user scrubs to a specific frame while the animation is playing, the Animation Delta Time node will read a stale non-zero delta from the last playback frame instead of 0, since manual frame scrubbing involves no elapsed animation time.</comment>

<file context>
@@ -33,6 +33,8 @@ pub struct AnimationMessageHandler {
 	fps: f64,
 	animation_time_mode: AnimationTimeMode,
+	/// Seconds of animation time elapsed between the previous frame and the current one.
+	animation_delta_time: f64,
 }
 impl AnimationMessageHandler {
</file context>

}
impl AnimationMessageHandler {
pub(crate) fn timing_information(&self) -> TimingInformation {
Expand All @@ -41,7 +43,11 @@ impl AnimationMessageHandler {
AnimationTimeMode::TimeBased => Duration::from_millis(animation_time as u64),
AnimationTimeMode::FrameBased => Duration::from_secs((self.frame_index / self.fps) as u64),
};
TimingInformation { time: self.timestamp, animation_time }
TimingInformation {
time: self.timestamp,
animation_time,
animation_delta_time: self.animation_delta_time,
}
}

pub(crate) fn animation_start(&self) -> f64 {
Expand Down Expand Up @@ -89,7 +95,11 @@ impl MessageHandler<AnimationMessage, ()> for AnimationMessageHandler {
responses.add(PortfolioMessage::UpdateDocumentWidgets);
}
AnimationMessage::SetTime { time } => {
// Elapsed animation time since the previous frame, in seconds (frozen while paused, so it reads 0 then)
let previous_animation_time = self.timestamp - self.animation_start();
self.timestamp = time;
let current_animation_time = self.timestamp - self.animation_start();
self.animation_delta_time = ((current_animation_time - previous_animation_time) / 1000.).max(0.);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: In FrameBased mode, animation_delta_time is computed from real-time timestamp differences, but the actual animation time progresses in fixed 1/fps increments (as shown in timing_information()). This decouples delta time from the frame-based animation time, causing non-deterministic playback for physics/simulation nodes. Consider differentiating by mode:

self.animation_delta_time = match self.animation_time_mode {
    AnimationTimeMode::TimeBased => ((current_animation_time - previous_animation_time) / 1000.).clamp(0., 0.1),
    AnimationTimeMode::FrameBased => if self.is_playing() { 1. / self.fps } else { 0. },
};

Also note the lack of an upper clamp in TimeBased mode — large time jumps (e.g., tab backgrounding, sleep/wake) could cause simulation explosions.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At editor/src/messages/animation/animation_message_handler.rs, line 102:

<comment>In `FrameBased` mode, `animation_delta_time` is computed from real-time timestamp differences, but the actual animation time progresses in fixed `1/fps` increments (as shown in `timing_information()`). This decouples delta time from the frame-based animation time, causing non-deterministic playback for physics/simulation nodes. Consider differentiating by mode:

```rust
self.animation_delta_time = match self.animation_time_mode {
    AnimationTimeMode::TimeBased => ((current_animation_time - previous_animation_time) / 1000.).clamp(0., 0.1),
    AnimationTimeMode::FrameBased => if self.is_playing() { 1. / self.fps } else { 0. },
};

Also note the lack of an upper clamp in TimeBased mode — large time jumps (e.g., tab backgrounding, sleep/wake) could cause simulation explosions.

@@ -89,7 +95,11 @@ impl MessageHandler for AnimationMessageHandler { + let previous_animation_time = self.timestamp - self.animation_start(); self.timestamp = time; + let current_animation_time = self.timestamp - self.animation_start(); + self.animation_delta_time = ((current_animation_time - previous_animation_time) / 1000.).max(0.); responses.add(AnimationMessage::UpdateTime); } ```
Suggested change
self.animation_delta_time = ((current_animation_time - previous_animation_time) / 1000.).max(0.);
self.animation_delta_time = match self.animation_time_mode {
AnimationTimeMode::TimeBased => ((current_animation_time - previous_animation_time) / 1000.).clamp(0., 0.1),
AnimationTimeMode::FrameBased => if self.is_playing() { 1. / self.fps } else { 0. },
};

responses.add(AnimationMessage::UpdateTime);
}
Comment on lines 97 to 104

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

In FrameBased mode, the animation time is determined by self.frame_index / self.fps. However, self.animation_delta_time is always calculated using self.timestamp (real-time based), which completely decouples it from the actual frame-based animation time. Furthermore, when the animation is playing in FrameBased mode, the delta time should be a constant 1. / self.fps rather than fluctuating based on real-time lag, ensuring deterministic playback. Additionally, capping the delta time to a reasonable maximum (e.g., 0.1 seconds) is a standard defensive programming practice to prevent physics or simulation explosions during large jumps or lag spikes.

			AnimationMessage::SetTime { time } => {
				// Elapsed animation time since the previous frame, in seconds (frozen while paused, so it reads 0 then)
				let previous_animation_time = self.timestamp - self.animation_start();
				self.timestamp = time;
				let current_animation_time = self.timestamp - self.animation_start();
				self.animation_delta_time = match self.animation_time_mode {
					AnimationTimeMode::TimeBased => ((current_animation_time - previous_animation_time) / 1000.).clamp(0., 0.1),
					AnimationTimeMode::FrameBased => if self.is_playing() { 1. / self.fps } else { 0. },
				};
				responses.add(AnimationMessage::UpdateTime);
			}

AnimationMessage::IncrementFrameCounter => {
Expand All @@ -110,6 +120,7 @@ impl MessageHandler<AnimationMessage, ()> for AnimationMessageHandler {
}
AnimationMessage::RestartAnimation => {
self.frame_index = 0.;
self.animation_delta_time = 0.;
self.animation_state = match self.animation_state {
AnimationState::Playing { .. } => AnimationState::Playing { start: self.timestamp },
_ => AnimationState::Stopped,
Expand Down
2 changes: 1 addition & 1 deletion node-graph/graph-craft/src/proto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,7 @@ impl TypingContext {
// List of all implementations that match the input types
let valid_output_types = impls
.keys()
.filter(|node_io| valid_type(&node_io.call_argument, call_argument) && inputs.iter().zip(node_io.inputs.iter()).all(|(p1, p2)| valid_type(p1, p2)))
.filter(|node_io| valid_type(&node_io.call_argument, call_argument) && inputs.len() == node_io.inputs.len() && inputs.iter().zip(node_io.inputs.iter()).all(|(p1, p2)| valid_type(p1, p2)))
.collect::<Vec<_>>();

// Attempt to substitute generic types with concrete types and save the list of results
Expand Down
1 change: 1 addition & 0 deletions node-graph/graphene-cli/src/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ pub async fn export_gif(
time: TimingInformation {
time: animation_time.as_secs_f64(),
animation_time,
animation_delta_time: 0.,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: In the GIF export loop, animation_delta_time is hardcoded to 0. for every frame, but it should reflect the time elapsed since the previous frame (1.0 / animation.fps for frames after the first, 0. for the first). As written, the new Animation Delta Time node will always output 0 in GIF exports, defeating its purpose for any animation using delta-time-dependent logic.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At node-graph/graphene-cli/src/export.rs, line 190:

<comment>In the GIF export loop, `animation_delta_time` is hardcoded to `0.` for every frame, but it should reflect the time elapsed since the previous frame (`1.0 / animation.fps` for frames after the first, `0.` for the first). As written, the new Animation Delta Time node will always output 0 in GIF exports, defeating its purpose for any animation using delta-time-dependent logic.</comment>

<file context>
@@ -187,6 +187,7 @@ pub async fn export_gif(
 			time: TimingInformation {
 				time: animation_time.as_secs_f64(),
 				animation_time,
+				animation_delta_time: 0.,
 			},
 			..Default::default()
</file context>
Suggested change
animation_delta_time: 0.,
animation_delta_time: if frame_idx > 0 { 1. / animation.fps } else { 0. },

},
Comment on lines 187 to 191

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

In export_gif, animation_delta_time is hardcoded to 0.. This means any nodes in the graph that rely on delta time (e.g., physics, procedural animations) will completely freeze or fail to update when exported to a GIF. Since GIF frames are rendered at a constant FPS, the delta time should be 1. / animation.fps.

Suggested change
time: TimingInformation {
time: animation_time.as_secs_f64(),
animation_time,
animation_delta_time: 0.,
},
time: TimingInformation {
time: animation_time.as_secs_f64(),
animation_time,
animation_delta_time: 1. / animation.fps,
},

..Default::default()
};
Expand Down
2 changes: 2 additions & 0 deletions node-graph/libraries/application-io/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ pub enum ExportFormat {
pub struct TimingInformation {
pub time: f64,
pub animation_time: Duration,
/// Seconds of animation time elapsed since the previous frame.
pub animation_delta_time: f64,
}

#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny)]
Expand Down
48 changes: 44 additions & 4 deletions node-graph/libraries/core-types/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ pub trait ExtractRealTime {
pub trait ExtractAnimationTime {
fn try_animation_time(&self) -> Option<f64>;
}
pub trait ExtractAnimationDeltaTime {
fn try_animation_delta_time(&self) -> Option<f64>;
}
pub trait ExtractPointerPosition {
fn try_pointer_position(&self) -> Option<DVec2>;
}
Expand Down Expand Up @@ -60,6 +63,7 @@ pub trait CloneVarArgs: ExtractVarArgs {
pub trait InjectFootprint {}
pub trait InjectRealTime {}
pub trait InjectAnimationTime {}
pub trait InjectAnimationDeltaTime {}
pub trait InjectPointerPosition {}
pub trait InjectPosition {}
pub trait InjectIndex {}
Expand All @@ -74,6 +78,7 @@ pub trait ExtractAll:
ExtractFootprint +
ExtractRealTime +
ExtractAnimationTime +
ExtractAnimationDeltaTime +
ExtractPointerPosition +
ExtractPosition +
ExtractIndex +
Expand All @@ -84,6 +89,7 @@ impl<
+ ExtractFootprint
+ ExtractRealTime
+ ExtractAnimationTime
+ ExtractAnimationDeltaTime
+ ExtractPointerPosition
+ ExtractPosition
+ ExtractIndex
Expand All @@ -99,6 +105,7 @@ impl<
impl<T: Ctx> InjectFootprint for T {}
impl<T: Ctx> InjectRealTime for T {}
impl<T: Ctx> InjectAnimationTime for T {}
impl<T: Ctx> InjectAnimationDeltaTime for T {}
impl<T: Ctx> InjectPointerPosition for T {}
impl<T: Ctx> InjectPosition for T {}
impl<T: Ctx> InjectIndex for T {}
Expand All @@ -112,6 +119,7 @@ impl<T: Ctx> InjectVarArgs for T {}
pub trait ModifyFootprint: ExtractFootprint + InjectFootprint {}
pub trait ModifyRealTime: ExtractRealTime + InjectRealTime {}
pub trait ModifyAnimationTime: ExtractAnimationTime + InjectAnimationTime {}
pub trait ModifyAnimationDeltaTime: ExtractAnimationDeltaTime + InjectAnimationDeltaTime {}
pub trait ModifyPointerPosition: ExtractPointerPosition + InjectPointerPosition {}
pub trait ModifyPosition: ExtractPosition + InjectPosition {}
pub trait ModifyIndex: ExtractIndex + InjectIndex {}
Expand All @@ -120,6 +128,7 @@ pub trait ModifyVarArgs: ExtractVarArgs + InjectVarArgs {}
impl<T: Ctx + InjectFootprint + ExtractFootprint> ModifyFootprint for T {}
impl<T: Ctx + InjectRealTime + ExtractRealTime> ModifyRealTime for T {}
impl<T: Ctx + InjectAnimationTime + ExtractAnimationTime> ModifyAnimationTime for T {}
impl<T: Ctx + InjectAnimationDeltaTime + ExtractAnimationDeltaTime> ModifyAnimationDeltaTime for T {}
impl<T: Ctx + InjectPointerPosition + ExtractPointerPosition> ModifyPointerPosition for T {}
impl<T: Ctx + InjectPosition + ExtractPosition> ModifyPosition for T {}
impl<T: Ctx + InjectIndex + ExtractIndex> ModifyIndex for T {}
Expand All @@ -136,13 +145,15 @@ pub enum ContextFeature {
ExtractFootprint,
ExtractRealTime,
ExtractAnimationTime,
ExtractAnimationDeltaTime,
ExtractPointerPosition,
ExtractPosition,
ExtractIndex,
ExtractVarArgs,
InjectFootprint,
InjectRealTime,
InjectAnimationTime,
InjectAnimationDeltaTime,
InjectPointerPosition,
InjectPosition,
InjectIndex,
Expand All @@ -158,10 +169,11 @@ bitflags! {
const FOOTPRINT = 1 << 0;
const REAL_TIME = 1 << 1;
const ANIMATION_TIME = 1 << 2;
const POINTER_POSITION = 1 << 3;
const POSITION = 1 << 4;
const INDEX = 1 << 5;
const VARARGS = 1 << 6;
const ANIMATION_DELTA_TIME = 1 << 3;
const POINTER_POSITION = 1 << 4;
const POSITION = 1 << 5;
const INDEX = 1 << 6;
const VARARGS = 1 << 7;
}
}

Expand All @@ -177,6 +189,7 @@ impl ContextFeatures {
ContextFeatures::FOOTPRINT => "Footprint",
ContextFeatures::REAL_TIME => "RealTime",
ContextFeatures::ANIMATION_TIME => "AnimationTime",
ContextFeatures::ANIMATION_DELTA_TIME => "AnimationDeltaTime",
ContextFeatures::POINTER_POSITION => "PointerPosition",
ContextFeatures::POSITION => "Position",
ContextFeatures::INDEX => "Index",
Expand Down Expand Up @@ -206,6 +219,7 @@ impl From<&[ContextFeature]> for ContextDependencies {
ContextFeature::ExtractFootprint => ContextFeatures::FOOTPRINT,
ContextFeature::ExtractRealTime => ContextFeatures::REAL_TIME,
ContextFeature::ExtractAnimationTime => ContextFeatures::ANIMATION_TIME,
ContextFeature::ExtractAnimationDeltaTime => ContextFeatures::ANIMATION_DELTA_TIME,
ContextFeature::ExtractPointerPosition => ContextFeatures::POINTER_POSITION,
ContextFeature::ExtractPosition => ContextFeatures::POSITION,
ContextFeature::ExtractIndex => ContextFeatures::INDEX,
Expand All @@ -216,6 +230,7 @@ impl From<&[ContextFeature]> for ContextDependencies {
ContextFeature::InjectFootprint => ContextFeatures::FOOTPRINT,
ContextFeature::InjectRealTime => ContextFeatures::REAL_TIME,
ContextFeature::InjectAnimationTime => ContextFeatures::ANIMATION_TIME,
ContextFeature::InjectAnimationDeltaTime => ContextFeatures::ANIMATION_DELTA_TIME,
ContextFeature::InjectPointerPosition => ContextFeatures::POINTER_POSITION,
ContextFeature::InjectPosition => ContextFeatures::POSITION,
ContextFeature::InjectIndex => ContextFeatures::INDEX,
Expand Down Expand Up @@ -253,6 +268,11 @@ impl<T: ExtractAnimationTime + Sync> ExtractAnimationTime for Option<T> {
self.as_ref().and_then(|x| x.try_animation_time())
}
}
impl<T: ExtractAnimationDeltaTime + Sync> ExtractAnimationDeltaTime for Option<T> {
fn try_animation_delta_time(&self) -> Option<f64> {
self.as_ref().and_then(|x| x.try_animation_delta_time())
}
}
impl<T: ExtractPointerPosition + Sync> ExtractPointerPosition for Option<T> {
fn try_pointer_position(&self) -> Option<DVec2> {
self.as_ref().and_then(|x| x.try_pointer_position())
Expand Down Expand Up @@ -311,6 +331,11 @@ impl<T: ExtractAnimationTime + Sync> ExtractAnimationTime for Arc<T> {
(**self).try_animation_time()
}
}
impl<T: ExtractAnimationDeltaTime + Sync> ExtractAnimationDeltaTime for Arc<T> {
fn try_animation_delta_time(&self) -> Option<f64> {
(**self).try_animation_delta_time()
}
}
impl<T: ExtractPointerPosition + Sync> ExtractPointerPosition for Arc<T> {
fn try_pointer_position(&self) -> Option<DVec2> {
(**self).try_pointer_position()
Expand Down Expand Up @@ -446,6 +471,11 @@ impl ExtractAnimationTime for OwnedContextImpl {
self.animation_time
}
}
impl ExtractAnimationDeltaTime for OwnedContextImpl {
fn try_animation_delta_time(&self) -> Option<f64> {
self.animation_delta_time
}
}
impl ExtractPointerPosition for OwnedContextImpl {
fn try_pointer_position(&self) -> Option<DVec2> {
self.pointer_position
Expand Down Expand Up @@ -517,6 +547,7 @@ pub struct OwnedContextImpl {
footprint: Option<Footprint>,
real_time: Option<f64>,
animation_time: Option<f64>,
animation_delta_time: Option<f64>,
pointer_position: Option<DVec2>,
position: Option<Vec<DVec2>>,
// This could be converted into a single enum to save extra bytes
Expand All @@ -531,6 +562,7 @@ impl std::fmt::Debug for OwnedContextImpl {
.field("footprint", &self.footprint)
.field("real_time", &self.real_time)
.field("animation_time", &self.animation_time)
.field("animation_delta_time", &self.animation_delta_time)
.field("pointer_position", &self.pointer_position)
.field("index", &self.index)
.field("varargs_len", &self.varargs.as_ref().map(|x| x.len()))
Expand All @@ -550,6 +582,7 @@ impl graphene_hash::CacheHash for OwnedContextImpl {
self.footprint.cache_hash(state);
self.real_time.cache_hash(state);
self.animation_time.cache_hash(state);
self.animation_delta_time.cache_hash(state);
self.pointer_position.cache_hash(state);
self.position.cache_hash(state);
self.index.cache_hash(state);
Expand All @@ -575,6 +608,7 @@ impl OwnedContextImpl {
let footprint = bitflags.contains(ContextFeatures::FOOTPRINT).then(|| value.try_footprint().copied()).flatten();
let real_time = bitflags.contains(ContextFeatures::REAL_TIME).then(|| value.try_real_time()).flatten();
let animation_time = bitflags.contains(ContextFeatures::ANIMATION_TIME).then(|| value.try_animation_time()).flatten();
let animation_delta_time = bitflags.contains(ContextFeatures::ANIMATION_DELTA_TIME).then(|| value.try_animation_delta_time()).flatten();
let pointer_position = bitflags.contains(ContextFeatures::POINTER_POSITION).then(|| value.try_pointer_position()).flatten();
let position = bitflags.contains(ContextFeatures::POSITION).then(|| value.try_position()).flatten().map(|x| x.collect());
let index = bitflags.contains(ContextFeatures::INDEX).then(|| value.try_index()).flatten().map(|x| x.collect());
Expand All @@ -584,6 +618,7 @@ impl OwnedContextImpl {
footprint,
real_time,
animation_time,
animation_delta_time,
pointer_position,
position,
index,
Expand All @@ -597,6 +632,7 @@ impl OwnedContextImpl {
footprint: None,
real_time: None,
animation_time: None,
animation_delta_time: None,
pointer_position: None,
position: None,
index: None,
Expand Down Expand Up @@ -646,6 +682,10 @@ impl OwnedContextImpl {
self.animation_time = Some(animation_time);
self
}
pub fn with_animation_delta_time(mut self, animation_delta_time: f64) -> Self {
self.animation_delta_time = Some(animation_delta_time);
self
}
pub fn with_pointer_position(mut self, pointer_position: DVec2) -> Self {
self.pointer_position = Some(pointer_position);
self
Expand Down
2 changes: 2 additions & 0 deletions node-graph/node-macro/src/parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,13 +565,15 @@ fn parse_context_feature_idents(ty: &Type) -> Vec<Ident> {
"ExtractFootprint"
| "ExtractRealTime"
| "ExtractAnimationTime"
| "ExtractAnimationDeltaTime"
| "ExtractPointerPosition"
| "ExtractPosition"
| "ExtractIndex"
| "ExtractVarArgs"
| "InjectFootprint"
| "InjectRealTime"
| "InjectAnimationTime"
| "InjectAnimationDeltaTime"
| "InjectPointerPosition"
| "InjectPosition"
| "InjectIndex"
Expand Down
8 changes: 7 additions & 1 deletion node-graph/nodes/gcore/src/animation.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use core_types::list::List;
use core_types::transform::Footprint;
use core_types::{CacheHash, CloneVarArgs, Color, Context, Ctx, ExtractAll, ExtractAnimationTime, ExtractPointerPosition, ExtractRealTime, OwnedContextImpl};
use core_types::{CacheHash, CloneVarArgs, Color, Context, Ctx, ExtractAll, ExtractAnimationDeltaTime, ExtractAnimationTime, ExtractPointerPosition, ExtractRealTime, OwnedContextImpl};
use glam::{DAffine2, DVec2};
use graphic_types::vector_types::GradientStops;
use graphic_types::{Artboard, Graphic, Vector};
Expand Down Expand Up @@ -60,6 +60,12 @@ fn animation_time(
ctx.try_animation_time().unwrap_or_default() * rate
}

/// Produces the elapsed animation time, in seconds, since the previous frame was rendered (or delta time-dependent node was evaluated).
#[node_macro::node(category("Animation"))]
fn animation_delta_time(ctx: impl Ctx + ExtractAnimationDeltaTime) -> f64 {
ctx.try_animation_delta_time().unwrap_or_default()
}

#[node_macro::node(category("Debug"))]
async fn quantize_real_time<T>(
ctx: impl Ctx + ExtractAll + CloneVarArgs,
Expand Down
1 change: 1 addition & 0 deletions node-graph/nodes/gstd/src/render_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ async fn create_context<'a: 'n>(
.with_footprint(footprint)
.with_real_time(render_config.time.time)
.with_animation_time(render_config.time.animation_time.as_secs_f64())
.with_animation_delta_time(render_config.time.animation_delta_time)
.with_pointer_position(render_config.pointer)
.with_vararg(Box::new(render_params))
.into_context();
Expand Down
Loading