Setup and Tour
This step will get your development environment set up and discuss the overall structure of the code you'll be working on.
Setup
The nakluV tutorial uses a C++ compiler, Node.js (to run the Maekfile.js
script), GLFW 3.4 (for window and event management), and the Vulkan SDK 1.3.290.0 (for talking to the GPU).
Compiler and library versions do (somewhat) matter here, since we're relying on a prebuilt binary blob for the refsol code.
You'll be building and running the tutorial code from the command line.
On macOS and Linux this means some sh
-like shell; on Windows this means cmd.exe
.
In all cases we expect that your C++ compiler, Node.js, and the command line git
utility are available in your path.
It will also be convenient if you can launch your preferred code editor from the terminal.
Get The Starter Code
The first step is the same for all OSs: clone the starter code repository.
You can put this in a subdirectory if you want, just make sure that your GLFW directory ends up as a sibling of the nakluV
directory (or edit the Maekfile.js
).
Get the Prerequisites - (Linux, x86_x64)
Compiler and Node.js: if you don't already have them, get a recent version of GCC's `g++` and Node.js's `node` from your OS's package manager.
For example, on Debian:
Build GLFW (from source):
download glfw-3.4.zip from the GLFW 3.4 release page, extract it as a sibling of your nakluV
directory;
then use cmake
to build and "install" it to glfw-3.4/out
.
(NOTE: you may also need to install the `xorg-dev` package before building.
See Compiling GLFW for more details.
You may also choose to build in Wayland support instead of X11 if you are on that tip.)
Putting that together:
Alternatively, install GLFW (from package manager). If your system provides a GLFW 3.4 package, you should be able to install that instead of building from source:
Install the Vulkan SDK:
download vulkansdk-linux-x86_64-1.3.290.0.tar.xz from LunarG's SDK downloads page and extract it somewhere convenient (I just put it in my home directory).
Source the included setup-env.sh
script in your terminal before you start a development session (sets the `VULKAN_SDK` environment variable, along with the layers path).
Putting that together:
Get the Prerequisites - macOS (arm64)
C++ Compiler:
we use the clang++
supplied with XCode (and available separately as a "command line tools" package).
I believe that if you set up the Homebrew package manager, it will automatically install the command line tools package.
Node.js, GLFW: Homebrew's package database is nicely up-to-date, it appears. So you should be able to just do:
Vulkan SDK:
there is a GUI installer available from LunarG's SDK page (scroll down).
Particularly, you want vulkansdk-macos-1.3.290.0.dmg
.
Put the VulkanSDK
folder wherever is convenient for you (I put it in my home directory).
It contains a setup-env.sh
script you should source in your shell before doing development work.
This script will set environment variables so our build script can find the SDK (and so Vulkan apps can find, e.g., the validation layer).
Get the Prerequisites - Windows (x86_x64)
C++ Compiler, Node.js, and Vulkan SDK (from the command line)
Windows has a package manager called winget
.
You can use it to install almost everything we need (except GLFW):
Small caveat: not sure our target version of the SDK (1.3.290.0) is available this way. The one you get might be close enough to "just work," however.
Node.js (GUI method): if you want to avoid winget
, an installer for Node.js is available from the Node.js web page.
C++ Compiler (GUI method): if you want to avoid winget
,
you can download the full installer for Visual Studio Community 2022 from Microsoft's Visual Studio Community page.
If you choose to install this way, make sure to check "Desktop development with C++" in the installer.
However, we don't actually need the full Visual Studio IDE, just the C++ compiler. Microsoft provides a package for this called the "Build Tools for Visual Studio 2022", which you can find on their downloads page (takes some looking around, though).
Command-Line note: on Windows, we use cmd.exe
as our shell.
Particularly, you'll want a shell with the visual studio build environment configured.
The shortcut "x64 Native Tools Command Prompt for VS 2022" installed by visual studio provides such a shell.
(If you can't run cl.exe
, you probably didn't use this shortcut to launch the shell;
if you can run cl.exe
but you get errors about architecture, make sure you are using the x64 version of the shortcut, not the x86 version.
Vulkan SDK: if you chose not to install via winget
, you can find a GUI installer for the Vulkan SDK on LunarG's SDK page.
Specifically, download the VulkanSDK-1.3.290.0-Installer.exe
executable.
It doesn't matter where you install (the installer will automatically add a `VULKAN_SDK` environment variable pointing to the install path, and our build script will use that to find the libraries/tools).
We only need "The Vulkan SDK Core" (you can uncheck all the optional components).
GLFW:
Download glfw-3.4.bin.WIN64.zip
from the GLFW 3.4 github release page.
Extract the contained glfw-3.4.bin.WIN64
folder as a sibling of the nakluV
folder holding the starter code.
If you build GLFW from source or place the library elsewhere, edit Maekfile.js
and update the glfw-related `/I` and `/LIBPATH:` arguments to the compiler and linker, respectively.
Get the Prerequisites - Other
Unfortunately, this tutorial is built around the idea of having a pre-built binary blob that you can link against.
If you'd like to use an OS/architecture combination not listed above it is unlikely that you'll be able to link against our pre-built refsol.o[bj]
object file.
Similarly, if you are on a supported platform but using a compiler which is not compatible with the intermediate files of the compiler we have chosen, you are probably also out of luck.
Regardless, please consider filing an issue so we can track demand for different platforms.
Build the Code
Now that you have the code and all the prerequisites, you should be able to build the code and run the output.
Linux/macOS:
Windows:
If you've got everything set up properly this will compile without errors. Running the executable will show a boring grey window:
Tour
Now that the code is building, let's take a quick stroll through the files and what they do.
Structurally, you can think of this code as a three-layer cake, with each layer becoming more specific. The base layer of the cake initializes the Vulkan API and the objects needed to use the GPU; the middle ("harness") layer manages the window and inputs -- it deals with the parts of Vulkan that every real-time graphics application needs (but not every Vulkan application needs), and provides a main loop to orchestrate the handling of events and the production of frames; and the top ("application") layer actually does real-time graphics using the support from layers below.
As implemented, the base and harness both live in struct RTG
(see RTG.hpp
and RTG.cpp
),
with some additional helper functions (perhaps think of them as sprinkles on the base layer of the cake) for dealing with common tasks like memory allocation provided by struct Helpers
(see Helpers.hpp
and Helpers.cpp
).
The application layer is implemented in struct Tutorial
(see Tutorial.hpp
and Tutorial.cpp
);
this is where you'll be adding most of your new code at first, since it is where you can immediately see visible results from your changes.
To see how the layers work together, look at this (slightly simplified) version of the main
function from (in main.cpp
):
The `configuration` object sets up parameters for the bottom two layers; then the `RTG` structure is constructed to instantiate the base and harness layers; then the `Tutorial` application is constructed and handed a reference to the base and harness layers so it can use them to set up relevant GPU resources; and finally the `RTG::run` function is used to orchestrate calls to `Tutorial`'s application callbacks.
The VK
macro
Throughout the existing code (and the code you write in this tutorial), you'll see a macro, VK
, which is defined as follows:
We wrap almost every Vulkan function call in a VK( )
so that if it returns an error that error will get thrown and reported.
Particularly, this code calls the function, and -- if the result isn't VK_SUCCESS
-- reports the result along with the function call and a helpful stringification of the error.
Note, particularly, the string_VkResult
function from the vulkan/vk_enum_string_helpers.h
header.
This header has lots of helpers for converting Vulkan enum
s into readable strings for debug output -- worth looking at when next you want to (e.g.) print out the current layout of an image.
In a production, go-as-fast-as-possible codebase we'd probably turn off this error checker -- i.e., #define VK( FN ) FN
.
But for our learning environment we want to catch problems as quickly as possible instead of only observing downstream consequences of a failure later.
The refsol::
As you look around in the code you'll probably notice a lot of functions don't seem to actually do much on their own, instead passing their parameters onward to a function in the refsol::
namespace:
Usage of refsol::
functions is the key to the "inside-out and backwards" strategy of this tutorial.
When you see a refsol::
function it means that we think this code is important to write and understand eventually, but we also know that writing it before you get to doing something actually interesting would be a huge drag on your enthusiasm.
The implementations of these boring-at-the-start functions in a binary blob (one of pre/*/refsol.*
, depending on platform).
By the end of this tutorial you will have replaced all the refsol::
calls with your own code.