Debugging AMD-Specific Issues with Driver Experiments Tool

Wed
30
Jul 2025

If you’re programming graphics using modern APIs like DirectX 12 or Vulkan and you're working with an AMD GPU, you may already be familiar with the Radeon Developer Tool Suite. In this article, I’d like to highlight one of the tools it includes - Driver Experiments - and specifically focus on two experiments that can help you debug AMD-specific issues in your application, such as visual glitches.


Not an actual screenshot from a game, just an illustration.

Starting with Driver Experiments

Before diving into the details, let’s start with the basics. Driver Experiments is one of the tabs available in the Radeon Developer Panel, part of the Radeon Developer Tool Suite. To get started:

  1. Download and unpack the RDTS package from the Radeon Developer Tool Suite page. The file typically named something like "RadeonDeveloperToolSuite-2025-07-01-1408.zip".
  2. This is a portable application, so no installation is needed. Simply launch "RadeonDeveloperPanel.exe".
  3. Click the Connect button. If everything is set up correctly, the tool will establish a connection with the AMD graphics driver installed on your system.
  4. In the Available Features panel, add Driver Experiments by clicking the (+) button. This will add the Driver Experiments tab to the main window.

The Driver Experiments tool provides a range of toggles that control low-level driver behavior. These settings are normally inaccessible to anyone outside AMD and are certainly not intended for end users or gamers. However, in a development or testing environment - which is our focus here - they can be extremely valuable.

Comprehensive documentation for the tool and its individual experiments is available at GPUOpen.com: Radeon Developer Panel > Features > Driver Experiments.

When using these settings, please keep in mind the following limitations:

Among the many available experiments, some relate to enabling or disabling specific API features (such as ray tracing or mesh shaders), while others target internal driver optimizations. These toggles can help diagnose bugs in your code, uncover optimization opportunities, or even verify suspected driver issues. In the next section, I’ll describe two experiments that I find especially helpful when debugging problems that tend to affect AMD hardware more frequently than other vendors.

Force NonUniformResourceIndex

This is about a topic I already warned about back in 2015, right after DirectX 12 was released, in my article "Direct3D 12 - Watch out for non-uniform resource index!". To recap: when writing shaders that perform dynamic indexing of an array of descriptors (buffers, textures, samplers), the index is assumed to be scalar - that is, to have the same value across all threads in a wave. For an explanation of what that means, see my old post: "Which Values Are Scalar in a Shader?" When it is not scalar (e.g. it varies from pixel to pixel), we need to decorate it with the NonUniformResourceIndex qualifier in HLSL or the nonuniformEXT qualifier in GLSL:

Texture2D<float4> allTextures[400] : register(t3);
...
float4 color = allTextures[NonUniformResourceIndex(materialIndex)].Sample(
mySampler, texCoords);

The worst thing is that if we forget about NonUniformResourceIndex while the index is indeed non-uniform, we may get undefined behavior, which typically means indexing into the wrong descriptor and results in visual glitches. It won't be reported as an error by the D3D Debug Layer. (EDIT: But PIX can help detect it.) It typically affects only AMD GPUs, while working fine on NVIDIA. This is because in the AMD GPU assembly (ISA) (which is publicly available – see AMD GPU architecture programming documentation) descriptors are scalar, so when the index is non-uniform, the shader compiler needs to generate instructions for a "waterfall loop" that have some performance overhead.

I think that whoever designed the NonUniformResourceIndex qualifier in shader languages is guilty of hours of debugging and frustration for countless developers who stumbled upon this problem. This approach of "performance by default, correctness as opt-in" is not a good design. A better language design would be to do the opposite:

  1. Assume every dynamic descriptor index to be non-uniform by default (safety first).
  2. Shader compilers should try hard to prove whether the value used for descriptor indexing is scalar across the wave (e.g. coming from a constant/uniform buffer, mixed with some immediate constants etc., like myCB.myConstIndex + 10) and then optimize it.
  3. In those rare cases when the compiler couldn’t optimize it, but we know the index is really scalar, and we determined this is our performance bottleneck, we should be able to opt-in for the optimization by using some UniformResourceIndex() qualifier, thus declaring that we know what we are doing and we agree to introduce a bug if we don’t keep our promise to ensure the index is really scalar.

But the reality is what it is, and no one seems to be working on fixing this. (EDIT: Not fully true, there is some discussion.) That’s where Driver Experiments can help. When you activate the "Force NonUniformResourceIndex" experiment, all shaders are compiled as if every dynamic descriptor index were annotated with NonUniformResourceIndex. This may incur a performance cost, but it can also resolve visual bugs. If enabling it fixes the issue, you’ve likely found a missing NonUniformResourceIndex somewhere in your shaders - you just need to identify which one.

Disable color texture compression

This relates to a topic I touched on in my older post: "Texture Compression: What Can It Mean?". "Compression" in the context of textures can mean many different things. Here, I’m not referring to packing textures in a ZIP file or even using compressed pixel formats like BC7 or ASTC. I’m talking about internal compression formats that GPUs sometimes apply to textures in video memory. These formats are opaque to the developer, lossless, and specific to the GPU vendor and model. They’re not intended to reduce memory usage - in fact, they may slightly increase it due to additional metadata - but they can improve performance when the texture is used. This kind of compression is typically applied to render-target (DX12) or color-attachment (Vulkan) and depth-stencil textures. The decision of when and how to apply such compression is made by the driver and depends on factors like pixel format, MSAA usage, and even texture dimensions.

The problem with this form of compression is that, while invisible to the developer, it can introduce bugs that wouldn’t occur if the texture were stored as a plain, uncompressed pixel array. Two issues in particular come to mind:

(1) Missing or incorrect barrier. Some GPUs may not support certain compression formats for all types of texture usage. Imagine a texture that is first bound as a render target. Rendering triangles to it is optimized thanks to the specialized internal compression. Later, we want to use that texture in a screen-space post-processing pass, sampling it as an SRV (shader resource). In DX12 and Vulkan, this requires inserting a barrier between the two usages. A barrier typically ensures correct execution order - so that the next draw call starts only after the previous one finishes - and flushes or invalidates relevant caches. However, if the GPU doesn’t support the render-target compression format for SRV usage, the barrier must also trigger decompression, converting the entire texture into a different internal format. This step may be slow, but it’s necessary for rendering to work correctly. That’s exactly what D3D12_RESOURCE_STATES and VkImageLayout enums are designed to control.

Now, imagine what happens if we forget to issue this barrier or issue an incorrect one. The texture remains in its compressed render-target format but is then sampled as a shader resource. As a result, we read incorrect data - leading to completely broken output, such as the kind of visual garbage shown in the image above. In contrast, if the driver hadn’t applied any compression, the missing barrier would be less critical because there’d be no format transition required.

(2) Missing or incorrect clear. I discussed this in detail in my older articles: "Initializing DX12 Textures After Allocation and Aliasing" and the follow-up "States and Barriers of Aliasing Render Targets". To recap: when a texture is placed in memory that may contain garbage data, it needs to be properly initialized before use. This situation can occur when the texture is created as placed in a larger memory block (using the CreatePlacedResource function), and that memory was previously used for something else, or when the texture aliases other resources. Proper initialization usually involves a Clear operation. However, if we don’t care about the contents, we can also use the DiscardResource function (in DX12) or transition the texture from VK_IMAGE_LAYOUT_UNDEFINED (in Vulkan).

Here comes the tricky part. What if we’re going to overwrite the entire texture by using it as a render target or UAV / storage image? Surprisingly, that is not considered proper initialization. If the texture were uncompressed, everything might work fine. But when an internal compression format is applied, visual artifacts can appear - and sometimes persist - even after a full overwrite as an RT or UAV. This issue frequently shows up on AMD GPUs while going unnoticed on NVIDIA. The root cause is that the texture’s metadata wasn't properly initialized. The DiscardResource function handles this correctly: it initializes the metadata while leaving the actual pixel values undefined.

The Driver Experiments tool can also help with debugging this type of issue by providing the "Disable color texture compression" experiment (and in DX12, also "Disable depth-stencil texture compression"). When enabled, the driver skips applying internal compression formats to textures in video memory. While this may result in reduced performance, it can also eliminate rendering bugs. If enabling this experiment resolves the issue, it’s a strong indicator that the problem lies in a missing or incorrect initialization (typically a Clear operation) or a barrier involving a render-target or depth-stencil texture. The next step is to identify the affected texture and insert the appropriate command at the right place in the rendering process.

Summary

The Driver Experiments tab in the Radeon Developer Panel is a collection of toggles for the AMD graphics driver, useful for debugging and performance tuning. I've focused on two of them in this article, but there are many more, each potentially useful in different situations. Over the years, I’ve encountered various issues across many games. For example:

Comments | #rendering #gpu #amd Share

Comments

[Download] [Dropbox] [pub] [Mirror] [Privacy policy]
Copyright © 2004-2026