Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
163837 stories
·
33 followers

Vue Basics: Understanding useId in Vue 3

1 Share

Learn how to use useId in Vue 3 to generate stable, unique IDs for reusable components, improve accessibility and avoid SSR hydration issues.

If you’ve ever built a reusable form component in Vue, you’ve probably hardcoded an id without much thought. For a single form field, that works as expected: the label is correctly associated with the input, and clicking the label focuses the field.

The challenge begins when that input becomes a reusable component.

What worked perfectly once can quickly become a problem when the same component is rendered multiple times. Suddenly, every instance produces the same id, leaving you with duplicate IDs, broken label associations and components that are no longer as accessible as they should be.

A common reaction is to generate IDs yourself using Math.random(), a counter or a UUID library. While those approaches solve one problem, they can introduce another, especially in applications that use server-side rendering.

This is where Vue’s useId comes in.

Rather than asking you to manage IDs manually, useId generates unique, stable identifiers that work seamlessly across reusable components and server-side rendering. In this article, you’ll learn why it exists, how it works and when you should reach for it in your own applications.

The Hidden Problem with IDs

Hardcoding an id isn’t inherently wrong. In fact, it’s the simplest solution when you’re building a one-off Vue page. The challenge starts when you turn that markup into a reusable component.

Consider this simple BaseInput component:

<script setup>
defineProps({
  label: String,
})
</script>

<template>
  <label for="input">
    {{ label }}
  </label>

  <input id="input" />
</template>

If you render the component once, everything works as expected.

<BaseInput label="Email Address" />

Now imagine using the same component to build a registration form.

<BaseInput label="First Name" />
<BaseInput label="Last Name" />
<BaseInput label="Email Address" />

Each component instance renders the same id, leaving your page with multiple elements that all look like this:

<input id="input" />

This creates invalid HTML because an id is meant to be unique within a page. More importantly, it breaks the relationship between the label and its corresponding input. Clicking a label may no longer focus the correct field, and assistive technologies like screen readers can struggle to determine which label belongs to which input.

You could solve this by passing a unique id to every component instance, but that also means every developer using your component now has one more thing to remember. Forget it once or accidentally reuse the same value, and the problem returns.

Ideally, a reusable component should handle this for you. The question is: how do you generate unique IDs automatically without making your component harder to use?

That’s exactly the challenge we’ll solve next.

Why Common Solutions Fall Short

Once you’ve decided that hardcoding an id isn’t a good fit for a reusable component, the next step seems obvious: generate one automatically.

A common approach is to use Math.random().

<script setup>
const inputId = Math.random().toString(36).slice(2)
</script>

<template>
  <label :for="inputId">Email Address</label>
  <input :id="inputId" />
</template>

Another option is to use a UUID library.

<script setup>
import { v4 as uuid } from 'uuid'

const inputId = uuid()
</script>

Both approaches solve the duplicate ID problem because every component instance receives a different identifier. If your application only runs in the browser, this may be all you need.

However, modern Vue applications don’t always render exclusively on the client. Frameworks like Nuxt often render components on the server first and then hydrate them in the browser. For hydration to work correctly, the HTML generated on the server must match what Vue expects on the client.

Random ID generators can’t guarantee that consistency. Each render produces a different value, so the server and client may generate different IDs for the same component. That’s where hydration issues can begin.

The goal isn’t just to generate unique IDs—it’s to generate IDs that are both unique and predictable across different rendering environments.

That’s exactly the problem useId was designed to solve.

Understanding useId

Now that we’ve seen why manually managing IDs doesn’t scale well, let’s look at Vue’s solution.

useId is a Composition API utility that generates a unique, stable ID for the current component instance. Instead of creating IDs yourself, you simply ask Vue for one and use it wherever your component needs an identifier.

Here’s the basic syntax:

<script setup>
import { useId } from 'vue'

const inputId = useId()
</script>

<template>
  <label :for="inputId">
    Email Address
  </label>

  <input :id="inputId" />
</template>

Every time a new instance of this component is rendered, Vue generates a different ID. That means you can render the component as many times as you like without worrying about duplicate IDs.

What makes useId different from Math.random() or a UUID generator isn’t just that the IDs are unique—it’s that Vue controls how they’re generated. Because the framework manages the rendering process, it can produce IDs that remain consistent across different rendering environments, making them OK in reusable components and applications that use server-side rendering.

It’s also worth noting what useId is not designed for.

The value it returns is meant for the DOM. Use it to connect labels with inputs, associate helper text with form fields or create relationships between HTML elements. It isn’t intended to replace database IDs, API identifiers or v-for keys.

Think of useId as a small utility with a very specific responsibility: helping your components generate reliable IDs automatically so you don’t have to manage them yourself.

Now that we understand what it does, let’s see how it fits into a real component you’ll likely build in almost every application: a reusable input component.

Practical Example: Building a Reusable Input Component

Let’s put useId into practice by building a reusable input component.

We’ll start with a simple version that accepts a label and renders an input field:

<script setup>
defineProps({
  label: String,
})
</script>

<template>
  <label for="input">
    {{ label }}
  </label>

  <input id="input" />
</template>

At first, this component looks perfectly fine. If you render it once, the label is correctly associated with the input.

The problem appears when the component is reused.

<BaseInput label="First Name" />
<BaseInput label="Last Name" />
<BaseInput label="Email Address" />

Because every instance renders the same id, all three inputs end up sharing ”input” as their identifier. That’s exactly the issue we discussed earlier.

Instead of asking every developer to provide a unique id, we can let the component generate one automatically with useId

<script setup>
import { useId } from 'vue'

defineProps({
  label: String,
})

const inputId = useId()
</script>

<template>
  <label :for="inputId">
    {{ label }}
  </label>

  <input :id="inputId" />
</template>

Now every instance of BaseInput receives its own unique identifier. The component is just as easy to use, but you no longer have to generate or manage IDs yourself.

Taking It a Step Further

Most reusable input components need more than just a label. It’s common to display helper text that provides additional guidance to the user.

For example, you might want to explain the password requirements.

Password
[_______________]

Must contain at least 8 characters.

To make this accessible, the helper text should be associated with the input using the aria-describedby attribute. This allows screen readers to announce the extra information when input receives focus.

With useId, creating that relationship is straightforward:

<script setup>
import { useId } from 'vue'

defineProps({
  label: String,
  helperText: String,
})

const inputId = useId()
const helperTextId = useId()
</script>

<template>
  <label :for="inputId">
    {{ label }}
  </label>

  <input
    :id="inputId"
    :aria-describedby="helperTextId"
  />

  <small :id="helperTextId">
    {{ helperText }}
  </small>
</template>

Here, each element has a clear purpose:

  • inputId connects the label and input.
  • helperTextId connects the helper text to the input.
  • Every component instance automatically gets its own set of IDs.

The result is a component that’s easier to reuse, more accessible, and far less prone to subtle bugs caused by duplicate IDs.

And the best part is that none of this complexity is exposed to the developer using your component.

<BaseInput
  label="Password"
  helper-text="Must contain at least 8 characters."
/>

From the outside, the component has a clean and simple API. Internally, it handles generating the IDs needed to keep everything correctly connected.

That’s one of the biggest advantages of useId. It allows your components to manage an implementation detail that consumers shouldn’t have to think about.

Why useId Is SSR-Safe

Earlier, we briefly mentioned that random ID generators can cause problems in applications that use server-side rendering (SSR). Now that you’ve seen useId in action, it’s easier to understand why.

When an application uses SSR, Vue renders your components on the server to generate the initial HTML. That HTML is then sent to the browser, where Vue “hydrates” it by attaching interactivity.

For hydration to succeed, the HTML generated on the server must match what Vue expects to render on the client. Random ID generators don’t guarantee that.

Imagine this component uses Math.random() to create an ID. The server might generate:

<input id="a8f3k2">

When the browser renders the same component during hydration, it could generate:

<input id="x9m7p1">

The component hasn’t changed, but the IDs no longer match. Vue now has to reconcile the difference, which can lead to hydration warnings or unnecessary DOM updates.

useId avoids this problem by predictably generating IDs. Because Vue controls the rendering process, it can ensure the same component receives the same ID during both the server render and client hydration.

The important takeaway isn’t that you need to understand SSR internals. It’s that useId lets you build reusable components without worrying about whether they’ll continue to work correctly in applications that use server-side rendering.

When to Use (and Avoid) useId

Like most Vue utilities, useId solves a specific problem. It’s not something you’ll use in every component, but when you need it, it removes a surprising amount of manual work.

A good rule of thumb is this: if your component needs to create a relationship between DOM elements using an id, useId is usually the right tool.

Some common examples include:

  • Associating a <label> with an <input>
  • Connecting helper text or error messages using aria-describedby
  • Linking elements with accessibility attributes such as aria-describedby
  • Building reusable form components that may be rendered multiple times on the same page

On the other hand, useId isn’t intended for application or business data. For example, you shouldn’t use it for:

  • Database record IDs
  • Order or invoice numbers
  • API resource identifiers
  • v-for keys
  • Anything that needs to be stored or shared outside the component

Those values represent your application’s data, while useId exists purely to generate stable IDs for the DOM.

If you’re unsure whether to use it, ask yourself a simple question:

“Am I identifying application data, or am I connecting HTML elements?”

If it’s about connecting elements in the DOM, useId is likely the right choice. If it’s about identifying business data, you should use an identifier that’s designed for that purpose.

Best Practices for useId

Before you start using useId throughout your application, keep these practical guidelines in mind:

  • Use useId only when your component needs a DOM ID. It’s ideal for linking labels, inputs, helper text and other accessibility attributes.
  • Generate IDs inside the component. Let the component handle its own implementation details instead of expecting every consumer to provide unique IDs.
  • Reuse the same ID for related elements. A label and its corresponding input should reference the same identifier. Generate additional IDs only when you’re creating a different relationship, such as linking helper text with aria-describedby.
  • Don’t use useId for application data. Database records, API resources and v-for keys all have different requirements and should use identifiers designed for those purposes.
  • Keep accessibility in mind. useId isn’t just about avoiding duplicate IDs—it’s also a simple way to build components that are easier for everyone to use.

These practices will help you write components that are easier to reuse, easier to maintain and ready for more complex applications as your project grows.

Wrapping Up

At first glance, useId seems like a small utility. It generates an ID, and that’s about it. But once you start building reusable components, you quickly realize it’s solving a much bigger problem than simply avoiding duplicate IDs. It removes a repetitive implementation detail and helps to preserve accessibility and consistency—even in applications that use server-side rendering.

More importantly, useId reflects a broader idea you’ll see throughout Vue: the framework handles common problems so you can focus on building features instead of reinventing infrastructure.

The next time you find yourself hardcoding an id inside a reusable component, take a moment to ask whether the component should be responsible for generating it instead. In many cases, that’s exactly what useId was designed for.

Read the whole story
alvinashcraft
13 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Say hello to Touch on Mac

1 Share
A MacBook Pro, shown open and facing forward against a black background, displaying a desktop wallpaper of abstract white light streaks against black. The macOS menu bar appears across the top and the Dock sits along the bottom of the screen.

Start getting ready for MacBook Pro with Touch today. Watch new developer videos, sign up for Group Labs, and check out new articles and resources.

Explore now

Read the whole story
alvinashcraft
44 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Migrating A Flutter Project to Built In Kotlin

1 Share
🙌 TL;DR I created a dart script to automatically migrate your Flutter project with ease. You can find it at the bottom of this article.

If your application or plugin supports Flutter version ≥ 3.44, then you might consider migrating it to use Built In Kotlin support. This is needed in order to support Android Gradle Plugin (AGP) versions ≥ 9. If you are not supporting Flutter version ≥ 3.44 or if your project is not currently using AGP ≥ 9, you don’t need to do this right now, but you might want to consider it.

I have undergone this migration process with one of my applications, and would like to share the process I had to go through, so you may have an easier going though it.

✋ If you have a Flutter plugin that is a pure Dart plugin (meaning no native Android code), then you don’t need to do anything regarding the migration and you can leave it as is.

Setting up Flutter ≥ 3.44

The first thing you need to do in order to migrate your application/plugin is to make sure you are on a Flutter version that supports it. That means, to upgrade your local Flutter version to at least 3.44. Before you upgrade the version, make sure you are on the stable channel in Flutter. You can do so by opening up a terminal and typing:

flutter channel

If the output shows you that you are on the stable channel, you can proceed to upgrade by typing:

flutter upgrade

This will upgrade your Flutter version to the latest one in the stable channel.

After doing this, we need to also declare in our pubspec.yaml file what our minimum supported Flutter version is:

environment:
sdk: ^3.2.0
flutter: '>=3.44.0'

Upgrading AGP ≥ 9 (the manual approach)

To upgrade the Android Gradle Plugin version to be ≥ 9, you need to open the android folder of your Flutter project. Make sure you have at least Android Studio Otter 3 since that is the first version that supports AGP 9. Once you have opened the project, you can use the AGP Upgrade Assistant under the Tools menu option.

Now, there are several changes you need to make in the project.

  1. In the build.gradle file located under android/app, remove the
id “org.jetbrains.kotlin.android”

in the plugins block. So this:

plugins {
id "com.android.application"
id "org.jetbrains.kotlin.android"
id "dev.flutter.flutter-gradle-plugin"
}

becomes this:

plugins {
id "com.android.application"
id "dev.flutter.flutter-gradle-plugin"
}

2. Change this block:

kotlinOptions {
jvmTarget = '17'
}

to

kotlin {
compilerOptions {
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
}
}

Moving on to the gradle.properties file:

3. Remove the

android.defaults.buildfeatures.buildconfig=true

4. Add the following attributes

android.builtInKotlin=true
android.newDsl=false

Not Done Yet (Dependencies)

It might seem like your past the migration, but there is one more step you need to do. If your Flutter project has any dependencies, you need to make sure those dependencies support built in Kotlin as well. So what you need to do is:

  • Go over each dependency and see if there is a version for it that has built in Kotlin support
  • If it doesn’t have one, you have two choices: Either swap it out with another dependency that gives you the same functionality but has built in Kotlin support, or don’t migrate your Flutter project until this dependency does

Automation

At this day and age, why would we want to do anything by hand? There’s no reason for it. That is why I created a pure Dart script you can run on your project to get the necessary output of what needs to be done.

When running it on a Flutter project of mine that was not migrated, I got the following output:

[TODO] Project "pill"
Path: PROJECT_PATH
Type: Flutter app
Result: Migration needed
- AGP: 8.9.1 Kotlin plugin version: 2.1.0 Gradle: 8.11.1
- android.builtInKotlin: not set android.newDsl: not set Kotlin source files: 1
- Kotlin plugin applied at android\app\build.gradle.kts:3
- Kotlin plugin declared (apply false) at android\settings.gradle:22
- Kotlin plugin declared (apply false) at android\settings.gradle.kts:23
- Dependency audit: 2 Android build file(s) checked; 1 package(s) apply the Kotlin plugin unconditionally, 0 only inside a condition.
Blockers:
* shared_preferences_android-2.4.23: build.gradle.kts:34 id("kotlin-android")
Proposed edits (nothing changes until you approve):
1. Remove the applied Kotlin plugin
android\app\build.gradle.kts
line 3: - id("kotlin-android")
2. Remove "android.defaults.buildfeatures.buildconfig=true" (rejected by AGP 9)
android\gradle.properties
line 4: - android.defaults.buildfeatures.buildconfig=true
3. Add android.builtInKotlin=true
android\gradle.properties
after line 6: + android.builtInKotlin=true
4. Upgrade AGP 8.9.1 -> 9.0.1
android\settings.gradle
line 21: - id "com.android.application" version '8.9.1' apply false
+ id "com.android.application" version '9.0.1' apply false
5. Upgrade AGP 8.11.1 -> 9.0.1
android\settings.gradle.kts
line 22: - id("com.android.application") version "8.11.1" apply false
+ id("com.android.application") version "9.0.1" apply false
6. Upgrade the Gradle wrapper 8.11.1 -> 9.3.1 (AGP 9 requires a newer Gradle)
android\gradle\wrapper\gradle-wrapper.properties
line 3: - distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
+ distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
7. Raise the Kotlin version pin 2.1.0 -> 2.3.20 (Flutter rejected anything below 2.2.20 when I checked) [optional]
android\settings.gradle
line 22: - id "org.jetbrains.kotlin.android" version "2.1.0" apply false
+ id "org.jetbrains.kotlin.android" version "2.3.20" apply false
Manual steps (this tool does not do these):
1. Replace the kotlinOptions block at android\app\build.gradle.kts:18 with a top-level block (outside android { }):
kotlin {
compilerOptions {
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
}
}
(Not automated: where this block belongs depends on your file.)
2. Upgrade or replace the plugins listed under "Blockers", or fall back to android.builtInKotlin=false on AGP 9 with the Kotlin plugin kept.
Note: Both settings.gradle and settings.gradle.kts exist in android. Gradle only uses one of them, so a stale duplicate can make this report misleading and doubles the proposed edits. Check which one is real and consider deleting the other.
Note: Keep the Kotlin plugin declaration with "apply false" in settings.gradle: on AGP 9 it pins the Kotlin version.

Warning: git reports uncommitted changes in:
PROJECT_PATH
Committing or stashing first makes the edits easy to review.
Continue anyway? [y/N]: y
Apply all 6 proposed edit(s)? [y]es / [n]o / [s]elect one by one: y

Optional: Raise the Kotlin version pin 2.1.0 -> 2.3.20 (Flutter rejected anything below 2.2.20 when I checked)
android\settings.gradle
line 22: - id "org.jetbrains.kotlin.android" version "2.1.0" apply false
+ id "org.jetbrains.kotlin.android" version "2.3.20" apply false
Apply this optional edit? [y/N]: y
updated android\app\build.gradle.kts
updated android\gradle.properties
updated android\settings.gradle
updated android\settings.gradle.kts
updated android\gradle\wrapper\gradle-wrapper.properties

Changed 5 file(s).
Backup of the originals: C:\Users\tomer\Documents\Projects\Pill/.builtin_kotlin_backup/2026-10-23T11-13-59
To undo, copy the backed-up files back over the originals, or use git. Delete the .builtin_kotlin_backup folder when you are done.
Next: run "flutter clean", "flutter pub get", then "flutter run".

It also automatically detects if your Flutter project is a plugin or an application and only proceeds to make changes based on your approval.

And after the migration, you will see:

Re-checking after the edits:

[WARN] Project "pill"
Path: PROJECT_PATH
Type: Flutter app
Result: Migrated, but blocked by dependencies that still apply the Kotlin plugin
- AGP: 9.0.1 Kotlin plugin version: 2.3.20 Gradle: 9.3.1
- android.builtInKotlin: true android.newDsl: not set Kotlin source files: 1
- Kotlin plugin declared (apply false) at android\settings.gradle:22
- Kotlin plugin declared (apply false) at android\settings.gradle.kts:23
- No Kotlin plugin is applied and builtInKotlin=true on AGP 9.0.1: already migrated.
- The Kotlin plugin declaration with "apply false" is a version pin. That is expected here: it keeps the Kotlin version at or above Flutter's minimum.
- Dependency audit: 2 Android build file(s) checked; 1 package(s) apply the Kotlin plugin unconditionally, 0 only inside a condition.
Blockers:
* shared_preferences_android-2.4.23: build.gradle.kts:34 id("kotlin-android")
Manual steps (this tool does not do these):
1. Upgrade or replace the plugins listed under "Blockers", or fall back to android.builtInKotlin=false on AGP 9 with the Kotlin plugin kept.
Note: Both settings.gradle and settings.gradle.kts exist in android. Gradle only uses one of them, so a stale duplicate can make this report misleading and doubles the proposed edits. Check which one is real and consider deleting the other.

Summary: 1 project(s) analyzed, 1 need attention.

You can find it here →

If you would like to read other articles I have written, you can see them here →

GitHub - TomerPacific/MediumArticles: A repository that contains code associated with various Medium articles I have written

References:


Migrating A Flutter Project to Built In Kotlin was originally published in Flutter Community on Medium, where people are continuing the conversation by highlighting and responding to this story.

Read the whole story
alvinashcraft
50 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

The AI advantage your company can keep

1 Share

New research across more than 200 enterprises looks at how shared knowledge can help companies coordinate their agents—and turn lessons from their work into an operational edge.

The post The AI advantage your company can keep appeared first on Microsoft Copilot Blog.

Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete

PIX 2610.28 – the biggest release of 2026

1 Share

Today we released PIX 2610.28, available for download here.

This release supports all the new retail and preview D3D12 features released today in DirectX Agility SDK 620 and 721. This is the first PIX build combining support for both retail and preview Agility SDKs, simplifying the user experience.

Beyond that, 2610.28 brings many new features and improvements since the last regular PIX release, PIX 2603.25, including all the features previewed in the May and June preview builds.

It also includes the most bug fixes we’ve ever released in a single version of PIX.

Contents

As always, many thanks to our hardware partners (AMD, Intel, NVIDIA and Qualcomm) for developing many of these features with us.

DirectX Dump Files

DirectX Dump Files are now available for use in retail games and applications! PIX has full support for analyzing them:

  • In the PIX UI
  • Programmatically through the PIX API
  • With AI agents using PIX’s MCP/CLI tools or your own tools built on the PIX API.

See the dedicated blog post for more information.

Shader Explorer

Shader Explorer is now available in main PIX releases, with offline and live shader analysis for supported AMD and Intel hardware.

We introduced the static-profiling workflow in the May preview. Start with a Compute, Graphics, or Mesh Shader Explorer project from File > New, choose a compiler and target architecture, and compile your source.

The analysis views provide:

  • GPU ISA correlation to source, when the compiler supplies source mappings.
  • Register pressure with allocated and used register counts.
  • Register-lifetime matrices showing liveness, reads, and writes.
  • Incoming and outgoing instruction dependencies.
  • Separate revisions for comparing shader and pipeline-state changes.

Static shader analysis in PIX Shader Explorer.

Additional static shader analysis views in PIX Shader Explorer.

Add live instruction profiling

This release adds the ability to collect live profiling data for a shader pipeline. PIX replays the captured workload in a GPU Capture and gathers instruction statistics from a compatible GPU plugin, then brings the results into Shader Explorer.

Instruction costs are accumulated across shader waves and loop iterations in the profiled event. Start with expensive instructions and inspect their correlated HLSL. Dependency and stall data can help explain the cost.

Live instruction profiling in PIX Shader Explorer, example 1.

Live instruction profiling in PIX Shader Explorer, example 2.

Live instruction profiling in PIX Shader Explorer, example 3.

Live instruction profiling in PIX Shader Explorer, example 4.

Move between GPU Captures and Shader Explorer with ease

You can now export pipelines from GPU Captures to Shader Explorer.

To use the new export feature, open a GPU Capture file, select an eligible GPU event, start analysis, and choose Profile from the Pipeline view. PIX brings across the pipeline state and shader compilation information.

GPU Capture to Shader Explorer profiling workflow, example 1.

GPU Capture to Shader Explorer profiling workflow, example 2.

Export requires the shader source and compilation PDBs. The feature currently supports exporting traditional graphics and compute PSOs. Other pipelines like raytracing or work graphs will be available in a future PIX release.

For more details, see the Shader Explorer documentation.

Support for new D3D runtime features

This release includes support for all new D3D12 features in Agility SDKs 620 (retail) and 721 (preview) that were released today. It also adds support for some existing D3D12 features such as D3D12 video.

DXR Clusters and Partitioned TLAS (preview)

PIX adds support for the new ExecuteIndirectRTASOperations preview features, including GPU-driven acceleration structure operations and new ways to organize raytracing geometry:

  • Cluster-level acceleration structures (CLAS).
  • Clustered bottom-level acceleration structures (CBLAS), built from clusters.
  • Partitioned top-level acceleration structures (PTLAS).

You can:

  • Capture and replay batched indirect acceleration-structure operations, including cluster builds and partitioned-TLAS updates.
  • Explore cluster geometry and partitioned acceleration structures in the Acceleration Structure Viewer, with hierarchy views grouped by instance or BLAS.
  • Open an individual CLAS directly for inspection.
  • Inspect build inputs and outputs in the Pipeline view, with labels identifying the operation and argument each buffer belongs to.
  • Read indirect operation arguments in the Buffer Viewer using structured layouts instead of raw byte dumps.
  • Inspect indirectly referenced vertex and index buffers with layouts that account for their supported formats and strides.
  • Export captures which are using the new API and RTAS types to C++, including the batched operations and their indirect argument data.

Inspecting DXR clusters and partitioned acceleration structures in PIX.

Clusters and PTLAS support require a compatible preview runtime and GPU driver.

Partial graphics programs

This release expands the partial-program support introduced in the May preview and included in June.

Improvements since June include:

  • Many robustness improvements to Capture/Replay
  • API Objects: show more detailed information.
  • VS Output: supported
  • Pixel History: supported.
  • Image visualizers: improved support
  • Export to C++: supported

Other new retail and preview D3D12 features

PIX adds full capture/replay support for new D3D12 features, including:

  • Async commands
  • Texture GUID Layouts
  • Serialized root signature
  • Fence Barriers
  • Explicit PSDB subobjects
  • GPU descriptor copies (preview)

D3D12 video capture/replay

PIX has long had the ability to work with applications that use D3D12 video APIs, but before this release, PIX would just capture the output of that video work.

PIX now has an opt-in setting to do full capture of D3D12 video decode, processing, and encoding work, with replay and C++ export support. This means that the full video APIs can be analyzed, and standard PIX features like buffer/texture viewers let you see the inputs/outputs of the video APIs.

You can enable the full video-capture option in PIX’s File->Settings page. It is off by default.

PIX settings for enabling full D3D12 video capture.

This support is marked as experimental for now, primarily because GPU plugins in PIX don’t support video queues so low-level hardware profiling information is not currently available. This will be fixed in a future version of PIX.

GPU Captures: other improvements

A new file format

Over the years, PIX’s file format for GPU Captures has proven to be a large performance and feature bottleneck. Although it was robust in lots of ways, it prevented us from saving additional data back to the capture file and had to be decompressed linearly.

We’re therefore excited to announce a new file format for GPU Captures with this release, which removes these restrictions and unlocks some exciting features and performance gains.

We have more planned around this, but in this release: PIX is now able to save resolved PDBs into the capture file, Start Analysis is no longer bottlenecked by single-threaded decompression, and captured screenshots are now visible from File Explorer. See their sections below to learn more!

Note that PIX can convert old captures to the new file format, so your old captures can get all these benefits. You will be prompted to do so when opening an older capture file.

Save resolved shader PDBs into your GPU Capture

The new capture format lets PIX save resolved shader PDBs inside the GPU Capture itself. After resolving symbols through the configured search paths or Resolve PDB, save the capture to keep those PDBs with it. This means that reopening the file no longer requires finding those same PDBs again. It also means that you can send the updated file to a colleague, and PDBs will automatically resolve for them.

Start Analysis / Replay performance improvements

The new capture format also allows us to better multithread Start Analysis. As mentioned above, we no longer need to decompress the capture linearly and can instead throw it all off to a threadpool. For captures that are gigabytes in size, this is a great speed-up.

We’ve also dedicated lots of attention to improving performance in other areas of how PIX plays back a capture. Depending on your workload, you should see that PIX is noticeably faster, especially when collecting timing data.

Start Analysis and replay performance improvements in PIX 2610.28.

View captured screenshot directly from File Explorer

This release finally brings one of the most requested quality-of-life features: the ability to view the captured screenshot directly from File Explorer. This is another feature that has been unlocked by the new file format and is very handy to distinguish between your different captures.

GPU Capture screenshot thumbnails displayed in Windows File Explorer.

Capture/replay robustness improvements

Application-Specific Driver State and Recreate-at-GPUVA are now enabled by default to improve capture correctness and compatibility with applications.

Tile Mappings Viewer

First introduced in the May preview, PIX now supports inspecting how reserved resources map to physical heaps. This information is available in the Texture Viewer’s new Mapping Details panel as well as new Resource Mappings and Heap Mappings views. See the Tile Mappings documentation.

Open a reserved resource in the Texture Viewer to use the Mapping Details panel. It provides:

  • An overlay to visualize unmapped tiles directly on top of the texture.
  • Mapping status per subresource.
  • Mapping information for the selected tile, with links to the Resource Mappings and Heap Mappings views.
  • General resource tiling information

Mapping Details for a reserved resource in the PIX Texture Viewer.

The Resource Mappings and Heap Mappings views include:

  • Automatically syncing the mapping state based on the currently selected event. A dedicated Mapping Events panel also provides a way to navigate between the capture’s initial mappings or subsequent UpdateTileMappings or CopyTileMappings operations.
  • A pivot table to inspect individual tile mappings.
  • A visualizer to quickly see tile mappings of a given subresource.

Inspecting tile mappings with PIX's Resource Mappings and Heap Mappings views.

Note: Captures migrated from older PIX versions do not contain the mapping data necessary and must be recaptured.

Dedicated Pipeline State Objects view

The Pipeline States view returns in this release. The pipeline state object (PSO) list now appears directly in the main view, with filters behind buttons. Use it to compare PSO configurations and navigate to their shaders.

The updated Pipeline States view for inspecting pipeline state objects in PIX.

The updated view improves navigation between related PIX data. Global ID links in the History panel now open the corresponding pipeline and select its first shader in the Pipeline Resource view for easy access. This release also fixes an issue that could cause the PSO list to disappear, and includes additional usability and text improvements.

Buffer Viewer improvements

The Buffer Viewer has a new Image mode to display data visually, rather than just a long list or table of numbers. This is notably useful for image data outside the texture viewer’s usual domain of DXGI formats (such as multidimensional tensors for machine learning and for more niche formats), or at other points along the pipeline where a format isn’t known yet or is unspecified (such as uploaded raw data or generic UAV’s). It’s also a convenience for cases that don’t need a full custom shader for visualization.

Image mode

Visualization uses the existing tensor keyword to specify the data type, dimensions, strides (defaulting to packed), and optional axis semantics (tensor<dataType, dimensions, strides, axesSemantics, …>), and it works with multidimensional tensors too (e.g. unorm8_t pixels[480][640]). Below is a 3-channel float32 planar image in CHW layout:

A three-channel float32 planar image in CHW layout displayed in the Buffer Viewer's Image mode.

Tensor data types can even hold structures, such as this 16-bit b4g4r4x4 pixel:

A tensor containing 16-bit b4g4r4x4 pixel structures visualized in the Buffer Viewer.

Color order

The channel color order can be configured with a new [[colors(…)]] attribute, for planar tensors and packed structs, currently supporting: bgr, rgb, gray.

Configuring image channel order with the colors attribute in the Buffer Viewer.

Channel Ranges

The color channel ranges normally have enough information from the data type alone (e.g. unorm8_t ranges [0, 255], unorm4_t ranges [0, 15], float32_t ranges [0, 1]…), but the new [[range(…)]] attribute can specify the low and high points explicitly (that is, the zero/black and full saturation/white points), such as range [[range(0, 1)]], [[range(0, 255)]], and [[range(-1, 1)]]. Below you can see an oversaturated image, and the proper range on the right: tensor<[[range(10000,40000)]] float32_t, {765,512}, increasing>:

An oversaturated buffer image on the left and the corrected image on the right using a channel range of 10000 to 40000.

Subtiling

Since multiple dimensions are supported, you can indirectly achieve nested subtiled arrangements like the D3D standard swizzle here. Axes can be flipped by negating a size component (e.g. {-8, 16} would mirror the first axis):

Visualizing nested subtiles in D3D standard swizzle layout with the Buffer Viewer.

Data Export

While in image mode, you can export images to PNG, BMP, or JPEG files, and copy the currently selected image to the clipboard (Ctrl+C or right-click context menu on the image). Additionally you can export buffer data to NumPy array .npy files to continue investigating tensor differences in Python, which preserves binary fidelity, dimensions, and data types (where possible). If there isn’t a 1:1 mapping to a NumPy tensor type, the data type is upcast to the nearest NumPy equivalent (e.g. bfloat16 to float32) or stored as opaque elements. Any strides (padded, broadcasted, reversed) are flattened first.

Buffer Viewer options for exporting image and tensor data.

Data Types

The buffer-format language gains more element types:

  • unorm* and snorm* types as familiar aliases to the older unint and snint types.
  • Low-precision floating point types:
    • float16m7e8s1_t – “Brain float” with standard infinity and NaN, mantissa with 7 bits, exponent with 8 bits, and final sign being 1 bit
    • float4m1e2s1fnnn_t – finite, no NaN
    • float6m3e2s1fn_t – finite, has NaN
    • float6m2e3s1fn_t – finite, has NaN
    • float8m3e4s1fnuz_t – finite, only unsigned zero with negative zero as NaN
    • float8m2e5s1fnuz_t – finite, only unsigned zero with negative zero as NaN
    • float8m4e3s1_t – has infinity, has NaN
    • float8m0e8s0fn_t – finite, mantissa has 0 bits, 0 sign bits, all 1’s set as NaN
    • float8m3e4b11s1fnuz_t – finite, only unsigned zero with negative zero as NaN
    • float8m3e4s1fn_t – finite, has NaN
    • (in addition to the existing float8m3e4s1_t and float8m2e5s1_t)
  • Packed integer types:
    • int1_t, int2_t, int4_t
    • uint1_t, uint2_t, uint4_t
    • unorm1_t, unorm2_t, unorm4_t
    • snorm2_t, snorm4_t
    • *Note field packing order depends on the current endianness setting.
  • Character types: char8_t, char16_t, char32_t. Character data displayed using the Buffer Viewer's character types.
  • Padding via the pad N statement was previously limited to bytes, but it now accepts a second parameter for the bit count (defaults to 8) or data type to pad out with. e.g. “pad 4, float32_t” pads out 3 floats worth, and “pad 12,1” pads 12 bits.

The new “Default buffer element format” setting controls the fallback data type used when PIX cannot determine a buffer’s format automatically:

The Default buffer element format setting in PIX.

Other fixes / usability improvements:

  • Fixed [[offset(N)]] attribute being ignored when there’s a single tensor.
  • Fixed the byte type to show unsigned, consistently with C# and C++.
  • Fixed display of normalized bitfields.
  • Fixed float64 formatting.
  • Fixed element counts for per-instance vertex buffers.
  • Preserved the starting offset when refreshing a buffer.
  • Fixed fast CSV exports that could produce an empty file.
  • Prevented an export file crash while a custom visualizer is active by disabling the unsupported export operation.
  • Fix row_major annotations in using statements on predefined matrix types like float4x4_t.
  • Clicking a format error moves the caret to the relevant location.
  • The Settings dialog Buffer Format section wasn’t responding to left clicks for editing.
  • Prevented the buffer-offset field from disrupting selection while typing.
  • Clarified that built-in buffer-format names cannot be renamed.

VS Output: new options

The screen-space VS Output view now overlays the scissor rectangle and viewport, with a legend explaining the displayed bounds. This makes clipping problems easier to diagnose without comparing coordinates manually.

The Relative To option switches between viewport-relative and render-target-relative positioning. Zoom to mesh frames the geometry’s bounding box in screen space.

Screen-space VS Output with scissor rectangle and viewport overlays.

Dr. PIX Tight Resource Alignment experiment

The Tight Resource Alignment experiment, available in the June preview, now joins the main release. Run it from Dr. PIX to estimate how much memory your placed resources could save by using D3D12_RESOURCE_FLAG_USE_TIGHT_ALIGNMENT.

The experiment compares the original resource layout with a tighter layout, including a compaction step that tries to reduce gaps between resources. Results include:

  • Per-heap estimated savings in bytes and percentages.
  • Original and tightly aligned resource extents.
  • Detailed resource offsets and allocation requirements.
  • Padding within allocations and between resources.
  • Warnings for heap overallocation or aliased resources.

The experiment requires an analysis device that supports tight resource alignment. Aliasing can affect the accuracy of the estimates.

Texture and Mesh Viewer improvements

  • Fix missing vertices and merged meshlets when an amplification shader drives a mesh shader.
  • Fix mesh-output capture when the shader has no payload or never reads its payload.
  • Fix incorrect mesh output caused by mismatched amplification- and mesh-shader payload layouts, including layouts with the same original size.
  • Fix misleading mesh output when instrumentation would exceed the mesh payload-size limit by reporting a specific error instead.
  • Fix sign extension for signed 16-bit mesh outputs.
  • Fix Force MSAA Sample Zero not being applied to Shader Model 6.6+ and half-precision texture loads.
  • Fix saved texture images including padding from the viewer canvas.
  • Fix incorrect histogram channels for BGRA and other swizzled formats.
  • Fix Pixel Details losing its relative selection position when switching textures.
  • Fix unexpected wrapping of Pixel Details values.
  • Fix a regression that prevented some custom texture visualizers from working.
  • Fix a crash involving some compressed texture formats.
  • Fix mismatches between displayed mesh and vertex data after selecting subsequent events in the event list.
  • Improve screen-space display of large meshes.
  • Fix a sporadic initially blank VS Output view.
  • Fix a sporadic mesh-view crash when rendering
  • Fix a VS Output crash involving an embedded root signature.
  • Fix the selected-pixel highlight becoming misaligned or leaving the texture canvas while scrolling.
  • Fix unwanted cross-axis panning and opening unrelated context menu after releasing mouse outside the texture viewer.
  • Add a 1:1 actual-size zoom button to the Texture Viewer.
  • Fix blurriness in textures when displayed at 1:1 zoom due to subpixel offsets.
  • Improve high-DPI image sizing.
  • Add horizontal and vertical panning via Home/End and Page Up/Down.
  • Fix scrollbar sizing and scrolling to the edge of a texture.

Other Pipeline View improvements

  • Fix incorrect overdraw and pixel-cost counts for pixel shaders with loops or early returns.
  • Fix invalid overdraw instrumentation when pixel-shader inputs densely fill the signature.
  • Fix handling of 16-bit and minimum-precision color outputs in highlighting and related visualizers.
  • Fix missing out-of-bounds reports for certain writes and atomics.
  • Fix dynamically indexed resource accesses sometimes being attributed to the wrong binding range.
  • Fix Shader Model 6.6 dynamic accesses being attributed to register zero instead of the binding’s lower bound.
  • Fix missing reports for out-of-bounds accesses exactly one element past the end of a resource range.
  • Fix missing shader-stage attribution for out-of-bounds bindless raytracing accesses.
  • Fix missing raytracing resource dependencies from helpers linked from another library.
  • Fix UI navigation through work-graph local root arguments.
  • Fix misc problems with resource-list filtering in the Pipeline view.
  • Fix shader-node selection being lost in the resources list.
  • Display Stream Output buffer strides.
  • Fix incorrect displayed minimum and maximum depth bounds.
  • Fix misleading contents being displayed for an empty vertex-buffer view.
  • Fix resource access tracking that could omit resources on repeated draws.
  • Add support for “Gather Accessed Resources” for work graphs.
  • Fix a Resource History crash when a capture was closed immediately after selecting an item.
  • Fix Pixel History synchronization that could produce incorrect results on some newer GPUs.
  • Fix Pixel History and Gold Image Diff ignoring the viewer’s flip setting.
  • Fix a crash when reopening Pixel History rapidly.
  • Replace repeated Variable Rate Shading dialogs with a dismissible warning banner in affected pixel-investigation views.
  • Fix static-analysis warnings being lost when changing adapters.
  • Fix false unnecessary-transition warnings for ExecuteIndirect argument buffers.
  • Fix missing CPU resource modifications in Resource History.
  • Display full terms for pipeline stage abbreviations.

Shader debugging, Edit and Continue, and symbols

  • Add Revert to Original to the Mod selector to clear both unapplied and previously applied Shader Edit and Continue changes together, after confirmation.
  • Fix local variables initialized from constants being marked unavailable.
  • Fix incorrect resolution of elements in static global multidimensional arrays.
  • Fix incorrect bitfield display in padded structures and structures with mixed-width members.
  • Fix incorrect member locations after nested structures with tail padding.
  • Fix instrumentation using instructions unsupported by Shader Models 6.0 and 6.1.
  • Fix debugging of vertex shaders whose input signatures already occupy all available registers.
  • Fix stepping into non-inlined helper functions and inspecting their local variables.
  • Fix missing raytracing callable-shader invocations in debug data.
  • Fix work-graph node-shader debugging ignoring the selected thread.
  • Fix work-graph debugging that could produce no debug data and time out on WARP.
  • Fix missing debug data for hull-shader patch-constant functions.
  • Fix debug-layer validation failures caused by missing raw/structured-buffer flags in instrumented DXIL.
  • Fix debugging of empty or methods-only HLSL structures.
  • Fix single-member structures being treated as scalars instead of displaying their member values.
  • Fix shader-debugger and mesh-output overflow retries that failed to grow their output buffer.
  • Fix shader-only edits making untouched pipeline states appear permanently unapplied.
  • Fix incorrect FXC/DXBC hashes used when suggesting a shader’s PDB filename.
  • Fix previously fetched IL being lost when asynchronous shader requests complete out of order.
  • Fix incorrect Shader Symbols counts.
  • Improve the presentation of embedded PDB data.
  • Fix valid composite symbol-server paths being rejected.
  • Fix source and PDB search-path changes not being persisted reliably.
  • Fix shader-reference resolution using outdated source search paths.
  • Fix incorrect dark-theme HLSL builtin-type colors.
  • Fix stale intercepted raytracing shader tables when edits replace or remove state-object modifications.
  • Fix subsequent replay failures caused by Shader Debugging or access tracking modifying the original raytracing shader-table buffers.
  • Improve shader modification through state-object collections and AddToStateObject chains.
  • Fix corrupted version text in the compiler-version mismatch dialog.

Capture-time improvements

  • Capture CPU writes correctly for some pooled upload and GPU-upload resources.
  • Capture newly placed upload resources in an already-recorded heap without replacing their data with zeros.
  • Avoid incorrectly deactivating adjacent, tightly packed placed buffers.
  • Preserve initial contents when placed/reserved resources or their heaps are destroyed.
  • Avoid a potential capture-side fence-wait deadlock.
  • Read partially populated reserved-buffer tiles without reading past the buffer’s end.
  • More efficiently handle reserved resources with no mapped tiles.

Export to C++ and analysis fixes

In addition to the new runtime and video coverage described above:

  • Generate valid resource-creation code when no castable formats are present.
  • Export supported state-object captures containing serialized root signatures instead of rejecting them as unimplemented.
  • Generate compilable pipeline streams for serialized root signatures and explicit precompiled shader databases without relying on unavailable SDK helper types.
  • Avoid conflicting debug-information settings across MSBuild and Ninja/NMake projects.
  • Preserve partial-program state and subobject lifetimes during modification.
  • Apply modified root signatures to the correct compute or graphics binding.
  • Treat compute-only generic programs as compute workloads.
  • Correct the buffer clear used by Dr. PIX primitive-culling analysis.
  • Report unsupported geometry/mesh-shader cases in the Vertex and Primitive Efficiency experiment rather than attempting them.

Misc other GPU Capture fixes/improvements

  • Fixed the Event List filter button and expansion of matching parent events.
  • Restored Enter-to-next-match behavior in Event List search.
  • Excluded PIX’s internal query-resolution copies from application event timing.
  • Fixed an exception when no GPU timeline lanes are available.
  • Fixed GPU event-lane lifetime leaks.
  • Handled invalid driver history-buffer data more gracefully.
  • Improved occupancy handling for plugins that cannot collect occupancy concurrently.
  • Fixed ExecuteBundle-related occupancy collection and wave-selection problems.
  • Fixed DXR Invocations crashes caused by invalid GPU IDs.
  • Fixed DXR Invocations instrumentation for state objects containing global root-signature subobjects.
  • Added bounds checks when interpreting acceleration-structure visualization data.
  • Made the acceleration-structure viewer’s Y-Up toggle actually take effect.
  • Applied default Y-Up and handedness settings consistently, with manual controls disabled while automatic orientation is active.
  • Fixed cases where Start Analysis was disabled for the first capture.
  • Fixed lost adapter selections or analysis remaining in the stopping state when switching captures.
  • Record the command line parameters passed to the captured app executable, displayed in the File Details pane.
  • Added a clearer error for an empty GPU Capture.

Timing Captures

Understand scheduling on heterogeneous CPUs

The heterogeneous-core timeline introduced in the June preview is now part of the main release. CPU lanes and thread activity distinguish core performance classes.

Core lane headers show a lightning-bolt indicator with the core’s efficiency class: class 0 represents efficiency cores, while higher values indicate higher-performance classes. Thread activity uses different colors for time on performance and efficiency cores. Hover over the activity graph for a breakdown of those fractions alongside stalled time.

Use these indicators to spot critical work scheduled on efficiency cores or background work occupying performance cores.

Since the preview, Range Details and Range Summary have also gained core-class analysis for PIX events and CPU samples in a selected range.

Heterogeneous CPU analysis distinguishing performance and efficiency cores in a Timing Capture.

This information comes from the captured machine, including when converting an ETL offline. Older captures without the required topology records cannot display it retroactively.

Compare CPU execution with normalized measurements

New experimental normalized CPU measurements help investigate changes in scheduling, boost, and throttling. They estimate execution relative to a reference core’s nominal performance rather than assuming that every millisecond of scheduled CPU time delivers the same capacity.

When a capture contains the required counters, normalized metrics and Range Summary values are discovered automatically. An experimental, default-off option adds more detail to Event Details:

  • Normalized execution.
  • Normalized performance ratio.
  • Delivered Performance coverage.

Core-placement percentages remain separate. Their tooltips now explain how child events and unknown cores affect the totals.

Normalized CPU measurements for investigating scheduling and delivered performance in a Timing Capture.

These are model-based estimates, not exact predictions of how the same workload would execute on another processor. Heterogeneous support currently uses the Windows performance model for AMD and Intel CPUs; heterogeneous ARM and other vendor models are unavailable. Results require adequate measured coverage and are shown as unavailable when the data is insufficient, not as zero work.

Range Summary improvements

Range Summary now works more like Range Details, with separate Items and Summary results. Use Summary to group and aggregate results, then inspect the contributing work in Items.

New controls include:

  • Text and regular-expression filtering alongside structured filters.
  • Separate column choices for Items and Summary.
  • Inclusive and exclusive percentages.
  • Expansion of hot regions using the selected ranking metric.
  • A Default action that restores the shipped grouping and display configuration.
  • Customize mode for editing how data is aggregated. Turn it off to hide the editing controls.
  • Summary columns can be edited, and the same column can be used for multiple summary types.
  • Percentage-of-parent aggregation shows how time is divided among child nodes.

The updated Range Summary analysis interface in PIX, example 1.

The updated Range Summary analysis interface in PIX, example 2.

These improvements also apply to the existing file-I/O and memory summary tables.

Asset-mapping improvements

PIX uses CSV asset mappings to associate byte ranges in archive files with asset names. This lets you see which assets a DirectStorage batch or Win32 file read is loading.

PIX now handles ambiguous archive-file paths more reliably when applying asset mappings.

Follow DirectStorage requests through processing stages

DirectStorage analysis now exposes queue and processing-stage information from compatible DirectStorage 1.4 preview-or-later event streams. Follow a request through file I/O and decompression, and correlate it with the related GPU upload, decompression, or copy work.

The new timeline lanes and Range Summary tables expose request information such as:

  • Source file and offset.
  • Source and uncompressed sizes.
  • Compression and destination type.
  • Queue and process/thread context.
  • Synchronization and completion information, where recorded.

Group requests to see where the most time is spent or the most data is loaded.

DirectStorage queue and request processing-stage analysis in a PIX Timing Capture.

Processing-stage analysis requires the corresponding runtime events. Those stages cannot be shown in older captures that lack the events.

DirectStorage improvements also include:

  • Compression and shuffle information in operation details.
  • Corrected out-of-order request accounting so operations are not consumed by the wrong batch.
  • More robust DirectStorage 1.4 request/fence correlation when events are orphaned.
  • Corrected Show in Timeline navigation for DirectStorage operations.
  • Kept selected enqueue markers visible above aggregated timeline markers.

Analyze Timing Captures with MCP

PIX now includes a standalone PixTimingCapturesMcp.exe server for MCP-capable clients. Register the installed executable as a standard-input/output MCP server, then open an existing .wpix capture. It does not require a running PIX UI or a listening network port.

The server supports:

  • CPU samples, stacks, and scheduling queries.
  • PIX CPU/GPU events and GPU-work correlations.
  • File-I/O and memory-allocation queries.
  • D3D object information.
  • DirectStorage queue and processing data.
  • Metric discovery and range statistics.
  • Comparing a metric across two ranges within one capture.
  • Reading and updating bookmarks.

Multiple captures can be open at once, with explicit capture IDs. The built-in metric comparison compares two ranges in the selected capture; it does not automatically compare different capture files.

When consolidated patterns share a name, MCP clients can use a unique display name to select the intended metric.

Symbol lookup initially uses the capture’s embedded cache for the captured application. Clients can opt into a wider process scope and external symbol lookup. Migration and bookmark updates can modify capture files.

For agents that support .mcp.json configuration, create a file like this in your working directory:

{
    "mcpServers": {
        "pix": {
            "type": "stdio",
            "command": "C:\\Program Files\\Microsoft PIX\\<PixVersion>\\PixTimingCapturesMcp.exe",
            "args": [],
            "tools": [
                "*"
            ]
        }
    }
}

The installation also includes an optional skill that guides your agent through the Timing Capture analysis techniques our engineers use. You can find it at C:\Program Files\Microsoft PIX\<PixVersion>\skills.

New scripted capture options

pixtool exposes more Timing Capture configuration for repeatable collection:

  • Circular recording and ring-buffer size.
  • Raw ETL output.
  • CPU sampling rate and stack collection.
  • File-I/O and memory instrumentation.
  • Managed-code data.
  • Capture duration or recording until process exit.
  • Minimal GPU instrumentation where supported by the application’s Agility SDK.

Existing camelCase option spellings remain supported. Optional configuration passed through the PIX API is now honored, and errors reported while finalizing a capture reach the API/CLI instead of being mistaken for success.

Raw ETL collection can also merge the image metadata needed for symbol resolution automatically. If the merge fails, PIX preserves the ETL and reports the manual workaround.

Faster large-capture analysis and more accurate memory totals

Opening and navigating large Timing Captures now uses less memory. PIX looks up saved symbols on demand and aggregates more results before passing them to the UI. Symbol loading is faster thanks to parallel function/source enumeration and lower-overhead caches.

D3D memory views distinguish an object’s logical size from the physical memory it owns. This avoids double-counting committed resources and their implicit heaps. Placed and reserved resources remain inspectable as objects without being counted as separate backing allocations.

Resource sizes no longer wrap at roughly 4 GiB, and allocation summaries show the D3D object type instead of a placeholder.

Timeline and navigation improvements

  • Keep matching Range Details results in view after filtering, scrolling to a visible selected row or back to the top.
  • Keep the expected pattern selected after deleting an entry in the consolidated-event editor.
  • Color sampled functions by module/function or callstack depth.
  • Show command-list debug names in GPU timing lanes.
  • Select a budget profile directly from the capture toolbar.
  • Use one lane-sorting dropdown containing the applicable ordering choices.
  • Resize bookmark popups to read and edit longer comments.
  • Filter ETL-conversion process lists by name or PID and sort them.
  • Honor the accumulated mouse-wheel delta when zooming, reducing delayed overshoot.
  • Respect Windows’ configured scroll amount.
  • Keep timeline labels from drifting vertically after repeated rendering.
  • Page long context-switch-stall lists instead of materializing the whole list at once.
  • Keep core-lane loading responsive and avoid a save/read deadlock.
  • Correct zoom-to-sampled-functions framing.
  • Preserve pinned tooltips during screen-snipping transitions.
  • Report the thread-activity time bin under the pointer correctly.
  • Keep context-switch/stall lanes visible at close zoom levels.
  • Show lanes containing GPU work, samples, or API-object activity even when they contain no PIX events.
  • Prevent stale asynchronous hover results from reopening at an old pointer location.
  • Match video-frame tooltips and selected images to the rendered tile at scaled zoom levels.
  • Correct Copy to Clipboard and Save as BMP for selected video frames.
  • Reduce contention between video decoding and other timeline work.

Metrics configuration improvements

  • Fix crashes when deleting configurations and restore Rename.
  • Honor Cancel without creating or changing a configuration.
  • Prevent duplicate Last Used configurations from accumulating.
  • Preserve existing configurations when importing another with the same name.
  • Keep named empty configurations across restarts and label them (empty).
  • Suggest unique names and explain naming conflicts before saving.

Metrics, memory, and symbol fixes

  • Keep shared Y-axis and histogram ranges up to date as metrics with the same units finish loading.
  • Improve consolidated CPU-event matching in budgets and cross-capture comparisons when thread IDs change.
  • Use each capture’s own consolidated events when calculating comparison statistics.
  • Include low-activity title threads in Summary’s CPU Thread/Core Usage table, grouping them instead of dropping their contribution.
  • Explain missing thread activity and avoid invalid utilization percentages for empty ranges.
  • Correct CPU execution/stall accounting when context-switch records are missing.
  • Copy Allocated Size and Unfreed Size cells as text instead of empty values.
  • Prevent duplicate metric entries and errors from repeated or out-of-order graph toggles.
  • Prevent budget-profile initialization from changing a metric list while it is still being constructed.
  • Prevent missed histogram updates and collection-modified errors during asynchronous loading.
  • Stop memory animations from following a replaced analysis model.
  • Release retained metric configuration, timeline lanes, and symbol graphs when captures or views close.
  • Release intermediate storage used while paging Range Details and Range Summary results.
  • Attribute source-analysis samples to the selected function’s stack node rather than its parent.
  • Avoid appending duplicate saved function/source records when adding newly resolved type information.
  • Repair older duplicate symbol data when migration is required.
  • Preserve same-timestamp kernel/user stack ordering.
  • Reduce long finalization delays caused by repeatedly sorting pending stack data.
  • Bound the memory retained for Delivered Performance collection and analysis.
  • Correct full-range metric queries at the maximum timestamp.
  • Correct GPU event completion’s handling of nested timing state.

Capture, conversion, and API reliability

  • Fix installed native Timing Capture API initialization when its managed runtime configuration was missing.
  • Reduce per-record allocation churn during circular ETW recording.
  • Distinguish viewer-local and UTC capture times in Summary.
  • Preserve the meaning of older stop timestamps recorded in the capturing machine’s local time.
  • Handle quoted filenames correctly in the ETL converter.
  • Avoid misleading validation errors for valid conversion filenames.
  • Enable a separate destination when migration is not in place.
  • Allow data-repair migration even when the capture already has the current schema version.
  • Rebuild changed indexes during capture migration instead of accepting stale same-name indexes.
  • Identify the associated metric in budget-detail headings.
  • Release replaced buffers when recalculating budget-exceedance markers.
  • Handle malformed or truncated ETW array payloads more robustly.

PIX API and command-line tools

PIX API SDK

The PIX API, introduced as a beta in the May preview, is included in the main release. Use it to build tools around PIX capture documents and device connections, including the new retail interfaces for DirectX dump analysis and shader profiling.

The installation includes:

  • Native headers under include.
  • lib\PixApi.lib and pixapi.dll.
  • C# extension assemblies.
  • Packaged API documentation in docs\pixapi.zip.

Python integrations use pythonnet with the C# layer rather than the earlier separately installed Python package. Native integrations should use the current headers rather than the removed IDL/WinMD surface.

Use the new online documentation alongside the PIX API samples when setting up an integration. Follow the sample’s version requirements and use headers from your installed PIX SDK.

Code built against older preview headers needs rebuilding: the factory interface ID and a number of API types have changed. Experimental native interfaces remain separate from the retail surface.

System Monitor

Several new counters are available in System Monitor:

  • Per-IHV counters exposed via plugins. Currently supported by AMD and NVIDIA.
  • Advanced Shader Delivery cache statistics, including PSO creation and cache-hit/miss categories.
  • System power and usage related counters, including battery discharge rate, CPU requested and nominal frequency, and CPU C-State.

To view available counters on your system open the System Monitor Modify Graphs panel. These counters are also recorded during a Timing Capture and can be plotted in the Metrics view.

Performance counters available in PIX System Monitor.

Remote connections and application launch

Manage paired PCs from PIX

The remote connection support introduced in the May preview uses the Xbox PC Remote tools to manage paired Windows PCs. PIX can deploy its engine and start the remote connection. A flyout shows connection status and setup instructions. You may need to acknowledge a UAC prompt on the remote device to deploy the engine and configure firewall rules; PIX will tell you when this is needed.

Paired-PC connection workflow in PIX, example 1.

Paired-PC connection workflow in PIX, example 2.

Reattach to Last Process and launch improvements

Reattach to Last Process is new since June. After restarting your application, use it to find processes with the previous executable name without reopening Attach to Process.

The launch controls now show longer recent-launch lists and command-line history.

When the defaults are suitable, use Launch directly without first going through Next. The capture-mode selector no longer unexpectedly toggles away from GPU Capture when you click the already-selected option, and its keyboard handling is improved.

Additional fixes include:

  • Launch the correct remote engine architecture from an ARM64 installation.
  • Match executable names correctly in Attach to Process filtering.
  • Clean up packaged applications that fail partway through launch.
  • Correct packaged-app recent-list cache-miss handling.
  • Use the required process access rights for packaged-app GPU capture.
  • Correct x86 child-process launch passthrough.
  • Handle connection timeouts like disconnects rather than displaying an unrelated error.
  • Avoid an adapter-selection worker-thread exception.
  • Release connection, docking, and System Monitor state when a connection closes.

Welcome, settings, and everyday usability

PIX now opens to a new Welcome page. Open recent documents with a double-click or Enter, or launch or attach to applications from the device connections list. If a connection fails, you can retry with one click. The page also links to documentation and feedback. It stays available as a tab, and you can choose whether it appears on startup.

The new PIX Welcome page with recent documents and device connections.

When a newer version of PIX is available, a notification shows the release highlights and a download link. You can reopen it from a button in the top right corner. The redesigned What’s New page, opened with F2, shows the installed version next to the latest release. It also lists past releases, with filters for feature, incremental, and preview releases, and shows each release announcement inside PIX.

PIX release update and What's New interface, example 1.

PIX release update and What's New interface, example 2.

PIX now follows the Windows light or dark theme by default through the Use System Theme setting. It switches immediately when the Windows theme changes while PIX is running. These changes first appeared in the June preview and are now part of the main release.

The installer now updates the required .NET Desktop and Visual C++ runtimes and includes WebView2. A failure to remove an older version of PIX no longer blocks installation of the new version.

Other UI improvements include:

  • Capture workflow:
    • Start Analysis no longer blocks views that don’t need analysis.
    • The Captures panel opens automatically for all GPU capture sources.
  • Opening and saving documents:
    • Captures without a thumbnail, including programmatic or non-rendering ones, show a more visible placeholder.
    • File opening uses the standard Windows file picker with a recent folder menu. It remembers the last folder used and offers an “All PIX Files” filter.
    • Opening a capture that is already open no longer fails.
    • Recent Documents checks the file type before opening a file and keeps entries after errors unrelated to the file.
    • Screenshot saving now handles file extensions correctly.
  • Tabs, docking and layout:
    • The selected tab stays visible when tabs overflow, and long titles are shortened in the middle.
    • Docking prefers nearby views, and panel-group splitters and layout rounding are fixed.
    • Detached panels no longer hit layout failures, and the cursor and window state recover after opening or minimizing.
  • Filtering and search:
    • Ctrl+E now focuses filters consistently in dialogs.
    • The About dialog filter works, and the dialog loads its content asynchronously.
  • Text editors and copying:
    • Text editors highlight other instances of the word under the cursor.
    • Improved horizontal scrolling and draggable editor overlays.
    • File Details text is easier to select and copy.
  • Keyboard: Escape closes popups. Switches support keyboard input, and keyboard focus is more reliable.
  • Visual polish:
    • Dark theme selection, breadcrumbs and panel titles are easier to read.
    • Improved tree-view scrolling and sizing.
    • Pipeline Resources shows a loading indicator and clearer errors.

PIX documentation on Microsoft Learn

Alongside this release, we’re bringing PIX documentation together on Microsoft Learn. The new site consolidates guidance previously spread across blog articles and existing documentation, with a common starting point for learning PIX and looking up its APIs. This blog will continue to carry release announcements.

Detailed PIX API documentation

The new documentation includes a getting-started guide for the PIX API and an extensive reference generated from its native headers. The guide explains the installed SDK layout, with examples that open a capture or establish a local connection through a PIX factory.

The WinPixEventRuntime APIs are separate and have their own documentation.

Public samples for your own tools

The new docs are complemented by samples in the public PIX API samples repository. New examples include:

  • Upgrading older GPU Capture files.
  • Running Dr. PIX experiments.
  • Inspecting captured pipeline state.
  • Inspecting resources and their views.
  • Opening Timing Captures and resolving symbols..

Download and feedback

Get PIX from the download page. Please don’t hesitate to contact us with feedback – either via the Send Feedback button in PIX or via the DirectX Discord.

The post PIX 2610.28 – the biggest release of 2026 appeared first on PIX on Windows.

Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete

Agentic Loops vs. Spec-Driven Development

1 Share

There are a lot of new terms being tossed around the AI developer world. Two new ones are Agentic Loops and Spec-Driven Development. This blog post will endeavor to clarify…

AI-driven software development today divides into two complementary but distinct patterns: agentic loop programming (autonomous, iterative agents) and Spec‑Driven Development (SDD, spec-as-source-of-truth). Both appear in modern toolchains: agentic loops power autonomous workflows and multi‑step reasoning, while SDD tames AI generation by anchoring code to machine‑readable contracts. This post compares the approaches, highlights tradeoffs, and provides compact, runnable C# examples you can adapt.

Short definitions

  • Agentic loop programming: goal → orchestrator →reason/plan → act (tools, etc.) → observe → orchestrator, loop where an LLM-driven agent runs multiple cycles, calls tools, observes results, and continues until the goal is met. Useful for autonomy, complex orchestration, and long‑horizon tasks.
  • Spec‑Driven Development (SDD): the machine‑readable spec is the primary artifact (single source of truth). Code (or tests) is generated/derived from the spec; at the end validates that implementations match the spec. SDD improves traceability, auditability, and reduces drift in AI-generated code.

When to choose which

  • Choose agentic loops when you need autonomy or multi‑step decision-making (automated repair, orchestration, continuous monitoring). Expect some non‑determinism but gain flexibility and problem-solving capability.
  • Choose SDD when determinism, correctness, and traceability matter (regulated domains, contracts, API-first development). SDD reduces drift and aids audit and compliance.

Agentic loop: risks and hardening
Main risks: goal drift, infinite loops, runaway cost/latency, security/tool misuse, auditability gaps. Mitigations: typed tool I/O, explicit stop conditions, iteration/cost caps, human checkpoints for high‑impact actions, rich logging/auditing, and canary testing. These patterns are essential before deploying agents to production

Spec‑Driven Development: risks and hardening
Main risks: over‑specification and maintenance burden; outdated or low‑quality specs yield brittle generation. Mitigations: invest in good spec discipline, machine‑readable formats (JSON, OpenAPI), auto‑derived tests, gates, and schema validation. SDD improves auditability because the spec itself is the contract.

Concrete C# examples
Below are compact patterns you can drop into a sandbox. The LLM calls are mocked — replace them with your LLM client (OpenAI/Anthropic) in real projects.

  1. Agentic loop pattern (C#)
    Purpose: show a simple agent loop that (a) reasons via LLM placeholder, (b) executes typed tools, (c) observes and updates memory, and (d) uses explicit stop conditions.

Key elements: ITool contract, AgentContext (memory), clear stop conditions, max iterations, and typed tool outputs for safer parsing.

Code (runnable pattern)

using System;
using System.Threading.Tasks;

public interface ITool<TInput, TOutput>
{
    Task<TOutput> ExecuteAsync(TInput input);
}

// Example tool: shell-like task
public class ShellTool : ITool<string, string>
{
    public Task<string> ExecuteAsync(string input)
    {
        // Simulate work
        return Task.FromResult($"Shell executed: {input}");
    }
}

public class AgentContext
{
    public string Goal { get; }
    public int Iteration { get; set; }
    public string Memory { get; set; }
    public decimal CostSoFar { get; set; }

    public AgentContext(string goal)
    {
        Goal = goal; Iteration = 0; Memory = ""; CostSoFar = 0m;
    }
}

public class Agent
{
    private readonly ITool<string, string> _tool;
    private readonly int _maxIterations;
    private readonly decimal _maxCost;

    public Agent(ITool<string, string> tool, int maxIterations = 10, decimal maxCost = 10.0m)
    {
        _tool = tool; _maxIterations = maxIterations; _maxCost = maxCost;
    }

    // Placeholder for LLM call; replace with actual client.
    private Task<string> CallLLMAsync(string prompt)
    {
        // Very simple mock that alternates between calling tool and finishing.
        if (prompt.Contains("satisfied", StringComparison.OrdinalIgnoreCase))
            return Task.FromResult("DONE");
        return Task.FromResult("ACTION:call_tool INPUT:run-step");
    }

    public async Task RunAsync(AgentContext ctx)
    {
        while (true)
        {
            if (ctx.Iteration >= _maxIterations)
            {
                Console.WriteLine("Stopping: reached max iterations.");
                return;
            }

            if (ctx.CostSoFar >= _maxCost)
            {
                Console.WriteLine("Stopping: reached cost budget.");
                return;
            }

            ctx.Iteration++;
            var observation = $"Goal:{ctx.Goal}; Memory:{ctx.Memory}; Iteration:{ctx.Iteration}";
            var plan = await CallLLMAsync($"Observe: {observation}\nDecide: is goal satisfied?");
            Console.WriteLine($"[LLM] plan={plan}");

            if (plan.StartsWith("DONE", StringComparison.OrdinalIgnoreCase))
            {
                Console.WriteLine("Agent: Goal satisfied.");
                return;
            }

            if (plan.Contains("ACTION:call_tool", StringComparison.OrdinalIgnoreCase))
            {
                // Parse input token (naive here)
                var toolInput = "run-step";
                var toolResult = await _tool.ExecuteAsync(toolInput);
                Console.WriteLine($"[Tool] {toolResult}");

                // Update memory and cost (mock cost per tool call)
                ctx.Memory += $" | iter{ctx.Iteration}:{toolResult}";
                ctx.CostSoFar += 1.0m;

                // Ask LLM to evaluate the observation
                var eval = await CallLLMAsync("Was result satisfactory?");
                if (eval.StartsWith("DONE", StringComparison.OrdinalIgnoreCase))
                {
                    Console.WriteLine("Agent: confirmed success after tool execution.");
                    return;
                }
            }
            else
            {
                Console.WriteLine("Agent: no actionable plan, stopping to avoid stray behavior.");
                return;
            }
        }
    }
}

// Example run
public static class Program
{
    public static async Task Main()
    {
        var agent = new Agent(new ShellTool(), maxIterations: 5, maxCost: 3.0m);
        var ctx = new AgentContext("Add feature X and pass unit tests");
        await agent.RunAsync(ctx);
    }
}

Notes on hardening:

  • Replace CallLLMAsync with structured prompts and parse JSON responses. Use typed JSON schemas for actions.
  • Add RBAC for tools that access network or shell. Log each step with timestamps and full inputs/outputs for audit.
  • Implement deciders that prevent repeated cycles (dedup memory) and ephemeral human checkpoints when necessary.
  1. Spec‑Driven Development pattern (C#)
    Purpose: show a minimal SDD flow: a machine‑readable spec (JSON) describes endpoints; a generator creates a service skeleton and a test scaffold. In production, an AI can implement methods from the spec and CI runs auto‑derived tests to enforce the contract.

Example Spec (spec.json)

{
  "service": "UserService",
  "description": "Create and get users",
  "endpoints": [
    { "name": "CreateUser", "input": { "name":"string","email":"string" }, "output": { "id":"guid" } },
    { "name": "GetUser", "input": { "id":"guid" }, "output": { "name":"string","email":"string" } }
  ],
  "constraints": {
    "email": "must be valid format",
    "name": "non-empty"
  }
}

Generator and test scaffold (simplified)

using System;
using System.IO;
using System.Text.Json;
using System.Text;

public class SpecGenerator
{
    public static void Generate(string specPath, string outDir)
    {
        var json = File.ReadAllText(specPath);
        using var doc = JsonDocument.Parse(json);
        var root = doc.RootElement;
        var serviceName = root.GetProperty("service").GetString();

        var sb = new StringBuilder();
        sb.AppendLine("using System;");
        sb.AppendLine("namespace Generated {");
        sb.AppendLine($"  public class {serviceName} {{");

        foreach (var ep in root.GetProperty("endpoints").EnumerateArray())
        {
            var name = ep.GetProperty("name").GetString();
            sb.AppendLine($"    // Auto-generated: {name} - implement per spec");
            sb.AppendLine($"    public object {name}(object input) => throw new NotImplementedException();");
        }

        sb.AppendLine("  } }");

        Directory.CreateDirectory(outDir);
        File.WriteAllText(Path.Combine(outDir, $"{serviceName}.cs"), sb.ToString());

        // Test scaffold example
        var test = new StringBuilder();
        test.AppendLine("using Xunit;");
        test.AppendLine($"public class {serviceName}Tests {{");
        test.AppendLine("  [Fact]");
        test.AppendLine($"  public void CreateUser_ShouldReturnId() => throw new NotImplementedException();");
        test.AppendLine("}");
        File.WriteAllText(Path.Combine(outDir, $"{serviceName}.Tests.cs"), test.ToString());
        Console.WriteLine(\"Generated files in: \" + outDir);
    }

    public static void Main(string[] args)
    {
        Generate(\"spec.json\", \"out\");
    }
}

How AI fits in SDD:

  • The spec is authoritative. An AI (or human) generates code that implements the spec. Tests are generated from the spec (asserting types, constraints, and examples). CI runs tests; failures trigger regeneration or human review. This establishes traceability and reduces drift

Making SDD production-quality

  • Use a formal schema (OpenAPI, JSON Schema, protobuf). Generate strong typed models (C# records/classes) from the spec. Use auto‑derived contract tests that validate constraints (email format, non-empty name). For more advanced workflows, integrate Roslyn-based analyzers that flag divergences at compile time, and store specs in a versioned repository for audit.

Practical comparisons and recommendations

  • Observability & Auditability
    • Agentic loop: requires explicit structured logging, typed tool outputs, and a chain-of-thought or step tracing. Without this, auditing is difficult.
    • SDD: natural audit trail because the spec is the contract; tests and commit history show intent and evidence.
  • Speed vs Overhead
    • Agentic loops can rapidly iterate and adapt but add runtime complexity (guardrails, tool contracts).
    • SDD requires upfront work on a machine‑readable spec but reduces downstream rework and improves compliance.
  • Typical real-world use-cases
    • Agentic loops: automated bug-fix agents, cloud orchestration that runs multi-step operations, autonomous data pipelines, or customer support agents that call external APIs.
    • SDD: API-first microservices in finance/healthcare, contract-based integrations, teams generating implementations from an agreed, audited spec.
  • Combining the two (hybrid patterns)
    • Use SDD to define critical contracts and expose typed tool interfaces to agents. Agents operate inside the guardrails defined by the spec. For example, an agent that patches code should only call tools whose inputs/outputs are specified in SDD artifacts. This approach balances autonomy with control and auditability.

Conclusion
Agentic loop programming and Spec‑Driven Development serve different needs. Agentic loops buy autonomy and multi-step problem solving. SDD buys determinism, traceability, and compliance. In practice, the strongest architectures blend them: use SDD to define and enforce the boundaries and contracts, and deploy agentic loops inside those boundaries to perform autonomous tasks. The C# patterns above give you a starting point: a simple agent loop pattern hardened with stop conditions and typed tools, and an SDD generator that shows how specs map to code and tests

Note: Much of the code was written by AI and is offered as is (AI makes mistakes, so proceed with caution). The text was also written by AI but significantly edited by me.

Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories