# Adreno GPU on Mobile: Best Practices

## Feature Summary

Adreno has a rich set of hardware and software features.  Here is a quick-start overview of what’s available, linking to more information:

- - [Renderer Architecture](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-renderer-architecture): Make the most of Adreno’s performance and battery usage by considering:
    - - [renderpass](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vulkan-render-pass) and [subpass](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#vulkan-subpass-handling) management (including [image layout](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vulkan-image-layout), [memory buffer usage](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-buffer-best-practices), manual [GMEM](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vk-qcom-tile-shading-best-practices) [optimization](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vk-qcom-tile-memory-heap-best-practices), [z-buffer setup](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-z-buffer) and [shader submission](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-graphics-submits-compute-dispatches-separate))
    - [reduced fragment](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-upscaling-best-practices) [shader invocations](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#vrs)
    - [swapchain setup](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-swapchain-android-phones)
    - [less performant features to avoid](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-less-performant-features)
- [Tile-based Rendering](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-rendering): [FlexRender™](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#flex-render), mid-frame, constantly chooses between [binning/GMEM](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-bin-minimization) and [direct/system-memory](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-direct-mode-triggers) mode: optimize for both (ideally with [Vulkan extensions](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-tile-shading-vulkan-extensions))
- [Render Surface Target](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#render-surfaces): The image at the end of the frame involves [sRGB](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#srgb-texture) and [pixel formats](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-use-srgb-with-fastest-pixel-format), [upscaling optimizations](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-upscaling-best-practices), and ensuring [optimal layout](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-use-render-pass-efficiently)
- [Universal BandWidth Compression (UBWC)](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#universal-bandwidth-compression-ubwc): increases memory bus throughput and reduces battery power
- [Z-buffer](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-z-buffer): Depth buffers should use the [optimal pixel format](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-use-fastest-depth-format)
- [LRZ, Early-Z and Fast-Z](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#lrz-early-z-fast-z): Hardware acceleration for depth buffering can dramatically improve performance
- [Vertex and Index Buffers](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vertex-and-index-buffers-best-practices): [Vertex](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vertex-buffer-layout) and [index](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-index-buffer-layout) buffer layouts impact performance, as does [submission order of buffer changes](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-batching-vertex-buffer-object-updates)
- [Texture Features](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#texture-features): [Sampler setup](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-textures-sampling-vulkan-setup) and [texture formats](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#texture-compression) impact performance, as can leveraging [driver-optimized texture features](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#texture-features)
- [Shaders](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#shaders): Adreno’s [unified](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#unified-shader-architecture)/[scalar](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#scalar-architecture) shader architecture enables many shader-level optimizations
- [Compute Shaders](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-compute-shaders): have some special considerations beyond [other shaders](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#shaders)
- [GPU-Driven Rendering](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#gpu-driven-rendering): involves using [compute shaders](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-compute-shaders) to offload more work from the CPU onto the GPU – consider a [tile-based](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vk-qcom-tile-shading-best-practices) approach
- [Low Priority Asychronous Compute (LPAC)](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#lpac): can efficiently perform some [compute shaders](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-compute-shaders) concurrently with other processing on the Graphics Pipe
- [Mesh Shaders](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-mesh-shading-best-practices) afford custom control over geometry generation and culling
- [Raytracing](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#raytracing): efficient simulation of light rays for beautiful imagery on low-power devices like mobile
- [OpenCL (download pdf)](https://developer.qualcomm.com/qfile/33472/80-nb295-11_a.pdf): is supported
- [2D Operation Hardware Acceleration](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-use-2d-operation-hardware-acceleration): is accessible through [graphic APIs](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-graphics-api)
- [Queries](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-queries): should be issued correctly to maximize performance and accuracy
- [Qualcomm True HDR](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#qualcomm-hdr): mobile devices featuring OLED screens support a higher dynamic range and wider color gamut: make use of this color depth
- [Variable Rate Shading (VRS)](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#vrs): allows a fragment shader to color one or more pixels at a time, so a fragment can represent one pixel or a group of pixels
- [Querying the driver version](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-querying-driver-version): helps you implement workarounds for older drivers and devices

## Best Practices Summary

Here is a quick-start overview of some of the most relevant development best practices, linking to more information:

### [Renderer Architecture](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#renderer-architecture-device-specific)

- [Prefer Vulkan to OpenGL ES](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-graphics-api)
- [Follow Vulkan best practices](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vulkan-best-practices)
- [Minimize renderpasses correctly](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-minimize-render-passes)
- [Setup renderpasses efficiently](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-use-render-pass-efficiently)
- [Maximize Universal BandWidth Compression (UBWC)](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-universal-bandwidth-compression-ubwc-best-practices)
- [Manually optimize for GMEM](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-tile-shading-vulkan-extensions)
- [Depth-only render efficiently](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-depth-only-render-efficient-setup)
- [Use swapchain efficiently](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-swapchain-android-phones)
- [Pass VK\_PIPELINE\_CREATE\_LINK\_TIME\_OPTIMIZATION\_BIT\_EXT in shipping builds to maximize performance](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#pipeline-create-link-time-optimization-bit)
- [Use Vulkan Adreno Layer in nonshipping builds](https://docs.qualcomm.com/doc/80-78185-2/topic/vk_adreno_layer.html#vk-adreno-layer)
- [Use buffer best practices](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-buffer-best-practices)
- [Separate graphics submits and compute dispatches](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-graphics-submits-compute-dispatches-separate)
- [Maximize indirect draw calls](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-indirect-draw-calls-best-practices)
- [Avoid less performant features](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-less-performant-features)
- [Use framerate extrapolation](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-use-framerate-extrapolation)
- [Ideal screenspace triangle size is at least 4 pixels, and not much larger than a bin](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-triangle-screen-size)

### Tile-based Rendering

- [Minimize the number of bins](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-bin-minimization)
- [Use tile-based draws when it fits your algorithm](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vk-qcom-tile-shading-best-practices)
- [Prefer MSAA to other anti-aliasing techniques (for small-pixel form factors like mobile, consider no anti-aliasing)](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#prefer-msaa-or-no-aa)

### Concurrent Binning

- [Issue each draw call such that it produces enough fragment shader work to parallelize with the next draw call’s binning](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-issue-enough-fragment-work-to-parallelize-with-binning)
- [Minimize dependencies – renderpass, barrier, Z-buffer-clear – that prevent concurrent binning](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-minimize-dependencies-renderpass-barrier-depth-target-clear)
- [Use the same Z-buffer (without clearing or invalidating it) between passes – or use separate Z-buffers per pass](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-use-logically-independent-depth-buffers-for-each-pass)

### [Render Surface Target](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#mobile-render-surface-target-device-specific)

- [Use HDR (high dynamic range)](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-qualcomm-hdr-best-practices)
- [Use the fastest possible sRGB pixel format](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-use-srgb-with-fastest-pixel-format)
- [Use the lowest render target resolution that looks good and upscale: prefer SGSR when possible, or frame buffer blits otherwise.](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-upscaling-best-practices)
- [On Android, consider relying on SurfaceFlinger’s efficient bilinear rescale](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-upscaling-best-practices)
- [Maximize use of Variable Rate Shading](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vrs-best-practices)
- [If using Multiple Render Targets, stay below device performance limits](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#mobile-render-surface-target-device-specific)

### Z-Buffer

- [Use the fastest possible depth format](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-use-fastest-depth-format)
- [Don’t disable LRZ (Low Resolution Z-Buffer)](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-lrz-do-not-disable) or [Early Z Rejection](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-early-z-rejection-do-not-disable)

### Vertex and Index Buffers

- [Lay out vertex buffers optimally](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vertex-buffer-layout)
- [Minimize the size of vertex buffers](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vertexattributescompress)
- [Minimize the size of index buffers](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-indexbuffercompress)
- [Batch vertex buffer object updates](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-batching-vertex-buffer-object-updates)

### Texture Features

[Use ASTC texture compression in the sRGB format](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#use-astc-texture-compression)

### Shaders

- [Use Adreno Offline Compiler to optimize shaders](https://qpm.qualcomm.com/#/main/tools/details/Adreno_GPU_Offline_Compiler)
- [Make instruction counts fit the instruction cache for binning, concurrent-binning, vertex, fragment, and compute shaders (LPAC compute shaders have a slightly larger instruction cache).](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-instruction-count) [Consider splitting up long shaders](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-instruction-count-long-shader-splitting)
- [Minimize GPR (general purpose register) usage.  Consider splitting up shaders that spill GPRs and cannot be further optimized](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-gpr-minimization)
- [Don’t reference too many unique vertex buffers, textures-and-SSBOs, samplers or uniform buffers](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-shader-unique-resource-performance-limits)
- [Minimize texture samples in vertex shaders](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vertes-shader-minimize-texture-fetches)
- [Load-balance shader-pipe and texture-pipe](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-texture-latency-masking)
- [Separate graphics submits and compute dispatches](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-graphics-submits-compute-dispatches-separate)
- [Maximize half precision](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-maximize-half-precision)
- [Minimize type casting](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-minimize-type-casting)
- [Prefer built-in instructions](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-built-in-shader-instructions)
- [Use hardware accelerated blits, convolution kernels, etc](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-use-2d-operation-hardware-acceleration)
- [Use Android-phone pre-rotation on Vulkan](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-use-2d-operation-hardware-acceleration)

### Compute Shaders

- [Most shader advice applies to compute shaders](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-shaders-performance-advice-summary)
- [Prefer fragment shaders to compute shaders](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-prefer-fragment-to-compute-shaders)
- [Instruction counts and GPR limits are similar – but not identical – to other shaders](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-compute-shaders-performance-like-other-shaders)
- [Minimize compute thread synchronization – when it’s necessary, prefer atomics](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-avoid-synchronizing-compute-threads)
- [Tune workgroup sizes and number of workgroups, taking into account shader stalls and whether the shaders read each others’ memory](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-compute-shader-workgroup-tuning)

### Mesh Shading

- [Most shader advice applies to mesh shaders](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-shaders-performance-advice-summary)
- [Avoid amplification shaders](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-avoidmeshshadingamplificationshaders)
- [Set the optimal wave count](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#mobile-meshshadingwavecountoptimal-specs)
- [Prefer per-vertex attributes to per-primitive attributes](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-prefervertexattributestoprimitiveattributes)
- [Keep mesh shader payload sizes under device limits](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-meshshaderpayloadunderlimit)
- [Consider using LPAC to load-balance with the Graphics Pipe](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#lpac)

### Raytracing

- [Prefer ray queries to ray pipelines](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-preferrayqueriestoraypipelines)
- [Minimize calls to rayQuery Proceed()](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-minimize-calls-to-ray-query-proceed)
- [Avoid proceed() calls in loops for non-opaque traversal that “accept first fit”](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-avoid-proceed-calls-in-accept-first-fit-loops)
- [Use only one ray query object; reuse as needed](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-use-one-ray-query-object)
- [Access ray query data through intrinsics](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-use-intrinsics-to-access-ray-query-data)
- [Prefer building, serializing and deserializing acceleration structures on the GPU](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-prefer-building-raytracing-acceleration-structures-on-gpu)
- [Refit and rebuild deformable raytraced meshes judiciously](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-raytracing-acceleration-structures-refitting-rebuilding)

### Queries

- [Use occlusion queries correctly for accuracy and efficiency](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-occlusion-query-correct-usage)
- [Use GPU timestamp queries correctly for accuracy](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-gpu-timestamp-query-correct-usage)

### XR/VR/AR (Extended Reality/Virtual Reality/Augmented Reality)

- Use [foveated rendering](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-foveated-rendering) to likely drastically improve performance and battery life with few or no noticeable artifacts

## Renderer Architecture

Here are some overarching issues to keep in mind while architecting a renderer that will make the most of Adreno’s performance and battery usage.

### Graphics API

Prefer Vulkan to OpenGL ES.  While Vulkan is not a perfect superset of OpenGL ES, in most respects it is more performant, more debuggable, and more fully featured.

### Render Pass

Minimize the number of render passes – for example, any time several consecutive passes use the same formatted color buffer, combine them (disabling depth and/or stencil if one or both are unused).  [Snapdragon Profiler](https://docs.qualcomm.com/doc/80-78185-2/topic/sdp.html#sdp) shows how renderpasses and subpasses are (or are not) merged on its [Rendering Stages](https://docs.qualcomm.com/doc/80-78185-2/topic/sdp.html#sdp) metric.

When combining the results of multiple passes (as in deferred rendering or blending transparent objects with opaque objects), prefer using alpha blending to the alternatives (such as the discard instruction, shader branching, stencil testing and compute shaders).

Many applications are fragment-shader bound, and Adreno has many tools to optimize such applications:

- Render target resolutions can be efficiently [upscaled in several ways](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-upscaling-best-practices)
- [Variable Rate Shading](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vrs-best-practices) shades fewer pixels
- Framerate [Extrapolation](https://github.com/quic/adreno-gpu-opengl-es-code-sample-framework/tree/main/samples/amfe_power_saving) can also nearly double a fragment-bound application’s framerate

Depending on content, these features can involve little to no visual quality degradation.

When performing depth-only rendering (like a depth-prepass), use an empty fragment shader and disable frame buffer writes to [leverage Fast-Z](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#fast-z).

Invalidate framebuffer contents as early as possible, so the [driver doesn’t wastefully resolve GMEM render target memory to system memory](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-rendering):

Tab Vulkan
Tab OpenGL ES

To invalidate framebuffer contents as early as possible, correctly use VK\_ATTACHMENT\_LOAD\_OP\_CLEAR and VK\_ATTACHMENT\_LOAD\_OP\_DONT\_CARE on your renderpass (and VK\_QCOM\_render\_pass\_shader\_resolve if a nonstandard shader resolve is desired).

Correct subpass handling is also essential.

Vulkan introduced render ‘subpasses’, which allow developers to set up render pipelines that explicitly state their usage, render target interactions, dependencies, transitions, etc. This allows GPUs to make informed decisions about how to handle these frame buffer transitions efficiently. To efficiently use GMEM, proper subpass use is crucial in [tile-rendering architectures](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-rendering) such as the Adreno GPU.

A properly structured renderpass allows Vulkan to instruct the GPU to execute all subpasses on a per-tile basis. That is, the full subpass chain can be executed for each tile, thus avoiding the need to resolve subpasses to system memory after each pass. Proper setup of these subpasses is required for the Vulkan driver to “merge” the subpasses into one. This can result in gains of over 10% frametime depending on subpass chain complexity and configuration.

Generally, a good Vulkan renderpass involves the following:

- Subpass count &gt; 1
- Renderpass has input targets
- Each resolve attachment is used in an exactly one subpass
- <cite>srcAccessMask</cite> is not <cite>VK_ACCESS_SHADER_WRITE_BIT</cite>, and <cite>dstAccessMask</cite> is not <cite>VK_ACCESS_SHADER_READ_BIT</cite>
- Starting from the second subpass where the <cite>input_attachments</cite> field is used, the <cite>dstAccessMask</cite> must be set to <cite>VK_ACCESS_INPUT_ATTACHMENT_READ_BIT</cite>

Note

Subpass merging only applies when the given surface is being rendered in binning mode. [Snapdragon Profiler](https://docs.qualcomm.com/doc/80-78185-2/topic/sdp.html#sdp) ‘Rendering Stages’ metric in [Trace](https://docs.qualcomm.com/doc/80-78185-2/topic/sdp.html#sdp-trace) capture identifies the mode these surfaces are being rendered with, and if proper merging has been done. Additionally, using the [Vulkan Adreno Layer](https://docs.qualcomm.com/doc/80-78185-2/topic/vk_adreno_layer.html#vk-adreno-layer) can also help identify if subpasses were not be merged properly by logging the <cite>VKDBGUTILWARN003</cite> flag.

- Sample Code:
    - - [SubPass](https://github.com/quic/adreno-gpu-vulkan-code-sample-framework/tree/main/samples/SubPass)
- [Tonemapping Efficiently](https://github.com/quic/adreno-gpu-vulkan-code-sample-framework/tree/main/samples/shaderResolveTonemap)

To invalidate framebuffer contents as early as possible, there are several options:

- glInvalidateFramebuffer() states that this framebuffer is not being used until further notice, and ensures no [GMEMLoad’s or GMEMStore’s will be performed with the frame buffer](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-rendering) – until the developer makes further API calls on it

> 
> 
> - glInvalidateSubFramebuffer() makes a similar guarantee for a subrectangle of the framebuffer
- glClear() minimizes [GMEMLoad’s and GMEMStore’s performed with the frame buffer](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-rendering) – while memset’ing pixel memory as required.  [Sample Code: Avoid GMEMLoads](https://github.com/quic/adreno-gpu-opengl-es-code-sample-framework/tree/main/samples/avoid_gmem_loads)
- [EXT_discard_framebuffer](https://registry.khronos.org/OpenGL/extensions/EXT/EXT_discard_framebuffer.txt) can also be used to avoid GMEMLoad’s
- [Minimizing GMEMStore’s performed with the frame buffer](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-rendering) is also important: [Sample Code: Minimize GMEMStores](https://github.com/quic/adreno-gpu-opengl-es-code-sample-framework/tree/main/samples/reduce_gmem_stores)

Tab Vulkan
Tab OpenGL ES

Pass VK\_PIPELINE\_CREATE\_LINK\_TIME\_OPTIMIZATION\_BIT\_EXT in shipping builds to maximize performance.

Prefer uniform buffers instead of push constants for performance.

- Avoid glClientWaitSync() and glFenceSync() (unless performing GPU write-back, which is discouraged for performance reasons) – the CPU needn’t be explicitly told to wait on the GPU, since glSwapBuffers() already handle this
- Use the latest version of OpenGL ES – there are numerous reasons (bugfixes and optimizations) to be on the latest, and few reasons not to be
- Use the KHR\_No\_Error gles extension for shipping builds to maximize performance

### Shader mode switching

Minimize pipeline and compute kernel switching to avoid unnecessary internal synchronization – and particularly minimize alternating between graphics submits and compute dispatches.

In other words, separate graphics submits and compute dispatches as much as possible – ideally issue a series of only graphics submits followed by a series of only compute dispatches.

And even within the phase (or, possibly less optimally, phases) of the frame where graphics submits take place, minimize interleaving pipelines – for example, prefer (SubmitGraphicsPipelineA 2 times, SubmitGraphicsPipelineB) to (SubmitGraphicsPipelineA, SubmitGraphicsPipelineB, SubmitGraphicsPipelineA).  The same rationale applies to dispatching compute kernels.

### Images

Vulkan image layout datastructures should be as specific as possible.  This is significantly more important for performance on Adreno hardware than on many other GPU’s.

[VK\_IMAGE\_CREATE\_MUTABLE\_FORMAT\_BIT should be avoided as much as possible on all devices prior to Adreno750.](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vk-image-create-mutable-format-bit)

### Buffer Best Practices

Flag all buffers read-only as much as possible.

Prefer Vertex Buffer Objects (VBOs) when possible.

Otherwise prefer uniform buffers (UBOs) provided the sum of their sizes for a given shader remain under 90% of 8K – 0.9\*8192 = 7372 bytes.  Note that the maximum size reported by graphics APIs (in Vulkan, vkPhysicalDeviceLimits::maxUniformBufferRange) is only a correctness limit – not a performance limit.  Note that the sum of all uniform buffers used by a shader – not just each uniform buffer individually – must stay under this limit to avoid this possible performance reduction.

For larger data sizes prefer textures over Shader Storage Buffer Objects (SSBOs).

If a UBO exceeds its optimal size, the compiler will attempt to determine which portion of the UBO may be accessed by the shader, and map only those portions of the UBO to constant RAM.  Dynamic or indirect indexing might prevent this optimization, so if your UBO might exceed its optimal size (and you choose not to use textures or SSBOs), prefer static indexing.

Vulkan: use VK\_MEMORY\_PROPERTY\_LAZILY\_ALLOCATED\_BIT for buffers that are not read from outside of the renderpass (especially MSAA attachments, which are larger than non-MSAA attachments). For example, a Z-buffer that exists only to be cleared and used for typical z-buffering within a single renderpass should use this flag.

### Vulkan

[Avoid less performant features of the API](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vulkan-less-performant-features)

If you encounter VK\_DEVICE\_LOST, calling VK\_EXT\_device\_fault is your only option – calling any other part of the API is undefined.

[Vulkan image layout datastructures should be as specific as possible.](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vulkan-image-layout-specific)

#### Swapchain

Use VK\_PRESENT\_MODE\_FIFO\_KHR and minImageCount=3 to most efficiently utilize the GPU (VK\_PRESENT\_MODE\_MAILBOX\_KHR can sometimes help with frame pacing/latency, but often can cost significant battery and generate significant heat for no benefit, as it may renders frames that are not presented to the player)

Vulkan: use [prerotation](https://docs.vulkan.org/samples/latest/samples/performance/surface_rotation/README.html)

### Triangle Screen Size

Ideally every triangle rasterized shades at least 4 pixels; tune level-of-detail (LOD) systems and content accordingly.

If a triangle spans multiple tiles in binning mode, the full triangle will be rasterized per tile – there are no added vertices at tile boundaries.  Therefore, many triangles much larger than the bin size in screenspace can be inefficient.

### Features to avoid for performance reasons

- - Vulkan:
    - - VK\_IMAGE\_CREATE\_MUTABLE\_FORMAT\_BIT: since the driver doesn’t know which view formats will be paired with the image, this often reduces performance.  Additionally this flag usually degrades or disables [UBWC](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-universal-bandwidth-compression-ubwc-best-practices)
    - VK\_EXT\_conditional\_rendering is unlikely to improve performance unless it’s skipping a large amount work, as its overhead is substantial
    - VK\_EXT\_vertex\_input\_dynamic\_state should be used minimally – static pipelines are strictly more performant
    - Avoid [VkPipelineInputAssemblyStateCreateInfo::primitiveRestartEnable](https://registry.khronos.org/vulkan/specs/latest/man/html/VkPipelineInputAssemblyStateCreateInfo.html) – simply performing multiple draws will almost always outperform this approach
- Tessellation hardware stages (hull shader, tessellator, domain shader)
- Client-side vertex arrays
- User clip planes

## [Tile-based Rendering](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-rendering)

### Bin Minimization

Some performance bottlenecks for a render target can be alleviated by reducing the number of bins the driver generates (which, in turn, often increases the number of pixels each bin contains).  The developer has several options:

- reduce frame buffer resolution
- use [Variable Rate Shading](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vrs-best-practices) (including foveated rendering) to render fewer fragments

- use fewer MSAA samples.  In particular, MSAAx2 is likely to be practically free (while this can generate more bins, and lots of small draws in the extra bins may incur more binning overhead, this and the additional resolve time are usually small enough to be hidden by other bottlenecks)
- render to fewer render targets at once

### [FlexRender™ technology (Hybrid Deferred and Direct Rendering mode)](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#flex-render)

The driver heuristics that determine [when to run binned or direct mode](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#flex-render) are not exposed to the developer, but generally these scenarios trigger direct mode:

- High ratio of texture samples in vertex shaders to vertices
- Small number of vertices and/or draws
- Use of tessellation or geometry shaders

### [Concurrent Binning](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#concurrent-binning-overview)

No additional steps are required to activate concurrent binning – but care should be taken to maximize its benefits.

Sequential render passes with dependencies might require synchronous binning – concurrent binning is only possible when there are “bubbles” in the render graph.  Consider the renderpass chain:

![Concurrent Binning](data:image/png;base64,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)

*Pass C* uses binning but – unfortunately – its binning process depends on *Pass B*, which depends on *Pass A*.

If we remove the binning dependencies from *Pass B* – so *Pass C*’s vertex shader doesn’t require any inputs from *Pass B* (perhaps by revisiting renderpass setup and barriers) – the driver might asynchronously perform binning during *PassB* and/or *PassC* on the concurrent binning pipe:

![Concurrent Binning](data:image/png;base64,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)

Another way concurrent binning can be prevented is reusing the same Z-buffer attachment with clears within a frame (you might issue these clears if, for example, the Z-buffer is being used for multiple purposes within a frame).  These clears define dependencies, and thus prevent concurrent binning for every render pass or compute operation that uses this Z-buffer attachment.

Try to use the same Z-buffer – without clears or invalidations – over multiple render passes to allow concurrent binning. (If this is not possible, giving each renderpass its own Z-buffer also allows concurrent binning, though this risks using more memory bandwidth and costs memory)

Another common case that fails to leverage concurrent binning is when the application is VSYNC limited, and the first surface processes all the geometry. In such a scenario, try to schedule some independent work before submitting that geometry-heavy render pass so both the independent work and the first surface’s geometry binning can happen in parallel.

If your app is CPU-bound and you’re willing to accept up to one extra frame of latency, another approach is to continue issuing rendering work until your synchronization primitives indicate that the CPU is ready to submit work from frame N+2 but the GPU has not yet completed frame N.  In this way, the driver is encouraged to perform frame N+1’s binning work concurrently with frame N’s non-binning work – particularly if most of the binning work occurs early in the frame.

### [Tile Shading Vulkan Extensions](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-shading-vulkan-extensions)

[Minimizing renderpasses](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-minimize-render-passes) is often critical to maximizing the benefits of using the tile shading Vulkan extensions, since this enables the developer to minimize synchronization and system memory usage in favor of [GMEM](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-rendering).

#### [VK\_QCOM\_tile\_memory\_heap](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#vk-qcom-tile-memory-heap-overview)

When your images and/or buffers fit the device’s tile memory constraints and are used over several render passes (as in deferred rendering), consider using [VK_QCOM_tile_memory_heap](https://registry.khronos.org/vulkan/specs/latest/man/html/VK_QCOM_tile_memory_heap.html) to allocate images and/or buffers on [GMEM](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-rendering) and have them stay resident as long as possible.

Use [VkTileMemorySizeInfoQCOM](https://registry.khronos.org/vulkan/specs/latest/man/html/VkTileMemorySizeInfoQCOM.html) to specify the amount of tile memory in use during the relevant render passes.

If an image/buffer is used only to hold intermediate results, allocate them in [GMEM](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-rendering) for as long as they are needed.  This saves bandwidth, which can translate into battery and/or performance savings.

If a render pass is to use one resource in tiled memory – and then stop using that resource and start using another resource – consider using [VkTileMemorySizeInfoQCOM](https://registry.khronos.org/vulkan/specs/latest/man/html/VkTileMemorySizeInfoQCOM.html) to allocate just enough memory to accommodate the largest of the resources and then alias each resource, reading and storing as needed.

#### [VK\_QCOM\_tile\_shading](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#vk-qcom-tile-shading-overview)

Always enable [VK\_QCOM\_tile\_memory\_heap](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vk-qcom-tile-memory-heap-best-practices) as well as VK\_QCOM\_tile\_shading so the driver efficiently uses whatever GMEM you leave unallocated.  Using just VK\_QCOM\_tile\_shading alone is never recommended, because then the driver tends to use far less GMEM than it otherwise could.

Always use [VK_DEPENDENCY_BY_REGION_BIT](https://registry.khronos.org/vulkan/specs/latest/man/html/VkDependencyFlagBits.html) for subpass dependencies and pipeline barriers that might execute during [per-tile blocks](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-shading-per-tile-block) – omitting [VK_DEPENDENCY_BY_REGION_BIT](https://registry.khronos.org/vulkan/specs/latest/man/html/VkDependencyFlagBits.html) will probably deactivate [VK_QCOM_tile_shading](https://registry.khronos.org/vulkan/specs/latest/man/html/VK_QCOM_tile_shading.html) and remove all associated benefits.

In the unlikely event that you find the driver employing [“Direct” Render Mode](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#flex-render) on a surface that uses a [per-tile block](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-shading-per-tile-block), performance on the [per-tile block](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-shading-per-tile-block) commands is likely very poor.  Every effort should be made to discourage the driver from using [“Direct” Render Mode](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#flex-render) during [per-tile blocks](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-shading-per-tile-block).

**Attachment Limitations**

* * *

A developer might reasonably think to use [VK_QCOM_tile_shading](https://registry.khronos.org/vulkan/specs/latest/man/html/VK_QCOM_tile_shading.html) to perform stores on a color attachment from a fragment shader to ensure that [GMEM](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-rendering) is used instead of system memory.  However, the typical fragment shading pattern is practically guaranteed to be as – and typically much more – performant than this approach, so using [VK_QCOM_tile_shading](https://registry.khronos.org/vulkan/specs/latest/man/html/VK_QCOM_tile_shading.html) to bind a color attachment as a storage image and access it through image load/store ops is not supported via fragment shader.

Similarly, neither compute nor fragment shaders may store to depth/stencil attachments, resolve attachments or input attachments – for such operations, the typical fragment shading pattern should be used.

On the other hand, storing to a color attachment from a compute shader is supported, and may perform acceptably.

**Optimizing** [Per-Tile Draws](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-shading-per-tile-draws)

* * *

Draws executed within a [per-tile block](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-shading-per-tile-block) ([per-tile draws](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-shading-per-tile-draws)) are optimized for GPU-driven rendering – specifically the case where a per-tile dispatch invokes a per-tile compute shader that writes data to an indirect buffer, followed by a per-tile vkCmdDrawIndirect\* that consumes that same buffer.

Other than such GPU-driven rendering, [per-tile draws](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-shading-per-tile-draws) may not perform well – for CPU-driven rendering, we recommend standard execution (where, in a single draw call, the render pass or dynamic rendering scope’s entire render area is accessible and is optionally stored in [GMEM](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vk-qcom-tile-shading-best-practices) by the [VK_QCOM_tile_memory_heap extension](https://registry.khronos.org/vulkan/specs/latest/man/html/VK_QCOM_tile_memory_heap.html)).

Given this, hopefully it’s clear how [per-tile draws](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-shading-per-tile-draws) are specifically for GPU-driven draws with tile-dependent draw data – regular draws will likely see a performance loss if executed within per-tile blocks.

Finally, you should ensure that each [per-tile draw](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-shading-per-tile-draws) contains only primitives that cover the current tile, since [per-tile draw](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-shading-per-tile-draws) are not affected by the visibility pass – instead they are executed no matter how the draw rasterizes within the tile (even if the tile’s pixels will not be written to!)

Some empirical results of using the extension are [collected here](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#mobile-vk-qcom-tile-memory-heap-empirical-results).

## [Render Surfaces](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#render-surfaces)

### [sRGB textures and render targets](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#srgb-texture)

Use [SRGB textures](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#srgb-texture):

Tab Vulkan
Tab OpenGL ES

Vulkan fully handles sRGB in both textures and swapchain presentable images.

Unfortunately, OpenGL ES assumes linear or RGB color space by default. As Adreno GPUs support sRGB color space for render targets and textures, [it is possible to ensure a correct color viewing experience with some extra work](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-qualcomm-true-hdr-setup).

The best-performing color format is:

- R10G10B10A2, as the hardware is optimized for this format
- R11G11B10A0 if even greater color depth is required, and alpha transparency is not
- RGBA16 if graduated transparency is required, or the (considerable) color depth of the above formats is insufficient

[Vulkan Sample: Qualcomm TrueHDR](https://github.com/quic/adreno-gpu-vulkan-code-sample-framework/tree/main/samples/hdrSwapchain)

### Upscaling

To reduce the load on the rendering hardware, an application can reduce the size of the render target used, e.g., if the native screen resolution is 1080p (1920x1080), it could be rendered to a 720p (1280x720) render target instead. Since the aspect ratio of the two resolutions is identical, the proportions of the image will not be affected.

The reduced-size render target will not completely fill the screen, but there are [numerous ways of upscaling](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-upscaling-best-practices) the reduced-size render target to match the full native display size.

This technique has been used successfully in console games, many of which make heavy demands on the GPU and, if rendering were done at full HD resolution, could miss framerate targets due to too much time spent during fragment shading.

Upscale to improve fragment-bound frames with:

Tab Snapdragon Game Super Resolution
Tab Android

[Snapdragon GSR](https://github.com/quic/snapdragon-gsr) is a single pass spatially-aware super resolution technique developed by Qualcomm Snapdragon Studios to achieve optimal super scaling quality at the best performance and power savings – it uses optimized image processing to, for most content, achieve a sharper, higher-quality image than the typical bilinear filtering approaches.

For applications written in Java, configure the fixed-size property of the GLSurfaceView instance (available since API level 1). Set the property using the setFixedSize function, which takes two arguments defining the resolution of the final render target.

For applications written in native code, define the resolution of the final render target using the function NativeWindow\_setBuffersGeometry, which is a part of the NativeActivity class, introduced in Android 2.3 (API level 9).

At every swap operation, the operating system takes the contents of the final render target and scales it up so that it matches the native resolution of the display.

You can also do this yourself with your preferred graphics API. The upscaling can be done either at the end of the rendering process or at some point in the rendering pipeline – for example, one approach is to render the geometry at 1:1 resolution, but apply postprocessing effects using render targets of a lower resolution.

Tab Vulkan
Tab OpenGL ES

vkCmdBlitImage() provides a [hardware-accelerated path](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-use-2d-operation-hardware-acceleration) to upscaling an image prior to display.

OpenGL ES 3.0 introduced support for frame buffer blit operations, so the contents of a draw buffer can be blit from one frame buffer to another. As part of the blit operation, the API also supports upscaling, which can be used to copy the contents of a texture of a smaller resolution to another texture of a larger resolution.

Note

Upscaling using a frame buffer blit is faster than the alternative approach of rendering a full screen quad directly to the back buffer, taking the reduced-size render target as a texture input.

On Android, at the end of the frame, if the final render target is a different resolution than the device’s native resolution then SurfaceFlinger will bilinearly rescale the final image to native resolution with efficiency.

### Bandwidth Optimization

Applications becoming memory-bandwidth limited can be a bottleneck due to the physical limitation of the GPU’s data access rate. The rate is not constant and varies according to many factors, including but not limited to:

1. Location of the data: is it stored in RAM, VRAM, or one of the GPU caches?
2. Type of access: is it a read or a write operation? Is it atomic? Does it have to be coherent?
3. Viability of caching the data: can the hardware cache the data for subsequent operations that the GPU will be carrying out, and would it make sense to do this?

Cache misses can cause applications to become bandwidth-limited, which significantly reduces performance. These cache misses are often caused when applications draw or generate many primitives, or when shaders need to access many locations within textures.

Here are ways to minimize cache misses:

1. Reduce the amount of data the GPU needs to access to perform the dispatch or draw call that is hitting this constraint
2. In OpenGL, ensure that [client-side vertex data buffers](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-less-performant-features) are used for as few draw calls as possible – ideally, an application should never use them

Graphics APIs provide several methods that developers can use to reduce the bandwidth needed to transfer specific types of data.

The first method is [compressed texture internal formats](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#texture-compression), which sacrifice texture quality for the benefit of reduced size. Many of the compressed texture formats supported divide the input image into 4x4 blocks and perform the compression process separately for each block, rather than operating on the image as a whole. While this seems inefficient from the point of view of data compression theory, doing so has the advantage of each block being aligned on a 4-pixel boundary. This allows the GPU to retrieve more data with a single fetch instruction, because each compressed texture block holds 16 pixels instead of a single pixel, as in the case of an uncompressed texture. Also, the number of texture fetches can be reduced, provided that the shader does not need to sample texels that are too far apart.

The second method is to always use [packed vertex data formats](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vertexattributescompress), unless the vertex data sets would suffer greatly from a reduction in the precision of their components.

The third method is to always use [indexed draw calls](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-geometry-instancing), and always use an [index type that is as small as possible](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-indexbuffercompress) while still being able to address all the vertices for the mesh being drawn. This reduces the amount of index data that the GPU needs to access for each draw call, at the expense of slightly more complicated application logic.

#### [Universal BandWidth Compression (UBWC)](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#universal-bandwidth-compression-ubwc)

Graphics APIs must be used correctly to maximize the use of [UBWC](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#universal-bandwidth-compression-ubwc).  For example, VK\_IMAGE\_TILING\_OPTIMAL generally uses [UBWC](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#universal-bandwidth-compression-ubwc) as expected, but these Vulkan features almost certainly disable [UBWC](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#universal-bandwidth-compression-ubwc):

- [Compute Shaders](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#compute-shaders-device-specific)
- Non-optimal tiling: VK\_IMAGE\_TILING\_LINEAR
- Any kind of Cpu readback from the Gpu, including VK\_EXT\_host\_image\_copy/VK\_IMAGE\_USAGE\_HOST\_TRANSFER\_BIT
- [VK\_KHR\_fragment\_shading\_rate](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#vrs)
- VK\_EXT\_fragment\_density\_map
- Aliased images: VK\_IMAGE\_CREATE\_ALIAS\_BIT
- Mutable images: VK\_IMAGE\_CREATE\_MUTABLE\_FORMAT\_BIT
- Sparse residency of any kind: VK\_IMAGE\_CREATE\_SPARSE\_RESIDENCY\_BIT, VK\_IMAGE\_CREATE\_SPARSE\_BINDING\_BIT, VK\_IMAGE\_CREATE\_SPARSE\_ALIASED\_BIT

## Z-Buffer

The best-performing depth format is:

- D16, if stencil is unnecessary and this provides sufficient precision – which, in many cases where the developer is tuning a z-reverse implementation appropriately to content, it will
- D24\_S8, if stencil is required
- D32, if stencil is not required and D16 does not provide sufficient precision

[UBWC](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#universal-bandwidth-compression-ubwc) supports all these formats.

## LRZ, Early-Z and Fast-Z

### [Low Resolution Z pass](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#lrz)

The ways in which [LRZ (Low Resolution Z-Buffer)](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-lrz-do-not-disable) and [Early Z Rejection](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-early-z-rejection-do-not-disable) optimizations can be disabled are complex.

Broadly speaking: blending, stencil use, manually writing to the depth buffer or UAVs, alpha-to-coverage use, the “discard” shader keyword, changing the depth comparison operator, reading from the framebuffer, or masked writes (whether to color channels or a subset of multiple render targets) all might disable [LRZ (Low Resolution Z-Buffer)](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-lrz-do-not-disable), [Early Z Rejection](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-early-z-rejection-do-not-disable) or both – so if any of these operations must be done, perform them as near the end of the frame as possible (or see if the details below allow any such operations for your specific case).

Warning

Several conditions cause the driver to disable LRZ for a period of time between two operations.

LRZ (test and write operations) will be disabled until the next API-surface-clear by performing any of the below, and then executing a depth-write:

> 
> 
> - changing the depth direction (eg the sign of the forward axis)
> - setting the depth function to ALWAYS or NOT\_EQUAL

LRZ write operations will be disabled until next API-surface-clear (note that test operations on the existing LRZ buffer will still be enabled) by performing:

> 
> 
> - any of the below, and then later performing a depth-write in the same draw:
> 
> 
> 
> > 
> > 
> > - blending implemented with:
> > 
> > 
> > 
> > > 
> > > 
> > > - the fixed-function pipeline
> > >         - logical operations that read from the framebuffer
> >     - a color-masked write (eg writing to a subset of a buffer’s color channels using graphics API calls rather than shader code)
> >     - a partial Multiple Render Target (MRT) write (eg writing to a subset of all specified render targets)
> - in any subpass, a depth-write and reading from any attachment modified in any prior subpass of this renderpass (the order of operations is irrelevant – it doesn’t matter if the depth-write happens before or after the read from the previously-modified-attachment)
> - any stencil operation (reads or writes) in almost any situation
> - a framebuffer fetch
> - “advanced” blending with extensions

LRZ (test and write operations) will be disabled for the current draw (note that by default the next draw will fully re-enable LRZ) if the fragment shader writes to:

> 
> 
> - a UAV (any kind of buffer)
> - depth or stencil

LRZ write operations will be disabled for the current draw (note that test operations on the existing LRZ buffer will still be enabled, and that by default the next draw will fully re-enable LRZ) if:

> 
> 
> - the draw’s active pipeline has alpha-to-coverage enabled
> - a fragment-shader:
> 
> 
> 
> > 
> > 
> > - uses discard
> >     - outputs sample coverage

Vulkan: LRZ (test and write operations) will be disabled if secondary command buffers are used (this limitation applies only for devices prior to Adreno650)

LRZ is fully active during direct rendering (presuming it isn’t [disabled for some other reason](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-lrz-do-not-disable)), but typically produces much less performance benefit than with binned rendering

### [Early Z rejection](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#early-z-rejection-overview)

Warning

Early Z-rejection is disabled if:

- Z-Buffer is written to from a fragment shader
- “Discard” shader instruction is used
- A fragment shader writes depth or stencil values – if these writes are necessary, perform them as late in the frame as possible
- Alpha-to-Coverage is enabled (minimize the use of this feature)

Adreno GPUs can reject occluded pixels at up to 4x the drawn pixel fill rate.

### [Fast-Z](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#fast-z)

Hint the driver to engage Fast-Z by using an empty fragment shader and disabling frame buffer write masks for renderpasses that modify Z values only.

If fast-Z doesn’t activate by default for indirect draws, it can be switched on with [glsl.](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-early-zindirect)

## [Vertex and Index Buffers](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#vertex-and-index-buffers-device-specific)

### Vertex buffer layout

Pass a single vertex buffer to the vertex shader.

The vertex buffer should begin with an interleaved array of all attributes that are used by the vertex shader to calculate final vertex positions – often this is nothing more than vertex positions arranged like: (xyz|xyz) – followed by one interleaved array of all other attributes.

Compress all attributes as much as possible.

For any asset with a coordinate range known in advance, try to map the data onto one of the supported, packed vertex data formats. Taking normal data as an example, it is possible to map XYZ components onto the GL\_UNSIGNED\_INT\_2\_10\_10\_10\_REV (OpenGL ES) format by normalizing the 10-bit unsigned integer data range of &lt;0, 1024&gt; onto the floating-point range &lt;-1, 1&gt;.

Tab Vulkan
Tab OpenGL ES

Vulkan does not yet support half-precision in vertex shaders.

OpenGL ES supports half-precision in vertex shaders with GL\_OES\_vertex\_half\_float.

The Adreno GPU provides hardware support for the following vertex formats:

- GL\_BYTE and GL\_UNSIGNED\_BYTE
- GL\_SHORT and GL\_UNSIGNED\_SHORT
- GL\_FIXED
- GL\_FLOAT
- GL\_HALF\_FLOAT
- GL\_INT\_2\_10\_10\_10\_REV and GL\_UNSIGNED\_INT\_2\_10\_10\_10\_REV

Always use the vertex format that provides acceptable precision and takes the least amount of space.

The compiler will usually optimize the binning vertex shader to use only the vertex attributes the vertex shader – called a [position-only vertex shader](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#position-only-vertex-shader) – that needs to compute final vertex positions – any attributes that are simply forwarded to the fragment shader (by Vulkan’s “layout”/”out” or OpenGL’s “varying/flat”) will be “refactored” into code that runs immediately before the fragment shader, resulting in better performance.

In addition to the above, vertex information can be [laid out in a cache-friendly way over a range of devices](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#vertex-cache-size), such that triangles sharing the same vertex are more often clustered together – doing so can reduce vertex cache misses and increase performance.

### Batching vertex buffer object updates

If you must modify Vertex Buffer Object contents on-the-fly when rendering a frame, batch as many of the VBO updates as possible before issuing any draw calls that use the modified VBO region. If using multiple VBOs, batch the updates for all the VBOs first, and then issue all the draw calls.

Not doing so might cause the driver to maintain multiple copies of an entire VBO, which reduces performance.

### Index types

A geometry mesh can be represented by two separate arrays. One array holds the vertices, and the other holds sets of three indices into that array. Together, they define a set of triangles.

Adreno GPUs natively support 8-bit, 16-bit, and 32-bit index types.

Prefer 8-bit indices when possible, 16-bit indices when necessary, and try to avoid 32-bit indices.

## [Texture Features](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#texture-features-device-specific)

### Sampling in Vulkan

Use VK\_DESCRIPTOR\_TYPE\_COMBINED\_IMAGE\_SAMPLER to access the GPU’s more performant Bindless mode.  We’ve seen separate samplers exhibit 2-5% less fill rate compared to combined samplers.

### Multiple textures

*Multiple texturing* or *multitexturing* is the use of [more than one texture at a time on a polygon.](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#multitexturing-limits-device-specific)

Effective use of multiple textures significantly reduces overdraw, saves ALU cost for fragment shaders, and avoids unnecessary vertex transforms.

### [Texture compression](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#texture-compression)

See [Texture Compression Examples](https://docs.qualcomm.com/doc/80-78185-2/topic/texture_compression_examples.html#texture-compression-examples).

### [Floating point textures](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#floating-point-textures-overview)

Adreno GPUs support floating point texturing features including the following:

- Texturing and linear filtering of FP16 textures via the GL\_OES\_texture\_half\_float and GL\_OES\_texture\_half\_float\_linear extension
- Texturing from FP32 textures via GL\_OES\_texture\_float

For a complete listing of supported texture and surface formats, refer to the [Texture formats](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#texture-formats) Feature Table.

### [Video textures](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#video-textures-overview)

[Video textures](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#video-textures-overview) are a standard API feature in Android (Honeycomb or later versions). Refer to Android documentation for additional details on surface textures at [http://developer.android.com/reference/android/graphics/SurfaceTexture.html](http://developer.android.com/reference/android/graphics/SurfaceTexture.html).

Apart from using the standard Android API as suggested, the standard OpenGL ES extension can also be used, e.g., if an application requires video textures. For more information, refer to [http://www.khronos.org/registry/gles/ extensions/OES/OES_EGL_image.txt](https://registry.khronos.org/OpenGL/extensions/OES/OES_EGL_image.txt).

## [Shaders](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#shaders-device-specific)

### Half-Precision

Adreno’s [scalar architecture](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#scalar-architecture) can be twice as power-efficient and deliver twice the performance while processing a fragment shader – if that fragment shader uses medium-precision 16-bit floating point (mediump) processing instead of high-precision 32-bit (highp) floating point.

Use strict half-precision types as much as possible.  When necessary, relaxed-precision will often produce 16-bit floating point code.

### Instruction count

A shader’s instruction count fitting the instruction cache is usually critical for avoiding shader stalls and thus achieving optimal performance.

If a shader must exceed a target device’s instruction limit (particularly if that shader rarely stalls on texture fetches and other slow operations that might allow the driver to swap in a different shader), consider [splitting the shader into multiple parts](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-split-up-draw-calls).

Compute shaders that run on [LPAC](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#lpac) have a [slightly higher instruction limit than other shaders.](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#lpac-shader-instruction-count-device-specific)  Compute shaders than run on the Graphics Pipeline have the [typical (slightly lower) limit.](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#instruction-count-device-specific)

A low [% Wave Context Occupancy](https://docs.qualcomm.com/doc/80-78185-2/topic/sdp.html#sdp) can indicate long shaders thrashing the instruction cache.

### GPR minimization

Keeping every shader’s register usage (called GPRs or General Purpose Registers) under the device limits will ensure that the maximum number of simultaneous waves execute, maximizing performance.

Modifying GLSL to save even a single instruction can save a GPR. Not unrolling loops can also save GPRs, but that is up to the shader compiler. Always profile shaders to make sure the final solution chosen is the most efficient one for the target platform. Unrolled loops tend to put texture fetches toward the shader top, resulting in a need for more GPRs to hold the multiple texture coordinates and fetched results simultaneously.

For example, if unrolling the loop presented below:

for (i = 0; i < 4; ++i) {
        diffuse += ComputeDiffuseContribution(normal, light[i]);
    }
    Copy to clipboard

The code snippet would be replaced with:

diffuse += ComputeDiffuseContribution(normal, light[0]);
    diffuse += ComputeDiffuseContribution(normal, light[1]);
    diffuse += ComputeDiffuseContribution(normal, light[2]);
    diffuse += ComputeDiffuseContribution(normal, light[3]);
    Copy to clipboard

Use [Vulkan specialization constants to implement “uber-shaders”](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-branching-vulkan-specialization-constants) (shaders that combine multiple shaders into a single shader that use static branching) or avoid the “uber-shader” design altogether. Using “uber-shaders” (without [Vulkan specialization constants](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-branching-vulkan-specialization-constants)) can sometimes reduce state changes and batch draw calls – but this often increases GPR count, which can reduce performance overall.

If GPR usage is too high, consider [splitting the shader into multiple parts](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-split-up-draw-calls).

A low [% Wave Context Occupancy](https://docs.qualcomm.com/doc/80-78185-2/topic/sdp.html#sdp) can indicate thrashing GPRs with too much register usage.

### Split up draw calls

If a shader has too many instructions to fit the Instruction Cache, or uses too many GPRs, splitting it into multiple shaders may improve performance.

Vulkan’s [subpasses](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#vulkan-subpass-handling), correctly authored, keep intermediate results in GMEM, and is often the fastest approach.  [Vulkan tile shading extensions](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-tile-shading-vulkan-extensions) are another way to keep intermediate results in GMEM.

Alternatively, values generated by ShaderA can be written to a texture or SSBO and later retrieved via by ShaderB – or the results of ShaderA can be alpha-blended into the results of ShaderB. Be aware that this approach risks a memory bandwidth bottleneck, so make sure there is plausible headroom in [% Texture Pipes Busy](https://docs.qualcomm.com/doc/80-78185-2/topic/sdp.html#sdp) before attempting this optimization, and profile before and after making the change.

### Minimize ALU Cost

Even when a shader fits within the [instruction cache](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-instruction-count), instruction choices involving [type-casting](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-minimize-type-casting) (including converting floating point values from 32-bit to [16-bit precision](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-maximize-half-precision)), [control flow (branches and loops)](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-shader-control-flow), [built-in shader instructions](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-built-in-shader-instructions) and more all impact ALU efficiency.

#### Minimize type casting

Minimize the number of type cast operations performed.

For example, assigning a float to a vec4 data type could prevent the compiler from performing optimizations.

For another example, the following code might be suboptimal:

uniform sampler2D ColorTexture;
    in vec2 TexC;
    vec3 light(in vec3 amb, in vec3 diff)
    {
        vec3 Color = texture(ColorTexture, TexC);
        Color *= diff + amb;
        return Color;
    }
    Copy to clipboard

Here, the call to the texture function returns a vec4. There is an implicit type cast to vec3, which requires an additional instruction. Changing the code as follows might not require the additional instruction:

uniform sampler2D ColorTexture;
    in vec2 TexC;
    vec4 light(in vec4 amb, in vec4 diff)
    {
        vec4 Color = texture(ColorTexture, TexC);
        Color *= diff + amb;
        return Color;
    }
    Copy to clipboard

For another example, the following code should take a single instruction:

int4 ResultOfA(int4 a) {
        return a + 1;
    }
    Copy to clipboard

Now suppose a slight error is introduced into the code. For the example, the floating-point constant value 1.0 is used, which is not the appropriate data type:

int4 ResultOfA(int4 a) {
        return a + 1.0;
    }
    Copy to clipboard

The code could now require eight instructions: the variable *a* is converted to vec4, then, the addition is done in floating point. Finally, the result is converted back to the return type int4.

This discussion generally applies to converting floating point values from 32-bit to 16-bit precision as well.

#### Control Flow

Minimizing non-uniform looping and branching often helps reduce GPR and ALU usage.

Every time the branch encounters divergence, or where some elements of the thread branch one way and some elements branch in another, both branches will be taken and the results of the irrelevant branch ignored.

Here are several types of branches, listed from best to worst performance:

- Branching on a Vulkan specialization constant (no performance hit – the driver is guaranteed to strip any unused branches at shader-compile-time)
- Branching on a constant, known at compile time (possibly no performance hit – the driver might strip any unused branches at shader-compile-time)
- Branching on a uniform variable (some performance hit from loading and testing the uniform variable per-wave – and both branches may be executed)
- Branching on a variable modified inside the shader (maximum likelihood of a performance hit – the driver will probably execute both branches)

#### Pack shader interpolators

Shader-interpolated values (GLES varyings or Vulkan out variables) require a GPR (general purpose register) to hold data being fed into a fragment shader. Therefore, minimize their use.

Use constants where a value is uniform. Pack values together as all shader interpolated values have four components, whether they are used or not. Putting two vec2 texture coordinates into a single vec4 value is a common practice, but other strategies employ more creative packing and on-the-fly data compression.

Note

OpenGL ES 3.0 and ES 3.1 introduce various intrinsic functions to carry out packing operations.

#### Pack scalar constants

Packing scalar constants into 4-vectors – or, failing that, 2-vectors – substantially improves hardware fetch effectiveness.

Consider the following code:

float scale, bias;
    vec4 a = Pos * scale + bias;
    Copy to clipboard

By changing the code as follows, fewer total instructions may be generated, because the compiler might replace several instructions with the more efficient “mad” instruction:

vec2 scaleNbias;
    vec4 a = Pos * scaleNbias.x + scaleNbias.y;
    Copy to clipboard

In this case, while the compiler might infer that the *scale* and *bias* variables should be stored in a single GPR to enable the “mad” instruction, it’s much more likely to store *scaleNbias* in a single GPR, which in turn increases the likelihood of optimal instruction generation.

Note

OpenGL ES 3.0 and ES 3.1 introduce various intrinsic functions to carry out packing operations.

#### Use built-in shader instructions

Built-in functions are an important part of the glsl specification and should always be preferred to writing custom implementation. These functions are often optimized for specific shader profiles and for the capabilities of the hardware for which the shader was compiled.

Note

gl\_VertexID and gl\_InstanceID are removed as per the [GL_KHR_vulkan_glsl](https://www.khronos.org/registry/vulkan/specs/misc/GL_KHR_vulkan_glsl.txt) extension, but gl\_InstanceIndex is available.

Note

gl\_Position, gl\_PointSize, gl\_ClipDistance, gl\_CullDistance are available in non-fragment stages

Refer to the [GL_KHR_vulkan_glsl](https://www.khronos.org/registry/vulkan/specs/misc/GL_KHR_vulkan_glsl.txt) extension for details on changes to GLSL built-ins in Vulkan.

### Avoid discarding pixels in the fragment shader

Some developers believe that manually discarding (also known as killing) pixels in the fragment shader boosts performance. However:

- If some pixels in a thread are killed and others are not, the shader still executes
- It’s hard to predict how the shader compiler will generate microcode involving discard, so even in the unlikely case where performance is increased on one device or driver, this gain may be reversed on another device or driver

In theory, if all pixels in a thread are killed, the GPU will stop processing that thread as soon as possible. In practice, “as soon as possible” isn’t soon enough to help performance, and discard operations – like [modifying depth in fragment shaders](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-modifying-depth-in-fragment-shaders) often [disable hardware optimizations](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-early-z-rejection-do-not-disable).

If a shader cannot avoid discard operations, the cost can be mitigated by executing the shader after (ideally all) opaque draw calls – generally, the later in the frame a discard operation takes place, the better.

### Avoid modifying depth in fragment shaders

Similar to [discarding fragments](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-avoid-discard-pixel), modifying depth in the fragment shader can [disable hardware optimizations](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-early-z-rejection-do-not-disable).

### Stay under performance-optimal resource limits

Stay under target device performance limits with respect to the number of unique resources referenced by a shader:

- [vertex buffers](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#vertex-buffers-referenced-by-shader-device-specific)
- [uniform buffers](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#uniform-buffers-referenced-by-shader-device-specific)
- [samplers](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#samplers-limit-unique-in-shader-device-specific)
- [textures and SSBOs](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#textures-and-ssbos-limit-unique-in-shader-device-specific) (textures and SSBOs share hardware resources)

### Minimize texture fetches in vertex shaders

Adreno is based on a [unified shader architecture](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#unified-shader-architecture), which means vertex processing performance is similar to fragment processing performance.

However, for optimal performance it is best to [minimize texture sampling in vertex shaders](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-texture-samples-in-vertex-shader-triggers-direct-mode) – and when this cannot be avoided, it is important to ensure that texture fetches in vertex shaders are localized (eg cache coherent) and always operate on compressed texture data.

One reason for this is that any such texture fetches will generally run twice – once in the [position-only shader](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#position-only-vertex-shader), and then again in the vertex shader – so you pay that cost twice.

The [position-only shader](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#position-only-vertex-shader) is designed to be very fast – but if it stalls on memory accesses, that often significantly slows the binning phase.  If a vertex shader performs too many texture fetches in a vertex shader, the driver will switch to [Direct Mode](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-flex-render-best-practices), which is often less performant than [Binning Mode](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-flex-render-best-practices).

### Latency Hiding, and Load Balancing the Texture Pipe and Shader Pipe

To hide latency, and thus avoid shader stalls and increase performance, the Snapdragon™ Adreno™ GPU shader compiler converts some memory access requests into texture fetches.

To allow this, the data buffer must be read-only and a storage type – in Vulkan, this means a SSBO or Shader Storage Buffer Object (VK\_DESCRIPTOR\_TYPE\_STORAGE\_BUFFER) – as the texture pipe is capable only of reading.  (The shader pipe can read and write).

Even with a read-only storage buffer, the Adreno driver will sometimes choose a global memory access over a texture fetch if it decides that the texture pipe is a bottleneck.

[Snapdragon Profiler](https://docs.qualcomm.com/doc/80-78185-2/topic/sdp.html#load-balance-shader-pipe-and-texture-pipe-sdp) can be used to monitor the effects of your changes on the texture pipe and shader pipe.

#### Texture Fetch Bottleneck Optimization

If texture fetches are a performance bottleneck, here are some optimization strategies:

- When possible, use vertex buffer objects instead of storage buffers.  Note that most [GPU-driven rendering architectures](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#gpu-driven-rendering) are more likely to bottleneck the texture pipe, since they typically use storage buffers instead of vertex buffers
- Use uniform buffers objects instead of textures or storage buffers if the data fits in the [optimal uniform buffer size.](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-uniform-buffer-maximum-performant-size)
- Minimize texture fetches
- Minimize [texture cache misses](https://docs.qualcomm.com/doc/80-78185-2/topic/sdp.html#sdp) by rewriting shaders to access one cluster of data before moving to the next – texture cache misses significantly contribute to any texture pipe bottleneck.  Hardware operates on blocks of 2x2 fragments, so shaders are more efficient if they access neighboring texels within a single block
- Avoid 3D textures, since fetching data from volume textures is expensive due to the complex filtering that needs to be performed to compute the result value
- Don’t exceed the performance limit the [number of samplers, as well as the sum of textures and SSBOs referenced by the shader](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-textures-ssbos-samplers-limit-unique-in-shader) (textures and SSBOs share hardware resources)
- Compress all textures to reduce memory usage and texture stalls
- Use mipmaps to better coalesce texture fetches and thus improve performance – at the cost of increased memory usage

Texture filtering can influence the speed of texture sampling. Filter performance is architecture/chip dependent, and you might see a benefit by using bilinear or nearest filtering over trilinear or anisotropic. Mipmap clamping may reduce the cost of using trilinear filtering, so the average cost might be lower in real-world cases. Nonetheless, adding anisotropic filtering multiplies with the degree of anisotropy – in other words, a 16x anisotropic lookup can be 16 times slower than a regular isotropic lookup. However, because anisotropic filtering is adaptive, this hit is taken only on fragments that require anisotropic filtering, which could be only a few fragments altogether. A rule of thumb for real world cases is that anisotropic filtering is, on average, less than double the cost of isotropic.

Shader-specific gradients, based on the dFdx and dFdy functions, cost more than a regular texture sample. These shader-specific gradients cannot be stored across lookups, so if a texture lookup is done again with the same gradients in the same sampler, it will incur the cost again.

In summary: usage of trilinear, anisotropic filtering, wide texture formats, 3D textures, texture lookup with gradients of different Lod, or gradients across a pixel quad may increase texture sampling time.  The driver can be encouraged to [load-balance the texture pipe with the shader pipe](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-texture-latency-masking).  [Snapdragon Profiler](https://docs.qualcomm.com/doc/80-78185-2/topic/sdp.html#load-balance-shader-pipe-and-texture-pipe-sdp) can be used to monitor the effects of your changes on the texture pipe and shader pipe.

### DXC and Glslang and the Texture Pipe

Many games and engines generate their shaders in one language and use conversion tools to translate these shaders into other languages – for example, HLSL can be converted to SPIRV using DXC or glslang.

These conversion tools might convert HLSL buffer types to GLSL storage buffers and vice versa – and, since storage buffers utilize the texture pipe, this can result in unexpected increases in texture usage.

The DXC documentation details what to expect when converting from HLSL to SPIRV: [https://github.com/Microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#constanttexturestructuredbyte-buffers](https://github.com/Microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#constanttexturestructuredbyte-buffers)

DXC’s texture format conversion is specifically documented as well: [https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#textures](https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#textures).

Aside from texture and buffer conversions, while most code generated from these tools works well on our hardware (and whenever possible we engage the creators of these tools to maintain and improve such code generation), sometimes these tools use instructions and idioms that perform well on PC and console, but have suboptimal performance on mobile GPUs.

### Compiling and linking during initialization

The compilation and linking of shaders is a time-consuming process that can produce framerate hitches if performed naively at runtime.  It is recommended that shaders are loaded and compiled during initialization.

Tab Vulkan
Tab OpenGL ES

Performing all calls to vkCreateGraphicsPipelines() and vkCreateComputePipelines() during initialization should eliminate any framerate hitches due to shader compilation.

After shaders have been loaded and compiled, glUseProgram should then be invoked to switch between shaders as necessary during the rendering phase.

For OpenGL ES 2.0, ES 3.0, and ES 3.1 contexts, the use of blob binaries is recommended. After compiling and linking a program object, it is possible to retrieve the binary representation, or blob, using one of the following functions:

- glGetProgramBinary (if using an OpenGL ES 3.0 or 3.1 context, then this function is considered core functionality)
- glGetProgramBinaryOES (if using an OpenGL ES 2.0 context and the GL\_OES\_get\_program\_binary extension is available)

The blob can then be saved to persistent storage. The next time the application is launched, it is not necessary to recompile and relink the shader. Instead, read the blob from persistent storage and load it directly into the program object using glProgramBinaryOES or glProgramBinary. This can significantly speed up application launch times.

Warning

Many OpenGL ES implementations incorporate build time GL state into program objects when they are linked. If that program is then used for a draw call issued in the context of a different GL state configuration, then the OpenGL ES implementation must rebuild the program on-the-fly. This behavior is legal in the OpenGL ES specification – however, it can cause serious problems for developers. It often takes a significant amount of time to rebuild the program object, which can lead to severe frame drops. The rebuild was not requested by the application, so the delay is unexpected and the reason for it may not be apparent.

On Adreno platforms, this is not an issue. The Adreno drivers never recompile shaders, so it’s safe to assume that program objects will never be rebuilt unless specifically requested.

## [Compute Shaders](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#compute-shaders-device-specific)

Prefer fragment shaders to compute shaders when possible, since compute shaders require kernel output to be written to memory before the next kernel can begin execute.  By comparison fragment shaders use Adreno’s concurrent resolve hardware, which can write the results of a fragment program while allowing another fragment program to begin executing simultaneously.

Keep [instruction counts](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-instruction-count) below [device limits](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#instruction-count-device-specific) like any other shader (although issuing shaders to [LPAC has a slightly higher instruction limit](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#lpac-shader-instruction-count-device-specific)).

Tune [workgroup number](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#compute-shader-workgroup-number-multiples) (depending on whether shaders read each other’s memory) and [workgroup sizes](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#compute-shader-local-group-multiples) (depending on whether or not the shader significantly stalls in ways that cannot be optimized away) to avoid potential performance bottlenecks.  Note the driver may not be able to execute the requested workgroup number or size optimally if the shader’s [GPR usage](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-gpr-minimization) or [instruction count](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-instruction-count) is too large to accommodate the simultaneous execution of the requested number of workgroups.

Warning

OpenGL ES: When calling glDispatchIndirect() with any workgroup size smaller than 64 – for example (32, 1, 1) = 32x1x1 = 32 – the Cpu may wait on the Gpu as a result of the driver mapping the indirect buffer to a larger workgroup size, since doing so involves a command buffer flush.  To avoid this, use a workgroup size that is at least 64 – for example (64, 1, 1) = 64x1x1 = 64.

layout(local_size_x = 32, local_size_y = 1, local_size_z = 1) in; //workgroup size = local_size_x*local_size_y*local_size_z = 32x1x1 = 32 -- make sure this equation produces at least 64 to avoid a Cpu-wait-on-Gpu
    Copy to clipboard

Avoid synchronizing compute threads if possible – if synchronization is required, prefer atomic operations between local groups to shader barriers.

[Separate graphics submits and compute dispatches as much as possible.](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-graphics-submits-compute-dispatches-separate)

## [GPU-Driven Rendering](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#gpu-driven-rendering)

### [Geometry instancing](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#geometry-instancing-overview)

Geometry instancing calls include:

Tab Vulkan
Tab OpenGL ES

vkCmdDraw, vkCmdDrawIndexed, vkDrawIndirectCommand, and vkDrawIndexedIndirectCommand support instanced rendering.

glDrawArraysInstanced, and glDrawElementsInstanced support instanced rendering.

### [Indirect draw calls](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#indirect-draw-calls-overview)

For example, if the renderer uses a scene graph, it is possible to cache the draw call arguments necessary to render a mesh’s nodes in a buffer object created at loading time. The buffer can then be used at render-time as an input to the glDrawArraysIndirect or glDrawElementsIndirect functions.

To ensure [fast-z mode for depth-only renderpasses](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#fast-z) that use indirect draws and SSBOs, activate SPIR-V ExecutionMode 4 (EarlyFragmentTest) by adding this qualifier in your GLSL shader:

layout(early_fragment_tests) in;
    Copy to clipboard

[VK\_QCOM\_tile\_shading](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-vk-qcom-tile-shading-best-practices) can allow for efficient Gpu-driven implementations if [tile-based draws](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#tile-rendering) suit your algorithm.

#### Vertex streaming versus attribute fetching

Vertex streaming is more performant than attribute fetching when architecting your renderer in the [GPU-driven style](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#gpu-driven-rendering).

## [Low Priority Asychronous Compute (LPAC)](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#lpac)

LPAC is available on A740 devices and higher.

Running a compute shader on LPAC requires extension [VK_KHR_global_priority](https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_global_priority.html).  Then you must request a queue that supports only compute and transfer and has [VkQueueGlobalPriority](https://registry.khronos.org/vulkan/specs/latest/man/html/VkQueueGlobalPriority.html) [VK_QUEUE_GLOBAL_PRIORITY_LOW_EXT](https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_global_priority.html).  This queue is the LPAC queue: it will run concurrently (and at a lower priority, generally taking longer to complete) than the graphics/compute/transfer queue (the Graphics Pipe).

LPAC has a slightly larger [instruction cache](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#lpac-shader-instruction-count-device-specific) than shaders than run on the Graphics Pipe.

## [Mesh Shading](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#mobile-mesh-shading-device-specific)

Avoid amplification shaders unless necessary – while they often perform well, they also incur a nonzero performance cost.

Set the [optimal wave count](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#mobile-meshshadingwavecountoptimal-specs) when possible.

Per-vertex attributes typically perform better than per-primitive attributes (even when per-primitive attributes might theoretically result in a smaller attribute memory footprint) – many if not most algorithms can entirely avoid the PerPrimitiveEXT keyword.

Keep mesh shader payload sizes under [device limits](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#mesh-shading-payload-size-threshold) to avoid a performance bottleneck.

Consider using [LPAC](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#lpac) to load-balance with the Graphics Pipe.

## [Raytracing](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#mobile-raytracing-device-specific)

### [Hit detection](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#raytracing-hit-detection-overview)

Vulkan implements hit detection with the VK\_KHR\_acceleration\_structure extension. This structure can be constructed on the CPU or GPU (we recommend [constructing it on the GPU](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-prefer-building-raytracing-acceleration-structures-on-gpu)). Unless your scene is entirely static, the structure will need to be updated as geometry and/or lights move.  While this updating cost can be substantial, it can also be amortized over several frames, often with few or zero artifacts.

Developers can query ray-hit information during any shader stage via the VK\_KHR\_ray\_query extension – any algorithm that requires ray-occlusion information likely starts here:

![../_images/raytracing2.png](data:image/png;base64,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)

### Application example: shadow generation

![../_images/raytracing3.png](data:image/png;base64,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)

With a rasterization-only pipeline, shadow generation can be expensive and may not generate consistently good visual results.

With ray tracing, shadows can be added with a few lines of shader code and a reasonably optimal acceleration structure.  Fine detail can be achieved without requiring intermediate surfaces or other algorithms – just querying per-pixel visibility provides precise occlusion information, the results of which can be further manipulated by other post-processing techniques.

![../_images/raytracing4.png](data:image/png;base64,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)

This sample generates a shadowmap in a subpass by raytracing from the main light source (a point-light). This query logic and the acceleration structure management are the only additions raytracing needs to determine pixel occlusion – the surrounding code remains the traditional rasterization approach.

// Initialize the query object
    rayQueryEXT rayQuery;
    rayQueryInitializeEXT(
        rayQuery,
        accelStructure,
        gl_RayFlagsTerminateOnFirstHitEXT,
        cullMask,
        WorldPos,
        minDistance,
        DirectionToLight,
        LightDistance);
    
        // Traverse the query -- do once for the first hit
        while(rayQueryProceedEXT(rayQuery))
        {
            // Hit something! Logic can be added here depending on the type of intersection
        }
    
        // Get the last intersection information
        if(rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionNoneEXT)
        {
             // Got an intersection -- this pixel is occluded, so retrieve distance
             float intersectionDistance = rayQueryGetIntersectionTEXT(rayQuery, true);
    
             // ...
        }
    Copy to clipboard

### Optimization

#### General Considerations

##### Instruction cache

Good instruction cache practices are even more important than usual for ray tracing shaders – remove dead code, factor away non-uniform branches and looping and shrink each shader’s total instruction size so it fits [target devices’ instruction cache](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-instruction-count).

##### 16-bit Precision

[Half-precision](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-maximize-half-precision) is, as ever, an easy performance win that often involves few to no additional artifacts.  The Vulkan API supports this with [VK_KHR_shader_float16_int8.](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_shader_float16_int8.html)

##### Culling

Simplifying acceleration structures speeds raytracing.  Aggressively cull your geometry as much as your content allows – frustum culling, portal culling, level-of-detail, and other techniques may apply.

##### Acceleration structures

**Building on the GPU**

* * *

Acceleration structures are typically built fastest on the GPU with [vkCmdBuildAccelerationStructuresKHR](https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdBuildAccelerationStructuresKHR.html), [vkCmdBuildAccelerationStructuresIndirectKHR](https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdBuildAccelerationStructuresIndirectKHR.html), [vkCmdCopyAccelerationStructureToMemoryKHR](https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdCopyAccelerationStructureToMemoryKHR.html) and [vkCmdCopyMemoryToAccelerationStructureKHR](https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdCopyMemoryToAccelerationStructureKHR.html) – as opposed to API calls that execute on the CPU.

Pass the [optimal number of acceleration structures](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#raytracing-acceleration-structure-number-single-invocation) when possible.

Prefer concurrently operating on acceleration structures with [LPAC](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#lpac) (rather than using any CPU approaches such as VkDeferredOperationKHR) whenever concurrent processing of acceleration structures is desired.

**Deformable Raytraced Meshes: Refitting vs Rebuilding**

* * *

Try to cache as much of your acceleration structure as long as you can, amortizing any refitting (with [VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR](https://registry.khronos.org/vulkan/specs/latest/man/html/VkBuildAccelerationStructureFlagBitsKHR.html)) and/or rebuilds (with [VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_BUILD_BIT_KHR](https://registry.khronos.org/vulkan/specs/latest/man/html/VkBuildAccelerationStructureFlagBitsKHR.html)) over multiple frames to stay within your frame budget.

Generally, refitting will be more efficient than rebuilding when a mesh hasn’t deformed much from the mesh’s original geometry (at the moment its acceleration structure was first built).  Once the mesh’s deformation causes the originally-built acceleration structure to poorly fit the mesh’s current geometry, a rebuild will be more efficient than a refit.  Of course this is highly content-dependent.

#### Prefer ray queries to ray pipelines

A8x and higher supports [ray tracing pipelines](https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_ray_tracing_pipeline.html).  However, when possible, we recommend using only [ray queries](https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_ray_query.html), as they are often comparatively more performant.  But when ray queries alone are not expressive enough – or when porting an existing raytracing codebase to Adreno – ray pipeline support is ready to go, and performs well for many types of content.

##### Ray Queries

#### Minimize calls to rayQueryProceed()

Our Vulkan SPIR-V compiler currently does not support function calls – therefore all calls to rayQueryProceed() are inlined. For example, if the application wraps calls to rayQueryProceed() in a ClosestHitTrace() function, and then calls ClosestHitTrace() in 10 different places in the code, the compiler could generate 10 copies of the traversal loop. The traversal loop might be 300 instructions, so this can easily result in shaders that don’t fit the [instruction cache and consequently perform poorly](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-instruction-count).

We’ve found that typically a couple of calls to proceed() doesn’t hurt performance.  However, for larger numbers of traversal loops it becomes more challenging for our compiler to achieve reasonable register allocation, which results in [GPR spilling](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-gpr-minimization) and other performance degradations.

#### Reuse one minimally-sized ray query object

Reuse the single ray query object as needed. A ray query object costs enough [GPRs that the concurrent wave count for the shader](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-gpr-minimization) may be reduced.  Thus make every effort to avoid more then one ray query object, reusing that one object across different calls to rayQueryProceed(). This reuse should be possible unless recursion is used – which itself is usually avoidable.  Minimize the size of the ray query object.

#### Access ray query data through intrinsics

Avoid copying data from a ray query into large, custom data structures – this will increase [GPR usage](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-gpr-minimization). Instead, use intrinsics to access the query object’s data.

#### Avoid proceed() calls in loops for non-opaque traversal that “accept first fit”

When using gl\_RayFlagsTerminateOnFirstHitEXT or gl\_RayFlagsCullOpaqueEXT, there is no need to call rayQueryProceedEXT in a while loop – this easy simplification helps the compiler generate code with [fewer branches](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-shader-control-flow).

For example:

rayQueryEXT rayQuery;
    rayQueryInitializeEXT(rayQuery, rayTraceAS, gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsCullOpaqueEXT, cullMask, WorldPos, minDistance, DirectionToLight, LightDistance);
    
    // Traverse the query. No need for a while(), since we want first hit or
    // non-opaque intersections only
    rayQueryProceedEXT(rayQuery));
    
    // Determine if the shadow query collided
    if(rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionNoneEXT)
    {
        // Got an intersection == Shadow, do something
    }
    Copy to clipboard

#### Use the optimal subgroup size

Avoid performance bottlenecks by setting the [device-specific optimal subgroup size](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#ray-query-optimal-subgroup-size).

##### Ray Pipelines

Minimize the size of all user-defined datastructures: ray payload, hit attributes, callable data and pipeline stack size.

[Prefer ray queries](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-preferrayqueriestoraypipelines) instead of ray pipelines when possible.

## 2D operation hardware acceleration

Adreno hardware accelerates common 2D operations:

- blits – with Vulkan, use vkCmdBlitImage()
- surface clears
- [Android-phone pre-rotation on Vulkan](https://docs.vulkan.org/samples/latest/samples/performance/surface_rotation/README.html) (recent OpenGL ES drivers do this automatically)
- rotation
- convolution kernels – for example, [with Vulkan’s VK_QCOM_Image_Processing](https://github.com/quic/adreno-gpu-vulkan-code-sample-framework/tree/main/samples/BloomImageProcessing)

Graphics APIs like Vulkan and OpenGL ES often map to Adreno’s specialized, high-performance hardware.

## [Queries](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#queries-device-specific)

### [Occlusion queries performance](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#occlusion-queries-device-specific)

The [number of queries and their frame-latency](https://docs.qualcomm.com/doc/80-78185-2/topic/spec_sheets.html#occlusion-queries-device-specific) should be respected to maximize performance.

The performance of occlusion queries is further affected by the number of bins – the higher the [bin count](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-bin-minimization) the more expensive the queries.

Run occlusion queries in [direct mode](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-flex-render-best-practices) whenever possible. One way to ensure this occurs is to issue all the queries for a frame in one batch after a flush; for example: Render Opaque -&gt; Render Translucent -&gt; Flush -&gt; Render Queries -&gt; Switch FBO.

If the driver sees that only queries have been issued to the surface then it switches to [direct mode](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-flex-render-best-practices).

Note

The overhead of queries will show up as a higher “% CP Busy” metric in [Snapdragon Profiler](https://docs.qualcomm.com/doc/80-78185-2/topic/sdp.html#sdp).

We’ve seen cases where issuing many queries to a binned surface causes a CP overhead increase of 20-40% – and this overhead drops to 4-6% in direct mode.

### Timer Query Accuracy

Timer queries should always be issued within a renderpass to maximize accuracy.

Timer queries are calculated over the entire set of binned tiles when in binning mode. For example, let’s assume that we have 50 draw calls and a [render target that requires 8 tiles to render.](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-bin-minimization) Let’s also assume we want to measure draw call 10 and instrument it with timer queries.

The entire command stream of 50 draws will be captured and run through the binning process to generate the [visibility streams](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#visibility-streams). During the rendering pass, the draw calls will be rendered according to the [visibility stream](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#visibility-streams) of each tile. Even if the geometry for draw call 10 only contributes to one tile, it will incur a small overhead for each tile (while processing the [visibility stream](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#visibility-streams)). This overhead and the actual rendering time will be accumulated and presented in the resulting timer query.

Note

The overhead mentioned above is small (2-5µs) but can add up if the draw call count is high and draws are present in many tiles. Starting with A5x, GPU optimizations to the [visibility stream](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#visibility-streams) have been added to reduce this overhead by “trimming” the end of the stream of draw calls that do not contribute to the tile. This optimization can be nullified if something like a full screen pass is issued as the last draw call to a render target.

## [Qualcomm True HDR setup](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#qualcomm-hdr)

To enable [Qualcomm True HDR](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#qualcomm-hdr) in OpenGL ES, the following extensions must be supported:

- EGL\_EXT\_gl\_colorspace\_display\_p3
- EGL\_EXT\_gl\_colorspace\_bt2020\_pq
- EGL\_EXT\_surface\_SMPTE2086\_metadata

Note

Vulkan Swapchain/WSI for Android only supports VK\_COLOR\_SPACE\_DISPLAY\_P3\_NONLINEAR\_EXT. For additional information on how to enable this for Vulkan, check out the [Enhancing graphics with wide color content](https://developer.android.com/training/wide-color-gamut) guide from the Android Developers Documentation.

### Set EGLSurface format

Set the EGLSurface format to R10G10B10A2:

EGLConfig EGLConfigList [1]; int ConfigAttributes [] = {
        EGL_RED_SIZE,10
        EGL_GREEN_SIZE,10
        EGL_BLUE_SIZE,10
        EGL_ALPHA_SIZE,2
        EGL_COLOR_COMPONENT_TYPE_EXT,
    EGL_COLOR_COMPONENT_TYPE_FIXED_EXT, EGL_NONE};
    
    eglChooseConfig (eglDisplay,ConfigAttributes,EGLConfigList,1, eglNumConfigs);
    Copy to clipboard

### Set color space

Set the color space of eglWindowSurface to EGL\_GL\_COLORSPACE\_BT2020\_PQ\_EXT.

EGLint attribs[] = {EGL_GL_COLORSPACE_KHR,EGL_GL_COLORSPACE_BT2020_PQ_EXT,EGL_NONE };
    EGLSurface eglSurface=eglCreateWidowSurface(eglDisplay,eglConfigParam, InWindow, attribs);
    Copy to clipboard

### Set metadata

Set the metadata attributes of eglSurface.

EGLint SurfaceAttribs [] = {
    EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT,       EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT,
        EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT,
        EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT,
        EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT,
        EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT,
        EGL_SMPTE2086_WHITE_POINT_X_EXT,
        EGL_SMPTE2086_WHITE_POINT_Y_EXT,
        EGL_SMPTE2086_MAX_LUMINANCE_EXT,
        EGL_SMPTE2086_MIN_LUMINANCE_EXT
    };
    static const DisplayChromacities DisplayChromacityList [] = { {{0.70800f, 0.29200f, 0.17000f, 0.79700f, 0.13100f, 0.04600f, 0.31270f, 0.32900f}}, // DG_Rec2020 };
    for (uint32_t i = 0; i < 8; i++)
    {
        eglSurfaceAttrib(PImplData->eglDisplay,eglSurface, SurfaceAttribs[i],EGLint(DisplayChromacityList[0].ChromaVals[i]* EGL_METADATA_SCALING_EXT));
    }
    Copy to clipboard

### Get the luminance of display on Android

See: [https://developer.android.com/reference/android/view/Display.HdrCapabilities.html#getDesired](https://developer.android.com/reference/android/view/Display.HdrCapabilities.html#getDesired)

…for methods like:

- MaxAverageLuminance
- MaxLuminance
- MinLuminance

## [Variable Rate Shading (VRS)](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#vrs)

Fewer pixels shaded means fewer rays queried – often with few to no additional artifacts.  Use [VRS](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#vrs) as aggressively as your content allows.

Tab Vulkan
Tab OpenGL ES

[VRS](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#vrs) is exposed through the VK\_KHR\_fragment\_shading\_rate extension.  VK\_KHR\_fragment\_shading\_rate takes a VkExtent2D argument where developers specify the width and height of the desired fragment size.

[VRS](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#vrs) is exposed through the QCOM\_shading\_rate extension. This extension includes a number of enumerations (e.g., GL\_SHADING\_RATE\_1X1\_PIXELS\_QCOM, GL\_SHADING\_RATE\_1X2\_PIXELS\_QCOM, etc.) for controlling the different fragment sizes. You can see a demonstration of the extension’s usage on our new [Adreno GPU OpenGL ES Code Sample Framework in GitHub](https://github.com/quic/adreno-gpu-opengl-es-code-sample-framework/tree/main/samples/shading_rate).

Note that [VRS](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#vrs) interoperates well with [foveated rendering](https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html#mobile-foveated-rendering) – you can seamlessly use them simultaneously.

## XR/VR/AR (Extended Reality/Virtual Reality/Augmented Reality)

### Stereographic Rendering

The following API calls supports efficient stereographic rendering – the driver captures issued commands for one eye and replays them for the other.  This saves CPU time (but has no impact on the GPU).

Tab Vulkan
Tab OpenGL ES

VK\_KHR\_multiview

GL\_OVR\_multiview

### Foveated Rendering

Use [foveated](https://www.qualcomm.com/news/onq/2021/07/evolution-high-performance-foveated-rendering-adreno) [rendering](https://www.qualcomm.com/developer/blog/2022/08/improving-foveated-rendering-fragment-density-map-offset-extension-vulkan) – and note that it interoperates well with [VRS](https://docs.qualcomm.com/doc/80-78185-2/topic/overview.html#vrs) ; you can seamlessly use them simultaneously.

## Querying the driver version to implement version-specific workarounds

Despite our best efforts, sometimes drivers ship with bugs.

When a future driver fixes some bugs, you may want to implement a workaround for certain versions of a driver and not others.  Here’s how:

### Vulkan

“driverVersion” can be queried from VkPhysicalDeviceProperties – it returns the complete major/minor/patch number of the driver version.

For Adreno, the 32-bit number of the driverVersion is encoded as major-minor-patch. The first 10 bits is the major version, the second 10 bits is the minor version and the remaining 12 bits is for the patch version.

The major number is usually fixed, while the minor number changes with each release. Patches are the variations of a minor release. We primarily use the minor number to identify different driver versions, while the patch number is generally used to identify workarounds and fixes for a given driver version.

Sample code:

#define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22)
    
    #define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3FFU)
    
    #define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xFFFU)
    
    // Workarounds for Adreno driver 676
    if (VK_VERSION_MINOR(version) == 676)
    {
       // Guard for known Adreno issue on patch < 17 and driver 676
       if (VK_VERSION_PATCH(version) < 17)
       {
          doWorkaround();
       }
       // After patch 17 we know the issue was addressed by the vendor
       else
       {
          doNormalFlow();
       }
    }
    Copy to clipboard

### adb

To check the driver version, run the command:

adb shell dumpsys SurfaceFlinger | grep GLES
    Copy to clipboard

Output is of the form:

GLES: Qualcomm, Adreno (TM) 740, OpenGL ES 3.2 V\@0676.0 (GIT\@6f08ddb, I5e1ee3b043, 1669189803) (Date:11/23/22)
    Copy to clipboard

Given: V@0676 as above, 676 is the driver promotion/minor number.

Shader stats (like for [Snapdragon Profiler](https://docs.qualcomm.com/doc/80-78185-2/topic/sdp.html#sdp)) for vulkan needs driver support: 636 promotion/minor number and above qualifies.

## Adreno APIs

Adreno GPUs support industry-standard APIs including:

- OpenGL ES 1.x (fixed function pipeline)
- OpenGL ES 2.0 (programmable shader pipeline)
- OpenGL ES 3.0
- OpenGL ES 3.1 + AEP
- OpenGL ES 3.2
- EGL
- Vulkan 1.0
- Vulkan 1.1
- OpenCL 1.1e
- OpenCL 2.0 Full Profile
- DirectX 11 FL 9.3
- DirectX 12 FL 12

Last Published: Mar 03, 2026

[Previous Topic
Occlusion queries](https://docs.qualcomm.com/bundle/publicresource/80-78185-2/topics/spec_sheets.md) [Next Topic
Texture Compression Examples](https://docs.qualcomm.com/bundle/publicresource/80-78185-2/topics/texture_compression_examples.md)