Ask HN: How were video games from the 90s so efficient?

286 points by eezurr ↗ HN
Title says it all. I am interested in discovering how games like roller coaster tycoon, sim city 2000, warcraft II, and descent (blown away by this one) managed to be created and function on computers that had 500 MB HDD, 300 MHz CPU, and 4 MB RAM.

I'd even broaden the question and ask how did windows 95 stay so small?

Is it possible to recreate this level of efficiency on modern systems? Im curious because I'm interested in creating simulation video games. Dwarf Fortress and Rimworld eventually both suffer from the same problem: CPU death.

If I create a window with C++ and SFML, 60 MB of RAM is used (not impressive at all). If I put 3,000,000 tiles on the screen (using Vertex Arrays), 1 GB of RAM is used (admittedly, that is impressive) and I can pan all the tiles around smoothly.

What other tricks are available?

237 comments

[ 2.5 ms ] story [ 281 ms ] thread
Low resolution screens, 1 byte pixels, …
There was no other alternative. They had to be efficient. These were the only resources they had to work with.

Reading about the tricks that were used to make the most out of the limited resources is fascinating to me as well! We should not take our current hardware for granted!

A lot of the gameplay, AI, etc was far more simplistic. Descent for instance is quite barren.
https://www.quora.com/Why-was-Roller-Coaster-Tycoon-written-...

Funny you should mention Rollercoaster Tycoon because it was actually written in Assembly for performance reasons.

For a rube like me who views assembly coders as masters of occult incantations, this is incredibly impressive
Assembly language is actually much simpler than even C, much less Lua or JS or Python. It just takes a lot more work to get anything done: five times as much code that's twice as hard to debug per line. Roller Coaster Tycoon is an incredibly impressive achievement, but not because writing it required memorizing a lot of occult trivia. It didn't. It just required effectively using the stuff you have available.
You should consider a mental framework of lean objectivity in the processes you code: what is to be done step by step operating with the real thing: memory. You want to turn that pixel on, so you should set that memory address to that value; structures become sequences of mentally labelled memory cells; your tools are the basic, sort of elementary: copy data, store somewhere the results of arithmetic and logic operations, branch conditionally etc. You reason in practical terms close to the actual low-level reality instead that through abstractions.

I think it helps develop a very good mindset, lean and faithful to base facts.

It’s still just programming and there are many assemblers that feature high level constructs to help out. I’d recommend trying out some Z80, 6502 or something more esoteric like uxn. Lots of emulators exist for old consoles. Then you’ll demystify it for yourself and look like a wizard to everyone else.
I recommend watching this: https://youtu.be/izxXGuVL21o

Naughty Dog Co-founder Andy Gavin discusses various hacks that were used on the Playstation to get Crash Bandicoot to run smoothly. The fuller version is also worth watching.

This one is my favourite. I would call this kind of programming both art and beauty.
If I remember correctly the two main hacks were making draw lists such that only certain polygon faces were rendered based on Crash's xyz position (the theory being that it isnt possible for other faces to be seen from that location), and also that he removed functions/files from Sony's standard C libs on the PS1 SDK?
I believe two other huge wins were how they padded the disc for faster reads(though maybe this was common) and how they could trivially cull polys based on the constraint that the game is on a rail. By knowing your position on the rail you just lookups what polys can be culled via precalculated tables.
Right, for the polys I think we are talking about the same thing :)

For the disk, I guess they viewed the disk as merely just an extension of RAM? If the read/write speeds were sufficient for them... page files galore :)

I also remember that they used to write bytes to disk in specific orders so that they would maximize the correct bytes being read into the buffer every microsecond

They realized that untextured polygons were way faster to draw than textured ones, so rather than texture the Crash model, they cranked up the polygon count and simply colored them. Often times, the triangles were on the same order of size as the pixels. It also avoided the playstation's lack of perspective correction on textures. It both made the character look gorgeous for the day, and made the code run faster.
Yes, I played many many hours of crash as a kid and he looked extremely vivid and bright. When I code in Three.js these days I also skip loading textures and just go for hex colors on polygons :)
The art of working with constraints does not seem to be lost; just look at late cycle console games.

A PS4 was/is effectively unchanged hardware between Nov 2013 and today, yet the late lifecycle games look great. Upon release of the hardware devs had plenty of performance to play with, then 5 years in they have honed their craft and are able to use all the tricks at their disposal to squeeze as much graphical and performance life out of a limited resource budget.

Look into the 64k demoscene, lots of tricks and tips that still work today.
My thoughts:

- The difference between 8 or 16 bit graphics at 320x200 30 fps and modern hdr 2k at 120 fps is orders of magnitude less data to manage.

- Software developers were not abstracted as far away from the hardware as they are today.

- Most games ran on DOS which was basically “bare metal” vs windows/multitasking/background processes..

- And you HAD to make efficient use of the compute and ram when dealing with limited resources, which honed the skills of serious game devs.

> Most games ran on DOS which was basically “bare metal”

Remember how games made you pick your sound card because the OS didn't provide an API for this?

The games were of low resolution and low details which meant that games could render with limited resources.
A game like RimWorld is also a lot more complex; the "AI" for your park's visitors in Rollercoaster Tycoon was quite simple; the AI of your RimWorld colonists is much more complex. Add to this the "time speedup" which is quite resource intensive (events happen faster → more CPU needed).

I'm sure RimWorld can be made more efficient; but it actually runs fairly well on my cheap laptop. There is, essentially, no real need. And any time spent on making the game run faster is taken away from all other development tasks (fixing bugs, adding features, etc.)

Dyson Sphere Program is maybe more interesting Unity game that can do impressive visuals Qnd scale with what is available.
RimWorld, especially with the runtime garbage collector mod, is super super well optimized. Massive mod lists with 0 bugs or performance issues and the only thing that cripples performance at all is the "perfect pathfinding" mod, and we can't really blame them for not being able to improve on A*!
They were efficient because they had to do less compared to a modern game.
Kind of a lame answer but: Google it. There are blogs online that reverse engineer and document old game engines.

Lots of abstracting as pointers, esoteric assembly optimization, compiler hacks, fewer external frameworks, less physics simulation, etc

Roller Coaster Tycoon was famously written in assembly by hand. There are some cool blog posts about it that have been posted to HN in the past iirc.
Michael Abrash wrote a book or two about it, and a lot of code, and was one of the experts in tweaking performance out of hardware back then.
These tricks are things that you could still pull off today. The difference is, outside of competitions or the demoscene, nobody _needs_ to pull them off - it greatly decreases your time-to-market to do things in a straightforward way, rather than a novel way. That wasn't true 20+ years ago - the straightforward way often brought you up against hardware limitations.
Having been a game programmer back in the day (C64, Amiga, Atari Jaguar, N64, and beyond to newer machines) I fully agree with your point.

My earliest days had the most fantastic tricks. So called game engines were useless. Now it’s the complete opposite.

What did you program on the N64? And which among those platforms was was your favorite?
> it greatly decreases your time-to-market to do things in a straightforward way

I mean, it's hard to disagree when you phrase it this way, but... really? In the old days (mid-'90s) studios like id released many games per year, some of which with completely new technology.

Modern studios and indie developers (!) who "do things in a straightforward way" can be happy to release even one game per year, and that's with a lot of reuse. Forget novel technology once a year!

So maybe these tricks don't really increase time to market that much, compared to other variables that are also in play?

I have zero information about (due also to little interest for) computer games, so this is just a wild speculation: maybe the visuals in terms of "levels/textures/objects/mesh/characters/voice/audio" dominate the planning, now?
The visuals are crap. Greed dominates the game industry.
Greed does dominate the industry, but there's more indie games than ever
I wrote "planning", meaning that the development of the fame assets maybe now takes more time than the actual engine/logic of the game itself.
> ... it greatly decreases your time-to-market to do things in a straightforward way ...

I'd actually like to link the YouTube channel of a person who is writing their own game engine and game at the same time: https://www.youtube.com/c/RandallThomas/videos

You can see how using Godot, Unity or Unreal (or most other engines) would have been much faster in regards to time to market.

Similar differences show up when you try to build the same project, once while using an engine and another without it, the performance can be much better if you write your own optimized code (supposing that you can do so in the first place), however the development still takes much longer, for example: https://www.youtube.com/watch?v=tInaI3pU19Y

Now, whether that matters to you or not is a different matter entirely: some care about learning a lot more about the lower level stuff, others just want to develop games and care more about the art/story/etc., while others care about selling them ASAP.

Broadly speaking, modern developers don’t have to deal with resource limitations (neither computing nor financial) so they instead devote that extra energy on completely unnecessary orchestration systems and build toolchains to feel like they’re doing something complex and challenging.
There are definitely startups with enough cash to spend time polishing their their CI systems, but I suspect the pressure in the game industry means this doesn't happen. That said, I have zero experience in the game industry.
Developers “didn’t feel like”, they were actually doing something complex, given the limitations of that time and context.
(comment deleted)
Im not sure i understand, but I can tell you it felt good when one learned that setting the stack pointer and using push and pop was faster than using register indirect addressing, bit twiddling to avoid multiplying or dividing, and fitting a mm/dd/yy on 14 bits. Constraints fuels a certain type of creativity. This is not absent today, but less frequent.
Right. I think it's mostly subconscious. If you crank out CRUD app or some basic FPS in Unity, you're left wanting more and turn to the toolchain to find that complexity. 25 years ago, devs had no unmet craving after optimizing netcode for an 8-player game with 28.8k users or implementing another soundcard API by hand.
I’d hazard a guess the VR development for platforms like Quest, where you’re effectively trying to render stereo at 90fps on mobile chipsets AND deal with the physical environment, is bringing back a lot of those “get every last cycle out of the hardware” skills.
> Broadly speaking, modern developers don’t have to deal with resource limitations (neither computing nor financial) so they instead devote that extra energy on completely unnecessary orchestration systems and build toolchains to feel like they’re doing something complex and challenging.

That is massively far from truth. Rendering graphics to the level modern games require is driving even current powerful hardware to its limits and it's far from "not having to deal with resource limitations".

Your post seems to be horribly condescending and not realising just how much work goes into squeezeing performance from modern GPUs and consoles.

Sure, but there are <1% of the games being produced that need to tackle those problems. Look at the most of 10,000+ games released on Steam or the endless mobile games released over the past year.

For every Naughty Dog, Riot, Take-Two, CDPR, or Bethesda, there are thousands of garbage studios. Thus my “Broadly speaking” caveat.

The source code for Doom is available, and much has been written about how it works.[1] Go look.

[1] https://doomwiki.org/wiki/Doom_source_code

This is probably an unsatisfying answer, but simply put, a lot of systems these days are just bloated. I'm not only talking about the OS/programs running (though it often contributes), but rather the creature comforts developers have taken on over the years in the name of productivity. In fairness, the scale of recent games wouldn't be quite so possible without our superfluous tooling, but it definitely comes at a cost. A great example is Minecraft, which was originally written in Java but later ported to C++ for more consistent performance. Java's extensive library support, object-orientation and quick-and-dirty graphics access made it a great tool for prototyping, but when it came to delivering a high-performance product it often fell short. There are many such anecdotes in the gamedev community, so choosing the right tool for the job is hugely important.

Dwarf Fortress and Rimworld are both interesting topics on their own though, and while I'm dreadfully underfamiliar with their codebases I do love the games to death. I'd guess that if you profiled them, the heaviest slowdown would be accessing/manipulating memory with hundreds of values across thousands of instances. Both games are extremely heavy-handed in their approach to simulation, so it wouldn't surprise me if that constituted the bulk of their usage. Dwarf Fortress itself is an interesting case study though, because it's world generation can take hours. As more years pass, it takes longer to generate, which is probably a testament to how many interconnected pieces it's weaving.

> Java...a great tool for prototyping, but when it came to delivering a high-performance product it often fell short.

I think this only been said in the context of games.

It really depends. Java isn't bad as server-side software, and there are benefits to using it's runtime. For client-side software though (particularly in 2012-2020), not many commercial PCs could play Minecraft at a decent framerate. Even now, feeding the Java version huge amounts of high-bandwidth memory is the only way to mitigate slowdown, and that still doesn't account for the micro-stuttering that you get when world generation occurs. In the context of Minecraft, it was a pretty obvious mistake. YMMV, but I'd still highly recommend against writing Java software for client-side stuff.
It's more understandable considering that it started as a Java applet which could be run in the browser.
The jetbrains IDEs are written in Java and they run very nice.

I'd wager the micro stuttering is GC?

We used to prototype game ideas in Java and I know a large embedded software firm who uses Java for prototyping. After it has been shown to work, they port to c/asm.
The biggest thing is lower resolution. 640x480 at 256 colors was a high-color, high-resolution mode at the time.

Small bitmaps, with indexed palettes and run-length-encoding to further shrink them.

Caches were tiny, memory was slower, hard disks massively slower, and if you had to hit a CD to load data, forget about it. So packing data was important, and organizing data for cache locality, and writing algorithms to take advantage of instruction pipelining and avoid branches.

Fabian Sanglard has a great blog that goes into the nitty gritty of a lot of these techniques as used in different games.

https://fabiensanglard.net/

There was no frameworks or high level languages to add a bunch of bloat, maybe?
I love this video: https://youtu.be/ZWQ0591PAxM

As part of a Kickerstarter campaign, Morphcat Games made this video explaining how they eked out a really incredible game with only 40 Kb (a lot like Super Mario Bros 2). I definitely recommend checking this out as they go over interesting compression methods and more general thought processes.

Thank you for sharing. These tricks are really clever!
When these software were rewritten, at that time developer are consider intelligent enough to understand Physics, mathematics etc..Now a days a Online Degree make Developers. The So called next generation of programming hide some low level optimization and developer often do not by pass them. They were written for Business application, but they use in game application. unlimited resources, operating system efficiency lost (as they suffer from same problem, as well as more security code is added to them that somewhere bottelneck it) are some other stuff that has effect on efficiency.
I wonder how many years ahead a game using optimization tricks would look compared to a game without that focus.
Modern game libraries very much use “tricks” in critical paths as far as resources are concerned.
I'll count this as modern: GTA San Andreas not having any zones was pretty impressive.
Factorio’s visuals may be merely “nice”, but Factorio running a megabase in real time is pretty mind blowing, logically.
The graphics (and sound) was much lower res, smaller worlds, fewer characters, less motion poses per character, prebaked character frames, the floors and ceilings were flat, the textures had far fewer colors, the lighting and shadows were prebaked onto static surfaces, game code was performance tuned at the assembly code level, symmetrical textures, low contrast textures … and we all needed to pay for 2MB more RAM to play Doom.
Layers and layers of abstraction are created in scale with the abundance of memory and processing assumptions. Some people call it bloat. Others call it efficient.
I'm gonna stray a little from the norm here.

Most people seem to think programmers of yore were smarter, and generally, that's probably true -on average-. I mean, there weren't boot camps back then.

That aside though, the scene has changed. Write your super efficient perfect game, and nobody will play it. Look at Ludem Dare, and all the JSxK competitions. Those smart people still exist today.

But the landscape has changed so much, that consumers want the pretty graphics, heavy resources, social aspects. They don't care if it's 1kb or 1tb.

In short, people of yesterday were resource constrained so had to write smart hacks. People today have many more resources available, and use those. Both are using every bit of what they have available.

Players have always cared a lot about graphics. Descent looked fckin amazing in 1995
The last game I was big into was Q1. When GLQuake came out, it felt like I was in the future. Compare GLQuake to even some indy game today and it looks awful.

You're right - expectations change and most everything else is nostalgia.

I had an Xbox hooked up around 2002 at a friends house (high school years for me) and their father commented that Dead or Alive looked like a movie to them. They didn’t know how games could look more realistic.

Crazy how far we have come since then.

Graphics programmer has got to be up there with chef and prostitute for evergreen, recession-proof careers. And the nice thing is, the tech doesn't even change that quickly. People are still using OpenGL, the original version of which was released before DOOM!
Whether it's recession proof or not is a fairly open question, one thing for sure is that it's not layoff proof. Game companies are notorious for doing mass layoffs or shutting down with little to no advance notice and as a game developer you can never be too sure about your job security at any one company, not to mention it's kind just accepted that you will be working longer hours for less pay.

As for the notion that the tech doesn't change that quickly, that is simply false. Graphics programming is unbelievably cut throat, competitive, and advances very fast. Not only are there advances in technology, but different games also have different aesthetics that often require very niche or customized development to get just right so as to avoid your game feeling generic.

Graphics APIs and hardware change almost every other year and the major titles have to adapt to the latest features. You can certainly make good games without focusing on graphics, plenty of good indie games or games that don't focus on graphics, but if you are a graphics programmer and your game does focus on that, you are constantly having to keep up with advances in technology.

I stand corrected! You sound like you actually know what you're talking about.

Sigh. The search for a field where you can learn some stuff once and coast continues....