ALON OR-BACH: Hi, I'm Alon Or-bach, a product manager
in Android games and graphics.
Today, I will update you on the latest on Vulkan on Android.
And I'll be joined by Dohyun Kim, who
will share some key optimization techniques we recommend
to maximize performance with Vulkan on Android.
Vulkan is a modern GPU API that enables
games and applications to get the best performance in Android
devices.
It is an open standard, developed collaboratively
by the industry-- game engine developers, implementers,
device manufacturers, and platforms,
working together to shape a powerful API.
Vulkan is designed from the ground up
to be suited to today's GPUs and chipsets.
By reducing CPU overhead, apps are
able to obtain better performance or power savings.
Vulkan enables distribution of CPU-intensive work
across multiple threads to make the most out
of multicore CPUs in Android devices.
And Vulkan is where developers can access the latest GPU
functionality on Android, from descriptor indexing
through to ray tracing.
Vulkan is now ubiquitous in Android,
with 85% of active devices supporting it
as of January 2023, with 77% supporting Vulkan 1.1, up
16% since last year.
Alongside having widespread support,
we know how important it is to have quality GPU drivers,
and we are consistently progressing ways
to identify driver bugs and performance issues.
We've added Vulkan 1.3 support in Android 13,
with supporting devices now available.
It includes a variety of new features and API improvements.
These were driven by developer feedback to the Vulkan
working group, with improvements to synchronization
and enabling simpler single render pass instance creation.
Several features are available to make managing and diagnosing
pipeline compilation easier, including
dynamic state, pipeline creation cache control, and creation
feedback.
We're proud that Android is an open platform, enabling devices
with a range of GPU capabilities.
Vulkan Profiles have been introduced
to help developers navigate and decide
what functionality to target.
Our team in Android forms the Android baseline profiles,
working with GPU vendors to ensure
we capture what is widely supported across devices today.
In parallel, we work with game engines and the Vulkan working
group in Khronos to shape road map profiles,
to ensure they meet future needs and are feasible to implement.
Since the last Summit, we've released Android Baseline
Profile 2022.
This builds upon the 2021 profile
and raises requirements further, including Vulkan 1.1 support
and useful features such as greater control over render
pass creation.
It is already available on over 79%
of Vulkan-supported devices.
You can find out more details on the Android Baseline Profiles
and Vulkan support information at the Android distribution
dashboards on developer.android.com.
I'll now pass you over to Dohyun for some key Vulcan
optimization techniques we recommend
to get the best performance for your games.
DOHYUN KIM: Hello, I'm Dohyun Kim.
I am a developer relations engineer on Android games.
In this talk, we will discuss ways to improve
Vulcan rendering performance.
Today, I will share three items--
binding vertex and index buffer, descriptor set update, and FSR
upscaling.
First, binding vertex and index buffers.
Our application draw call requires a bound vertex buffer.
If you are using application [INAUDIBLE] indexed,
our bound index buffer is also required.
We will evaluate two different strategies
for binding the buffers.
First, on the left side, we will use small independent buffer
and index buffers for each object.
We will bind the buffers before each draw call.
Before drawing the A objective, we
will bind A's vertex and index buffer,
and therefore, drawing the B object,
we will bind B's vertex and index buffer.
On the right side, we will combine the vertex and index
data for object A and B into a single vertex/index buffer.
Because the data is combined, we only
need to bind the buffers once before making the draw
call to draw both the A and B object.
You don't need to rebind the buffers between your draw
calls.
And if you draw more objects, and add this object
into the vertex and index buffer,
you don't need to rebind the buffers.
The method on the right has one fewer power
of binding course compared to the method on the left.
So how they do perform?
To compare performance, I have made a sample application
that use both methods.
It draws many copies of the same [? teapot. ?]
Since we are evaluating the binding overhead,
for the left side, we will create
the same number of buffers as our draw count,
even though the data in each buffer is identical.
The individual binding laws at 79 FPS, the single binding
laws at 84 FPS.
In this situation, binding buffers once generate 5.73%
increase in performance.
This test is performed on a Pixel 7 Pro device, which
has a pixel of [INAUDIBLE] frequency
because this has led to the [? CPU war. ?]
To fix frequency, we use that fixed performance mode.
In games, for where objects are open generated and deleted,
creating and distributing buffer for a new object
is expensive and could decrease performance.
Creating one big buffer--
shared buffer performs better than creating
many small buffers.
In Vulkan, our descriptors cell lengths, buffers, and texture
data are through draw call.
Object textures usually don't change between frames.
You [? need ?] buffer data for an object
might change every frames.
So we will focus on uniform buffer updates.
There are many ways to update uniform buffers.
We will test five different methods.
First, use the dynamic offset in the binded descriptor set API.
The second, copy memory to mapped memory address.
Third, use push constant to force--
use the push descriptor set extension.
Finally, fifth, core update descriptor set API.
With the push constant, it is important to note
that they have a size limit.
On the Pixel 7 Pro device, the limit is the 128 byte.
It is also important to note that the push descriptor set
extension is not available on all devices.
You will need to check for the extension
and enable it if it is available.
Are you interested in which one is the fastest?
I'm using the same thing as before,
but changed the implementation related to the descriptor set
to compare the performance.
I would test it on a Pixel 7 Pro with the fixed performance
mode enabled.
Here is the result. On this device, updating
the descriptor set is slowest.
Dynamic offset was 4.2% faster.
Memory copies were 26% faster.
And push constant were 21% faster.
The fastest approach may change depending on device used.
Push constant buffer size limit will
change depending on the GPU, and some older GPU
has a performance penalty with the push constant.
There were no performance threshold
for the push descriptors set, since the Pixel 7 Pro Mali
GPU doesn't currently support the push descriptors set
extension.
I'm now repeating the test on Android 7
on the result device, which supports the extension.
Performance results are similar with the Pixel 7 Pro,
but push descriptor set is the slowest.
Next, I would like to talk about the FSR.
FSR is AMD FidelityFX Super Resolution
system for upscaling image.
Rendering at a lower resolution requires less work for the GPU
and can increase FPS.
If we look at our performance results,
you can see the rendering at 1080p launch at 20 FPS,
while rendering at 540p launched at 30 FPS.
However, the visual quality is clearly not good as at 540p.
FSRs of scaling from a lower resolution source
to a higher resolution target.
We can use it to take the 540p image
and upscale it to up to 1080p.
This launch at 26 FPS, which is only a bit slower than just
rendering at 540p.
FSR launch are [INAUDIBLE] as a post process pass,
so it doesn't need to any special Vulkan extension.
It has optional settings, such as adding sharpening,
which can add the additional quality to same image.
You can also improve quality by increasing
the resolution of the base image, such as using 720p.
But the higher the resolution of the base image, the smaller
your performance improvement.
Here, we can see the result. It can
be hard to see the detail on the small mobile device,
so I scaled up part of the images.
You can see FSR from the 540p to 1080p
isn't as good as full 1080p, but it is better looking than 540p.
And I attached all scaled up images of 64 by 64 pixels.
You can identify the difference more easily.
On the 540p, pixels are squeezed,
but FSR has better quality than this,
even though it has the same 540p best past result.
I personally recommend to use this feature
in power-saving mode.
If you are learning on our highest graphic quality
setting, it is better to draw at the full resolution.
But for lower graphic quality setting, or power-saving mode,
using FSR has improved FPS while increased
quality compared to lower resolutions
with a simple scaling.
If you are using Unreal Engine, AMD has on your plug-in.
So you can easily integrate to use FSR.
And if you are not using the Unreal engine,
you can use it by adding our [INAUDIBLE] [? dispatcher ?]
[? core ?] after the best [INAUDIBLE] pass.
There is an FSR 2, but it needs extra input information,
such as [INAUDIBLE],, so it is usually
not used in mobile environment.
Therefore, it is harder to use FSR 2 on mobile device.
This concludes my presentation.
Thank you for watching.
[MUSIC PLAYING]