Compiling a valid SPIR-V compute shader crashes igc64.dll with access violation 0xC0000005 during vkCreateComputePipelines , but only at optimization -O1 and above ( -O0 works fine). The trigger: a struct containing a PhysicalStorageBuffer (buffer-device-address) pointer member is loaded from an array reached via another BDA pointer stored in a UBO, then the pointer member is dereferenced. Bisection shows the crash depends on the loaded struct having 2 or more members — a single-pointer-only struct survives; adding any second member (any type) reproduces the crash. Minimal Slang source, compiled .spv (crashing and surviving variants), and a self-contained Vulkan harness are attached. Verified 3/3 crashes at -O3 , 0/3 at -O0 .
Environment:
• GPU: Intel(R) Iris(R) Xe Graphics (Tiger Lake)
• Driver version: 32.0.101.7085
• Vulkan API version: 1.4.323
• OS: Windows
• Faulting module: igc64.dll
• Exception: 0xC0000005 (access violation), constant fault offset
• Trigger call: vkCreateComputePipelines
// Variant 2: remove the index-table indirection entirely -- index the
// pointer array directly with globalSplatID.
struct SplatSetDesc
{
float* centersAddress;
uint64_t colorsAddress;
};
struct SceneAssets
{
SplatSetDesc* splatSetDescriptors;
};
[[vk::binding(0, 0)]] ConstantBuffer<SceneAssets> assets;
[[vk::binding(1, 0)]] RWStructuredBuffer<float4> outBuf;
[numthreads(64, 1, 1)]
[shader("compute")]
void main(uint3 dispatchThreadID : SV_DispatchThreadID)
{
const uint globalSplatID = dispatchThreadID.x;
SplatSetDesc desc = assets.splatSetDescriptors[globalSplatID];
float* ptr = (float*)(uint64_t(desc.centersAddress) + uint64_t(globalSplatID) * 12ul);
float3 center = float3(ptr[0], ptr[1], ptr[2]);
outBuf[globalSplatID] = float4(center, float(desc.colorsAddress));
}
// repro_harness.cpp
//
// Self-contained, minimal Vulkan harness to reproduce an igc64.dll access
// violation (0xC0000005) when creating a compute pipeline from SPIR-V that
// loads a struct (containing a pointer/BDA member) through a buffer-device-
// address pointer that was itself read from a uniform buffer.
//
// Build (from a "x64 Native Tools Command Prompt for VS" or with vcvars64
// sourced):
// cl /nologo /EHsc /std:c++17 repro_harness.cpp ^
// /I "%VULKAN_SDK%\Include" ^
// /link /LIBPATH:"%VULKAN_SDK%\Lib" vulkan-1.lib /out:repro_harness.exe
//
// Usage:
// repro_harness.exe repro.spv
//
// The pipeline creation call is wrapped in Windows SEH (__try/__except) so
// the harness can catch the access violation and report CRASHED/SURVIVED
// instead of the whole process dying with no diagnostic output.
#define VK_USE_PLATFORM_WIN32_KHR
#include <vulkan/vulkan.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <vector>
#include <windows.h>
static std::vector<uint32_t> readSpirv(const char* path) {
std::ifstream f(path, std::ios::binary | std::ios::ate);
if (!f) {
fprintf(stderr, "Failed to open %s\n", path);
exit(2);
}
size_t size = static_cast<size_t>(f.tellg());
f.seekg(0);
std::vector<uint32_t> code(size / sizeof(uint32_t));
f.read(reinterpret_cast<char*>(code.data()), size);
return code;
}
// Runs vkCreateComputePipelines guarded by SEH so a driver-side access
// violation (0xC0000005 in igc64.dll) is caught here instead of killing the
// process with no output.
static int createPipelineGuarded(VkDevice device, VkPipelineLayout layout,
VkShaderModule module,
VkPipeline* outPipeline) {
VkPipelineShaderStageCreateInfo stage{};
stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
stage.module = module;
stage.pName = "main";
VkComputePipelineCreateInfo ci{};
ci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
ci.stage = stage;
ci.layout = layout;
__try {
VkResult r = vkCreateComputePipelines(device, VK_NULL_HANDLE, 1, &ci,
nullptr, outPipeline);
if (r != VK_SUCCESS) {
printf("vkCreateComputePipelines returned VkResult=%d\n", r);
return 1;
}
return 0;
} __except (EXCEPTION_EXECUTE_HANDLER) {
DWORD code = GetExceptionCode();
printf("CRASHED: SEH exception 0x%08lX during vkCreateComputePipelines "
"(this is the igc64.dll access violation)\n",
code);
return -1;
}
}
int main(int argc, char** argv) {
if (argc < 2) {
fprintf(stderr, "usage: %s <shader.spv>\n", argv[0]);
return 2;
}
std::vector<uint32_t> spirv = readSpirv(argv[1]);
printf("Loaded %s (%zu bytes)\n", argv[1], spirv.size() * sizeof(uint32_t));
// --- Instance ---
VkApplicationInfo appInfo{};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "igc-repro";
appInfo.apiVersion = VK_API_VERSION_1_2;
VkInstanceCreateInfo instCi{};
instCi.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instCi.pApplicationInfo = &appInfo;
VkInstance instance;
if (vkCreateInstance(&instCi, nullptr, &instance) != VK_SUCCESS) {
fprintf(stderr, "vkCreateInstance failed\n");
return 2;
}
// --- Pick the first physical device (edit index below if you have
// multiple GPUs and want to target a specific one) ---
uint32_t gpuCount = 0;
vkEnumeratePhysicalDevices(instance, &gpuCount, nullptr);
if (gpuCount == 0) {
fprintf(stderr, "No Vulkan physical devices found\n");
return 2;
}
std::vector<VkPhysicalDevice> gpus(gpuCount);
vkEnumeratePhysicalDevices(instance, &gpuCount, gpus.data());
VkPhysicalDevice phys = gpus[0];
VkPhysicalDeviceProperties props{};
vkGetPhysicalDeviceProperties(phys, &props);
printf("Using GPU: %s (driverVersion=0x%08X, apiVersion=0x%08X)\n",
props.deviceName, props.driverVersion, props.apiVersion);
// --- Logical device with bufferDeviceAddress enabled ---
float prio = 1.0f;
VkDeviceQueueCreateInfo qci{};
qci.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
qci.queueFamilyIndex = 0;
qci.queueCount = 1;
qci.pQueuePriorities = &prio;
VkPhysicalDeviceBufferDeviceAddressFeatures bdaFeatures{};
bdaFeatures.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES;
bdaFeatures.bufferDeviceAddress = VK_TRUE;
const char* deviceExts[] = {"VK_KHR_buffer_device_address"};
VkDeviceCreateInfo devCi{};
devCi.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
devCi.pNext = &bdaFeatures;
devCi.queueCreateInfoCount = 1;
devCi.pQueueCreateInfos = &qci;
devCi.enabledExtensionCount = 1;
devCi.ppEnabledExtensionNames = deviceExts;
VkDevice device;
VkResult devResult = vkCreateDevice(phys, &devCi, nullptr, &device);
if (devResult != VK_SUCCESS) {
fprintf(stderr, "vkCreateDevice failed: %d\n", devResult);
return 2;
}
// --- Shader module ---
VkShaderModuleCreateInfo smCi{};
smCi.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
smCi.codeSize = spirv.size() * sizeof(uint32_t);
smCi.pCode = spirv.data();
VkShaderModule module;
if (vkCreateShaderModule(device, &smCi, nullptr, &module) != VK_SUCCESS) {
fprintf(stderr, "vkCreateShaderModule failed\n");
return 2;
}
// --- Descriptor set layout matching the shader's bindings (UBO @0,
// storage buffer @1). No actual buffers need to be bound to reach
// the crash -- it happens purely during shader compilation. ---
VkDescriptorSetLayoutBinding bindings[2]{};
bindings[0].binding = 0;
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[0].descriptorCount = 1;
bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
bindings[1].binding = 1;
bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[1].descriptorCount = 1;
bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
VkDescriptorSetLayoutCreateInfo dslCi{};
dslCi.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
dslCi.bindingCount = 2;
dslCi.pBindings = bindings;
VkDescriptorSetLayout dsl;
vkCreateDescriptorSetLayout(device, &dslCi, nullptr, &dsl);
VkPipelineLayoutCreateInfo plCi{};
plCi.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
plCi.setLayoutCount = 1;
plCi.pSetLayouts = &dsl;
VkPipelineLayout layout;
vkCreatePipelineLayout(device, &plCi, nullptr, &layout);
// --- The call under test ---
VkPipeline pipeline = VK_NULL_HANDLE;
int rc = createPipelineGuarded(device, layout, module, &pipeline);
if (rc == 0) {
printf("SURVIVED: pipeline created successfully\n");
} else if (rc > 0) {
printf("FAILED (clean VkResult error, not a crash)\n");
}
// rc < 0: CRASHED, already printed above.
return rc < 0 ? 1 : 0;
}
Compiling a valid SPIR-V compute shader crashes igc64.dll with access violation 0xC0000005 during vkCreateComputePipelines , but only at optimization -O1 and above ( -O0 works fine). The trigger: a struct containing a PhysicalStorageBuffer (buffer-device-address) pointer member is loaded from an array reached via another BDA pointer stored in a UBO, then the pointer member is dereferenced. Bisection shows the crash depends on the loaded struct having 2 or more members — a single-pointer-only struct survives; adding any second member (any type) reproduces the crash. Minimal Slang source, compiled .spv (crashing and surviving variants), and a self-contained Vulkan harness are attached. Verified 3/3 crashes at -O3 , 0/3 at -O0 .
Environment:
• GPU: Intel(R) Iris(R) Xe Graphics (Tiger Lake)
• Driver version: 32.0.101.7085
• Vulkan API version: 1.4.323
• OS: Windows
• Faulting module: igc64.dll
• Exception: 0xC0000005 (access violation), constant fault offset
• Trigger call: vkCreateComputePipelines
attached files:
repro_min.slang — minimal shader source that reproduce the issue
self contained harnses cpp