Readit News logoReadit News
rvrb commented on We rewrote the Ghostty GTK application   mitchellh.com/writing/gho... · Posted by u/tosh
pjmlp · 14 days ago
Another thing it does badly and I am not sure if they are sorting out with the new design, is the magic methods crap.

They had to copy that bad idea from Unity, where methods are named in a specific way and then extracted via reflection.

Either provide specific interfaces that components have to implement, use attributes, or make use of generics with type constraints.

Maybe for Unity that made sense as they started with scripting languages, and then bolted on Mono on the side, but it never made sense to me.

rvrb · 14 days ago
> extracted via reflection

I think you are talking about dispatch of virtual methods, which is still a thing, but the performance cost can be somewhat mitigated.

the names of the methods are interned strings (called `StringName`). a naive implementation will allocate the `StringName`, but you can avoid the allocation with a static lifetime string. we expose a helper for comptime strings in Zig[0].

then, extension classes need to provide callback(s)[1] on registration that lookup and call the virtual methods. as far as I know, the lookup happens once, and the engine stores the function pointer, but I haven't confirmed this yet. it would be unfortunate if not.

at least right now in GDZig, though this might change, we use Zig comptime to generate a unique function pointer for every virtual function on a class[2]. this implementation was by the original `godot-zig` author (we are a fork). in theory we could inline the underlying function here with `@call(.always_inline)` and avoid an extra layer indirection, among other optimizations. it is an area I am still figuring out the best approach for

virtual methods are only ever necessary when interfacing with the editor, GDScript, or other extensions. don't pay the cost of a virtual method when you can just use a plain method call without the overhead.

[0]: https://gdzig.github.io/gdzig/#gdzig.builtin.string_name.Str...

[1]: https://github.com/godotengine/godot/blob/e67074d0abe9b69f3d...

[2]: https://github.com/gdzig/gdzig/blob/5abe02aa046162d31ed5c52f...

rvrb commented on We rewrote the Ghostty GTK application   mitchellh.com/writing/gho... · Posted by u/tosh
Fraterkes · 15 days ago
Thx for the work you're doing! Just out of curiosity, I sometimes struggle to write performant C# Godot code because it's hard to interface with the engine without doing a lot of back and forth conversions to engine types. You end up doing a lot of allocations. Did you run into that kind of stuff while creating your bindings?
rvrb · 14 days ago
My understanding may be out of date, but the C# support was created before GDExtension existed. The team has been working hard on porting it over to GDExtension. Once they are done, it should be much more performant, and they will finally be able to ship only one version of the editor. I believe the original C# bindings do a lot of unnecessary marshaling at the ABI.

With GDExtension, the core builtin types like `Vector3` are passed by value. Avoid unnecessarily wrapping them in Variant, a specialized tagged union, where you can. You can see the documentation here; you have direct access to the float fields: https://gdzig.github.io/gdzig/#gdzig.builtin.vector3.Vector3

Engine objects are passed around as opaque pointers into engine managed memory. The memory for your custom types is managed by you. You allocate the engine object and essentially attach userdata to it, tagged with a unique token provided to your extension. You can see the header functions that manage this: https://github.com/godotengine/godot/blob/e67074d0abe9b69f3d...

But, this is how the lifetimes for the objects gets slightly hairy (for us, the people creating the language bindings). Our goal with the Zig bindings is to make the allocations and lifetimes extremely obvious, a la Zig's philosophy of "No hidden memory allocations". It is proving somewhat challenging, but I think we can get there.

There's still a lot of surprising or unintuitive allocations that can happen when calling into Godot, but we hope to expose those. My current idea is to accept a `GodotAllocator` on those functions (and do nothing with it; just use it to signal the allocation). You can read the code for the `GodotAllocator` implementation: https://github.com/gdzig/gdzig/blob/master/gdzig/heap.zig#L8...

If we succeed, I think Zig can become the best language to write highly performant Godot extensions in.

rvrb commented on We rewrote the Ghostty GTK application   mitchellh.com/writing/gho... · Posted by u/tosh
rvrb · 15 days ago
I haven't worked with GTK, but what you are describing here sounds reminiscent of what we have been dealing with trying to build Godot bindings in Zig with a nice API. the project is in mid-flight, but Godot:

  - has tons of OOP concepts: classes, virtual methods, properties, signals, etc
  - a C API to work with all of those concepts, define your own objects, properties, and so on
  - manages the lifetimes of any engine objects (you can attach userdata to any of them)
  - a whole tree of reference counted objects
it's a huge headache trying to figure out how to tie it into Zig idioms in a way that is an optimal API (specifically, dealing with lifetimes). we've come pretty far, but I am wondering if you have any additional insights or code snippets I should look at.

working on this problem produced this library, which I am not proud of: https://github.com/gdzig/oopz

here's a snippet that kind of demonstrates the state of the API at the moment: https://github.com/gdzig/gdzig/blob/master/example/src/Signa...

also.. now I want to attempt to write a Ghostty frontend as a Godot extension

rvrb commented on I made a real-time C/C++/Rust build visualizer   danielchasehooper.com/pos... · Posted by u/dhooper
Surac · 15 days ago
but why? I have to admit it's a fun project
rvrb · 15 days ago
here, I'll copy the first paragraph of TFA for you:

> Many software projects take a long time to compile. Sometimes that’s just due to the sheer amount of code, like in the LLVM project. But often a build is slower than it should be for dumb, fixable reasons.

rvrb commented on Partially Matching Zig Enums   matklad.github.io/2025/08... · Posted by u/ingve
agons · 20 days ago
Is there a reason the Zig compiler can't perform type-narrowing for `u` within the `U::A(_) | U::B(_)` "guard", rendering just the set of 2 cases entirely necessary and sufficient (obviating the need for any of the solutions in the blog post)?

I'm not familiar with Zig, but also ready to find out I'm not as familiar with type systems as I thought.

rvrb · 20 days ago
it can narrow the payload: https://zigbin.io/7cb79d

I think the post would be more helpful if it had a concrete use case. let's say a contrived bytecode VM:

  dispatch: switch (instruction) {
      inline .load, .load0, .load1, .load2, .load3 => |_, tag| {
          const slot = switch (tag) {
              .load => self.read(u8),
              else => @intFromEnum(tag) - @intFromEnum(.load0),
          };
          self.push(self.locals[slot]);
          continue :dispatch self.read(Instruction);
      },
      // ...
  }
"because comptime", this is effectively the same runtime performance as the common:

  dispatch: switch (instruction) {
      .load => {
          self.push(self.locals[self.read(u8)]);
          continue :dispatch self.read(Instruction);
      },
      .load0 => {
          self.push(self.locals[0]);
          continue :dispatch self.read(Instruction);
      },
      .load1 => {
          self.push(self.locals[1]);
          continue :dispatch self.read(Instruction);
      },
      .load2 => {
          self.push(self.locals[2]);
          continue :dispatch self.read(Instruction);
      },
      .load3 => {
          self.push(self.locals[3]);
          continue :dispatch self.read(Instruction);
      },
      // ...
  }
and this is in a situation where this level of performance optimization is actually valuable to spend time on. it's nice that Zig lets you achieve it while reusing the logic.

Deleted Comment

rvrb commented on Partially Matching Zig Enums   matklad.github.io/2025/08... · Posted by u/ingve
rvrb · 20 days ago
I did not realize you could inline anything other than an `else` branch! This is a very cool use for that.
rvrb commented on Resizable structs in Zig   tristanpemble.com/resizab... · Posted by u/rvrb
AndyKelley · a month ago
I think you're misinterpreting OP's excitement here. The technique isn't novel but the ergonomics are.

> you can implement this pattern ergonomically in C++,

lmao

rvrb · a month ago
> I think you're misinterpreting OP's excitement here. The technique isn't novel but the ergonomics are.

This, of course, but I also consciously made the decision to write for an audience less familiar with the language and concepts being discussed. That excitement can translate to engaging a less disgruntled and more curious reader than the OP.

u/rvrb

KarmaCake day130March 14, 2021View Original