Skip to content

Development

Karn Kaul edited this page Oct 25, 2022 · 3 revisions

Introduction

Building facade requires C++20, CMake 3.18+ (ideally 3.22+), and the Vulkan SDK (for validation layers and glslc), but beyond that is largely agnostic to any particular development environment. This page provides guidelines for some commonly used workflows.

Environment

An editor that supports C++ syntax highlighting and integrates CMake 3.22+ is recommended:

  • Visual Studio Code + CMake Tools + Ninja
  • Visual Studio (CMake mode)
  • CLion

However it's not required, you can just edit, build, and debug entirely over the command line if you so wish.

Fresh Builds

VSCode

  1. Use Open Folder and select the project root
  2. Select a Configure (and Build) Preset from the bottom bar, eg default / Debug
    1. Recommend using the ninja-ubsan Configure Preset if using GCC / Clang
  3. Select CMake: Configure from the command palette
  4. Select CMake: Build from the command palette
  5. Press Ctrl + F5 or the "bug" button on the bottom bar to debug the selected target (facade)

Visual Studio CMake

  1. Use Open Folder and select the project root
  2. Choose Project > Generate Cache or similar to invoke CMake and configure the project
  3. Build as usual (Menu bar / Ctrl + Shift + B)
  4. Debug as usual (Menu bar / F5)

Project files

This is for generating Visual Studio solutions, Xcode workspaces, Code::Blocks projects, etc. using CMake and then developing on that.

  1. Open CMake
  2. Select the project root as the source directory
  3. Select a desired build directory: this is where the solution and project files will be generated
  4. Click Configure, select the desired generator and options, and configure the project
  5. Click Generate, then Open Project
  6. Build and debug as usual

Command Line

  1. Configure a build
    1. Using a preset: cmake -S . --preset=default
    2. Without presets: cmake -S . -B out -G "Ninja Multi-Config" ...
  2. Build
    1. Using a preset: cmake --build --preset=Debug
    2. Without presets: cmake --build out --config=Debug
  3. Debug out/.../facade using your favourite debugger

Subsequent Builds

Once a CMake cache (build_dir/CMakeCache.txt and build_dir/CMakeFiles/...) has been generated, the above steps aren't required again until it is deleted / somehow corrupt / etc. Simply invoke the build, which will itself invoke CMake again if necessary. To manually clean a build directory, simply delete it and configure it again.

Validation Layers

If the Vulkan SDK is installed and in PATH, validation layers should be picked up and enabled automatically in Debug builds. Otherwise, run Vulkan Configurator, enable validation layers through its interface, and then debug / run facade. (It doesn't need to be launched through Vulkan Configurator.) There is also launch.json as a template for using custom SDK install paths with VSCode at the end of this page.

Embedding Shaders

glslc is required in PATH to compile GLSL to SPIR-V (Vulkan SDK should automatically install it), which is done by CMake + embed-shader (a tool in the project) before facade is built. The compiled SPIR-V is embedded into C++ sources and referenced by the application. This can be disabled by unsetting the CMake option FACADE_BUILD_SHADERS, in which case modifications to GLSL shaders will not be reflected in the build.

Pull Requests

Pushing directly to main is not permitted except in very specific circumstances (mainly to fix a broken branch): it should always be in a state where it builds and runs cleanly.

Formatting

Any code files changed / added must also have been run through clang-format (and the configuration in .editorconfig). It is recommended to set this up through your IDE, ideally formatting on save. Alternatively, invoke clang-format on the file after editing it. EditorConfig can be installed via IDE plugins / extensions, and will work silently after that.

Code Reviews

Changes make it into main via PRs through other branches. All reviewers must checkout the target branch, build and run it locally, before approving an open PR. A PR must have been approved, passed all CI checks, and have no unresolved conversations before it can be merged.

Merging

PRs to main should be squashed and merged. PRs from forks will be merged in by maintainers when they deem it ready. For PRs between branches in the repo, the reviewers should leave the merge for the author to complete, unless it blocks their work / author is unavailable / etc. This allows the author to make further changes / close the PR after it has been approved.

C++20 Limitations

The lowest common denominator of hardware for facade is Raspberry Pi 4, which as of this writing offers GCC 10.2 on aarch64 Raspbian. That unfortunately adds some constraints to the breadth of C++20 available for use, until that platform offers GCC 11 natively. Note: while the Mesa v3d Vulkan driver for Raspberry Pi 4 is quite reliable now, it is still currently experimental.

Limitation Workaround
No <syncstream> Use a mutex
No heteregeneous lookup for unordered containers None
No std::make_unique_for_overwrite Use facade::make_unique_for_overwrite
No floating point overloads for std::from_chars Use std::stod etc
Limited <ranges> support, if any Use <algorithm>
Limited sugar like reduced typename, using enum Use C++17 syntax

VSCode launch.json template

{
  // Modify this file and copy to .vscode/ (in the project root) to enable F5 debugging, 
  // setting custom working directories, using a custom Vulkan loader, etc
  "version": "0.2.0",
  "configurations": [
    {
      "name": "(gdb) Launch",
      "type": "cppdbg",
      "request": "launch",
      "program": "${command:cmake.launchTargetPath}",
      "args": [],
      "stopAtEntry": false,
      "cwd": "${workspaceFolder}",
      "environment": [
        {
          "name": "LD_LIBRARY_PATH",
          "value": "/path/to/vulkan-sdk/lib"
        },
        {
          "name": "VK_LAYER_PATH",
          "value": "/path/to/vulkan-sdk/etc/vulkan/explicit_layer.d"
        }
      ],
      "externalConsole": false,
      "MIMode": "gdb",
      "setupCommands": [
        {
          "description": "Enable pretty-printing for gdb",
          "text": "-enable-pretty-printing",
          "ignoreFailures": true
        },
        {
          "description": "Set Disassembly Flavor to Intel",
          "text": "-gdb-set disassembly-flavor intel",
          "ignoreFailures": true
        }
      ]
    }
  ]
}