itch.io is community of indie game creators and players

Devlogs

Working with Odin on MacOS

Cute Exporter
A downloadable tool for Windows and macOS

Odin is a great programming language. It lives up to at least this part of its mission statement.

joy of programming

I hadn’t done any low level programming for a while before I started using Odin. Now I can’t get enough.

Odin, while great, does have its rough edges. You can expect that from a newer language with a smaller community. And because the creator Ginger Bill develops on a Windows machine there’s a few more rough edges in MacOS vs Windows. I am going to talk about one of the issues I ran into on Mac and how I solved it.

(Look I develop on MacOS. I come from a web background and like the OS and it’s ecosystem. Sue me.)

OSX Calling Convention Bug

Related Issue

I am not sure of the root problem, but Odin isn’t passing certain structs properly. I think it has to do with the MacOS ABI wanting them passed as a 128 bit vector, but again I’m not sure.

This appears to be a major issue and it is, however we can work around it. In my code and tool I use Raylib. Odin’s foreign system is awesome, making it super easy to include anything compiled to C code. I link to the raylib .a file and done… In a perfect world. As mentioned, the calling convention is broken for a few structs. But as mentioned, there is a solution. It’s a simple solution. We can pass in our structs normally and modify our calls to pass in a pointer parameter for output instead of directly returning.

RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera); // Returns the screen space position for a 2d camera world space position

Becomes

RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera, Vector2 *out); // Returns the screen space position for a 2d camera world space position

Before I was doing this a not smart way. I was modifying the entire raylib source to make these changes to the calls. This worked fine. I didn’t think much of it as I was building out Cute Exporter. Then it came time to start making sure it worked for windows. Windows doesn’t have these calling convention issues. Do I now need to maintain two different versions of raylib for MacOS and Windows solely to work around Odin? That could work but doesn’t seem optimal. Then I realized, Odin has a great foreign system we can do this better.

Instead of modifying raylib directly I made a small wrapper C library around functions that needed to change. I don’t even need to do it for every raylib function, only the broken ones. And in Windows we can import the raylib library code and be done.

In the C wrapper

#include "raylib.h"
Vector2 raylibext_GetScreenToWorld2D(Vector2 vec, Camera2D cam, Vector2 *input, Vector2 *result) {
	Vector2 res = GetScreenToWorld2D(*input, cam);

	*result = res;

	return res;
}

I can then import my wrapper functions and work with them to have the same Odin calling convention in Windows or MacOS. (Odin also has a great system for OS specific code.)

//import_darwin.odin
//The _darwin part means this file only gets included and compiled when on a Darwin (MacOS) system.
when ODIN_OS == "darwin"{
	foreign import raylib_host {
		"libraylib.a",
		"raylib_extended.o",
	}
};
foreign raylib_host {
	@(link_name="raylibext_GetScreenToWorld2D")
	_GetScreenToWorld2D :: proc(raylib._Vector2, raylib.Camera2D, ^raylib._Vector2, ^raylib._Vector2) -> raylib._Vector2 ---;
}

GetScreenToWorld2D :: proc "c" (coord: raylib._Vector2, camera: raylib.Camera2D) -> raylib._Vector2 {
		result : raylib._Vector2;
		input := raylib._Vector2{coord.x, coord.y};
		_GetScreenToWorld2D(input, camera, &input, &result);
		return raylib._Vector2{result.x, result.y};
}

The advantage of this method being, I don’t need to maintain a different branch of my changes to raylib. If a newer version or bug fix come out I don’t need to worry about conflicts.

In the end it’s nothing too complex or crazy and that is fully owed to how flexible and awesome Odin is.

Download Cute Exporter
Leave a comment