Examples and Tests

A list of included examples and integration tests.

Examples

Examples showcasing the use of the Tephra library are present in the /examples folder. They are packaged into a single executable that can switch between demos with a numeric command line parameter when ran.

The first example, chosen by default or with 1 as the sole parameter, is a reimplementation of the official Vulkan C++ cube demo, displaying a textured rotating cube.

The second example that runs when passed 2 as the command line parameter, is a ray tracing demo that implements a simple path tracer of a Cornell box. It uses the trace_ray_query.hlsl compute shader that needs to be compiled to SPIR-V. If you are not using the VS solution, you will need to compile it yourself with DXC -HV 2021 -fvk-use-scalar-layout and copy it to the target directory. Can't be bothered to learn enough cmake for this, sorry.

The examples do not use any third party windowing library, both for ease of distribution and to be a faithful recreation of the official cube demo. In your own code, you should preferably use something like GLFW instead, see the GLFW Vulkan guide for details. All the platform-dependent code and the window event loop is contained in /examples/window.hpp. It is mainly tested on Windows, if you find issues on other platforms, please submit a PR :).

The /examples/examples_common.hpp file contains an Example abstract class with a number of virtual methods used to update and render images. This allows the windowing class to run any example that implements that interface. /examples/tephra-examples.cpp then contains an entry point for the project that just dispatches one of the examples.


Integration Test Suite

Tephra also contains tests of its basic functionality, located in /tests, using the Microsoft Unit Testing Framework for C++. The /tests/tests_common.hpp file contains common setup for those tests - it creates a basic Tephra tp::Application and tp::Device and for each of the several queues used during testing, also a tp::JobResourcePool. It also loads data shared between several tests, such as shader pipelines. Unlike the code in the examples, it does not create a window and functions fully headlessly. Rather than functioning as unit tests, they set up scenarios from the point of view of the user of the API and check for the correct result, sometimes by making use of Tephra's reporting of statistics. As such, they currently work only in debug build configuration.

The tests are split up by category:

Setup Tests

Tests to verify the functionality of setting up a basic Tephra environment. That involves creating an tp::Application, choosing a physical device, creating a logical device from it and creating tp::JobResourcePool objects. These tests run prior to the common code in /tests/tests_common.hpp to ensure it is functional for further tests.

Buffer Tests

Tests to verify the creation, memory allocation and use of tp::Buffer objects and their views. They are tested as persistent (created through tp::Device::allocateBuffer), as well as job-local and pre-initialized buffers when used inside a tp::Job. In the latter case, Tephra must be able to correctly alias buffers with non-overlapping usages. A naive allocator would fail the tests. Host mapping and readback is checked here as well, so it can be used to verify results in further tests.

Image Tests

Similar tests as above, but with tp::Image and tp::ImageView objects. Images have some additional complexity to deal with, such as arrays, mipmaps and, for the purposes of automatic synchronization, layouts.

General Job Tests

These tests verify the basic functionality of jobs, like creation, submission and synchronization. The number of barriers is checked to match expectations

Compute Tests

Tests for compute passes, inline and deferred recording of tp::ComputeList objects, and their proper synchronization within a job. Properly functioning pipelines and descriptors are also required for this.

Complex Tests

More complex tests of specific behavior or use cases can also be added. For example, the streaming compute test checks the capability of asynchronously and continuously streaming data to the GPU, processing it there and reading the results back. It tests both for correctness and for minimal memory allocation.