Live data from Hacker News

Ubershaders: A Ridiculous Solution to an Impossible Problem (2017)

dolphin-emu.org

61–70 of 72 posts

Re: Ubershaders: A Ridiculous Solution to an Impossible Problem (2017)

#61
post #30
post #16

Earlier quoted context omitted.

It is an issue with some modern games (I recently played a title that had a "Preparing Shaders..." loading screen); the main difference is that those games know the full set of what they need to do and can precompile most of them up-front, while an emulator like Dolphin needs to handle whatever the game throws it on the fly. Also, games might know what shaders it can skip and what it can't, but Dolphin can't skip sha…

It’s not a problem for the PS/Xbox/Switch. They have known hardware and it can all be recompiled. But from what I’ve heard it’s often still an issue on PCs (I’m a Mac guy). I’ve seen videos of shader compilation stutters, even in games with a precompilation step that’s supposed to avoid that. Digital Foundry has covered this many times. The link in a sibling comment to them on Eurogamer is a great place to start.

Steam works around this by letting users enable shader precompilation in settings. If you want a console-like experience (eg. like Steam Deck) and you don't care so much about storage space, you can toggle it on and eliminate the stutter before booting up. Most people leave this off, which really ruins the experience on shader-heavy engines like UE4.

This is generally an everyone problem, though. If gaming on Mac was caught-up with where Linux is today, there would probably be a few precompilation steps there too. If you wanted to play Fallout 3 on your Android/iPhone device, it's the same story.

Re: Ubershaders: A Ridiculous Solution to an Impossible Problem (2017)

#62
post #18

It's interesting to see the parallels between this and an engine for a dynamic programming language. The one I'm most familiar with is JavaScript. When you first need to run something, you run it on the interpreter (JS) / ubershader (Dolphin). But once you know it's going to be run repeatedly (rarely for JS, almost always for Dolphin), you kick off an async compilation to produce JIT code (JS) / a specialized shader…

While JS can be interpreted, V8 and SpiderMonkey (the two most common JS engines) will _always_ compile before execution -- JS is _never_ directly interpreted these days (aside from more niche engines). https://v8.dev/blog/ignition-interpreter https://firefox-source-docs.mozilla.org/js/index.html#javasc...

That's not exactly right. I believe you're mixing up bytecode generation with compilation. The source code will indeed be compiled down to bytecode, but that doesn't count since it does not change the generality. As in, bytecode is no more specialized than the original source code. It's the same with shaders — the ubershader does not interpret the original shader source text.

Both V8 and SM will interpret the bytecode until it warms up enough to be compiled to specialized machine code. ("Warms up" == "is observed to execute enough times".) There are some subtle distinctions about whether the interpreter is implemented in C++ or generated by a variant of the JIT code compiler, but as with the shaders the main point is whether it's executed in a way that works for everything or is specialized to a particular purpose (and varying degrees of specialization are implemented, with various mechanisms for falling back to a more general execution mechanism if the specialization assumptions no longer hold).

Your SpiderMonkey doc link points to a section named "JavaScript Interpreter". The title is correct, that section is indeed about the mechanisms for interpreting JavaScript [bytecode].

The V8 link is a little tricky, since it leads off with "Code is initially compiled by a baseline compiler", but if you read a little further, it says "...the V8 team has built a new JavaScript interpreter, called Ignition, which can replace V8’s baseline compiler". Basically, V8 experimented for a while with dropping the interpreter, but for the reasons described well in that document, they went back to initially running in an interpreter. The article is quite nice and describes quite a bit about the tradeoffs involved. It's 8 years old, but I believe the overall picture isn't that different today.

(Source: I am an engineer on the SpiderMonkey team.)

Re: Ubershaders: A Ridiculous Solution to an Impossible Problem (2017)

#63
post #5

Has it really been 9 years since I started working on Ubershaders? I'm a little surprised no better solution has come along. Vulkan didn't even exist back then (and DirectX 12 had only just released) but instead of making things better, it digs it's feet even deeper into the assumption that all shaders will be known ahead of time (resulting in long "shader recompilation" dialogs on startup on many games). I've been t…

Very cool work!

I had to solve a similar problem years ago, during the transition from fixed function to shaders, when shaders weren't as fast or powerful as today. We started out with an ubershader approximating the DX9/OpenGL 1.2 fixed functions, but that was too slow.

People in those days thought of rendering state being stored in a tree, like the transform hierarchy, and you ended up having unpredictable state at the leaf nodes, sometimes leading to a very high permutation of possible states. At the time, I decomposed all possible pipeline state into atomic pieces, eg, one light, fog function, texenv, etc. These were all annotated with inputs and outputs, and based on the state graph traversal, we'd generate a minimal shader for each particular material automatically, while giving old tools the semblance of being able to compose fixed function states. As for you, doing this on-demand resulted in stuttering, but a single game only has so many possible states - from what I've seen, it's on the order of a few hundred to a few thousand. Once all shaders are generated, you can cache the generated shaders and compile them all at startup time.

I wonder if something like this would work for emulating a Gamecube. You can definitely compute a signature for a game executable, and as you encounter new shaders, you can associate them with the game. Over time, you'll discover all the possible state, and if it's cached, you can compile all the cached shaders at startup.

Anyhow, fun stuff. I used to love work like this. I've implemented 3DFx's Glide API on top of DX ages ago to play Voodoo games on my Nvidia cards, and contributed some code to an N64 emulator named UltraHLE.

Re: Ubershaders: A Ridiculous Solution to an Impossible Problem (2017)

#64
post #5

Has it really been 9 years since I started working on Ubershaders? I'm a little surprised no better solution has come along. Vulkan didn't even exist back then (and DirectX 12 had only just released) but instead of making things better, it digs it's feet even deeper into the assumption that all shaders will be known ahead of time (resulting in long "shader recompilation" dialogs on startup on many games). I've been t…

>On the positive side, ubershaders do solve the problem, and modern GPU drivers do a much better job at accepting ubershaders than they did 9 years ago. Though that's primarily because (as far as I'm aware) examples of Dolphin's ubershader have made their way into every single shader compiler test suite.

How'd that come to be? Just interesting code for test suites or did you guys advocate for it to be included?

Re: Ubershaders: A Ridiculous Solution to an Impossible Problem (2017)

#65
post #62

Earlier quoted context omitted.

While JS can be interpreted, V8 and SpiderMonkey (the two most common JS engines) will _always_ compile before execution -- JS is _never_ directly interpreted these days (aside from more niche engines). https://v8.dev/blog/ignition-interpreter https://firefox-source-docs.mozilla.org/js/index.html#javasc...

That's not exactly right. I believe you're mixing up bytecode generation with compilation. The source code will indeed be compiled down to bytecode, but that doesn't count since it does not change the generality. As in, bytecode is no more specialized than the original source code. It's the same with shaders — the ubershader does not interpret the original shader source text. Both V8 and SM will interpret the bytecod…

I don't think your reply conflicts with my comment. I was clarifying that modern JS engines do not directly interpret JS. JS is always compiled to bytecode which is then interpreted or compiled to machine code.

Re: Ubershaders: A Ridiculous Solution to an Impossible Problem (2017)

#66
post #5

Has it really been 9 years since I started working on Ubershaders? I'm a little surprised no better solution has come along. Vulkan didn't even exist back then (and DirectX 12 had only just released) but instead of making things better, it digs it's feet even deeper into the assumption that all shaders will be known ahead of time (resulting in long "shader recompilation" dialogs on startup on many games). I've been t…

I still don’t understand why you didn’t use the precompiled shaders packed with the games… you’re emulating the GameCube or Wii GPU, and it’s never going to change, and the games provide precompiled shaders.

That's the trick, they actually don't provide precompiled shaders as you know them. The graphics hardware back then was fixed function pipelines with a tremendous number of options to configure how they work. The downside is that you can't run truly arbitrary code but the upside is that they can instantaneously switch behavior as fast as setting a register.

Prior to ubershaders the emulator took a configuration for the hardware pipeline and turned that into a shader, which took time to compile. Ubershaders work by emulating the entire fixed function pipeline in one glorious shader until the smaller, more efficient shader can be compiled and slipped in.

Basically, the ubershader is the only thing that can actually understand the "shaders" packaged with the game and start using them with zero latency.

Why not just precompile all the possible hardware combinations? There's far more combinations than atoms in the universe. Why not just precompile all the hardware combinations that the game actually uses? There's no way to tell before hand without examining every branch of the game's code which ranges in difficulty from "computationally prohibitive" to "fundamental theorems of how computers work says this is impossible".

The article mentions that some users actually passed around cached shader packs, but that solution was brittle.

Re: Ubershaders: A Ridiculous Solution to an Impossible Problem (2017)

#67
post #19

Earlier quoted context omitted.

They're precompiled for the console GPU architecture, not the PC architecture, so they can't be used directly and still need to be emulated - I think those precompiled shaders are the input to the ubershader.

The GAMES THEMSELVES are precompiled for the PowerPC architecture, not the PC architecture, though. That didn’t stop anyone from creating Dolphin. GPUs (I’m told) have far fewer instructions to emulate than a CPU, so I’d think that low level emulation of the Flipper shaders would be no trouble. Can’t translate or transpile them to PC GPUs though because those instruction sets are somewhat secret, I think. I know noth…

The ubershader is the thing that emulates Flipper at a low enough level to use the precompiled "shaders" directly. Prior to that the precompiled "shaders" were examined and recompiled into individual shaders, a process that took time.

(Why "shaders" in quotes? Because they weren't shaders as we know them today but really more like lists of hardware flags for how to flow data through a fixed function pipeline)

Re: Ubershaders: A Ridiculous Solution to an Impossible Problem (2017)

#68
post #5

Has it really been 9 years since I started working on Ubershaders? I'm a little surprised no better solution has come along. Vulkan didn't even exist back then (and DirectX 12 had only just released) but instead of making things better, it digs it's feet even deeper into the assumption that all shaders will be known ahead of time (resulting in long "shader recompilation" dialogs on startup on many games). I've been t…

Very cool work! I had to solve a similar problem years ago, during the transition from fixed function to shaders, when shaders weren't as fast or powerful as today. We started out with an ubershader approximating the DX9/OpenGL 1.2 fixed functions, but that was too slow. People in those days thought of rendering state being stored in a tree, like the transform hierarchy, and you ended up having unpredictable state at…

> contributed some code to an N64 emulator named UltraHLE

That's a blast from the past, I distinctly remember reading up about UltraHLE way back when and then trying it our and for the first time being able to play Ocarina of Time on my middle class PC with almost no issues, that was magical.

Re: Ubershaders: A Ridiculous Solution to an Impossible Problem (2017)

#69
I've thought about writing a GPU side interpreter for SDF definitions for a while. I made a SDF shader generator that dumps out shaders with hard coded values, but doing it with bytecode would be cool. I'm sure this has been done before..
Post reply on HN