An interactive course · Dolphin emulator

How the GameCube drew worlds.

We start from the very beginning — what a digital picture even is — assuming you know nothing about computer graphics. Once the vocabulary is solid, we follow a frame through Nintendo's remarkable Flipper chip and out through Dolphin's emulator. Sixteen modules, each with live, software-rendered labs so you can drag, spin and break every idea, not just read it. No game artwork ships with this page; every image is drawn procedurally in your browser.

Part I

3D graphics fundamentals

This first part is pure fundamentals — nothing about any specific console or chip yet. It's the toolkit that every real-time renderer is built from, whether that's a phone, a modern PC GPU, or the chip we'll meet in Part II. We build the vocabulary one word at a time — pixel, vertex, transform, rasterise, texture, shade, depth-test — and you can drag and see each idea in a live lab. If a term is ever used before it's explained, that's a bug; tell us. Everything in Parts II and III is just these seven ideas wired together.

Modules 01–07Difficulty Absolute beginnerLabs 6 interactive
Module 01 · Part I

Pictures as pixels

GoalUnderstand, from scratch, how a picture on a screen becomes a grid of numbers a computer can store and change.

Let's start with what a picture on a screen physically is. Lean in close to any display — a TV, a phone, the family CRT the GameCube was plugged into — and the image falls apart into a grid of tiny glowing dots. Each dot is a pixel (from “picture element”), and it can show exactly one colour at a time. Step back and your eye fuses the grid into a smooth image. That's all a digital picture is: a rectangular grid of single-colour dots.

To put a picture inside a computer, then, we just need a way to write down one colour as numbers. The trick — and it's borrowed straight from your own eyes, which sense colour with three kinds of cone cell — is to describe every colour as a mix of red, green and blue light. Three numbers, each usually from 0 (none) to 255 (full blast), and any colour on screen falls out: (255, 0, 0) is pure red, (255, 255, 255) is white, (0, 0, 0) is black, (255, 207, 59) is a warm yellow. This scheme is called RGB, and each of the three numbers is a channel.

Now string it together. One picture = a grid of pixels; one pixel = three numbers. So a whole picture is just a long, ordered list of numbers — three per pixel, row after row. The block of memory that holds that list for the image currently on screen has a name you'll hear constantly for the rest of this course: the framebuffer. Draw into the framebuffer, and you've drawn onto the screen.

Analogy · A mosaic

A mosaic portrait is thousands of small single-colour tiles. Up close: coloured squares. Across the room: a face. A pixel grid is a mosaic whose tiles you can repaint sixty times a second — and the framebuffer is the numbered order sheet telling you which tile goes where. (If you've taken the sibling audio course, this should feel familiar: a framebuffer is to images exactly what a list of samples is to sound — the thing itself, as plain numbers.)

Two more words and the module's vocabulary is complete. The resolution is the size of the grid — how many pixels across and down. More pixels = finer detail = more memory: a 640 × 480 image at 3 bytes per pixel is about 900 KB, which is why resolution was precious on 2001-era hardware (you'll see the exact, surprisingly small numbers the GameCube worked with in Module 13). And a grid-of-pixels image is called a raster image — remember the word, because Module 04 is about rasterisation, the moment 3D shapes get turned into exactly this kind of grid.

R = 255 G = 207 B = 59 an 8 × 8 raster image one pixel, magnified …is just three numbers 64 pixels × 3 bytes = 192 bytes of framebuffer
Pictures as numbers. An image is a grid of pixels; each pixel is three channel values for red, green and blue. Store the whole grid's numbers in memory and you have a framebuffer — change a number, and a dot on screen changes colour.

Play with the idea below. The little landscape is repainted from scratch at whatever grid size you pick — drag the resolution from a chunky 8 × 8 up to 256 × 256 and watch detail appear as pixels shrink. Then switch off channels: with only R lit you're literally seeing one third of the framebuffer's numbers. Hover any pixel to read its three values.

Pixel lab — an image is a grid of numbersCanvas · live
Key takeaways
  • A digital image is a grid of pixels; each pixel is one colour, stored as three RGB numbers (0–255 per channel).
  • The framebuffer is the memory holding those numbers for the on-screen image — draw into it and the screen changes.
  • Resolution = the grid's size; more pixels means finer detail and more memory.
  • A grid-of-pixels image is a raster image — the destination every 3D scene must eventually reach.
Module 02 · Part I

From 3D to triangles

GoalSee how a 3D shape is described before it's drawn — as points in space joined into triangles — and why triangles, specifically.

Module 01 gave us the destination: a grid of coloured pixels. But a game world isn't stored as pixels — if it were, you could only ever look at it from one fixed angle, like a photograph. A 3D world is stored as geometry: shapes floating in space, which the machine re-draws from wherever the camera happens to be, sixty times a second.

The atom of that geometry is the vertex (plural vertices): a single point in 3D space, written as three coordinates (x, y, z) — how far right, how far up, how far away. A vertex on its own is just a dot; the shape appears when you join vertices up. Join two and you have an edge; join three and you have the workhorse of all real-time graphics: the triangle. A model built from many triangles sharing their vertices — a character, a tree, Hyrule — is called a mesh.

Why triangles, and not squares or pentagons or smooth curves? Three reasons, and they're all about being cheap and foolproof:

  • A triangle is always flat. Three points define exactly one plane — you cannot bend a triangle. A four-cornered polygon can twist like a Pringle, and then “which way is it facing?” has no single answer. Triangles never have that problem.
  • A triangle is unambiguous. Its three corners settle everything about it: its edges, its facing, and (as Module 04 will show) exactly which pixels it covers, with three simple tests.
  • Triangles are universal. Any polygon can be diced into triangles, and any curved surface can be approximated by enough small ones. So the hardware only needs to be brilliant at one thing, done millions of times.
Analogy · A disco ball

A disco ball looks round from across the room, but it's really hundreds of small flat mirrors. Every “curved” surface you've seen in a game is the same trick: flat triangles, small enough that your eye rounds them off. Use more, smaller mirrors and the ball looks rounder — at the cost of more work.

a vertex (x, y, z) 6 triangles 12 triangles 24 triangles the same “circle”, three budgets — more triangles, rounder silhouette
Triangle budgets. The same circle built from 6, 12 and 24 triangles (each fan of thin lines is one triangle per slice). Every curved surface in a GameCube game is this idea in 3D: more triangles look smoother but cost more to draw, so artists spent them like money.

One more idea completes the picture. A vertex usually carries more than its position: it can also hold a colour of its own, a texture address (Module 05), and a little arrow describing which way the surface faces there (Module 06). These extras are the vertex's attributes, and they're the raw ingredients every later stage cooks with. When you meet the real hardware in Module 09 you'll find it has an entire sub-system just for fetching vertices and their attributes efficiently — on a console, even the shape of this data is a performance decision.

Key takeaways
  • 3D worlds are stored as geometry (redrawable from any angle), not pixels.
  • A vertex is a point (x, y, z); a mesh is many triangles sharing vertices.
  • Triangles win because they're always flat, unambiguous, and can approximate anything.
  • Vertices carry attributes — colour, texture coordinates, a facing arrow — used by every later module.
Module 03 · Part I

Transforms & projection

GoalFollow a vertex from its model's own coordinates to a position on your flat screen — and see why far things look small.

We now have a mesh of vertices in 3D, and a screen that is a flat 2D grid. This module is the bridge: how does a point (x, y, z) end up at a particular pixel? The answer is a short chain of coordinate hops — worth learning by name, because the hardware in Module 10 is literally organised around them.

A mesh is modelled in its own private coordinates, with the origin somewhere convenient — between a character's feet, say. That's model space. To place the character in the level — walked to the bridge, turned to face east — every vertex is moved, rotated, maybe scaled, into the level's shared coordinates: world space. Then comes a sneaky, brilliant step: rather than move a camera through the world, we shift the entire world the opposite way, so the camera sits at the origin looking straight ahead. Those camera-relative coordinates are view space, and they make the final step almost embarrassingly easy.

Each of those hops is carried out by a matrix — don't flinch at the word. A matrix here is just a small table of numbers (4 × 4) that, multiplied against a vertex, applies a move-rotate-scale in one go. The magic property, and the only one you need: multiplying two matrices together fuses two steps into one. Model→world and world→view collapse into a single model-view matrix, so the machine does one multiplication per vertex instead of a parade of them. When Module 10 mentions the GPU's “matrix memory,” this table of numbers is what lives there.

Analogy · Tracing on a window

Stand at a window, close one eye, and trace what you see onto the glass with a marker. You have just performed a perspective projection: every 3D point was collapsed along its line of sight onto a flat pane. Renaissance artists did exactly this with gridded glass panes; a renderer does it with arithmetic. The window is your screen; your fixed eye is the camera.

So how does the arithmetic version work? In view space the camera looks down the z axis, so a vertex's z is simply its distance ahead. Perspective projection is then one division: divide x and y by z (scaled by the lens strength). A point twice as far away gets its offsets halved — and that single division is the entire reason distant things look small, rails converge, and 3D feels three-dimensional. The lens strength comes from the field of view (FOV): wide angle = more world squeezed onto the screen.

Worked example

A vertex sits at (1.0, 0.5) sideways/up in view space. At depth z = 2 it projects to (1.0/2, 0.5/2) = (0.5, 0.25) on screen. Walk it back to z = 4 and it lands at (0.25, 0.125) — same object, half the size. That's perspective, in its entirety.

Two footnotes complete the picture. First, the region the camera can actually see — a truncated pyramid spreading from the eye, capped by a near and far plane — is called the view frustum; geometry outside it is thrown away or trimmed at its walls (clipping), so no time is wasted on things behind you. Second, there's a simpler projection that skips the divide entirely: orthographic projection just drops the z — no shrinking with distance at all. It looks wrong for worlds but perfect for flat things, which is exactly why games use it for menus and HUDs.

eye near plane = your screen far plane same-size cubes, different depths their projections: near = tall, far = short FOV everything outside the two long edges is clipped
Projection, seen from the side. Each vertex slides along its line of sight onto the near plane — the screen. Because the lines all meet at the eye, a far cube spans a shorter stretch of the plane than a near one: divide by depth, and distant things shrink. The wedge between the long edges is the view frustum.

The lab below runs this entire module in about forty lines of arithmetic — the same forty lines the hardware version bakes into silicon. Rotate the cube (model→world), pull it away from the camera (world→view), widen the lens, and — the best button on this page — flip between perspective and orthographic and watch depth itself switch off: the ground grid's rails stop converging and distance stops mattering.

Transform lab — spin a wireframe cube through the pipelineCanvas · live
Key takeaways
  • A vertex hops through named spaces: model → world → view → screen.
  • Each hop is a matrix — a small number-table — and matrices fuse, so the chain collapses to one multiply per vertex.
  • Perspective projection is a division by depth; that one divide is why far things look small.
  • The frustum bounds what's visible (clipping trims the rest); orthographic projection skips the divide — used for menus and HUDs.
Module 04 · Part I

Rasterisation — from triangle to pixels

GoalWatch the pivotal moment of all real-time graphics: deciding exactly which pixels a triangle covers, and smoothly spreading the vertices' values across them.

Here is the hinge of the whole course. Module 03 left our triangle projected onto the screen — but a screen (Module 01) isn't a sheet of paper; it's a grid of pixels. Something has to decide, pixel by pixel, which cells of the grid this triangle covers. That act is rasterisation — literally “turning into a raster image” — and the machinery that does it is called the rasteriser.

The rule is stricter than you might guess. A pixel isn't “a bit covered” or “mostly covered” — the hardware tests one exact point, the pixel centre, and asks a yes/no question: is this point inside the triangle? The elegant part is how it asks. Each of the triangle's three edges splits the plane into two halves. For each edge there's a tiny formula — an edge test, just two multiplies and a subtract — whose sign says which side of that edge the point is on. Inside the correct half-plane of all three edges at once? Then the pixel centre is inside the triangle, and the pixel is covered. No trigonometry, no curves: three sign checks, repeated across the grid at ferocious speed. This is the single operation GPUs were invented to do.

Analogy · Stencilling a wall

Rasterisation is spray-painting through a stencil onto graph paper. The stencil's opening is the triangle; the graph-paper cells are pixels. A cell counts as painted if its centre dot got paint. Cells along the cut edge become the familiar “staircase” of jagged pixels — walk close to any GameCube-era game and you can see exactly those stairs, because this is precisely how its pictures were made.

inside all 3 → covered wrong side of one edge → out each dashed line is one edge test: “which side are you on?”
Three half-planes make a triangle. Extend each edge into a full line (dashed) and ask, for a pixel centre, which side it falls on — a two-multiply edge test. A centre on the inner side of all three lines (cyan arrows) is covered; fail even one and it isn't. This yes/no per centre is all rasterisation is.

Interpolation: spreading the vertices across the face

Knowing a pixel is covered isn't enough — we need to know what value it should take. The three vertices carry attributes (Module 02): colours, and soon texture coordinates and lighting. A pixel in the middle of the triangle should get a smooth blend of all three. Delightfully, the machinery is already paid for: those three edge-test numbers double as weights. A pixel near vertex A gets a big A-weight and small B and C weights; normalise the three and you can blend any attribute: value = wA·A + wB·B + wC·C. These weights are called barycentric coordinates, and this blend — interpolation — is how a three-cornered description becomes a smoothly varying surface. Every gradient, every texture lookup, every soft lighting falloff in Parts I and II rides on it. (One honest complication waits for Module 05: after Module 03's perspective divide, the blend must be depth-corrected or textures visibly swim — the hardware handles it, but it's why the maths is called perspective-correct interpolation.)

In the lab, drag the three vertices around the big-pixel grid. Every dot is a pixel centre; cells light up when their centre passes all three edge tests — watch the staircase crawl along the edges as you move a vertex. Then switch to interpolate and the covered pixels take their barycentric blend of the three vertex colours.

Rasteriser lab — drag a triangle over the pixel gridCanvas · drag
Key takeaways
  • Rasterisation decides which pixels a triangle covers, by testing each pixel centre.
  • The test is three edge tests — sign checks against the triangle's three half-planes. Cheap, exact, and massively repeatable.
  • The same edge values, normalised, become barycentric weights that interpolate any vertex attribute across the face.
  • Edge staircases (“jaggies”) are the visible fingerprint of the pixel-centre rule.
Module 05 · Part I

Texturing — gluing pictures onto triangles

GoalLearn how a flat image is wrapped over geometry, how a pixel picks its colour from that image, and why distant textures shimmer without mipmaps.

Interpolated vertex colours (Module 04) give you smooth gradients — but a brick wall isn't a gradient. Modelling every brick as geometry would cost millions of triangles, and Module 02 taught us triangles are money. The industry's great cheat: keep the wall as two triangles and glue a picture of bricks onto them. The picture is a texture, and to keep vocabularies straight, its pixels get their own name: texels.

The glue is a pair of numbers per vertex called UV coordinates: u says how far across the texture that corner grabs, v how far down, each running 0 to 1. They're vertex attributes, so Module 04's interpolation spreads them across the face for free: every covered pixel gets its own fractional (u, v), and the renderer fetches the texture there — a step called sampling. (This is where the perspective-correct fine print from Module 04 earns its keep: interpolate UVs without the depth correction and the picture visibly bends and swims across the surface — a glitch PlayStation-era games wore openly; the hardware we meet in Part II corrects it.)

Analogy · Wrapping paper

Texturing is gift-wrapping geometry. The wrapping paper is the texture; the UVs are pencil marks on the paper saying “this printed spot glues to this corner of the box.” Stretch the paper between the marks and the pattern lands on the surface. Aim two corners at the same spot on the paper and the pattern squashes; that stretching and squashing is entirely under the artist's control.

Sampling: which texel, exactly?

An interpolated (u, v) almost never lands dead-centre on one texel — it lands between texels (the audio course hits the identical problem between audio samples, and solves it the identical way). The renderer must decide what colour “between” means, and the decision has a quality ladder:

  • Nearest — grab whichever texel is closest. Cheapest, but surfaces look blocky up close, and the winner snaps from texel to texel as things move — edges crawl and sparkle.
  • Bilinear filtering — fetch the four surrounding texels and blend them by closeness, in both directions. Smooth up close, and the standard on GameCube-class hardware.

But bilinear hides a nastier problem at the other end of the distance scale. Picture a 64-texel-wide floor tile so far away it covers 4 pixels on screen. Each pixel now “contains” hundreds of texels, but a bilinear sample still reads only four of them — a random pinprick into a region that ought to be averaged. Move the camera a hair and the pinprick lands somewhere new: the distance shimmers and boils. Readers of the audio course will recognise this instantly — it is aliasing, the same disease as audio's folded-back frequencies: detail finer than your sampling grid doesn't vanish, it turns into garbage.

The graphics cure is the mipmap: alongside the full texture, pre-store it at half size, quarter size, and so on down to a single texel — each level a correctly averaged miniature (the memory cost is only one-third extra). When a surface is far away, sample the level whose texels are about the size of your pixels: the averaging was done in advance. Distant ground goes calm; the price is a touch of blur far away, and a visible seam where levels change — which fancier filtering (trilinear: blend two levels) smooths over.

(0,0) (1,1) the texture (u,v space) UVs map it on screen: near rows huge… …far rows: many texels per pixel → shimmer mipmap chain 64² 32² 16²… pre-averaged miniatures — far pixels read a small level
UVs, perspective, and the mipmap fix. Vertex UVs pin the texture (left) onto geometry (middle). Perspective packs ever more texels into each distant pixel — single samples there just pick noise, which animates as shimmer. A mipmap chain (right) pre-averages the texture at every scale so far pixels can read a level that's already calm.

The lab renders a genuine perspective floor, texel by texel, in software. Let it scroll and compare the three filters — the difference is unmistakable in motion: nearest crawls everywhere, bilinear is lovely up close but boils in the distance, and bilinear + mipmap calms the horizon at the cost of softness. Every texture-quality slider you've ever seen in a game is negotiating this exact trade.

Texture-filter lab — shimmer vs blur on a receding floorCanvas · animated
Watch the horizon: nearest/bilinear boil while scrolling; mipmaps trade the boil for calm blur.
Key takeaways
  • Textures put detail on cheap geometry; texels are the texture's own pixels; per-vertex UVs are the glue.
  • Interpolated UVs land between texels — nearest snaps (blocky, crawly); bilinear blends the 4 neighbours.
  • Distant surfaces cram many texels per pixel; single samples turn that detail into shimmer — texture aliasing.
  • Mipmaps pre-average the texture at every scale (⅓ extra memory) so distant pixels sample calm data.
Module 06 · Part I

Light & shade

GoalUnderstand how flat triangles are made to look lit — normals, the diffuse and specular recipes, and the per-vertex (Gouraud) shading that defined the era.

Texture a sphere with our checkerboard and it still looks like a flat sticker, because nothing about it responds to light. Real objects are bright where they face a light source and dark where they face away. To fake that, each point on a surface needs to know which way it's facing — and that's one more vertex attribute: the normal, a tiny arrow of length one sticking straight out of the surface. Flat wall: all normals parallel. Sphere: normals fan outward like a hedgehog. Normals are authored with the mesh, and (Module 04 again) they interpolate across triangles like any other attribute.

With normals in hand, the era's entire lighting model is two intuitions dressed as arithmetic:

  • Diffuse — matte surfaces are brightest facing the light, fading as they turn away. Arithmetically: brightness follows the angle between the normal and the light direction (the famous N·L — 1 facing dead-on, 0 at 90°). Chalk, cloth, skin: mostly diffuse.
  • Specular — shiny surfaces add a bright highlight where light bounces mirror-like toward your eye; the tighter the highlight, the glossier the material reads. It depends on the viewer's position, not just the light's — tilt a glossy card and the gleam slides.

Add a floor of ambient light — a flat minimum so shadowed sides don't crush to pure black, standing in for all the light bouncing around the room — and you have the classic recipe: colour ≈ ambient + diffuse·(N·L) + specular·gleam. It's an approximation physicists would wince at, and it drew a decade of beautiful games.

Analogy · A torch in a dark room

Hold a torch near a matte wall: the bright patch is where the wall faces the torch, dimming smoothly around it — that's diffuse. Now aim it at a framed picture: one glaring spot of glass jumps out, and it moves as you move — that's specular. The dim visibility everywhere else, from light bouncing off every wall, is ambient.

normals N light direction L …to 0 facing away N·L ≈ 1 here… flat: one colour per face Gouraud: blended corners same four faces, same four lit corners — the only difference is whether colours blend ACROSS faces (Module 04's interpolation)
Normals meet the light. Each surface point's normal (cyan) is compared with the light direction (amber): aligned = bright, perpendicular = dark — that's N·L diffuse shading. Right: the same lit values displayed flat (one colour per face — visible facets) vs Gouraud (corner values interpolated — the facets melt).

Where to run the recipe: per-vertex (Gouraud) shading

One decision remains: run that recipe at every pixel, or only at every vertex? Per-pixel is far more work — 2001-era hardware could not afford it as a general diet. The era's standard, and the GameCube's native mode, is per-vertex lighting, universally called Gouraud shading (after Henri Gouraud, 1971): compute the full recipe at the three corners only, then let Module 04's barycentric interpolation smear the resulting colours across the face for free. For a mesh of thousands of vertices covering hundreds of thousands of pixels, that's a spectacular discount.

The discount has a tell, and once you see it you'll spot it in every GameCube game: interpolation can only blend between corner values, never invent a peak inside a face. Diffuse light varies slowly, so it survives beautifully — but a tight specular highlight that should bloom mid-triangle gets sampled at three corners that all miss it: the highlight dims, wobbles, or breaks into a starburst on coarse meshes. In the lab, drop specular to zero and flat vs Gouraud is a subtle facet story; crank specular and drag the light slowly — watch the highlight pulse as it crosses faces. That pulse is Gouraud's signature, and half the reason Part II's TEV tricks (Module 11) were so prized for faking crisper shine.

Lighting lab — a per-vertex-lit sphereCanvas · drag
Key takeaways
  • Normals tell each surface point which way it faces; they're vertex attributes like colour and UVs.
  • The classic recipe: ambient floor + diffuse (N·L) + a viewer-dependent specular highlight.
  • Gouraud shading runs the recipe per vertex and interpolates colours — the era's affordable standard, and the GameCube's native mode.
  • Its tell: tight specular highlights dim and pulse, because interpolation can't invent a peak inside a face.
Module 07 · Part I

Depth & blending

GoalSolve the last fundamental problem — near things must hide far things — with the z-buffer, and see why transparency refuses to be solved the same way.

One problem remains before Part I's toolkit is complete, and it sounds too obvious to need solving: near things must appear in front of far things. But triangles are drawn one after another, each splatting its pixels into the framebuffer — and a triangle drawn later lands on top, regardless of whether it's actually nearer. Draw the mountain after the castle and the mountain sits in front of the castle.

The oldest fix is the painter's algorithm: sort everything by distance and draw back-to-front, like a painter laying down sky, then hills, then trees. It fails in two ways. Sorting thousands of triangles every frame is expensive — and worse, sometimes no correct order exists: two triangles that pass through each other (or three that overlap in a cycle) each need to be both in front of and behind the other. A whole-triangle decision can't be right everywhere.

The fix that won is gloriously blunt: stop deciding per triangle and decide per pixel. Next to the framebuffer, keep a second grid the same size — the z-buffer (or depth buffer) — holding, for every pixel, the depth of the nearest thing drawn there so far. When a triangle wants to colour a pixel, its interpolated depth (Module 04's machinery again — depth is just one more attribute) is compared first: nearer than what's stored? Draw, and record the new depth. Farther? Skip. Draw order stops mattering; intersecting triangles resolve exactly, pixel by pixel, along their true crossing line. The cost is honest: a whole second buffer's worth of memory and a read-compare-write at every covered pixel — remember that bill when Module 08 shows you the extreme lengths Flipper's designers went to to pay it.

Analogy · The take-a-number deli

Every pixel runs a deli counter where lowest number wins. Each arriving triangle-pixel holds a ticket — its depth. The counter keeps the smallest ticket seen so far, and a newcomer either beats it (new colour, new record) or is turned away. Nobody needs to queue in order, because the record-keeping makes order irrelevant. That's the z-buffer.

eye one pixel's line of sight surface A · depth 0.35 surface B · depth 0.62 this pixel's records colour z-buffer 0.35 B arrives with 0.62 → 0.62 > 0.35 → rejected, whatever order they were drawn in
Per-pixel depth records. Along one pixel's line of sight, surface A (depth 0.35) and surface B (0.62) both want the pixel. The z-buffer stores the nearest depth drawn so far; B's test fails against A's record, so A shows — even if B was drawn last.

Blending: where the z-buffer meets its match

Everything so far assumed opaque surfaces. But games need glass, water, smoke, fire — things you can see through. For that, a pixel gains a fourth channel next to R, G and B: alpha, its opacity. Drawing a translucent pixel means alpha blending: mix the incoming colour with whatever's already in the framebuffer — result = new·α + behind·(1−α) — the on-screen cousin of the audio course's mixing equation.

And here's the sting: blending re-introduces the ordering problem the z-buffer just solved. Mixing with “what's already there” only gives the right answer if everything behind the glass was drawn first — order matters again, by construction. Worse, the z-buffer actively fights you: a translucent surface that writes its depth then rejects things behind it that should have shown through, drawn a moment later. The standard truce, on GameCube as everywhere since: draw all opaque geometry first (z-buffer reigns), then sort just the translucent bits back-to-front and blend them on top. It's a compromise with real cracks — sort by what, for triangles that interpenetrate? — and transparency has stayed genuinely awkward in real-time graphics ever since. In the lab, drop the magenta triangle's alpha and watch each mode fail in its own way.

Z-buffer lab — two triangles that pass through each otherCanvas · live

That completes the toolkit: pixels, triangles, transforms, rasterisation, textures, lighting, and depth. Every frame of every GameCube game is these seven ideas run in a row, millions of times, sixteen milliseconds at a time. Time to meet the chip that ran them.

Key takeaways
  • Later draws overwrite earlier ones, so visibility needs solving; whole-triangle sorting (painter's algorithm) is costly and sometimes impossible.
  • The z-buffer stores per-pixel depth and keeps the nearest — order-independent, exact even for intersecting triangles.
  • Alpha blending mixes new colour with the framebuffer (new·α + behind·(1−α)) for glass, smoke and water.
  • Blending needs back-to-front order again — so engines draw opaque first, then sorted translucents. Transparency stays painful.
Part II

The Flipper GPU

Now we put the Part I toolkit into real silicon. The GameCube draws everything with one remarkable chip — Flipper — that is far more than a graphics processor: it's the hub the whole console hangs off. These six modules cover the chip itself, how the CPU feeds it commands, its hardware transform-and-lighting unit, the TEV combiner that gave GameCube games their looks, its texture machinery, and the strange, wonderful journey a finished pixel takes to reach a 2001 television. From here on we'll name real hardware, but every piece maps back to a fundamental you already know.

Modules 08–13Difficulty HardwareLabs 2 interactive
Module 08 · Part II

Meet Flipper

GoalMeet the actual chip — who designed it, what lives inside it, and why its designers buried memory inside the silicon itself.

The GameCube's graphics chip is called Flipper, and it has a pedigree worth knowing. It was designed by ArtX, a company founded largely by engineers from Silicon Graphics — the team whose previous console work had produced the Nintendo 64's graphics hardware. ArtX was acquired by ATI while Flipper was in development (which is why the finished console wears an ATI badge), and the chip was manufactured by NEC. It runs at 162 MHz — exactly one third of the 485 MHz Gekko CPU, keeping the two clocks in lockstep.

Calling Flipper a “GPU” undersells it. It's really the console's hub — what we'd now call a system-on-chip. Alongside the graphics pipeline it houses the memory controller for the console's main RAM, the I/O interfaces for controllers, discs and memory cards, the video interface that drives the TV (Module 13) — and the entire audio DSP, the star of this course's sibling audio course. Every sound and every pixel that console ever produced passed through this one piece of silicon.

The headline feature: memory inside the chip

Now recall two bills that Part I ran up. Module 07's z-buffer demands a read-compare-write at every covered pixel; Module 05's texture sampling demands four texel fetches per pixel, per texture. At 2001 clock speeds, ordinary external memory simply could not serve that firehose — bandwidth, not arithmetic, is the classic wall GPUs hit. Flipper's designers answered with the chip's defining feature: they put the hottest memory inside the chip itself, as embedded 1T-SRAM — about 3 MB of it, split into two pools:

  • An embedded framebuffer — the EFB, roughly 2 MB — where the frame being drawn lives: Module 01's framebuffer and Module 07's z-buffer, physically on the die. Every pixel write, depth test and blend happens here at full speed, never touching external RAM.
  • A texture cacheTMEM, about 1 MB — staging the textures currently in use so Module 05's four-fetches-per-pixel habit is served on-chip too.

The result: the operations Part I showed to be the most punishing became the operations Flipper never worried about. The trade — and Part II keeps meeting it — is that on-chip memory is small, and everything must be staged in and copied out through main RAM (24 MB, itself 1T-SRAM). Module 12 shows how textures squeeze into that 1 MB; Module 13 shows the copy-out dance the little EFB forces on every single frame.

Analogy · A chef's mise en place

A restaurant kitchen doesn't run to the pantry for every pinch of salt: the chef keeps a small tray of everything within arm's reach and restocks it between rushes. Flipper's embedded memory is that tray — tiny (2 MB EFB, 1 MB TMEM) but reachable without breaking stride, while the pantry (24 MB of main RAM) holds everything else and refills the tray in bulk.

Clock
162 MHz
⅓ of Gekko's 485 MHz
Designed by
ArtX → ATI
ex-SGI team · fabbed by NEC
EFB
≈2 MB
embedded 1T-SRAM framebuffer
TMEM
1 MB
embedded texture cache
TEV stages
16
Module 11's combiner
Textures/pass
8
simultaneous texture maps
Flipper system-on-chip architecture The Gekko CPU on the left sends a command stream into Flipper. Inside Flipper, the command processor feeds the XF transform unit, the setup/rasteriser, the TEV combiner and the pixel engine writing to the 2 MB embedded framebuffer, with a 1 MB texture memory feeding the TEV. Flipper also contains the memory controller, I/O, the audio DSP and the video interface. Main RAM sits on the right; the video interface drives the TV. Gekko CPU 485 MHz PowerPC Flipper 162 MHz · ArtX/ATI · system-on-chip CP command processor XF transform & lighting Setup / raster edge tests · Mod 04 TEV 16 combiner stages PE · blend + z pixel engine · Mod 07 TMEM 1 MB textures EFB 2 MB colour + z texels pixels Memory controller · I/O RAM, disc, pads, cards Audio DSP → the sibling audio course VI · video interface scans a frame out to the TV Main RAM 24 MB 1T-SRAM TV interlaced · Mod 13 commands (Mod 09) finished frame copies out to RAM (Mod 13)
The whole chip at a glance. The left column is Part I in silicon: the CP feeds the XF, triangles are set up and rasterised, the TEV colours each pixel, and the pixel engine blends and depth-tests into the on-chip EFB. The right column is everything else Flipper does for the console — including housing the audio DSP. Modules 09–13 walk this diagram top to bottom.

On paper. Flipper's four pixel pipelines at 162 MHz give a theoretical fill rate of about 648 megapixels per second, and Nintendo quoted 6–12 million polygons per second as a realistic in-game figure rather than a lab-condition peak — a small marketing rebellion in an era of inflated numbers. Treat all such figures as ceilings; real frames spend their budget on Part I's whole chain, not one stage.

Key takeaways
  • Flipper: designed by ex-SGI engineers at ArtX (finished under ATI), fabbed by NEC, clocked at 162 MHz — ⅓ of the CPU clock.
  • It's a system-on-chip: graphics plus the memory controller, I/O, video interface and the audio DSP.
  • Its defining trick is ~3 MB of embedded 1T-SRAM: a ~2 MB EFB for the frame being drawn and 1 MB TMEM for textures.
  • Embedded memory bought bandwidth — the true currency of Module 05's and 07's per-pixel costs — at the price of being small.
Module 09 · Part II

Feeding the chip — the CP, the FIFO & vertex formats

GoalSee how the CPU streams work to Flipper without ever waiting for it — and how clever data formats kept precious memory small.

A GPU is only as fast as the queue feeding it. If the CPU had to hand Flipper one triangle at a time and wait for each to finish, both chips would spend most of every frame idle. So the two processors communicate the way a busy kitchen does: through an order rail. The CPU writes drawing commands into a ring of memory called the FIFO, and Flipper's command processor (the CP, the front door in Module 08's diagram) consumes it at its own pace. Producer and consumer never wait for each other unless the ring runs completely full or empty.

The CPU side has a lovely piece of engineering grease. Writing single words to memory-mapped hardware is slow, so the Gekko CPU has a write-gather pipe: point it at the FIFO and every individual store the game makes is silently collected into full-size bursts. The game's code just says “position, colour, position, colour…” and the hardware packs the stream densely behind its back. GX — the GameCube's graphics API — is essentially a library of small functions that push bytes into this pipe.

Analogy · The sushi rail

The CPU is the kitchen, Flipper is the diner, and the FIFO is the conveyor rail between them. The kitchen keeps placing plates without asking whether the last was eaten; the diner keeps lifting plates without visiting the kitchen. Both run flat out, and only a completely full or empty rail ever stalls anyone.

Display list
A pre-recorded run of commands stored in RAM that the CP can replay on demand. Draw-the-castle once, then replay it every frame for the cost of one “call” — sparing the CPU from re-issuing thousands of commands.
Vertex attribute
One ingredient of a vertex (Module 02): position, normal, colour, UVs. On GameCube each can be sent inline or fetched by the CP from an array.
Indexed arrays
Instead of repeating a vertex's data every time a triangle uses it, store the data once in an array and send a small index. Shared vertices — and meshes share almost everything — are stored once.

That last row deserves the spotlight, because it's where the console's memory frugality shines. Flipper's vertex formats are configurable per attribute: a game declares, up front, “positions are 16-bit integers with a scale, colours are RGBA bytes, UVs are 8-bit fractions, and everything arrives as indices into these arrays.” A cube needs only 8 positions in memory even though its 12 triangles reference corners 36 times; a terrain's height grid can be 16-bit instead of float. Compare the audio course's Module 04 — 4-bit ADPCM instead of 16-bit PCM — and you'll see the same house philosophy: on a console, the format of the data is a design decision, and bytes are budget.

Gekko CPU GX calls write-gather pipe FIFO ring · in main RAM GPU reads behind CP decode commands → XF Module 10 attribute arrays (RAM) positions: p0 p1 p2 p3 … normals: n0 n1 n2 n3 … UVs: t0 t1 t2 t3 … vertex in the FIFO: idx 7 · idx 7 · idx 2 CP looks them up a corner shared by 6 triangles is stored once — indices are the memory saving
The feeding path. Game code makes GX calls; the write-gather pipe packs them into a FIFO ring in main RAM; the CP consumes the ring at its own pace, resolving small vertex indices into full attributes from shared arrays. Nobody waits for anybody — the FIFO absorbs the difference.

Emulator foreshadowing. Everything Dolphin knows about a frame arrives through this FIFO — which is why Dolphin's graphics debugger can record a “FIFO log” of the raw command stream and replay it, frame-perfect, with no game running at all. When Part III translates GPU state into shaders, the state it's translating is precisely what these commands set.

Key takeaways
  • CPU and GPU are decoupled by a FIFO command ring in main RAM — producer and consumer run flat out.
  • Gekko's write-gather pipe turns the game's individual stores into efficient bursts; GX is the library that feeds it.
  • Display lists pre-record command runs for cheap replay.
  • Configurable vertex formats + indexed attribute arrays store shared data once, in the smallest type that suffices — bytes are budget.
Module 10 · Part II

Hardware T&L — the XF unit

GoalSee Modules 03 and 06 cast into silicon: the transform unit that moves, clips and lights every vertex without the CPU lifting a finger.

Once the CP has assembled a vertex, it hands it to the XF unit — the transform engine, and the reason the GameCube's spec sheet could say hardware T&L (transform and lighting). On mid-90s consoles, Module 03's matrix-multiply-per-vertex ran on the CPU (or a CPU-adjacent coprocessor), eating cycles the game wanted for physics and AI. Flipper's generation moved the whole job onto the graphics chip: the CPU describes where things are, and XF does the arithmetic for every single vertex.

XF is Module 03 made physical. It holds a small on-chip matrix memory — dozens of slots for model-view and texture matrices — and each incoming vertex says which slots to use. That per-vertex choice is sneakily powerful: a jointed character loads one matrix per bone, and each vertex picks (or blends) its bone's matrix — skinned animation, essentially free. After transforming, XF handles clipping against Module 03's frustum, and the perspective divide happens on the way to the rasteriser.

Then comes the “L.” XF implements Module 06's recipe — per-vertex, Gouraud-style, exactly as that module described — for up to 8 hardware lights, whose results land in two colour channels — two independent lit colours per vertex (each with its own alpha). Why two? Because they flow downstream as separate ingredients: a game can light the main material on channel 0 and, say, a rim or highlight term on channel 1, and let Module 11's TEV combine them in different ways. XF also runs texgen — generating or transforming texture coordinates per vertex, which is how effects like projected shadows and environment-mapped shine get their UVs without the CPU computing any of it.

Analogy · The pasta machine

XF is a hand-cranked pasta machine bolted to the pipeline: raw vertex dough goes in one end and every piece comes out rolled (transformed), trimmed to the tray (clipped) and seasoned (lit) — identically, tirelessly, one per crank at 162 MHz. The CPU's only job is choosing which rollers to install: it loads the matrices and light settings, then feeds dough.

vertex in from the CP transform pick a matrix slot (Mod 03) matrix memory bones pick slots clip vs the frustum (Mod 03) light 8 lights → 2 colour channels (Mod 06) texgen make/transform UVs (Mod 05) to raster one vertex per crank — position, two lit colours and up to eight sets of texture coordinates come out the far end
XF, station by station. Each vertex is transformed by matrices chosen from on-chip matrix memory, clipped against the frustum, lit per-vertex into two colour channels, and given generated texture coordinates. Every station is a Part I module running in fixed-function silicon.

Note what XF is not: programmable. You cannot upload code to it; you configure it — which matrices, which lights, which texgens — from a fixed menu. The same is true of the TEV we meet next, and that single word, fixed-function, is the entire plot of Part III: Dolphin must recreate a menu-driven chip on modern GPUs that only speak programmable shaders.

Key takeaways
  • XF is hardware T&L: Module 03's transforms and Module 06's per-vertex lighting, in fixed-function silicon.
  • On-chip matrix memory with per-vertex slot choice makes skinned characters nearly free.
  • Up to 8 lights accumulate into two per-vertex colour channels; texgen manufactures UVs for effects.
  • XF is configured, not programmed — remember “fixed-function” for Part III.
Module 11 · Part II

The TEV — pixel shaders before pixel shaders

GoalUnderstand the texture environment unit — the 16-stage combiner that decided the colour of every GameCube pixel, and the source of the era's famous looks.

A rasterised pixel arrives carrying ingredients: up to eight texture samples (Module 05), the two lit vertex colours interpolated across the face (Modules 06 and 10), and a handful of constants. Something must decide how they combine into one final colour — texture times lighting? plus a glow? blended by a mask? On a modern GPU you'd write a small program (a pixel shader) to say so. Flipper predates pixel shaders. Instead it offers the TEV — the Texture EnVironment unit — and it is the single most characterful piece of silicon in the console.

The TEV is a chain of up to 16 stages. Each stage is the same tiny machine: it selects four inputs — called a, b, c and d — from a menu (a texture's colour, a rasterised colour channel, a konst constant, the previous stage's result, 0, 1…), and computes, per channel:

out = a·(1−c) + b·c + d

(plus an optional bias, a scale, and a clamp — and a parallel copy of the whole apparatus for alpha). Squint at that formula: it's a dial. With c at 0 you get a + d; at 1 you get b + d; in between it fades — and because c can be a texture or a lit colour, the fade can vary per pixel. Results land in one of a few accumulator registers that later stages read back — so stages compose, like a recipe of up to sixteen steps.

Analogy · A mixing desk, not an instrument

A synthesiser plays anything (a shader). The TEV is a mixing desk: sixteen channel strips, each with the same knobs — pick sources, set the blend, route to a bus. You can't teach a desk a new knob, but a good engineer patches those sixteen strips into effects the designers never imagined. That's exactly what GameCube-era graphics programmers were: mix engineers, one a·(1−c)+b·c+d at a time.

ingredients · 8 texture colours · 2 rasterised colours · konst constants · previous result · registers R0–R2 · 0, 1, ½… a b c d a·(1−c) + b·c + d then bias · scale · clamp (+ a twin path for alpha) c is the dial register R0–R2 or prev …becomes an input to the next stage — up to 16 in a chain one TEV stage
One stage of sixteen. Four selectors pick the stage's a, b, c, d from the ingredient menu; the fixed blend a·(1−c)+b·c+d (with bias/scale/clamp, and a parallel alpha path) writes a register that later stages can read. Configuration, not code — but sixteen composable steps go a very long way.

What sixteen little blends can do

The obvious patch is one stage: texture × lighting (set b = texture, c = rasterised colour — try it below). But composed stages built the era's signature looks. Cel-shaded games in the Wind Waker style fed a lit colour through a gradient-ramp texture to snap smooth Gouraud shading into flat poster bands, and layered outline tricks on top. Metroid Prime-style visor effects — raindrops and reflections that seem to sit on the glass in front of your face — were TEV layerings of environment maps and overlays. Heat shimmer, dream sequences, poison fog: games achieved these as full-screen TEV recipes over an already-rendered frame (using a copy trick Module 13 explains). None of this was “a feature” — it was engineers discovering what the mixing desk could be patched into.

The lab below is one genuine TEV colour stage, run per pixel in software over a procedural stone texture and a fake per-vertex lighting gradient. Start with the presets, then free-patch: set c to konst and pick a colour — you've built a tint fader. Set a to ras, b to konst, c to tex — now the texture chooses where the konst colour appears. Sixteen of these, remember, chained.

TEV lab — patch one combiner stageCanvas · live
input a
input b
input c — the dial
input d — added
out = a·(1−c) + b·c + d  ·  per channel, clamped to [0,1] — tex is a procedural stone wall; ras is a fake per-vertex lighting gradient

Counting configurations. Each stage picks 4 inputs from over a dozen sources, an operation, bias, scale, clamp and destination — for colour and alpha — times 16 stages, times the texture, texgen and lighting state feeding it. The number of distinct TEV setups games can (and do) use is astronomical. File that thought: it is exactly the combinatorial explosion that makes Part III's Module 14 hard.

Key takeaways
  • The TEV decides every pixel's final colour: up to 16 chained combiner stages, each computing a·(1−c)+b·c+d (with bias/scale/clamp, plus an alpha twin).
  • Inputs come from textures, the two lit colour channels, konst constants and result registers — so stages compose.
  • It's configured, not programmed — “pixel shaders before pixel shaders.”
  • The era's famous looks — cel-shading bands, visor overlays, full-screen filters — were TEV recipes, not features.
Module 12 · Part II

Textures on GameCube — tiles, formats & CMPR

GoalSee how textures are actually stored — tiled layouts, a toolbox of pixel formats, and the 4×4-block CMPR compression that made 1 MB of TMEM feel bigger.

Module 08 left us a problem shaped like a triangle of constraints: textures make scenes rich (Module 05), TMEM is 1 MB, and main RAM is 24 MB for everything. This module is how the GameCube squeezed whole worlds through that needle: store textures in clever layouts, in the smallest format each one can tolerate, and compress the big ones 4-to-1.

Tiled layouts: locality is bandwidth

First, a subtlety about order. The obvious way to store an image is row by row — but think about how Module 05 reads it: a bilinear fetch wants a little 2×2 square of texels, and a triangle's pixels wander around a 2D neighbourhood. In row-major order, vertically adjacent texels live far apart in memory. So GameCube textures are stored in small rectangular tiles — little blocks (4×4 texels in many formats) laid out one after another, each filling exactly one 32-byte memory burst. A neighbourhood fetch lands in one or two bursts instead of four scattered rows. Nothing about the image changes; only the byte order does — remember this when Dolphin has to un-shuffle it in Part III.

A toolbox of formats

Second, per-texture thrift. Like the vertex formats of Module 09, texture formats are a menu, and artists picked the cheapest that survives:

FormatBits/texelWhat it storesTypical use
I4 / I84 / 8Intensity only (greyscale)Light maps, shadow blobs, font glyphs
IA4 / IA88 / 16Intensity + alphaSmoke, glow sprites, UI
RGB56516Full colour, coarse (5+6+5 bits)Opaque surfaces without gradients
RGB5A316Switches per texel: 5-bit colour + punchy alpha, or 4-bit colour + 3-bit alphaTextures needing some translucency
RGBA832Full 8-bit everything (stored as two 16-bit halves in the tiles)The few textures that must be perfect
CI4 / CI84 / 8Index into a small colour paletteFlat-shaded art, recolourable skins
CMPR4Block compression — this module's starAlmost everything big

CMPR: four bits per texel that look like sixteen

The workhorse is CMPR, the GameCube's block compression format (the same family as the PC world's S3TC/DXT1). It rests on a simple observation: within any small patch of a real texture, the colours usually vary along one direction — light wood to dark wood, grass to shadow. So compress each 4×4 block of texels independently into 8 bytes:

  • Store two endpoint colours, c0 and c1 (16-bit RGB565 each) — the ends of that block's colour range.
  • Derive two more colours between them, at ⅓ and ⅔ of the way — four palette entries for the price of two.
  • For each of the 16 texels, store a 2-bit index picking one of the four.

Count it: 2 bytes + 2 bytes + 16 × 2 bits = 8 bytes for what RGB565 stores in 32 — a clean 4:1 saving (8:1 against full RGBA8). And because every block is the same tidy size, the hardware can decode any texel directly during sampling, no unzipping step, no history. The cost is visible if you know where to look: a block whose colours don't fit on one line — a rainbow sparkle, a noisy patch — must be flattened onto four colours, and blocky 4×4 artefacts appear. (One GameCube nicety: if c0 ≤ c1 the fourth entry becomes transparent instead, buying cut-out alpha — foliage! — for free.)

Analogy · Paint by numbers, per postage stamp

CMPR turns each 4×4 stamp of the image into a paint-by-numbers kit: the kit box lists just two paints (the endpoints), you're allowed to mix two blends of them, and the stamp is a grid of numbers 1–4. Sixteen numbered cells and a two-paint list is vastly smaller than sixteen full colour swatches — and it looks right whenever the stamp really was painted from two paints, which, in nature, is almost always.

8 bytes on disc / in TMEM c0 · 565 c1 · 565 16 × 2-bit two stored endpoints… …and an index per texel the 4-colour palette c0 ⅔c0+⅓c1 ⅓c0+⅔c1 c1 two derived for free — or, if c0 ≤ c1, slot 4 = transparent (cut-out alpha) decode any texel directly indices… 0012 0123 1233 2333 …rebuild the block a smooth gradient — CMPR’s perfect case: 32 bytes became 8, and you can barely tell
One CMPR block. Eight bytes hold two RGB565 endpoints and sixteen 2-bit indices; the decoder derives two in-between colours and paints by numbers. 4:1 smaller than RGB565, decodable at random during sampling — and honest about its weakness: blocks whose colours don't sit near one line get flattened.

The lab compresses a procedural image block by block, live. Scrub across it (or click the image): smooth sky and sea blocks reconstruct almost perfectly; then find the hard-edged sun rim or the noisy rocks at lower left, where four palette entries clearly aren't enough. This exact encoder-vs-image negotiation happened for every texture the era shipped.

CMPR lab — compress a texture, one 4×4 block at a timeCanvas · live
c0 · stored
c1 · stored
⅔c0+⅓c1
⅓c0+⅔c1
Key takeaways
  • Textures are stored as small tiles matched to 32-byte memory bursts — neighbourhood fetches arrive in one burst.
  • Formats are a thrift menu: 4-bit greyscale to 32-bit RGBA, palettes included — pick the cheapest that survives.
  • CMPR packs each 4×4 block into 8 bytes: two RGB565 endpoints, two derived colours, 2-bit indices — ~4:1, decodable at random.
  • Its failure mode is any block whose colours don't fit one line — and c0 ≤ c1 buys 1-bit cut-out alpha.
Module 13 · Part II

From EFB to your TV

GoalFollow a finished frame out of the chip: the EFB copy, YUV conversion, the deflicker filter, and the video interface that feeds a 2001 television.

Module 08 promised the little EFB would force a dance, and here it is. The frame Flipper just drew lives in ~2 MB of on-chip memory — but the television can't see inside the chip, and the next frame needs that space now. So every frame ends with a copy-out: dedicated hardware sweeps the EFB and writes the finished image into main RAM. The destination buffer is called the XFB (eXternal FrameBuffer), and the copy engine is no dumb mover — it transforms the image on the way through.

First, it converts colour. TVs of the era didn't want RGB; broadcast video speaks YUV — brightness (Y) separated from colour (U, V) — and, because your eye resolves brightness far more finely than hue, the colour components are stored at half horizontal resolution (so-called 4:2:2). The XFB is YUV, which also halves its memory versus 24-bit RGB. It's the video cousin of everything Module 12 preached: spend bits where perception looks.

Second — and this is pure 2001 — the copy can filter vertically. A standard-definition TV draws interlaced video: odd scanlines one sixtieth of a second, even lines the next. A crisp single-pixel-tall detail therefore blinks at 30 Hz — the notorious interlace flicker. The copy engine's copy filter blends each output line with its neighbours on the way to the XFB, softening exactly those details. Games shipped genuinely, deliberately blurred vertically — tuned for glass CRTs. (Half the “wow” of emulators today is simply switching that filter and interlacing off.)

Analogy · The darkroom print

The EFB is the photographer's negative — pristine, full-detail, but useless to hang on a wall and needed for the next shot. The copy-out is making the print: on the way to paper (the XFB) the image is colour-converted for the paper stock (YUV) and slightly softened so it looks right in the living room's frame (the deflicker filter). The negative is then wiped and reused.

EFB size
640×528 max
pixels, on-chip
EFB formats
24-bit
RGB8+Z24, or RGBA6 for destination alpha
XFB colour
YUV 4:2:2
half-res chroma in main RAM
TV output
480i / 480p
576i PAL · progressive needs component

Those EFB numbers hide a real design trade worth reading twice. About 2 MB is enough for 640×528 pixels at 24 bits of colour plus 24 bits of depth — and no more. Want destination alpha for layered effects? The same 24 bits must be re-cut as 6 bits per channel (RGBA6): you buy an effect with colour precision. Want higher resolution? There is nowhere to put it — the ceiling is burned into the die. (Numbers around the edges of this — exact row counts, the anti-aliasing pixel format — vary by source and mode, so treat the table above as the shape rather than gospel.)

One more copy-out trick ties Part II together: the engine can copy a region of the EFB into memory as a texture instead of as an XFB. Render the scene, copy it to a texture, feed it back through Module 11's TEV — that loop is how the era did heat haze, Wind Waker-style full-screen effects, real-time reflections and Metroid-style visor distortion. Remember EFB copies; in Part III they become one of Dolphin's most consequential settings.

field 1 · odd lines · t = 0 ms field 2 · even lines · t = 16.7 ms a one-line-tall detail (amber) exists only in the odd field — it vanishes every other 60th of a second: interlace flicker copy filter copy-out blends neighbours each XFB line mixes the EFB line with its neighbours — the detail now lives (softly) in BOTH fields deliberate vertical blur, tuned for CRTs — the softness emulator players notice when it's finally switched off
Interlacing and the deflicker filter. An SD television alternates odd and even scanlines each 60th of a second, so a one-line detail strobes at 30 Hz. The EFB copy engine's vertical copy filter spreads every detail across neighbouring lines on its way to the XFB — no more strobe, at the cost of shipped-in softness.
EFB
on-chip frame, colour + z
Copy engine
deflicker filter · RGB→YUV · or → texture
XFB
finished frame in main RAM
VI
scans lines out at TV rhythm
TV
480i CRT (480p with component)

The last hop is the VI — the video interface from Module 08's diagram. It reads the XFB line by line at the television's fixed rhythm (interlaced 60 Hz fields for NTSC, 50 for PAL; progressive 480p over component cables in games that support it) and generates the analogue signal. The VI doesn't wait for the GPU: it scans out whatever XFB it's pointed at, on schedule, forever. Games juggle two or three XFBs so one can be scanned while the next is copied — and the CPU can even scribble into an XFB directly, which is exactly how some games draw menus and videos. That little fact will come back with teeth in Module 15.

Key takeaways
  • Frames are drawn in the on-chip EFB, then copied out every frame to an XFB in main RAM for display.
  • The copy converts to YUV 4:2:2 (half-res colour) and can vertically filter to tame interlace flicker — era-authentic softness.
  • The EFB tops out around 640×528; 24-bit colour or 6-bit-per-channel with destination alpha — resolution and precision were die-sized.
  • EFB→texture copies feed frames back through the TEV (reflections, haze, full-screen looks); the VI scans XFBs to the TV on a fixed clock.
Part III

Emulating it in Dolphin

Finally: how does software on your PC stand in for that physical chip? Graphics is where emulation gets philosophically weird — Dolphin must recreate a GPU with no shaders on top of modern GPUs that speak nothing but shaders, fake a 2 MB on-chip framebuffer that games poke directly, and decide how faithful to be to a machine that was tuned for interlaced CRTs. These three modules connect the whole course to the actual settings you'll toggle in the emulator.

Modules 14–16Difficulty EmulationLabs 1 interactive
Module 14 · Part III

A GPU without shaders, on a GPU with nothing but

GoalUnderstand the central translation problem of GameCube graphics emulation — TEV state into generated shaders — why it stutters, and how ubershaders answer it.

Recall the two kinds of machine this course has now shown you. Flipper (Modules 10–11) is fixed-function: a menu of switches — TEV stage inputs, lighting channels, blend modes — with behaviour selected, not written. Your PC's GPU is the opposite: it has essentially no fixed-function colour hardware left, only programmable units running shaders written in languages like GLSL. So Dolphin's video code is, at heart, a translator: it watches the state the game programs into the virtual Flipper (through Module 09's FIFO), and for each distinct configuration it generates the source code of a shader that behaves identically — a chain of multiply-adds mimicking each TEV stage, Module 10's lighting recipe, the fog, the blend — then hands it to your GPU driver to compile and run.

Elegant — with one structural flaw. Module 11's note counted the configurations: they're astronomical, so Dolphin cannot pre-compile “all GameCube shaders” up front. It only discovers which ones a game uses when the game uses them. And the first time each new configuration appears — a boss's new attack, a particle burst, a room's new material — the frame cannot be finished until the driver compiles the new shader. Driver compilation takes tens of milliseconds; a 60 fps frame is 16.7 ms. The result is shader-compilation stutter: the game hitches at exactly the moments something new happens, which is exactly when you care. Caching compiled shaders on disk helps the second playthrough; it cannot help the first encounter, and no amount of CPU speed fixes it.

Analogy · The interpreter at the door

Specialised shaders are like hiring a dedicated translator for each document type that crosses your desk: unbeatable once trained, but the first contract in Portuguese stops the office for an hour of hiring. An ubershader is a single simultaneous interpreter who already speaks everything: each sentence takes a little longer, but nothing ever stops the office.

That interpreter is Dolphin's celebrated answer, shipped in 2017 after years of work: the ubershader. Instead of generating a specialised program per configuration, write one enormous shader that receives the TEV/XF configuration as data — uniforms describing the 16 stages' selections — and executes Module 11's a·(1−c)+b·c+d loop with branches, per pixel, at runtime. A software TEV interpreter, running on the GPU. Compile it once when the game boots and every configuration a game can ever set is already runnable. The price is honest: all that branching makes every pixel cost more, so ubershaders lean on raw GPU muscle. Hence the hybrid mode most players use: render new configurations through the ubershader while the specialised version compiles in the background, then swap over — steady frames, specialised speed, a few frames late.

game sets TEV/XF state via the FIFO (Mod 09) specialised shaders seen this config before? look up the shader cache no → generate GLSL, driver compiles ⏱ 10s of ms — the frame waits felt as a hitch: compilation stutter then: fastest possible pixels ubershader one giant shader, compiled at boot reads the config as data, loops over 16 TEV stages per pixel never stalls — every pixel pays a tax hybrid: cover the gap, swap when compiled
Two translations of the same chip. Left: generate a small, fast shader per TEV configuration — and eat a driver compile (a visible hitch) on every first sight. Right: one boot-time ubershader interprets the configuration per pixel — steady but heavier. The dashed path is the hybrid most players use.

The lab simulates the trade. A stream of draw calls arrives; walking into new areas brings configurations the cache has never seen. Watch the frame-time strip: specialised runs lowest but spikes savagely on every first encounter; ubershader draws a calm, slightly higher plateau; hybrid stays calm while the cache quietly fills. The modes share one random seed, so the comparison is fair.

Ubershader lab — stutter vs steadyCanvas · simulated
Key takeaways
  • Dolphin translates fixed-function TEV/XF state into generated shader source, compiled by your GPU driver.
  • Configurations are astronomical and only discovered at use — first sights force compiles mid-game: shader stutter.
  • Ubershaders invert the idea: one boot-compiled giant shader interprets the configuration as data, per pixel.
  • Steady-but-heavier vs fast-but-stuttery; hybrid mode uses the ubershader as a bridge while specialised shaders compile behind it.
Module 15 · Part III

The hard parts

GoalTour the places where faithful emulation and playable speed genuinely fight: EFB access, XFB realism, resolution scaling and texture-cache accuracy.

Shaders were the glamorous problem. The grinding ones all trace back to a single theme: games were written against Part II's exact memory layout, and a PC GPU doesn't have one. Four examples define the settings you'll meet in Module 16.

1 · Games that read the framebuffer back

On real hardware the EFB is just memory-mapped pixels, and Nintendo let the CPU read and write it directly. Games used that freely: reading a pixel under the cursor to detect what you're aiming at, sampling rendered colours to drive gameplay or save-file thumbnails, poking pixels for effects. On a PC, the emulated frame lives across a bus in GPU memory; reading one pixel back means a pipeline stall — the GPU must finish everything queued, then ship data back — a catastrophe repeated per frame. Dolphin therefore offers accuracy tiers: emulate EFB access faithfully (correct, sometimes slow), or skip it (fast, and the handful of games that depend on it quietly break — a cursor that selects nothing, a mechanic that never triggers).

2 · Real XFB vs a convenient lie

Module 13's honest path — EFB → YUV XFB in RAM → VI — is how some games play videos and draw menus: they write into the XFB with the CPU, never “drawing” at all. Emulating that faithfully means actually maintaining CPU-visible XFB memory and scanning display output from it (correct even for those tricks, but the output inherits real-hardware softness and resolution). The fast path skips the XFB round-trip and presents the rendered frame directly — beautiful and quick, until a game plays its intro video into the XFB and you get a black screen. Modern Dolphin defaults to faithful-enough hybrids, but the tension is permanent: the more literally you emulate Module 13, the more you also emulate its deliberate blur.

3 · Internal resolution: the upgrade that can't be free

The single most-loved emulator feature: render at 3×, 5×, 7× the EFB's resolution — internal resolution scaling — and 2001 geometry snaps into modern crispness, because Part I's triangles are resolution-independent mathematics. But Module 13 taught you the trick that breaks it: EFB→texture copies. Games building bloom or heat-haze sample those copies at hand-tuned pixel offsets — half a texel here, one line there — calibrated forever against a 640-wide frame. Scale the frame 3× and a “one pixel” offset is now a third of what the artist intended: glows sit off-centre, blur pyramids drift, outlines double. Dolphin carries per-game fixes and heuristics, but the honest statement is: native-calibrated sampling maths can only be perfectly right at native size.

4 · The texture cache dilemma

Module 12's textures live in game RAM, and games may rewrite them at any moment — animated water, video textures, procedural page flips. Faithful emulation would re-decode (un-tile, un-CMPR, re-upload) every texture every time it might have changed; that's absurdly slow, so Dolphin keeps decoded textures in a cache keyed by hashes of the source bytes. Hash rarely and a game that rewrote a texture shows the stale one; hash paranoidly and the CPU burns. That's the “texture cache accuracy” slider: literally how suspicious to be, purchasable in frames per second.

Analogy · Restoring a film for IMAX

Blowing a classic up for a giant screen flatters everything shot on the original negative — that's geometry at high internal resolution. But every effect that was composited at print size — matte lines, optical glows — now sits visibly wrong, because its measurements were in print pixels. The restorer chooses per shot: keep it faithful and soft, or remaster and risk artefacts. Dolphin's accuracy settings are those choices, made per game.

native 1× — as calibrated “sample 2 px right” the glow lands exactly where the artist tuned it 3× internal resolution — same “2 px” “2 px” is now ⅓ of the intended distance — the glow hugs the source, off-centre ← should land there
Why bloom breaks at high IR. An effect calibrated as “sample N pixels away” against the native 640-wide EFB (left) keeps its numeric offset when the frame is rendered 3× larger (right) — so the sample lands a third of the intended distance away. Geometry scales for free; hand-tuned pixel arithmetic doesn't.
Key takeaways
  • CPU reads/pokes of the EFB are trivial on hardware, pipeline-stalling on PC — hence accuracy tiers that trade correctness for speed.
  • Faithful XFB emulation keeps CPU-drawn videos/menus working but inherits native softness; skipping it is fast until a game writes the XFB directly.
  • Internal-resolution scaling flatters geometry but breaks native-calibrated sampling (bloom offsets) in EFB-copy effects.
  • The texture cache trades hashing suspicion for speed — stale textures vs burned CPU.
Module 16 · Part III

Try it in Dolphin

GoalTurn the whole course into the graphics settings you can now read fluently — and recap the full journey of a pixel.

You now have the full picture, so let's make it hands-on. Open Dolphin's Graphics configuration and read it with this course's eyes. (Exact option names shift between Dolphin versions — treat these as the ideas to look for, not gospel strings.)

  • Backend (Vulkan, OpenGL, Direct3D, Metal…) — which backend API, shader language and driver Module 14's generated code is fed to. Backends differ mostly in compilation behaviour and driver quality, which is why stutter can differ per backend.
  • Internal resolution — Module 15’s beautiful, imperfect upgrade: render at a multiple of the native resolution. Geometry scales free; watch bloom-heavy games for misaligned glow.
  • Shader compilation mode — Module 14 as a menu: specialised (fastest, stutters), ubershaders always (steadiest, heaviest), or the hybrid/asynchronous modes that bridge with the ubershader while compiling. “Compile shaders before starting” trades boot time for early smoothness.
  • “Store EFB copies to texture only”-style options — Module 13's copy-out, tiered: keep copies purely as GPU textures (fast; breaks games that read them from RAM) or mirror them into emulated RAM (faithful, slower). The same tiering exists for XFB copies.
  • “Skip EFB access from CPU”-style options — Module 15's readback problem as a checkbox: speed, purchased with the handful of games whose aiming cursors and mechanics read pixels.
  • Texture cache accuracy — Module 15’s texture cache hashing-suspicion slider; nudge toward accurate when animated textures misbehave.
  • Scaled EFB copies, per-pixel lighting, widescreen & aspect hacks — enhancement toggles whose artefacts you can now predict: each one substitutes modern maths where a game expected Module 13's exact layout.
Analogy · The cockpit after the hangar tour

Aircraft cockpits look like walls of arbitrary switches — until you've walked the hangar and seen the engine, the flaps and the fuel lines each switch commands. You've just walked Flipper's hangar. Dolphin's Graphics dialog is the cockpit: every checkbox is wired to a stage you've now touched, and the figure below draws the wiring.

the whole course in one row CP · FIFO Mod 09 XF · T&L Mod 10 rasterise Mod 04 TEV Mod 11 EFB Mods 07 · 13 XFB → VI → TV Mod 13 FIFO player (debug tool) per-pixel lighting internal resolution shader modes / ubershaders EFB copies · EFB access XFB handling · deflicker off every checkbox in the Graphics dialog is a knob on one of these boxes — you can now predict what each breaks
Settings, mapped. The pipeline you've walked — CP → XF → rasteriser → TEV → EFB → XFB → VI — with the family of Dolphin options that governs each stage underneath. Reading the dialog is now just reading this diagram.

A great closing exercise, in the spirit of the audio course's final A/B test: pick a bloom-heavy GameCube title, set internal resolution to native 1×, and stare at a glow effect; then jump to 3× and watch Module 15 happen — the world sharpens while the glow slides subtly off its source. Then open the shader-compilation modes and walk into a new area under specialised vs hybrid ubershaders: the hitch you feel (or don't) is Module 14, live. You're no longer toggling mystery checkboxes — you're steering a pipeline you know stage by stage.

Course complete ✦
  • You can follow a pixel from a vertex in model space to a scanline on a CRT — and through Dolphin to your monitor.
  • You know why one chip housed the whole console's soul: framebuffer, textures, audio DSP and all.
  • You can read Dolphin's Graphics dialog as a map of Flipper — and predict what every speed hack trades away.
  • Sibling course: how the same little box made sound — the DSP you met in Module 08 has sixteen modules of its own.
  • And when you'd rather drive GX than study it: the homebrew course shows you how to write your own code for this machine — every TEV stage on this page is yours to program.

An interactive course built for the Dolphin project, companion to the GameCube audio course. Every picture on this page is drawn procedurally in your browser — the labs are little software renderers implementing the very algorithms they teach. This page contains no game artwork. Hardware and emulation details are grounded in public documentation and Dolphin's own source; where exact figures vary by source, the text says so.

Source of truth · Source/Core/VideoCommon/ · Source/Core/VideoBackends/ · Source/Core/Core/HW/VideoInterface/