1 of 53

It's Not About the API

​

Fast, Flexible, and Simple Rendering in Vulkan

Mason Remaley, 2024

2 of 53

Introduction

  • Hey, I'm Mason Remaley!
  • Independent game developer
  • ZSF board member
  • Taught grad/undergrad

3 of 53

Graphics APIs

4 of 53

What is a graphics API?

  • Can't run x64 on this
  • Communicate with it via Graphics API

5 of 53

Modern Graphics APIs

6 of 53

​

Vulkan

DirectX

Metal

Windows

✔

✔

✗

Linux

✔

✗

✗

macOS

~

✗

✔

XBox

✗

✔

✗

Playstation

✗

✗

✗

Switch

✔

✗

✗

7 of 53

​

Vulkan

DirectX

Metal

Managed by nonprofit and runs the most places

✔

✗

✗

8 of 53

A Brief History of Graphics APIs

<1992

1992

​

2004

​

2014

​

Vendor specific APIs, e.g. IRIS GL

IRIS GL becomes OpenGL

Move from fixed function to shader model

GPGPU increasingly relevant, modern APIs closer to the metal

9 of 53

10 of 53

11 of 53

12 of 53

13 of 53

14 of 53

15 of 53

16 of 53

17 of 53

18 of 53

19 of 53

20 of 53

21 of 53

  • vkGetImageMemoryRequirements WAS NOT SUPPOSED TO BE GIVEN NAMES
  • YEARS OF VkMemoryPropertyFlags yet NO REAL-WORLD USE FOUND MORE THAN DYNAMIC/STATIC
  • Wanted to go faster anyway for a laugh? We had a tool for that: It was called "GUESSING"
  • "Yes, please give me a vkDeviceMemory with VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT bit and VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT" - Statements dreamed up by the utterly deranged

​

LOOK at what Graphic Programmers have been demanding your Respect for all this time, with all the 3080s & RTXs we built for them

​

(This is REAL graphics, done by REAL graphics programmers):

​

​

​

​

​

​

"Hello I would like 10 graphics compute transfer present queues please"

​

They have played us for absolute fools

Vulkan

22 of 53

This Is What Peak Performance Looks Like

also it's simpler

23 of 53

Outline

  1. Vulkan Concepts
  2. Fast, Flexible, and Simple Rendering in Vulkan
  3. What makes a good API?

24 of 53

Vulkan Concepts

25 of 53

Vulkan Devices

  • vkPhysicalDevice
    • vkLogicalDevice

26 of 53

Command Buffers

  • VkQueue
    • VkCommandPool
      • VkCommandBuffer

27 of 53

Pipelines

  • VkPipeline
    • VkShaderModule
    • VkPipelineLayout
    • VkDescriptorSet
      • VkDescriptorSetLayout

​

​

​

28 of 53

Swapchains

  • VkSwapchainKHR

29 of 53

Synchronization

  • Semaphores
  • Fences
  • Barriers

30 of 53

Memory Management

  • GPU side
    • Manual, page allocator
    • Lots of flags
      • Device local
      • Host visible
      • Host coherent
      • Host cached
      • etc
  • CPU side
    • manual, heap/pool allocation

31 of 53

Takeaway

  • This complexity exists in old style APIs
  • It's hidden in the graphics drivers
  • As a result:
    • Worse performance
    • Variable performance
  • Can work around, but have to fight the API
  • So sure, Vulkan is more LOC…
  • But only have to get it right once

​

32 of 53

Fast, Flexible, and Simple Rendering in Vulkan

33 of 53

High Level Strategy: Two Lenses

  • Lens #1: Programming the API

34 of 53

High Level Strategy: Two Lenses

  • Lens #1: Programming the API
  • Lens #2: Programming the hardware

35 of 53

Memory Management

  • What do we need VRAM for?
    • Mostly for level data!
  • One alloc per memory type per level!

​

36 of 53

Vertex Data

  • Vertices are just data
  • Concat into single buffer, interpret in shader
    • "Vertex pulling"
  • Offers flexibility and performance
  • Index buffers still useful

​

​

37 of 53

Uniforms

  • Put them in a buffer, upload it up front
  • Index into it via the instance ID
    • VK_EXT_descriptor_indexing
  • "bindless"

38 of 53

Draw Call Generation

  • Fill buffer with draw arguments
  • Tell the GPU to draw them
    • "Draw indirect"

39 of 53

Materials

  • Already have this ability!
  • Have a single shader w/ switch(material_index) { … }
    • An "übershader"
  • Conditionals?? In my shaders??

​

40 of 53

Materials: Details

// Example data layout

struct MaterialInstance {

u16 material; // Which material are we

u16 data_1; // Misc material data

u32 data_2; // Misc material data, OR extra data index

};

​

// GLSL union tip

layout(binding = 1) readonly buffer FooMats { FooMat foo_mats[]; };

layout(binding = 1) readonly buffer BarMats { BarMat bar_mats[]; };

​

41 of 53

Summary

  1. Generate indirect argument buffer
  2. Generate draw data buffer
  3. Generate material buffers
  4. Issue single draw call

​

TLDR: fill up a few memory mapped buffers, issue a single draw call

42 of 53

What this gets us?

  • Minimal driver overhead
  • Minimal API surface area
  • Flexibility to generate draw data from multiple thread, from the GPU

43 of 53

Things we skipped…

  • Instancing
  • Extra passes
    • Post processing, compute, transparency
  • Doesn't change our overall strategy

44 of 53

Sounds nice, is this actually fast though?

45 of 53

46 of 53

47 of 53

48 of 53

49 of 53

50 of 53

What makes a good API?

51 of 53

What makes a good API?

  • APIs enable communication with an underlying system
    • Well designed APIs facilitates this
    • Poorly designed APIs obstruct it
  • If despite this it doesn't facilitate your goals…
    • Either the system is at fault, or the goal is
  • Vulkan has flaws, being low level isn't one of them
  • Design for hardware, not for APIs!

52 of 53

📷 Useful Links

​

53 of 53

📷 Q&A

  • gamesbymason.com
    • Blog (RSS), newsletter, talks
    • Open source
    • Games