The Next Mainstream Programming Language Tim Sweeney
The Next Mainstream Programming Language Tim Sweeney
The Next
Next Mainstream
Mainstream
Programming
Programming Language:
Language:
A
A Game
Game Developer’s
Developer’s Perspective
Perspective
Tim Sweeney
Epic Games
Outline
Game Development Process
What kinds of code are in a game?
– Game Simulation
– Numeric Computation
– Shading
Where are today’s languages failing?
– Modularity
– Reliability
– Concurrency
Game Development
Game Development: Gears of War
Resources
– 10 programmers
– 20 artists
– 24 month development cycle
– $10M budget
Software Dependencies
– 1 middleware game engine
– ~20 middleware libraries
– OS graphics APIs, sound, input, etc
Software Dependencies
Gears
GearsofofWar
War
Gameplay Code
Gameplay Code
~100,000
~100,000lines
linesC++,
C++,script
scriptcode
code
Unreal
UnrealEngine
Engine33
Middleware
MiddlewareGame
GameEngine
Engine
~500,000
~500,000 lines C++code
lines C++ code
…
Ogg
Ogg wx
wx ZLib
ZLib
Speex
DirectX
DirectX OpenAL
OpenAL Vorbis
Vorbis
Speex Widgets
Widgets
Data
Data
Speech
Graphics Audio Music Speech Window Compr-
Graphics Audio Music
Codec
Codec
Codec
Window
Library
Compr-
ession
Codec Library ession
Game Development: Platforms
class Actor
Base class of
{
gameplay int Health;
objects void TakeDamage(int Amount)
{
Health = Health – Amount;
Members if (Health<0)
Die();
}
}
Actor
Player
Enemy
InventoryItem
Weapon
Actor
Player
Enemy
Dragon
Troll
InventoryItem
Weapon
Sword
Crossbow
Software Frameworks
The Problem:
Users of a framework
need to extend the functionality
of the framework’s base classes!
The workarounds:
– Modify the source
…and modify it again with each new version
– Add references to payload classes, and
dynamically cast them at runtime to the
appropriate types.
What we would like to write…
Base Framework Extended Framework
package Game; Package MyGame extends Game;
Example (C#):
Given a vertex array and an index array, we
read and transform the indexed vertices into
a new array.
Vertex[] Transform (Vertex[] Vertices, int[] Indices, Matrix m)
{
Vertex[] Result = new Vertex[Indices.length];
for(int i=0; i<Indices.length; i++)
Result[i] = Transform(m,Vertices[Indices[i]]);
return Result;
};
May be NULL
May be NULL May be NULL
Solved problems:
Random memory overwrites
Memory leaks
Solveable:
Accessing arrays out-of-bounds
Dereferencing null pointers
Integer overflow
Accessing uninitialized variables
Dependent types
int The Integers
nat
nat<n The Natural Numbers
Dependent functions
Sum(n:nat,xs:[n]int)=.. Explicit type/value dependency
a=Sum(3,[7,8,9]) between function parameters
Universal quantification
Sum{n:nat}(xs:[n]int)=..
a=Sum([7,8,9])
How might this work?
Comprehensions (a la Haskell),
for safely traversing and generating
collections
Successors(xs:[]int):[]int=
foreach(x in xs)
x+1
How might this work?
Neat Trick:
In a machine word (size 2n), encode an integer ±2n-1 or a pointer to a
variable-precision integer
Thus “small” integers carry no storage cost
Additional access cost is ~5 CPU instructions
But:
A natural number bounded so as to index into an active array is
guaranteed to fit within the machine word size (the array is the proof
of this!) and thus requires no special encoding.
Since ~80% of integers can dependently-typed to access into an
array, the amortized cost is ~1 CPU instruction per integer operation.
class C C x;
{
int a; What is x, really?
};
What are objects in Java/C#?
class C C x;
{
int a; What is x, really?
};
class C C x;
{
int a; What is x, really?
};
class C C x;
{
int m; What is x, really?
};
class C C x;
{
int m; What is x, really?
};
class C C x;
{
int m; What is x, really?
};
Why???
Dynamic Failure: Conclusion
Xbox 360
– 3 CPU cores, 6 hardware threads
– 24-wide GPU
PlayStation 3
– 1 CPU core, 2 hardware threads
– 7 SPU cores
– 48-wide GPU
PC
– 1-2 CPU cores, 1-4 hardware threads
The Idea:
– Any thread can modify any state at any
time.
– All synchronization is explicit, manual.
– No compile-time verification of
correctness properties:
• Deadlock-free
• Race-free
The C++/Java/C# Model:
“Shared State Concurrency”
This is hard!
How we cope in Unreal Engine 3:
– 1 main thread responsible for doing all work we
can’t hope to safely multithread
– 1 heavyweight rendering thread
– A pool of 4-6 helper threads
• Dynamically allocate them to simple tasks.
– “Program Very Carefully!”
Huge productivity burden
Scales poorly to thread counts
There must be a better way!
Three Kinds of Code: Revisited
Gameplay Simulation
– Gratuitous use of mutable state
– 10,000’s of objects must be updated
– Typical object update touches 5-10 other objects
Numeric Computation
– Computations are purely functional
– But they use state locally during computations
Shading
– Already implicitly data parallel
Concurrency in Shading
Look at the solution of CG/HLSL:
– New programming language aimed at
“Embarassingly Parallel” shader programming
– Its constructs map naturally to a data-parallel
implementation
– Static control flow (conditionals supported via
masking)
Concurrency in Shading
Conclusion: The problem of data-parallel concurrency is effectively solved(!)
“Proof”: Xbox 360 games are running with 48-wide data shader
programs utilizing half a Teraflop of compute power...
Concurrency in Numeric
Computation
These are essentially purely functional algorithms, but they
operate locally on mutable state
Solutions?
– Rewrite as referentially-transparent functions?
– Message-passing concurrency?
– Continue using the sequential, single-threaded approach?
Concurrency in Gameplay Simulation:
Software Transactional Memory
The idea:
Update all objects concurrently in arbitrary order,
with each update wrapped in an atomic {...} block
With 10,000’s of updates, and 5-10 objects touched per
update, collisions will be low
~2-4X STM performance overhead is acceptable:
if it enables our state-intensive code to scale to many threads,
it’s still a win
Example:
Outlawing runtime exceptions through dependent types
– Out of bounds array access
– Null pointer dereference
– Integer overflow
Exceptions impose sequencing constraints on concurrent execution.
Evaluation Strategy
Lenient evaluation is the right default.
Support lazy evaluation through explicit
suspend/evaluate constructs.
Eager evaluation is an optimization the compiler may
perform when it is safe to do so.
Language Implications
Effects Model
Purely Functional is the right default
Imperative constructs are vital features
that must be exposed through explicit
effects-typing constructs
Exceptions are an effect
Memory model
– Garbage collection should be the only option
Exception Model
– The Java/C# “exceptions everywhere” model should be
wholly abandoned
• All dereference and array accesses must be statically
verifyable, rather than causing sequenced exceptions
– Exceptions are an effect
– No language construct except “throw”, and calling functions
with explicitly-annotated exception-effects should
generate an exception
Syntax
Pascal/ML family:
f{n:nat}(as:[]int,i:nat<n)=as[i] Seems promising
Conclusion
A Brief History of Game Devlopment
1972 Pong (hardware)
Algebraic Datatypes
– Unions done right
Compare to: C unions, Java union-like class
hierarchies
– Maybe t
C/Java option types are coupled to
pointer/reference types
IO, ST
– With STRef, you can write a pure function that
uses heaps and mutable state locally, verifyably
guaranteeing that those effects remain local.
The Genius of Haskell
Comprehensions
Sorting in C
int partition(int y[], int f, int l);
void quicksort(int x[], int first, int last) {
int pivIndex = 0;
if(first < last) {
pivIndex = partition(x,first, last);
quicksort(x,first,(pivIndex-1));
Sorting in Haskell
quicksort(x,(pivIndex+1),last);
}
}
int partition(int y[], int f, int l) {
int up,down,temp;
sort [] = [] int cc;
sort (x:xs) = sort [y | y<-xs, y<x ] ++ int piv = y[f];
up = f;
[x ] ++ down = l;
sort [y | y<-xs, y>=x] do {
while (y[up] <= piv && up < l) {
up++;
}
while (y[down] > piv ) {
down--;
}
if (up < down ) {
temp = y[up];
y[up] = y[down];
y[down] = temp;
}
} while (down > up);
temp = piv;
y[f] = y[down];
y[down] = piv;
return down;
}
Why Haskell is Not My Favorite
Programming Language