DirectStorage on Windows 11: Cutting the CPU Out of the Asset-Loading Hot Path
DirectStorage moves game asset loading off the serial CPU path and into batched, asynchronous queues that can deliver data straight to GPU resources. Here's how the real API works and what to watch for.
05 Apr 2026, 04:10 UTC

The problem: fast drives, slow loads
Modern NVMe SSDs can stream gigabytes per second, yet game load times often don't reflect that. The reason is the path the data takes. In a traditional loader, the CPU issues file reads, waits on I/O, decompresses the data into system memory, and only then copies it into GPU-visible memory. Every stage is serialized on the CPU, and the GPU sits idle while it happens. As asset counts grow into the tens of thousands per level, the CPU becomes the bottleneck, not the disk.
DirectStorage is Microsoft's answer: a Windows API that lets a game submit batches of read requests that land data in GPU-accessible memory with far less CPU involvement per request. The key engineering point is that it makes asset loading asynchronous and batched, and on supported configurations it can pair with GPU decompression so compressed data travels from disk to GPU without a CPU decompress-and-copy stage.
What DirectStorage actually is
DirectStorage is a separate API from Direct3D 12, though it is designed to work alongside it. The main interfaces are:
IDStorageFactory— the entry point, obtained viaDStorageGetFactory.IDStorageQueue— a submission queue you configure with a destination (for example, a D3D12 device for GPU-bound requests) and a capacity.IDStorageFile— an opened file handle you enqueue reads against.
A load is expressed as a DSTORAGE_REQUEST, which describes the source file offset, the size, the compression format, and the destination — either a CPU buffer or a D3D12 resource such as a buffer or texture subresource. Requests are enqueued with EnqueueRequest and submitted with Submit; completion is tracked with a D3D12 fence or an event. The CPU still initiates and synchronizes everything — DirectStorage does not remove the CPU from the loop — but it removes the CPU from the per-asset data path, which is where the time went.
A minimal sketch
The following illustrates the real API shape. It is a structural sketch, not a complete program — error handling, resource creation, and synchronization details are omitted — and it has not been compiled or tested here. Build against the DirectStorage NuGet package or the version shipped with the Windows SDK you target.
#include <dstorage.h>
#include <wrl.h>
using Microsoft::WRL::ComPtr;
ComPtr<IDStorageFactory> factory;
DStorageGetFactory(IID_PPV_ARGS(&factory));
// Open the asset file
ComPtr<IDStorageFile> file;
factory->OpenFile(L"level1.pack", IID_PPV_ARGS(&file));
// Create a queue targeting the D3D12 device
DSTORAGE_QUEUE_DESC queueDesc{};
queueDesc.SourceType = DSTORAGE_REQUEST_SOURCE_FILE;
queueDesc.Capacity = DSTORAGE_MAX_QUEUE_CAPACITY;
queueDesc.Priority = DSTORAGE_PRIORITY_NORMAL;
queueDesc.Device = d3d12Device; // ID3D12Device*
ComPtr<IDStorageQueue> queue;
factory->CreateQueue(&queueDesc, IID_PPV_ARGS(&queue));
// Enqueue one read into a D3D12 buffer resource
DSTORAGE_REQUEST request{};
request.Options.SourceType = DSTORAGE_REQUEST_SOURCE_FILE;
request.Options.DestinationType = DSTORAGE_REQUEST_DESTINATION_BUFFER;
request.Options.CompressionFormat = DSTORAGE_COMPRESSION_FORMAT_GDEFLATE;
request.Source.File.Source = file.Get();
request.Source.File.Offset = assetOffset;
request.Source.File.Size = compressedSize;
request.UncompressedSize = uncompressedSize;
request.Destination.Buffer.Resource = dstBuffer; // ID3D12Resource*
request.Destination.Buffer.Offset = 0;
request.Destination.Buffer.Size = uncompressedSize;
queue->EnqueueRequest(&request);
// Signal a fence when the batch completes
queue->EnqueueSignal(fence.Get(), fenceValue);
queue->Submit();
// Later: wait on the fence (event or D3D12 fence wait) before using dstBuffer
The pattern that matters: enqueue many requests, submit once, and consume results when the fence signals. That batching is what amortizes CPU overhead across thousands of assets.
Hardware, OS, and honest expectations
DirectStorage runs on Windows 11 and on later builds of Windows 10; the exact minimum build and which features (notably GPU decompression) are available where is version-sensitive, so check current Microsoft documentation before committing to a minimum spec. The headline benefits depend on hardware:
- NVMe SSD: the API is designed around NVMe-class latency and parallelism. It functions on slower storage, but the gains shrink.
- GPU decompression: the GDeflate codec can be decompressed by the GPU on supported drivers/hardware, which is where the largest CPU savings come from. Without it, decompression still happens on the CPU.
- Up-to-date GPU driver: required for the optimized path.
Treat any specific percentage improvement figure with skepticism: results depend heavily on asset size distribution, compression ratio, storage device, and how well the engine overlaps loading with other work. Measure on your own content rather than trusting marketing numbers.
Trade-offs and fallback
The main cost is engineering complexity. You are adopting a second asynchronous I/O system with its own queues, fences, and failure modes, and you must keep a conventional CPU-loader path for machines where the fast path isn't available or beneficial. The usual approach is a capability check at startup (attempt to create the factory and queue, and query hardware characteristics) and route loads through DirectStorage only when it is present and the storage device justifies it. Compression format choice also becomes a build-pipeline decision: GDeflate-compressed assets need tooling to produce them.
How to verify it works
- Build the official DirectStorage samples (available from Microsoft's DirectX sample repositories) on your target machine and confirm they run.
- Instrument both paths: record wall-clock load time and CPU utilization for the same asset set through your legacy loader and through DirectStorage.
- Use a GPU trace tool (for example, PIX on Windows) to confirm requests are landing in GPU resources and, if using GDeflate, that decompression is occurring on the GPU rather than in your CPU threads.
- Test the fallback: run on a machine or VM without the fast-path prerequisites and confirm the CPU loader engages cleanly.
Takeaway
DirectStorage's real contribution is architectural: it turns asset loading from a serial, CPU-bound chore into a batched, asynchronous pipeline that matches how NVMe hardware and GPUs actually want to be fed. If you are building or maintaining a Windows game engine, prototype the queue/fence pattern on a representative asset set, measure against your existing loader, and keep a solid CPU fallback. If you are a player, the practical checklist is short: a current Windows build, an NVMe SSD, a recent GPU driver — and games that actually ship DirectStorage support.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.