Engine design

Folder structure

  • Use lowercase filenames

  • Don’t use spaces, use underscores.

Source code

connector/  «project root»
├── .clang-format  «Clang Format configuration»
├── .clang-tidy  «Clang Tidy configuration»
├── .git-blame-ignore-revs  «git ignore revisions»
├── .gitignore  «git ignore»
├── .readthedocs.yml  «Read The Docs configuration»
├── CHANGELOG.rst
├── CMakeLists.txt
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.rst
├── .github/  «GitHub templates and action configurations»
├── assets/
│   ├── models/
│   └── textures/  «textures»
├── benchmarks/
├── cmake/  «CMake helpers»
├── configuration/
├── documentation/
│   ├── CMakeLists.txt  «CMake file for the documentation»
│   ├── cmake/  «documentation cmake helpers»
│   └── source/  «documentation source code»
├── example/  «example application»
├── include/  «header files»
├── shaders/
├── src/  «source code»
├── tests/
├── third_party/  «third party dependencies»
└── vma-dumps/

Application

vulkan-renderer/  «application root»
├── inexor-vulkan-renderer.exe  «executable»
├── ...
├── assets/
├── shaders/
└── ...

Dependency management

  • In general we try to keep the number of dependencies at a minimum.

  • Dependencies are downloaded directly by CMake.

Criteria for library selection

If we really need a new library, it should be selected based on the following considerations:

  • Are you sure we need this library? Can’t we solve the problem with C++ standard library or some other library we are already using?

  • The library must have an open source license which is accepted by us (see Licenses).

  • It must be in active development.

  • It must have a good documentation.

  • A sufficient number of participants must have contributed so we can be sure it is reviewed.

Coding style

The easiest way to get the right format is to use the provided clang-format file in the root directory.

Other styles which cannot be applied automatically are listed below:

  • Use #pragma once as include guards

  • Own headers are included with quotes

  • Includes are ordered as follows
    • Own headers

    • empty line

    • Third Party Libraries

    • empty line

    • System Libraries

  • Use C++17 namespace style namespace inexor::vulkan-renderer

  • No using <namespace>

  • For default member initialization use brace instead of equal initialization

  • Prefer American English over British English

  • Use spaces to indent

  • Use Linux line ends (ln) in your commits

  • Use /// for multiline documentation instead of /**/

Naming convention

Open the .clang-tidy file and search for readability-identifier-naming to get the naming convention used by this project.

Error handling

  • Use exceptions for error handling, as proposed by the C++ core guidelines.

  • More information about why to use exceptions can be found here.

Get methods

  • Name: Don’t use prefix get_. Give the get method the same name as the resource it returns.

  • For complex types (std::vector, std::string), return a const reference.

  • Don’t const the return type for simple types (int, float), because this prevents move semantics to be applied.

  • For simple types (int, float), just copy the return value.

  • Mark get methods as [[nodiscard]] in the header file only.

  • Mark get methods as const, so they don’t change members.

  • Do not add documentation for get methods, since it is self-explanatory.

  • Keep get methods directly in the header file.

  • Do not add inline since get methods in header files are always inlined.

  • The get method should not run any other code, like checking if the value is actually valid. Since we are using RAII, the value to return must be in a valid state anyways.

  • Use operator overloading sparingly. Prefer get methods instead.

Examples:

[[nodiscard]] const glm::vec3& position() const {
    return m_position;
}

[[nodiscard]] float aspect_ratio() const {
    return m_aspect_ratio;
}

Removed clang-tidy checks

bugprone-narrowing-conversions

Same as cppcoreguidelines-narrowing-conversions

cppcoreguidelines-avoid-magic-numbers

Alias of readability-magic-numbers

cppcoreguidelines-c-copy-assignment-signature

Alias of misc-unconventional-assign-operator

cppcoreguidelines-non-private-member-variables-in-classes

Alias of misc-non-private-member-variables-in-classes

cppcoreguidelines-pro-bounds-array-to-pointer-decay

Not suitable for this project.

google-readability-todo

We do not care about any TODO assignments or related issues.

hicpp-explicit-conversions

Alias of google-explicit-constructor

hicpp-move-const-arg

Alias of performance-move-const-arg

hicpp-no-array-decay

Alias of cppcoreguidelines-pro-bounds-array-to-pointer-decay

hicpp-uppercase-literal-suffix

Alias of readability-uppercase-literal-suffix

llvm-header-guard

#pragma once is used.

modernize-use-trailing-return-type

Trailing return types are not used.

readability-magic-numbers

Too many places where it would be useless to introduce a constexpr value.

readability-uppercase-literal-suffix

Just a style preference.

Code design

Literature

The following books inspired Inexor’s code design:

General considerations

  • Organize the code in components.

  • Split declarations and definitions, if possible.

  • Make appropriate use of the standard library.

  • Avoid data redundancy in the engine. Do not keep memory copied unnecessarily.

  • Do not duplicate code. Find an appropriate abstraction which accounts for the scenario.

  • Try to keep dependencies between components at minimum because single components (e.g. classes) should be as recyclable as possible.

  • Use spdlog instead of printf or std::cout for console output.

  • Use assert to validate parameters or necessary resources during development (debug mode).

  • Document the code using doxygen comments. Code without documentation is almost useless.

  • Make sure the code is platform-independent. For now, we will support Windows and Linux but not Mac OS.

  • Use Vulkan memory allocator library for Vulkan-specific memory allocations like buffers.

  • Do not allocate memory manually. Use modern ++ features like smart pointers or STL containers instead.

  • Don’t use global variables.

  • Don’t use the singleton pattern as it makes thread safety and refactoring difficult.

  • Don’t use call-by-value for returning values from a function call.

  • Don’t use macros for code generation or as a replacement for enumerations.

C++ core guidelines

  • The C++ code guidelines are a set of rules to use for modern C++ projects created by the C++ community.

  • In the following section, we will list up the entries which are of considerable interest for the Inexor project.

  • There will be some gaps in the number as we skipped some of the less importer ones.

  • Also the code guidelines have gaps by default (blank space for new rules).

Philosophy

Interfaces

Functions and class methods

Classes

Enumerations

Resource management

Classes

Follow rule of 0 and rule of 5

Performance

Design patterns

Regressions

  • If something used to work but it’s broken after a certain commit, it’s not just some random bug.

  • It’s probably an issue which was introduced by the code change which was submitted.

  • It’s important for us to keep working features in a stable state.

  • You can use git bisect for tracing of the commit which introduced the bug.