HNHacker News
TopNewBestAskShowJobs

fleabitdev

422 karma · joined June 12, 2020

submissionscomments
fleabitdev··on Piet-GPU Progress: Clipping
Thanks for writing these updates, Raph - back in 2019, your early piet-gpu blog posts helped to reignite my interest in 2D rendering. I recently learned that you also invented Cairo's trapezoidal-coverage algorithm, which was my original introduction to anti-aliased 2D rendering ten years ago!

Over the last few weeks, this interest has finally spiralled into an attempt to develop a renderer of my own. I've come up with a design which seems promising, but I do have one question for which I haven't yet found a good answer:

It seems to be common wisdom that 16xMSAA has unacceptable quality for 2D rendering. In practice, though, I'm struggling to find any test-cases where I can differentiate 16xMSAA from 256-shade analytical AA with the naked eye. The only exception has been fine strokes (e.g. downscaled text, or the whiskers on this Ghostscript tiger [0]), which tend to "shimmer" or "sparkle" as they move. Does MSAA have any other common failure modes that I should be aware of?

[0]: https://threejs.org/examples/webgl_loader_svg.html

fleabitdev··on Final Fantasy 7: An oral history (2017)
And it's all MIDI, running on a 24-voice synthesiser with a 470-kilobyte sample bank.

Whenever I'm composing, and I'm tempted to be perfectionist about instrument fidelity or mixing quality, I listen to some Uematsu tracks then go back to my piano roll.

fleabitdev··on Prefix Sum with SIMD
I recently experimented with Rust's std::simd library prototype, and it gave me the same sense of wasted potential.

My program performed complicated processing of large 8-bit grayscale images. Naive use of portable SIMD made several important parts of the algorithm run ten times faster than my integrated-GPU implementation! (OpenGL ES 3.0 over ANGLE on an Intel HD Graphics 520)

I think the sheer power of modern SIMD has slipped past most peoples' radar. A multicore processor with AVX2 has number-crunching throughput comparable to a low-end GPU, and for some workloads, the extra flexibility of the CPU can unlock an additional order-of-magnitude performance uplift. (The comparison may be a little different if you're programming the GPU with modern compute shaders - I'm not sure.)

Portable SIMD programming is also just fun! Trying to keep my working set within the 32 KiB L1 cache, trying to keep my data packed within individual 8-bit lanes, and lacking access to some basic operations like integer multiplication and division - it almost feels like programming for a retro games console.

fleabitdev··on Fixing stutters in Papers Please on Linux
Windows uses a similar polling-based approach, with a warning that you shouldn't poll for new gamepads every frame for performance reasons:

https://docs.microsoft.com/en-us/windows/win32/xinput/gettin...

In general, "notifying the program when a new device has become available" seems to be a surprisingly difficult problem. I've encountered trouble with multiple device types across multiple platforms.

fleabitdev··on The QOI File Format Specification
Exactly what I was looking for, thanks! :)
fleabitdev··on The QOI File Format Specification
Reading that blog post, I was surprised to learn that many modern games dedicate more than half of their filesize to textures. I haven't played an AAA game in more than a decade, but I would have thought that meshes and (particularly) audio would use up more space.

It sounds like developers are stuck between a rock and a hard place. They need one of the specific compressed pixel formats which can be efficiently read by the GPU, but those formats are about ten times larger than a similar JPEG file, they don't losslessly compress well (modulo RAD Game Tools' discoveries here), and recompressing raw pixels to a GPU-addressable format at load time would be orders-of-magnitude too slow.

RAD Game Tools' approach here is clever, but it feels like a bit of a hack. The obvious next step would be a lossy compressed image format which can decompress directly to BC7, exploiting spatial-correlation tricks similar to those which PNG uses to get better results than a gzipped BMP file. Has anybody tried this already?

fleabitdev··on The QOI File Format Specification
I've dealt with image decoding for a game engine before. The images in question were large pixel-art texture atlases, stored as PNG files and loaded at startup. Their slow decoding speed caught me by surprise, given that the file format is 25 years old!

The reason turned out to be that Deflate's design makes it very hard to parallelise or SIMD-accelerate. Even the best available decoders are basically forced to process a byte at a time, in a straight line from start to finish, which obviously isn't a good match for modern CPUs. The 3x to 4x decompression speedup here is nice, but I can't help but feel a little sad about how poorly this format is taking advantage of available compute resources. (The ultimate dream would be a format which is so parallel-friendly that you can just send the binary blob to the GPU and decompress it in a compute shader!)

Even a rule like "each row is compressed separately, with a table of row lengths at the beginning of the file" might have been enough - this would have made compression ratios slightly worse, but complexity wouldn't have suffered too much. With only six different chunk types, we could perhaps imagine a branchless decoder where each row's decoding state is stored in its own SIMD lane, and the results for several rows are all emitted at the same time...

fleabitdev··on How will MIDI 2.0 change music? (2020)
This is quite a good example of the hard limitations of high-quality sampling. If you composed a violin melody line which absolutely needed a fast attack, Kontakt (a £359 library) would give you:

- A "sustain" articulation with fast attack on the loudest dynamic (which also happens to be molto vibrato), but a slower attack on quieter dynamics

- An "attack" dial which can emulate slower attacks, but not faster ones

- A "staccato" articulation which has an instant attack, but a duration of about 0.5 seconds

- No legato, so even if you get the attack right, fast runs and ornaments are going to sound a little strange

I've encountered similar limitations with dedicated orchestral libraries. If it's not the attack, it's the expression, or the maximum note duration, or the speed of an ornament, or the timbre of the specific instrument they recorded, or something else which always dangles perfection just out of your reach.

I suspect you'd have similar problems with even the most expensive sample libraries; it's one more reason to opt out of that game altogether. If you lower your standards for audio fidelity, your musical freedom increases. When you're composing for the Nintendo DS, nobody's going to notice if you manually tweak the attack of your string sample, or fake an ornament using pitch-bend, or crossfade between two velocity layers.

SWAM is one possible escape route, but you're correct that it costs a fortune. It also has high CPU requirements, so you might need to bounce your SWAM parts to an audio track, and you'd have limited ability to put together seven SWAM violins and call it a "violin section".

fleabitdev··on How will MIDI 2.0 change music? (2020)
The most "GM-like" experience I've encountered in a modern commercial product is the Kontakt factory library. It has almost complete General MIDI 1 coverage, plus a handful of random additions (a dozen electric pianos, samples recorded from some vintage synths, an SATB choir singing six different vowels, several drum kits...)

The quality is good, but it's not in the same league as specialised libraries. There are usually only two or three velocity layers (for example, the choir can only sing a subdued "aaa" or an intense "AAA"). The orchestral instruments only come with a few articulations, and the guitars and pop brass only provide one articulation each. None of the instruments have sampled legato.

Unfortunately, I think this is probably the quality ceiling for a sample library with good General MIDI coverage. If you were to add more velocity layers, articulations and legato transitions, the number of samples would start to grow exponentially, and the cost of the library would grow to match. I'm not sure there's any way around it.

I went through a phase where I was trying to put together a really comprehensive collection of samples, but in hindsight it was a fool's errand - you'll get diminishing returns fast, unless you're willing to spend huge amounts of money. Accurately emulating a live performer is a nice option to have, but it isn't actually a requirement for making good music. Huge amounts of excellent music has been made using synthesiser presets, or using instrument samples which would be considered "cheap" or "low-quality" nowadays. As long as you're willing to adapt your compositions to the instruments you have available, rather than stubbornly writing string melodies which could only be performed by a real-life violinist, you'll do well.

fleabitdev··on Bevy's First Birthday: a year of open source Rust game engine development
First things first: congratulations on your success! You seem to have single-handedly lit a fire under the Rust gamedev ecosystem :)

When I heard about Bevy last year, I was skeptical that game developers could achieve good iteration times by compiling Rust code in debug mode. It clearly works well for small programs, but I was concerned that it might not scale; in particular, that large dependencies might slow down linking, that CPU-intensive games would be likely to run at non-interactive speeds in debug mode, and that just a few seconds of compile time might be enough to break developers' flow.

When people are working on large game projects in Bevy, have they encountered these problems in practice? If not, how have the problems been avoided?

(Full disclosure: I developed GameLisp, a scripting language for Rust games, partly motivated by compiled languages' poor iteration times.)

fleabitdev··on Long Hundred
I'm developing a big-pixel retro platformer game. Rather than the traditional power-of-two tile sizes, I ended up going for 36x36 tiles. It's much easier to draw repeating patterns when tiles can be evenly divided into halves, thirds, quarters, sixths, and so on.
fleabitdev··on OO in Python is mostly pointless
How do you compensate for the lack of type-dependent name resolution? MyModule.foo(my_t) seems verbose, compared to my_t.foo()
fleabitdev··on GameLisp: Scripting language for Rust game development
Sounds like an interesting approach - can you point me towards any open-source code written in this style?
fleabitdev··on GameLisp: Scripting language for Rust game development
Hotloading is a near-future goal in the project's roadmap. There are some initial notes here, if you're curious:

https://github.com/fleabitdev/glsp/issues/10

Thanks to the last 25 years of improvements in CPU tech, it's less "patching raw binary artifacts into the running program" and more "recompiling the entire source tree in a couple of hundred milliseconds" :)

fleabitdev··on GameLisp: Scripting language for Rust game development
But the * suffix marks functions which might stagger their execution over multiple frames. If a function calls fire-arc* , then that function should also have the * suffix; otherwise, the suffix would be meaningless.

You're asking for the ability to refactor a synchronous function into an asynchronous function without needing to review the function's callsites. By definition, this would come with a high price: when writing a step handler, you'd need to assume that any function call could be refactored so that it doesn't return until several frames in the future. Even for a Lisp, that seems chaotic!

fleabitdev··on GameLisp: Scripting language for Rust game development
Very good point! I'm caught between a rock and a hard place...

Apart from their other downsides, stackful coroutines can make asynchronous code less readable. For example, it's not obvious that your self:move() call is split across several frames.

Implicit coroutine construction has the silent failure mode which you describe.

Explicit coroutine construction, (coro f), would make `yield-from` even more noisy than it already is!

I wonder whether a naming convention would solve the problem? If all coroutine functions and methods have names like fire-arc* and move* , then your synchronous call to fire-arc* would stick out like a sore thumb. This would also force the programmer to visit every callsite after changing a synchronous function into an asynchronous one, or vice versa. It wouldn't exactly be convenient, but I think I value explicitness more highly than convenience in this case.

fleabitdev··on GameLisp: Scripting language for Rust game development
Genuinely interesting suggestion!

I used Lua coroutines, Python generators and Ruby fibers as my prior art; in all three cases, they're resumed by invoking a method on the coroutine object.

A lambda function which resumes a coroutine can simply be written as:

  (fn0 (coro-run the-coro))
This is more ugly than passing in the coroutine directly, but it's also more explicit. I'm concerned that if the "resume coroutine" operation looks like a normal function call, the control flow of coroutines might become too difficult to follow.

To the best of my knowledge, the only way to get rid of `yield-from` would be to switch from stackless to stackful coroutines. GameLisp used to have stackful coroutines, but they added a large complexity burden to the virtual machine, and they were so expensive that I could never bring myself to use them for entity scripting. My instinct is that other game developers would feel the same way.

I'll need to give this some more thought.

fleabitdev··on GameLisp: Scripting language for Rust game development
The pros are summarised here:

https://gamelisp.rs/reference/introduction-for-rust-programm...

The main cons would be the lack of LuaJIT, the lack of C or C++ bindings, and the fact that Lua is spectacularly more mature and stable :)

(Full disclosure: I'm the project's developer)

fleabitdev··on GameLisp: Scripting language for Rust game development
Thanks for your interest!

The new reference manual for version 0.2 (which I'll release within a few days) discusses Bevy specifically. Short version: GameLisp isn't a natural fit for a multithreaded ECS, but it's possible to make it work.

Because the language is so dynamic and late-bound, it shouldn't be too difficult to hack in hotloading yourself. I intend to make it easier in the future. There's some preliminary discussion here:

https://github.com/fleabitdev/glsp/issues/10

fleabitdev··on Cakelisp: A Programming Language for Games
Hello! I'm the developer of GameLisp :)

You're correct that I've chosen safety and convenience over performance. I might eventually consider integrating the Cranelift code generator to try to achieve LuaJIT-like performance, but it's definitely not on the short-term roadmap.

In practice, I find that GameLisp is more than fast enough to script a busy 2D platformer, as long as you're sensible about using Rust for the more CPU-intensive parts of your game engine.

Incidentally, I'm planning to release GameLisp 0.2 within the next few days - happy to field any other questions while I'm here!

fleabitdev··on GameLisp, a scripting language for Rust game development
Hi folks - I made this :)

The initial release was only two days ago, after working on the language on-and-off for several years. Happy to answer any questions!

fleabitdev··on Show HN: GameLisp, a scripting language for Rust game development
Because the glsp crate and all of its dependencies are pure Rust code, a game engine which uses GameLisp can be compiled directly into WebAssembly. The playground is implemented in about 300 lines of Rust and about 900 lines of vanilla JavaScript.

Making a full-fledged WebAssembly game engine would be a little more difficult, but none of that difficulty would come from GameLisp :)

fleabitdev··on Show HN: GameLisp, a scripting language for Rust game development
Hi folks!

I very recently released GameLisp, a game scripting language for Rust, after working on it on-and-off for several years. The language has some innovations in its garbage collector, its object system and its Rust API which I hope you might find interesting.

Happy to answer any questions!

← PreviousPage 3 of 3