1 of 29

DOOM on Glium

or how I learned to stop worrying and love OpenGL

2 of 29

Hi, I’m Cristi!

I do ML stuff at re:infer

3 of 29

Rule 34 of Programming Languages

4 of 29

Rule 34 of Programming Languages

If it exists, then DOOM runs on it.

- Johannes Carmackael

5 of 29

6 of 29

7 of 29

Hacker News new | threads | comments

[...] difficult to port code [...] to Rust [...]. There is a port of Doom to Rust. It has a lot of unsafe code, because Doom's internal memory structures are not directly compatible with Rust's. Such problems will recur as big packages with delicate internals are ported over to Rust.

Animats (458 days ago)

8 of 29

First Version

  • Used unsafe all over the place:
    • Reading binary formats through #[repr(C)] pointer mem::transmute-s.
    • Getting around borrowck.
    • Biggest: raw OpenGL calls.

9 of 29

Second Version

  • 100% safe code
    • Reading binary formats through #[repr(C)] pointer mem::transmute-s.
      • BurntSushi’s byteorder
    • Getting around borrowck.
      • Stop being a doofus
    • Biggest: raw OpenGL calls.
      • Tomaka’s glium

10 of 29

GL Fixed-Function Pipeline (ca. 1997)

Vertex Data

Culling Clipping

Attributes

Transforming & Lighting

Primitive Data

Rasterisation

Alpha/Depth/Stencil Tests

Alpha Blending

Eyeballs

Texture Data

11 of 29

GL Fixed-Function Pipeline (ca. 1997)

glMatrixMode(GL_MODELVIEW);

glLoadMatrixf(modelview_matrix);

glEnable(GL_TEXTURE_2D); // and many more

glBindTexture(GL_TEXTURE_2D, my_texture);

glBegin(GL_TRIANGLES);

for (int i=0; i<n; ++i) {

glVertex3f(x[i], y[i], z[i]);

glColor3f(r[i], g[i], b[i]);

}

glEnd();

12 of 29

Modern OpenGL (another lie)

Vertex Shader

Data (buffers)

Fragment Shader

Tests & Blending

Eyeballs

Data (buffers)

Geometry Shader

13 of 29

Modern OpenGL

shader = glCreateShader(GL_VERTEX_SHADER);

glShaderSource(shader, 1, source, strlen(source));

glCompileShader(shader)

int result;

glShaderiv(shader, GL_COMPILE_STATUS, &result)

if (result == 0) {

int log_length;

glShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);

char* buffer = malloc(log_length + 1);

glGetShaderInfoLog(shader, log_length, NULL, buffer);

}

14 of 29

OpenGL API: Resource Management

  • Create a resource (buffer/program) etc.
  • Bind the resource.
  • Set properties and/or upload data to GPU.
  • Once unused, delete (if you remember).

15 of 29

OpenGL API: Drawing

  • Bind all resources in turn (buffers, programs).
  • Enable/disable parts of pipeline.
  • Call glDrawArrays for one model/pass.
  • Bind resources that differ (or pay overhead).
  • Call glDrawArrays again.
  • etc.

16 of 29

OpenGL API: Giant State Machine

  • OpenGL calls are valid only in certain sequences.
  • Error handling: silent/queryable at best, undefined behaviour at worst.
  • Bugs can be caused by interactions between very distant calls.

17 of 29

glium

18 of 29

glium

19 of 29

Glium: Stateless API

  • The glium::draw takes as arguments all state and diffs it with previous call.

20 of 29

Glium: Strong, Static Typing

  • Data is stored in strongly typed Buffer<[T]>
  • Textures have semantics encoded in type:
    • 76 different texture types! (1d/2d/3d, sRGB, compressed etc.)

21 of 29

Glium: Strong, Static Typing

#[repr(C)]#[derive(Copy, Clone)]pub struct StaticVertex {pub a_pos: [f32; 3],pub a_atlas_uv: [f32; 2],pub a_tile_uv: [f32; 2],pub a_tile_size: [f32; 2],pub a_scroll_rate: f32,pub a_row_height: f32,pub a_num_frames: u8,pub a_light: u8,}

let vertices: Vec<StaticVertex> = vec![];

vertices.push(StaticVertex {/* ... */});// ...

let buffer: VertexBuffer<StaticVertex> =

VertexBuffer::immutable(

window.facade(), &vertices)

22 of 29

Modern OpenGL

shader = glCreateShader(GL_VERTEX_SHADER);

glShaderSource(shader, 1, source, strlen(source));

glCompileShader(shader)

int result;

glShaderiv(shader, GL_COMPILE_STATUS, &result)

if (result == 0) {

int log_length;

glShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);

char* buffer = malloc(log_length + 1);

glGetShaderInfoLog(shader, log_length, NULL, buffer);

}

23 of 29

Glium: Resource Management

Program::new(self.window.facade(),ProgramCreationInput::SourceCode {vertex_shader: &vertex_src,fragment_shader: &fragment_src,..Default::default()})

24 of 29

Glium: Multi-threading

  • Send + Sync + Context Management
    • Means it can be done.

25 of 29

Glium: Rust Philosophy

  • Zero-cost abstractions.
    • Exception: context check which can be disabled with an unsafe call.

26 of 29

Glium: Rust Philosophy

  • Straight-forward ‘code generation’.
    • Not a ‘framework’ or an ‘engine’.
    • Easy(-ish) to figure out what raw OpenGL calls result from a Glium call.
    • Enables the experienced programmer to perform bleeding edge optimisations.

27 of 29

Glium: Rust Philosophy

  • Convenient is fast.
    • Beginner code is near optimal.
    • Sensible defaults.

28 of 29

Glium: Rust Philosophy

  • Concise.
    • Allow shortcuts, but not at the cost of correctness.
    • DOOM on Glium is ~800 LOC smaller (out of ~5000).

29 of 29

Questions?