Skip to content

[Architecture] Kernel type and size and package freedom level #114

@miruji

Description

@miruji

Summary

We discussed this a bit with @ogyrec-o, but I decided it would be better to move this here so it would be easier to work in this direction later.

Under consideration:

  1. Microkernel or monolithic kernel
  2. Native or fully isolated packages

Problem / Motivation

To start, I’ll say that Freven wants to be a meta engine (at least that was the case in other issues).

In its current state, I see Freven as a monolithic or partially hybrid core. At the same time, packages are fully isolated inside the engine and dependent on its capabilities.


It is worth pointing out that a meta engine tends toward a hybrid or micro engine. It is simply like a physical characteristic.

I see a meta engine as a microkernel. This is when you can replace most parts, for example: a different render pipeline, while the methods themselves and the foundation for rendering are provided by the engine.

Current state

Originally the engine had .dll/.so library support. But later they were cut out, apparently because it was too complicated.

Instead, WASM is used now:

  • Isolated (requires manual import/export wiring), so it’s tied only to the engine
  • Manual control over this VM, its memory, and all abstractions around it
  • Performance loss (I don’t know what library is used now, but wasm3 is at least up to 6x worse, and usually many times worse)

So right now you can only do what the engine gives you. Want something else? No, you can’t do that.

So you’ll have to request it from the engine, and the engine itself will end up supporting a huge amount of stuff.

So you end up with something like a Linux hybrid kernel or a monolithic kernel. That’s also why I connect this with the codebase growing to more than 110 thousand lines.


In general, microkernels don’t have that; usually it’s just a couple thousand lines of code. That is, there’s an opinion that microkernels are larger and not more secure, but that’s not true. On the contrary, microkernels are many times more secure, but slower because of IPC. But here it depends on how well you implement IPC between processes and how much protection you need.

Classic example for microkernels:

problem microkernel solution
driver crash separate process
security IPC + memory isolation
modularity userspace services

And you do not need to write any VM or use WASM, you work natively.


It is worth noting that in all modern engines: Unreal, Godot, Unity - this is the norm. They work with native.

engine extension approach
Unreal Engine C++ modules + plugins (.dll/.so), full native access
Godot GDExtension (native modules) + built-in scripts (GDScript)
Unity C# runtime (IL2CPP / Mono) + native plugins

i.e. this is what was originally in Freven, but was removed.

Although these engines are not microkernels, they are a good example of a hybrid engine when native support exists. And there is nothing like: "well, the guy formatted my disk". For this there are their own mechanisms, and moreover we can write a bunch of them if we are paranoid.

Proposed direction

As I understood, after rc10 it is planned to discuss how to implement native.

The idea of @ogyrec-o as I understood it:

  • wasm is a portable safe layer for ordinary mods/scripts
  • native is a trusted layer for advanced/perf/os/native libs

I just want to say that WASM, as mentioned above, bloats the engine and requires massive support. It is also suitable only for small logic mods, but not for proper mods. That is, it is initially a small niche. But if you think otherwise = you want to bloat the engine more, because that is a consequence of such an opinion.

As for native - it is precisely a fairly large part of mods and packages. As mentioned, we can wrap it for safe operation.


My version is essentially this: since after rc10 there is a plan to either implement or attempt to plan native support, we will end up with the same kind of bridges as in WASM. That is the only truth in this case.

From this it follows that we are literally duplicating bridges, and WASM loses its purpose.

WASM also loses its purpose because it is faster to do the following:

  • Without manual import/export wiring for the user’s module
  • Easier to maintain the engine without abstractions — avoid bloating it
  • Native support exists
  • No need to compile WASM as an intermediate representation

Therefore, it seems to me that WASM is not appropriate here and it was not designed for portability of game mods.

Alternatives considered

1

I actually asked this question because I support the Freven engine and ran into a completely identical problem in my RTS programming language. issue

There is literally the exact same problem there. I initially made the language with support for functions and so on, it’s normal to have print inside a language, right?

But these are platform-dependent things. For example, WASM does not have them => you remove part of the language. Or bare metal also does not have them => you remove all of this.

Therefore I realized that a meta-engine - where packages extend the core - is the right way to avoid bloating the core and to maintain platform independence.

Essentially, as was said in some other issues in Freven - this was planned to be exactly the same here.


The problem is precisely that WASM will not work for this - you will simply end up doing hundreds of import/export forwarding calls for packages, building a bunch of mechanisms for it and manually managing it.

It is much easier and better to implement native support - but then it becomes clear that WASM is not needed as I said above.


And when we come to .so/.dll - we don't like it. Although it is standard practice in other engines.

But both RTS and Freven would want a single mod file or something platform-independent. Like Minecraft with its JVM where mods do not depend on the system.

I already said how this affects the core above. But as for code - I see one of the solutions as writing your own bytecode.


Theoretical idea of bytecode:

  • Assume we are building a package/mod from Rust/C/C++ ...
  • Then via LLVM we get not .wasm but .ll - this is a representation of code for CPU architecture
  • For example we built .ll for x86_64 on Linux
  • After that we lower it with our compiler into bytecode (Inkwell already can read it)
  • In the end we do not need to work with bytecode or build it on the user side who runs it
  • The user or engine simply uses the bytecode and it works identically on any system

Therefore packages become cross-platform and native.

We can also add:

  • Memory isolation
  • Contracts if we are paranoid about security
  • Linking system methods and modules (a normal thing for engines, like use system libc if it is referenced in bytecode)

Ultimately, this solves the problems of the meta engine and the micro kernel.

As a result, we get:

  • Cross-platform capability
  • Native speed
  • Isolation
  • Modular system

At the same time, writing a compiler and an interpreter on bytecode can be done quite quickly; in fact, we are not doing magic, but simply lowering LLVM IR into bytecode and forwarding it. Moreover, Rust already has libraries for this. I am currently testing it, and in just one day I already have a working base of such a machine, which can simply be extended.


A couple more things related to greater/less comparison:

  • WASM

  • Hybrid-native (.dll/.so)

  • Bytecode

  • It would be incorrect to compare WASM and bytecode as if they were the same thing.

  • It would be incorrect to say that bytecode is a VM — bytecode does not necessarily require a VM.


2

In general, I have not seen WIT used in Freven. It is a kind of standard agreement for how to pass types and so on.

It is an alternative option if we stay on WASM and still want to write a large number of import/export bindings.

In that case we simply need WIT — because it allows us to avoid working with raw data dumps and instead properly configure the bridges.

Ownership / boundary impact

As I said above, everything depends on how this will be implemented.

If doing a full microkernel, then the engine will become much lighter, and everything will simply be moved out for specific tasks. So the engine will become not just lighter, but also cleaner and more modular. This will also support flexibility for additions and changes.

But a microkernel requires understanding everything listed above and writing the proper IPC. That is, if everything is written correctly, then there will be no headache or disagreement. Moreover, microkernels in general have long been recognized by the scientific community as better, but the problem itself is that people do not always know how to write IPC properly.

That is why they either use a hybrid approach or a monolithic variant. So right now I see Freven, as I said above, as a clumsy and bloated monolith. Something like Godot, but Godot does not limit you with WASM in any way.

Non-goals

Overall, this is an outside perspective. I just see a similar example to my own, and since I really love large technological pipelines and care about this engine, I decided to write this issue.

I think this should be addressed now also because it helps avoid mistakes during the design phase, and later you may no longer be able to change it. Or at least so that over time you keep this issue in mind if you go down the WASM path, to avoid problems or understand my point of view and why other engines think the same way too.

The key question, if we are looking at Minecraft mods as I said above, is that we need Bytecode, not WASM. Because Java uses the JVM. And if you are not making a meta engine, then just use regular native. But regular native can simply be improved into a more elegant solution in the form of bytecode. In general, bytecode is something like a win-win for both the engine developer and the user.


Yes, if anything, there is no intention here to argue with anyone, it's just my opinion from my personal experience about kernels in general and how I see it from a technical point of view.

I don't know what would be better to choose for Freven, I can only describe how I see this question.

Yes, if you didn't understand what this question is about, because it is quite technical and complex, just throw it into a neural network, then it will describe it more briefly for you.

Metadata

Metadata

Assignees

No one assigned

    Labels

    status:triageNew / untriaged. Needs initial review and classification.type:architectureLong-term structural / contract / system design work, not just isolated implementation.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions