Writing a cluster UI or an infotainment application usually means dealing with high-level abstraction introduced by the editors and libraries. Regardless of which one we are using, they all must have a module communicating with the Graphics processing unit itself — using a driver Application Programming Interface (API). The most commonly used driver interface used for accessing GPU features is OpenGL. It makes it possible to implement various graphical effects using relatively simple GPU access calls because complex low-level operations will be hidden behind a lot simpler abstraction.
This abstraction is meant to be easy to understand, for the programmers and the tool designers. However, there is this well-known rule about the abstraction — It always comes without a cost. The higher the abstraction level we have, the less aware we are of what is going on, the more we rely on the implementation we have no access to, and the less customizable our software is.
These are one of many reasons why the market is looking towards something better, looking for a more efficient and safe way of accessing GPU resources. And here is where the new generation interface — Vulkan — starts to shine.
As you may guess, driver quality has a lot of impact on the overall application performance. The OpenGL driver is a complex piece of software which due to the way it is designed, has to manage the GPU access context internally. This impacts three major critical aspects of automotive area:
Starting with the first point — if we do not have the driver code in case of any errors, performance problems, or optimizations related to the driver — we are on our own. From version to version, from one hardware to another one, we rely on the different quality of the driver implementations. Our control over the application is becoming limited. In some cases, this may be critical not only for the system efficiency but also for its stability.
In automotive, we are dealing with cases where there are multiple operating systems running in parallel on one single hardware platform. If these OSs are sharing resources — these resources have to be managed. OpenGL based driver is not exposing this information, and we may have hard times to predict how much memory we still have available.
Vulkan’s design addressed all of these problems. This modern API driver is by design much less complicated comparing its ancestor. Application using Vulkan introduces much less risk when moving it across different hardware platforms or when upgrading the driver version. It allows the application author for a lot more optimizations and makes it possible to monitor GPU resources usage and workload much more precisely.
Just like in the OpenGL case, we still can use advanced frame debugging tools like Renderdoc. We can capture and analyze details of our rendering pipeline setup, including performance counters, input, and output images and viewing the rendering operations in a timeline and sniff general rendering statistics if needed.
*** Statistics viewer ***File size: 191.51MB (481.42MB uncompressed, compression ratio 2.51:1)
Persistent Data (approx): 0.02MB, Frame-initial data (approx): 481.37MBDraw calls: 9
Dispatch calls: 0
API calls: 17
API:Draw/Dispatch call ratio: 1.88889
12 Textures — 469.75 MB (469.75 MB over 32x32), 5 RTs — 40.55 MB.
Avg. tex dimension: 2642.29x2642.29 (2642.29x2642.29 over 32x32)
6 Buffers — 0.73 MB total 0.05 MB IBs 0.68 MB VBs.
511.03 MB — Grand total GPU buffer + texture load.
The software development process always includes certain methods to deal with potential software issues. This is why we introduce processes, we continuously test what we create, setting up CI servers, or we write unit tests. We have to deal with the fact that mistakes happen. One fact about them is that the longer it takes us to detect one, the more costly they may become. Therefore it is always better to detect any potential issues as early as possible. The same applies to graphics applications. OpenGL way of dealing with errors is very simple but has several problems:
Vulkan introduces the concept of validation layers. It is a set of optional software components that validate all Vulkan function calls. They find not only errors but also potential issues and misalignment’s in-memory operations. Any kind of issues or undefined behaviors is explicitly pointed out together with a potential solution and hints on how to deal with it. Vulkan API is explicit and very strict when it comes to setting states and allocating GPU memory. Among that — it makes it possible to write own validation layers for user-specific use cases. Focusing on finding potential issues as early and possible, and helping developers fixing them on very early project stages, Vulkan brings a new quality to the software development process.
# Example of debug message from the validation layervalidation layer: VkFence 0x1180000000118 is in use. The Vulkan spec states: Each element of pFences must not be currently associated with any queue command that has not yet completed execution on that queue (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkResetFences-pFences-01123)
Considering we are free to extend this system even more by writing our validation layers, the software validation potential is now customizable as it was never before.
Sharing the GPU resources is very much present in modern automotive software designs. This, on its own, is a subject for a separate article. We can now point out a few main aspects:
One of the main differences between OpenGL and Vulkan API is the fact that the second one gives us full support for multi-threaded rendering. We can now submit commands to GPU command queues from different threads. Nowadays, rendering systems architectures consider spreading the command upon multiple command queues having the synchronization entities in place to keep the relation between computation results in place. Vulkan also introduces a possibility to utilize more than one physical device at the same time. Due to these facts, we are now fully ready to utilize all GPU resources, and we can now free some of the CPU computing power. Speaking of CPU usage, the Vulkan execution model makes it possible to encapsulate graphics commands into so-called command buffers. We do not have to recreate these buffers each frame like it was the case for OpenGL. Command buffers can now be prepared in advance and stored in the GPU memory. They are accessible just like regular GPU side resources by using a handle. Another fact is that we can benefit from computing operations moving heavy computations to the GPU compute shaders, or optimize processing using transfer queues for memory transfer operations.
Looking at all the areas where Vulkan helps to improve GPU related development process, it looks like a way to go for the library and tools developers in the Automotive industry. When we have a predictable driver implementations and extendable internal error handling system, we will have validation possibilities never seen before. The sooner we take advantage of the new API, the sooner safety-critical applications development will be brought to the next level.
Below you can find an example of PBR Instrument Cluster rendered in real-time using Vulkan API with following features: