Vulkan is a new (2016) API from Khronos group (the maintainers of OpenGL and many more OpenWhatevers).
It is low-level than OpenGL and alternatives and feels more like “GPU programming” than “graphics programming”. A deep dive into technicalities may be well worth it, since you’re supposedly writing your own engine.
Vulkan trades some boilerplate for a more refined and thought-out paradigm where you don’t have to track whatever you think your driver is tracking in its deep pockets of mutable state.
While the amount of boilerplate code may appear unruly it is very straight-forward and can be easily stowed away under some combinators and utility packages.
FAQ
-
Q: Why not OpenGL?
A: Reasons. -
Q: Do I need to learn OpenGL first?
A: No. -
Q: Do I need new hardware to run Vulkan?
A: Maybe. It’s more of a driver thing.
Typical flow
-
Initial boilerplate:
-
Create window with SDL or GLFW.
- Pick either, but you’ll have to use its event processing.
-
Get a list of extensions to create
Instance
. -
Create
Instance
and windowSurface
. - Pick physical device.
-
Create logical
Device
. -
Select rendering and presentation
Queue
s. -
Create
CommandBuffer
andDescriptor
pools. - Create synchronization primitives.
- Create memory allocator (optional).
-
Create window with SDL or GLFW.
-
Load static resources:
- Vertex and Index buffers for meshes.
- Textures, texture arrays and cube maps.
- Samplers.
-
Set up rendering:
-
Create one or more
RenderPass
. -
Create
Framebuffer
for eachSwapchain
and its resources to render into.- Should be updated each time window changes size. It’s a major PITA.
-
Create your shaders (
Pipeline
s) that will run inRenderPass
es.- You can compile their bytecode right in, or load on startup and update.
-
Create one or more
-
Render stuff:
- Acquire image index to use its resources (synchronization, framebuffer).
-
Allocate and fill
CommandBuffer
.-
Bind
RenderPass
.-
Bind one of its
Pipeline
.-
Bind
DescriptorSet
, if any. - Bind vertex and index buffers.
- Set Push Constants.
- Draw!
-
Bind
-
Bind one of its
-
Bind
-
Submit
CommandBuffer
to rendering. -
Submit
Swapchain
to presentation. - Wait on synchronization.
- Free transient resources (buffers, memory)
Resource management
While not imposing any particular way to deal with lifetimes, every beginXxx/endXxx pair of functions has a matching withXxx form that accepts bracketing function. This allows to use ordinary bracket
, unflited bracket
or managed
/resourcet
combinators to control the scope and lifetime.
The main problem is keeping transient resources long enough for the frame that uses them to be rendered and/or presented. When you receive a freshly-acquired image index that means that all resources for the same index could be safely disposed or reused. Beyond that, the usual concurrency practices are to be observed.
It is advised to retain data for that aren’t changing. This is in stark contrast with Immediate Mode rendering where everything is transient and CPU is hamster-busy telling GPU to draw exactly the same scene in all the details again and again.
For example, camera projection value does not change every frame, so you’d better not recalculate and reupload it. Furthermore, camera view may not change every frame, and that allows to squash projection and view together to free vertex shader from doing extra work for each vertex in the world.
There is supplemental VulkanMemoryAllocator package to manage GPU memory on a higher level than raw Vulkan bindings allow.
Ecosystem
TBD
Official guides
Those are usually quite basic and in C (C++ even), but that doesn’t matter too much. It’s the data flow and entity relations that matter.