-
-
Notifications
You must be signed in to change notification settings - Fork 30
Description
Hello, amazing library, I am using the web export and this has increased development speed tremendously, thank you for your work.
I am curious on thoughts/best practices around native type marshalling - I've noticed in debug output that in cases where native types are created on the stack and returned they will trigger the gc.alloc debug command, example is in Vector3 methods:
Vector3 Vector3::cross(const Vector3 &p_with) const {
Vector3 ret(
(y * p_with.z) - (z * p_with.y),
(z * p_with.x) - (x * p_with.z),
(x * p_with.y) - (y * p_with.x));
return ret;
}Will result in an allocation. I have been implementing TS extensions to allow for mutating the same object with the same logic for many of these utility functions like add, scale, etc. and keeping static instances for utilities. Also for getters on a node3d like node.global_position.x the global_position will allocate a Vector3 which seems like an expensive operation. Same with _input events for mouse/keyboard - they will be wrapped and allocated.
Do you think this is something that doesn't require optimization? I am trying to be as strict as possible in WASM environment, creating a wrapper for marshalling simple types without copies. Was curious your thoughts on this approach?
export class Extensions {
static cachedPosition: Vector3 = new Vector3(0, 0, 0);
static cachedRotation: Vector3 = new Vector3(0, 0, 0);
static cachedPositionNode: Node | null;
static root: Node | null;
static Dispose() {
Extensions.cachedPositionNode = null;
}
static SetRoot(node: Node) {
Extensions.root = node;
}
private static get positionNode() {
if (!Extensions.cachedPositionNode) {
Extensions.cachedPositionNode = Extensions.root?.get_node('/root/Zone/GDBridge') as Node;
}
return Extensions.cachedPositionNode;
}
static Eval(node: Node3D, code: string) {
return Extensions.positionNode?.call('eval_plain', code, node);
}
static GetPosition(node: Node3D): Vector3 {
this.cachedPosition.x = Extensions.positionNode.call('eval_plain', 'node.global_position.x', node);
this.cachedPosition.y = Extensions.positionNode.call('eval_plain', 'node.global_position.y', node);
this.cachedPosition.z = Extensions.positionNode.call('eval_plain', 'node.global_position.z', node);
return this.cachedPosition;
}
static GetBasisZ(node: Node3D): [number, number, number] {
const x = Extensions.positionNode.call('eval_plain', 'node.transform.basis.z.x', node);
const y = Extensions.positionNode.call('eval_plain', 'node.transform.basis.z.y', node);
const z = Extensions.positionNode.call('eval_plain', 'node.transform.basis.z.z', node);
return [x,y,z]
}
}and GDBridge:
extends Node
var _expr_cache := {}
func eval_plain(expr_str: String, node: Node3D) -> Variant:
var expression: Expression
if _expr_cache.has(expr_str):
expression = _expr_cache[expr_str]
else:
expression = Expression.new()
var err = expression.parse(expr_str, ["node"])
if err != OK:
push_error("Parse error in expression: %s" % expr_str)
return null
_expr_cache[expr_str] = expression
var result = expression.execute([node])
if expression.has_execute_failed():
push_error("Execution error in expression: %s" % expr_str)
return null
return result
func _input(event: InputEvent) -> void:
if get_viewport().gui_get_focus_owner():
return
if event is InputEventMouseButton:
var node = get_node("/root/Zone")
node.call("input", event.button_index);
if event.button_index == MOUSE_BUTTON_RIGHT:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED if event.pressed else Input.MOUSE_MODE_VISIBLE)
elif event is InputEventPanGesture:
var node = get_node("/root/Zone")
node.call("input_pan", event.delta_y);
elif event is InputEventMouseMotion:
var node = get_node("/root/Zone")
node.call("input_mouse_motion", event.relative.x, event.relative.y);