Setting up Bazel for Vulkan projects

Bazel for Vulkan projects

Pavlo Stavytskyi
CodeX
5 min readMay 6, 2022

--

Running projects that use Vulkan graphics API with Bazel build system.
Photo by Javier Miranda on Unsplash

In this blog post, we will see how to set up an environment for Vulkan projects built with Bazel. Then, in the second part, we are going to see how Bazel can build GLSL shaders for Vulkan.

Vulkan — is an open standard for 3D graphics and computing that enables cross-platform access to GPUs. It targets high-performance real-time 3D graphics applications, such as video games and interactive media. You can learn more about Vulkan from its official page.

Bazel — is an open-source build system with advanced local and distributed caching, optimized dependency analysis, and parallel execution. It supports a wide variety of languages including C++. You can learn more about Bazel from its official page.

We will stick closely to Vulkan’s official environment setup guide but build everything with Bazel. To do this, we are going to use the rules_vulkan project.

In addition, we will set up tools that are usually used along with Vulkan such as:

  • GLM — a library for linear algebra operations.
  • GLFW — a cross-platform API for creating windows.

You can find a complete source code from this blog post on GitHub using the link below.

Prerequisites

Installing Bazel

This blog post assumes you have a Bazel (or its wrapper, Bazelisk) installed on your machine. Visit the official documentation to learn how to do it.

Downloading Vulkan SDK

Before doing any Bazel setup, we must download and install Vulkan SDK to our machine. This step would be exactly the same as you would follow the official Vulkan documentation.

Setting up the project

As usually with Bazel projects, we must initialize the project workspace. To do this, in the root directory of the project (let’s call it vulkan-bazel-samples) we need to create a file named WORKSPACE.

vulkan-bazel-samples
└── WORKSPACE

Configuring Bazel for Vulkan

First, we need to add Vulkan support to our Bazel project. We are going to use the rules_vulkan repo for this. Open WORKSPACE file and fill it with the code below.

WORKSPACE file contents. Part 1 — configuring rules_vulkan in the project

Here we are fetching rules_vulkan as a git_repository and configuring it in the project using vulkan_repositories macro.

We will see how to use it later in the blog post. However, before that, we need to configure additional third-party libraries that are usually used in Vulkan projects.

Configuring GLM

GLM — is a library for linear algebra operations.

First, we need to make it available in our project. To do this, we will use a http_archive build rule which will download the sources from a GitHub archive.

WORKSPACE file contents. Part 2 — loading GLM library

This way we are fetching the library sources in our project and we need to configure a BUILD file that will tell Bazel how to build it. We pass the path to this file as build_file parameter above. This file would be placed in the root directory of the fetched library.

Obviously, we would need to create this BUILD file. We will need to place it under the third_party/glm folder and call it glm.BUILD. Below are its contents.

third_party/glm/glm.BUILD file contents

Nothing fancy here, as we’re just telling Bazel where to get source files for the library.

After the actions above, the GLM library will be available as an external workspace @glm. For easier usage, we can create a simple alias for it. To do this, create a third_party/glm/BUILD file and populate it with the contents below.

third_party/glm/BUILD file contents

Below is the file structure of the project so far.

vulkan-bazel-samples
├── third_party
│ └── glm
│ ├── glm.BUILD
│ └── BUILD

└── WORKSPACE

Next, we will include a GLFW library in the project.

Configuring GLFW

GLFW — is a cross-platform API for creating windows.

The algorithm for adding it to the project is similar to GLM. However, we would need to perform a bit more sophisticated configuration. Append the following code to the WORKSPACE file.

WORKSPACE file contents. Part 3 — loading GLFW library

Similar to GLM, we need to specify a BUILD file for the GLFW library. To do this, under the third_party/glfw directory create a file named glfw.BUILD. Next, we will need to populate it with the contents.

First, for each of the supported operating systems, we need to pick the right set of implementation source files, define statements, and linker flags. For the sake of saving space, the below snippet defines the corresponding variables but omits their values. However, the full code for this file could be found on GitHub.

third_party/glfw/glfw.BUILD file contents. Part 1

Next, we need to define a cc_library target that will build those sources either for Linux or Windows operating systems.

third_party/glfw/glfw.BUILD file contents. Part 2

Similarly, to build the GLFW sources for macOS, we need to define an objc_library target.

third_party/glfw/glfw.BUILD file contents. Part 3

Finally, we are defining a public Bazel target that links GLFW API headers with the implementations we defined before.

third_party/glfw/glfw.BUILD file contents. Part 4

In addition, create a third_party/glfw/BUILD file that defines simple alias for the external @glfw repository.

third_party/glfw/BUILD file contents

Here is the updated file structure we have so far.

vulkan-bazel-samples
├── third_party
├── glm
│ │
│ └── glfw
│ ├── glfw.BUILD
│ └── BUILD

└── WORKSPACE

Writing and building Vulkan code

Now, we are ready to add some C++ code that uses Vulkan API. It won’t do anything fancy, just show an empty window and print the list of Vulkan extensions. This is sufficient to demonstrate that the project was configured correctly.

Create a main.cpp file under the env_setup directory. Fill it with the code below, which is taken from the official Vulkan tutorial.

env_setup/main.cpp file contents

Next, we need to add a BUILD file for our source code under the env_setup directory.

env_setup/BUILD file contents

We are defining an env_setup binary target that uses both glfw and glm libraries we configured before. In addition, it depends on vulkan_cc_library from the @rules_vulkan external workspace. This is how we enable Vulkan support for this target.

Below you can find the updated project structure.

vulkan-bazel-samples
├── env_setup
│ ├── main.cpp
│ └── BUILD

├── third_party
└── WORKSPACE

Running the code

Finally, we are ready to build and run the project with Bazel. To do so, run the following command in the terminal.

bazel run //env_setup

If all is done correctly, you should see an empty window and a bunch of Vulkan extensions printed in the console.

Conclusion

That’s it! In this blog post, we have seen how to set up a project that uses Vulkan graphics API and build it with Bazel using rules_vulkan repo. In addition, we have seen how to add GLM and GLFW libraries to such a project that serve as integral companions when working with graphics APIs.

In part 2 we will see how to build GLSL shaders with Bazel and apply this knowledge to run a project that draws a triangle.

All the source code from this blog post could be found on GitHub.

References

  • rules_vulkan — a repository that enables Bazel support for Vulkan projects, created by Juan David Adarve.

--

--

Pavlo Stavytskyi
CodeX
Writer for

Staff Software Engineer at Meta • Google Developer Expert for Android, Kotlin