At GDC 2018 there was a lot of coverage of the Vulkan API, which is an open-standard, cross-platform, and extensible 3D graphics API from the Khronos Group. While Qualcomm Developer Network has posted previous blogs on how to use Adreno SDK for Vulkan, at GDC2018 we showcased Space Dock, the first demonstration using Vulkan Graphics for a VR experience on a standalone mobile VR headset, and Lineage 2 Revolution by Netmarble Games demonstrating PC-quality gaming on mobile using Vulkan.
The Vulkan API is designed to address the needs of today’s graphic hardware and is often described as “near to metal” because it allows for close inspection and usage of GPU features. This is incredibly exciting for graphics programmers because its design facilitates cross platform development between PC, console and mobile devices.
As the Adreno™ 5xx and 6xx series GPUs found on the Qualcomm® Snapdragon™ mobile platforms (Snapdragon 820 and above) support Vulkan, we thought it would be helpful to provide a quick refresher and then discuss a few key features and benefits of Vulkan. In addition, we are also including some resources for getting started with Vulkan on Adreno.
Goals and Key Features
Vulkan is the successor to OpenGL, but was designed on a fresh slate and is not backwards compatible. It features a reduced API set for high-performance, cross-platform rendering on a wide range of hardware. It includes a layered architecture where elements like debugging and optimization can be easily disabled for release builds. It was designed from the ground up to leverage today’s multi-threaded CPUs and provide programmers with explicit control over what is rendered and how.
Unlike OpenGL, Vulkan requires developers to be explicit about every aspect of rendering from selecting graphics capabilities and specifying rendering operations to controlling memory access and synchronizing rendering events. It’s not uncommon to write tens or even hundreds of lines of initialization code just to set up everything. As a result, Vulkan code tends to be very “up front”, meaning that a lot of programming you might have traditionally done in later phases with other graphics APIs are done during initialization with Vulkan. This can allow for some inherent optimizations.
For example, with Vulkan you typically create a “rendering pipeline” up front, specifying everything from vertex assembly to various shader routines and other transformations required to produce the final rendered image. In addition, Vulkan requires you to create a pipeline for each variation of graphics data (e.g., for different vertex layouts) but in doing so, provides you with more opportunities to optimize for these variations (e.g., based on hardware capabilities discovered).
OpenGL originated before the age of multi-core processors and maintains a state machine, which makes it difficult to leverage multi-threading to improve performance. A goal for Vulkan was to be multi-threading friendly, and it does so in part, by providing a command buffer system that can leverage all CPU cores to submit tasks to the GPU and perform memory operations. Command buffers are allocated from “command pools” and then sent to a queue for asynchronous execution. The correct order of execution is controlled by the developer using events, barriers, semaphores, and fences.
Like other elements of Vulkan, command buffers are typically “recorded” up front. For example, a command buffer to render a triangle could include commands to start a render pass, bind to a graphics pipeline, draw, and end the render pass (a “render pass” provides the graphics driver the structure and requirements to optimally set up the hardware for rendering). To maintain in-game performance, we recommend you create command buffers once up front and don’t re-record command buffers if their commands will not change.
One additional tip is to use our Qualcomm Snapdragon Profiler as shown here, to see how commands are being executed and to visually follow CPU and GPU timing, which can help in debugging multi-threaded applications.
Vulkan supports shaders through an intermediate byte-code language called SPIR-V which is also used in OpenCL. SPIR-V fulfills a number of goals, but its byte-code design offers a number of benefits. For example, shader logic can be written in a number of high-level shader languages and then compiled to SPIR-V, which facilitates porting and usage of existing high-level shader code. Since the resulting byte code is platform agnostic, the same shaders can be used across PC, console, and mobile development. Shaders are typically compiled up front (usually through an offline compiler) to eliminate deferred shader compilation, which can provide more predictable rendering performance.
Finally, it’s worth re-emphasizing that while Vulkan provides near-to-metal explicit control, it’s also focused on providing a cross platform API. While you might query the hardware through Vulkan and create different versions of entities like rendering pipelines that are optimized for certain hardware, once all of this is set up you can focus on higher level tasks that are common to all platforms.
The benefits offered by Vulkan are exciting for graphics programmers looking to develop or port games and/or VR solutions to mobile platforms. When paired with one of our platforms with an Adreno 5xx and 6xx GPUs, Vulkan offers a potent API for high-performance graphics on mobile. For more information we recommend checking out the following three links:
- Our Adreno SDK for Vulkan is available here.
- We have an extensive multi-part series on YouTube for developing with Vulkan on Adreno. You can find Part 1 here.
- There is also an excellent introductory tutorial on Vulkan here.
So now that you’ve seen what’s possible, what will you create with Vulkan on mobile?