12 ms·
Low-latency scripting for game engines
- shmerl 2y agoI must be missing some idea used there. Why does scripting need emulation of hardware?
- fp64 2y agoI don't have an answer either, but I've seen people who want to use bytecode/a VM using RISC-V just because it's well defined and tooling is already there. This has less to do with RISC-V hardware, or even really the fact that it's RISC-V, and more with using a simple bytecode and VM - e.g. JVM could also work there, but the Java ecosystem comes with what some people consider considerable pain.
- neonsunset 2y agoMany game developers have been successfully using XNA/FNA with C# on all kinds of platforms (and, of course, Unity existed for a long time, and now we have Godot and Stride too). You get to access all the low-level bits when you need to, and you don't have to sacrifice productivity or performance for scripting otherwise.
- boffinAudio 2y agoIt doesn't need emulation of hardware per se, but most scripting environments are already virtual machines of some variety or another - and RISC-V just happens to be the latest in a long line of virtual machine architectures available for the purpose. The author is cleverly using libriscv for this purpose - and it is indeed a neat use of the library - but I fail to see the 'scripting' part, personally - isn't it scripting when you can just load a text file off the disk, containing program instructions, have it interpreted into some (hopefully optimized) interim representation, and then pushed through a virtual machine? I don't see the "text files" part here - but perhaps it gets more relevant in parts 2/3/4, which I haven't read yet, either... EDIT: I heard you like ABI, so I put a foreign ABI within your ABI so you can ABI with your ABI .. actually, this started off as a joke but its kind of painful to think about. This isn't really "scripting" .. its more "embed a foreign ABI into your application so you can throw a foreign binary at it, any time, and modify program behaviour extensively "without needing to recompile" .. maybe the author should rename the article "how to embed a virtual machine into your app so you never have to write (your app code) again .." I can see why they call it "scripting" since, this is what you usually do when embedding the Lua VM into an app - but the fact that I have to use a compiler disqualifies the term 'scripting' from being applicable here, imho ..
- exDM69 2y ago> but the fact that I have to use a compiler disqualifies the term 'scripting' from being applicable here, imho .. I'm not sure if this is a useful distinction to make. Lua, perhaps the most used scripting language out there, runs through a compiler too and then runs a bytecode interpreter on the resulting code. Most scripting languages work this way. The compiler is still there even if it gets invoked at runtime. Most games ship the compiled bytecode files, not the script source. The key part here is loading the "script" bytecode at runtime and then executing it in the host, allowing reloading and restarting the script without restarting the "host" process (often a game engine with long load times and lots of resident assets like textures, shaders, models etc that don't need reloading). The host process could also include a mechanism to invoke the compiler at runtime, pass in the script as text, grab the output and then run it as it does currently. This would be identical to how Lua et al work, but quite a bit of work to set up for limited benefit.
- boffinAudio 2y agoHey - the distinction is important but I don't think you've got it right. Out-of-the-box, Lua runs through an interpreter - not a compiler. A compiler produces native machine code from a human-readable programming language for the target platform - whereas an interpreter (in some cases) produces an interim representation (bytecode) for a virtual machine, not necessarily native - and in other cases, simply executes script code line-by-line, without the interim step. In the case of Lua, this interim representation is in the form of bytecode which is then further interpreted by a virtual machine, pretending that it is a register-based machine vastly different to the actual hardware it is running on. This particular aspect is the only feature that the authors' libriscv usage and the Lua VM have in common - but that doesn't make it scripting. And scripting means (to my jaded 40-years of development brain) to load text files from a resource (disk or otherwise), interpret it in some interim form, and then push through a secondary mechanism for final execution in the native environment. >The key part here is loading the "script" bytecode at runtime and then executing it in the host, allowing reloading and restarting the script without restarting the "host" process (often a game engine with long load times and lots of resident assets like textures, shaders, models etc that don't need reloading). So this is where our definitions conflict and go awry, because to me all you've described is runtime loading of a binary resource. Is loading and parsing a .PNG resource that appears in your executive bundle somewhere, also considered "Scripting" in your opinion? Because I don't think that is an accurate use of the term, personally. It would be scripting if the file (or memory blob) was somehow modifiable by the user, and then interpreted without further interaction required on the part of the developer - but in this case (the article) the developer (not the end user) still has to compile a binary blob, integrate it into their application, and then 'run it'. This is more appropriately referred to as runtime resource modification and execution - for which the action of "scripting" is a superset - but the fact that there are two compilers involved in this project (native to build the .exe, and risc-v cross-compiling to build the binary blob resource for integration) means that we are far, far away from the typical scripting mechanic. Its important to make this distinction. This isn't about scripting. It is about runtime resource modification and processing - but the different paths taken to the same fork in the road between scripting and compiling are vastly different and should not be conflated. This isn't to say that integrating libriscv as a virtual machine in ones application execution environment is not a brilliant idea with a great deal of merit - just that the author is incorrectly using terms, and this must simply not be allowed. There is no interpreting happening here until runtime - when the binary risc-v blob is loaded and parsed by a virtual machine. If there were some mechanism to load code intended for the risc-v component at runtime, compile it, and load it for execution/report errors in the script/etc. - then I would say that the scripting workflow has been completed for this project - and it would be appropriate to use the term - but that has not happened here. It still requires a separate compilation (risc-v machine code) step, and there are still many, many aspects of the scripting workflow that are not implemented in this project ..
- fsniper 2y agoIsn’t it mentioned as “sandboxing”? If the script runs in a controlled vm, it can not reach the game’s internal state unless it is exposed to the vm.
- bawolff 2y agoI was kind of wondering that too. Perhaps for sandboxing, but then i wonder why not something like webassembly or even eBPF. I kind of wish the author went more into the why of it.
- fwsgonzo 2y agoNothing wrong with using WebAssembly, if you wish to. eBPF I'm not so sure is a good fit. Most people are just fine with Lua, LuaJIT or Luau though. This is really just for especially interested, or if you really need low latency.
- gudzpoz 2y ago> ... but Lua still creates issues now and then. Like if you forget return, the return value is that of the last expression. Is this true? Or was it true for some Lua 2.X versions? Because it does not seem to be the case for Lua 3.0 and on: [1] > If control reaches the end of a function without a return instruction, then the function returns with no results. As to the idea of using an emulator for scripting, it seems a bit similar to approaches using WebAssembly. But looking into the benchmarks [2] provided by libriscv, it is quite interesting to see it outperforms luajit and wasm3 (in its "hand-picked" tests, at the very least). [1] https://www.lua.org/manual/3.0/manual.html https://www.lua.org/manual/3.0/manual.html [2] https://github.com/fwsGonzo/libriscv?tab=readme-ov-file#benchmarks https://github.com/fwsGonzo/libriscv?tab=readme-ov-file#benc...
- sebstefan 2y agoI've forgotten `return` plenty of times and to the best of my recollection it's always returned `nil` to the caller. Maybe he's talking about something else than pure lua? Something with the game's bindings?
- fwsgonzo 2y agoMaybe I'm old now. I'll remove it from the article! Reading back on it, it seems like I'm writing negatively about Lua, despite using it for many years. I think highly of Lua, and especially think it has made so many things easier for people over the years. I actually chose Nelua as one of the examples (Part 3) because of it!
- fwsgonzo 2y agoI spent a lot of time on those benchmarks. I think my conclusion is just that because WASM isn't a register-machine architecture, you basically have to implement a register allocator to be really fast. wasmtime uses a complex allocator to great success. wasm3 has done it too, but it's just a basic one and it won't be able to compete with GCC or Clang. I could be wrong here, but what else is there? During my testing, it was never in question if libriscv was faster. I used my usual benchmarks: STREAM (memory) and CoreMark (CPU).
- dazzawazza 2y agoThis is seriously cool but I really think we are losing our way in game development. We used to create DSLs to make scripting of games simpler and less error prone... so that coders/designers could write Scripts/AI in a language better suited to the problems they face. It was sort of a side issue that is was a sandbox although that was really useful for lots of reasons. Now we seem to be creating more and more complex toolchains while complaining that game development is getting more and more expensive. We're not solving the correct problems at all. Oh well.
- johnnyanmac 2y agoEven if it's c++ to c++, the goal of scripting at the end of the day is quick iteration. What is "quick iteration" for scripting will depend on the scriptor. This article is clearly targeted at engineers rolling their own engine, so that audience may find it beneficial keep everything in one language if they are already comfortable with it. As long as the core engine doesn't need to re-compile every little change, it's a win.
- fwsgonzo 2y agoYeah, I can confirm that the iteration speed is fast. I consider the scripting portion a solved problem right now, having used it so long. Gamedev is so many other things though, and practically every corner of the engine has to have convenience functionality in order to reduce the overall workload. It's rough for gamedevs right now, I feel like.
- johnnyanmac 2y agoIMO the big tools are convinent enough to use. But the rampup time to really understand how the engine ticks is dreadful, because documentation drops off a cliff once you want to do more than animate a few dozen actors in a level (and tbf, sometimes that is indeed enough to ship). The other half of the problem is asset production time. But no game engine is really solving that problem. As you'll discover in that domain, the art community is a lot more protective of their techniques and pipelines than programming. Even more so if looking for game ready asset production. Same issue, different cause.
- sebstefan 2y ago> 2. Install a RISC-V compiler > On Linux this is fairly straight-forward. Well... Of course on Linux it would be straight forward I'd love for games to start having any/all system languages as the embedded scripting language. But with Linux being around ~2% of the market share (with "~" in a hand-wavey way), I was kinda looking for the Windows steps to see if how much of a pain this would actually be if somebody wanted to set it up.
- fwsgonzo 2y agoAh yeah, I have installed RISC-V with a oneliner in WSL. So it's not too far off on Windows, but I mostly program on Linux these days. You can also build on a Linux VM and share it to Windows, but what kind of iteration loop is that? I don't know. Perhaps clang-cl could be used on Windows? It depends on how much of a run-time environment one wants.
- nxobject 2y agoI just think it's neat that the canonical script package is an Linux ELF binary.
- pdpi 2y agoIs there anything Linux-specific about ELF? I was under the impression it's a pretty common format for executables, used by a whole bunch of systems. It certainly predates Linux, as it was (according to Wikipedia) introduced in 1988 with SVR4.
- gmueckl 2y agoThe ELF format is shared between different platforms. There are identifiers for the target OS and CPU in the ELF header.
- lenkite 2y agoThis is wonderful! No need for a second language for plugins/extensions if your software is written in C++. Hell, it is mind-blowing - using a RISC-V emulator to run C++ scripts. Lol - this is not something that immediately strikes one's brain. Looking forward to fully digging into this next weekend.
- highsea 2y agoReminds me how Quake3 shipped with a VM to compile/run the C game code.
- ceronman 2y agoIf I understand correctly, the idea here is to provide safe scripting by running a RISC V emulator. The "scripts" would be written in C/C++ compiled to RISC V. And then these compiled scripts would be run inside the game engine's emulator. The emulator's library then will allow to share some memory between the emulated program and the host game engine by sharing some memory. All this sounds like a poor re-invention of Web Assembly 1.0. I think Wasm is a better option for most cases because: - There are more advanced Wasm runtimes that can do JIT compilation and be very fast. - There are probably more languages compiled to Wasm than to RISC V. Especially for higher level languages, which is attractive for scripting. - There is better tooling for debugging Wasm. - Interoperability is better specified in Wasm, it was designed from the ground up for that. And with Wasm components it's even better. This is specially important for different host architectures.
- fwsgonzo 2y agoThis whole comment sounds dismissive towards both me and RISC-V as an architecture people actually run their computers on. I have a lot of blog posts that you can read to get up to speed. Or maybe you already made up your mind.
- JonChesterfield 2y agoRisc-v is a hardware ISA optimised for education. Wasm is a sandboxed IR optimised for compilers. It would be phenomenally unlikely for risc-v to be better than wasm in the exact domain wasm was built to excel at.
- thefaux 2y agoBoth are targets for a general purpose programming language. As OP commented below, wasm is a stack machine while riscv is a register machine. It would surprise me if there is any wasm program for which there is not a semantically equivalent and faster riscv implementation.
- JonChesterfield 2y ago
- slaymaker1907 2y agoThere was an interesting paper I read the other day about a scheme for guaranteed constant time dynamic allocation and deallocation. The idea was you make all objects identical in size (they used 32 bytes). Then, on deallocation of a 32 byte node, you just put the node on a free list. Then, when a new allocation request comes in decrement the ref count of any *direct* reference(s) and move those objects to the free list if their ref count goes to 0. Therefore, even if you have some huge list that goes out of scope, only the 1st node goes on the freelist, but the rest of the list will get freed eventually assuming you continue to allocate new objects. I thought it was a really cool idea and it seems totally viable for things like a game engine. All the pointer chasing would be expensive, but being able to freely allocate memory without much lag would be a really nice property, particularly for games with plugins like Roblox, Factorio, etc. where code quality is often out of your control. https://lptk.github.io/files/ctrc-2024-05-09.pdf https://lptk.github.io/files/ctrc-2024-05-09.pdf
- bjconlan 2y agoI think this practice is generally good for "most fast things" I noticed that https://github.com/kparc/ksimple/blob/31370a2c799a2a0e491d52be6fded818b50d3908/a.h#L10 https://github.com/kparc/ksimple/blob/31370a2c799a2a0e491d52... uses this approach too (this is the core of the k apl which is used in finance and engineering industries so very fast for data processing applications. Not sure how it fares in a more general setting but wouldn't be surprised if it was also competitive.
- spintin 2y ago[dead]