KhronosGroup / KhronosGroup/Vulkan-ValidationLayers

RT SPIR-V has wrong error with multiple pipelines built at once

Open
#13,048 0 comments 0 reactions 1 assignee Claimed by @arno-lunarg View on GitHub
Bug RT
Dominant language
C++
Stars
1k
Forks
504
Avg merge
11h 35m
Merged PRs (30d)
224

Description

AI generated error, test fails though, so error seems real

In `PreCallValidateCreateRayTracingPipelinesKHR:`

```c++
for (uint32_t i = 0; i < count; i++) {
...
uint32_t stateless_data_i = 0; // <-- reset for every pipeline
for (uint32_t stage = 0; stage < pCreateInfos[i].stageCount; stage++) {
if (const auto shader_ci = FindStructInPNextChain(...)) {
skip |= stateless_spirv_validator.Validate(
*chassis_state.stateless_data[stateless_data_i].pipeline_pnext_module, ...);
++stateless_data_i;
```

So pipeline 1's entries start where pipeline 0's ended, but the index restarts at 0.

With two create infos that both inline SPIR-V:
- i=0 validates stateless_data[0] ✅
- i=1 validates stateless_data[0] again, now labelled pCreateInfos[1]

This is the gap the code already documents at

▎ // TODO - https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/10029
▎ // This works for Compute because there is a single shader, but stateless_data will need to be indexed into

Compute genuinely is safe (one shader, and it guards on i == 0). Ray tracing is the case that TODO was worried about.

(feel free to add a way to `vkt::rt` to write the following test)

```c++
TEST_F(NegativeRayTracingPipeline, AtomicsSecondCreateInfo) {
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_KHR_MAINTENANCE_5_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::rayTracingPipeline);
AddRequiredFeature(vkt::Feature::maintenance5);
AddRequiredFeature(vkt::Feature::shaderInt64);
AddRequiredFeature(vkt::Feature::shaderSharedInt64Atomics);
RETURN_IF_SKIP(InitFrameworkForRayTracingTest());
RETURN_IF_SKIP(InitState());

const char* bad_closest_hit_shader = R"glsl(
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : enable
#extension GL_EXT_shader_atomic_int64 : enable
#extension GL_KHR_memory_scope_semantics : enable
layout(set = 0, binding = 0) buffer ssbo { uint64_t y; };
void main() {
atomicAdd(y, 1);
}
)glsl";

const char* good_closest_hit_shader = R"glsl(
#version 460
#extension GL_EXT_ray_tracing : require
layout(set = 0, binding = 0) buffer ssbo { uint y; };
void main() {
y = 0;
}
)glsl";

std::vector good_shader;
GLSLtoSPV(m_device->Physical().limits_, VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, good_closest_hit_shader, good_shader,
SPV_ENV_VULKAN_1_2);
std::vector bad_shader;
GLSLtoSPV(m_device->Physical().limits_, VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, bad_closest_hit_shader, bad_shader,
SPV_ENV_VULKAN_1_2);

VkShaderModuleCreateInfo good_module_ci = vku::InitStructHelper();
good_module_ci.pCode = good_shader.data();
good_module_ci.codeSize = good_shader.size() * sizeof(uint32_t);
VkShaderModuleCreateInfo bad_module_ci = vku::InitStructHelper();
bad_module_ci.pCode = bad_shader.data();
bad_module_ci.codeSize = bad_shader.size() * sizeof(uint32_t);

VkShaderObj miss_shader(*m_device, kMissGlsl, VK_SHADER_STAGE_MISS_BIT_KHR, SPV_ENV_VULKAN_1_2);

std::array good_stages;
good_stages[0] = vku::InitStructHelper();
good_stages[0].stage = VK_SHADER_STAGE_MISS_BIT_KHR;
good_stages[0].module = miss_shader;
good_stages[0].pName = "main";
good_stages[1] = vku::InitStructHelper(&good_module_ci);
good_stages[1].stage = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR;
good_stages[1].module = VK_NULL_HANDLE;
good_stages[1].pName = "main";

std::array bad_stages = good_stages;
bad_stages[1] = vku::InitStructHelper(&bad_module_ci);
bad_stages[1].stage = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR;
bad_stages[1].module = VK_NULL_HANDLE;
bad_stages[1].pName = "main";

VkRayTracingShaderGroupCreateInfoKHR group_ci = vku::InitStructHelper();
group_ci.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR;
group_ci.generalShader = VK_SHADER_UNUSED_KHR;
group_ci.closestHitShader = 1;
group_ci.anyHitShader = VK_SHADER_UNUSED_KHR;
group_ci.intersectionShader = VK_SHADER_UNUSED_KHR;

VkRayTracingPipelineInterfaceCreateInfoKHR interface_ci = vku::InitStructHelper();
interface_ci.maxPipelineRayHitAttributeSize = 4;
interface_ci.maxPipelineRayPayloadSize = 4;

const vkt::DescriptorSetLayout ds_layout(*m_device, {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}});
const vkt::PipelineLayout pipeline_layout(*m_device, {&ds_layout});

std::array pipeline_cis;
pipeline_cis[0] = vku::InitStructHelper();
pipeline_cis[0].flags = VK_PIPELINE_CREATE_LIBRARY_BIT_KHR;
pipeline_cis[0].stageCount = size32(good_stages);
pipeline_cis[0].pStages = good_stages.data();
pipeline_cis[0].groupCount = 1;
pipeline_cis[0].pGroups = &group_ci;
pipeline_cis[0].layout = pipeline_layout;
pipeline_cis[0].pLibraryInterface = &interface_ci;
pipeline_cis[1] = pipeline_cis[0];
pipeline_cis[1].pStages = bad_stages.data();

std::array pipelines = {{VK_NULL_HANDLE, VK_NULL_HANDLE}};
m_errorMonitor->SetDesiredErrorRegex("VUID-RuntimeSpirv-None-06278", "pCreateInfos\\[1\\]");
vk::CreateRayTracingPipelinesKHR(*m_device, VK_NULL_HANDLE, VK_NULL_HANDLE, size32(pipeline_cis), pipeline_cis.data(), nullptr,
pipelines.data());
m_errorMonitor->VerifyFound();

for (VkPipeline pipeline : pipelines) {
if (pipeline != VK_NULL_HANDLE) {
vk::DestroyPipeline(*m_device, pipeline, nullptr);
}
}
}

```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.