Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions naga/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ unicode-ident = { workspace = true, optional = true }
cfg_aliases.workspace = true

[dev-dependencies]
bytemuck.workspace = true
diff.workspace = true
env_logger.workspace = true
hashbrown = { workspace = true, features = ["serde"] }
Expand Down
124 changes: 120 additions & 4 deletions naga/tests/naga/snapshots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,109 @@ Note: this is an issue with snapshot configuration, not code. If you added a new
}
}

fn spirv_cross_stage_name(stage: naga::ShaderStage) -> &'static str {
match stage {
naga::ShaderStage::Vertex => "vert",
naga::ShaderStage::Fragment => "frag",
naga::ShaderStage::Compute => "comp",
naga::ShaderStage::Task => "task",
naga::ShaderStage::Mesh => "mesh",
naga::ShaderStage::RayGeneration => "rgen",
naga::ShaderStage::Miss => "rmiss",
naga::ShaderStage::AnyHit => "rahit",
naga::ShaderStage::ClosestHit => "rchit",
}
}

fn run_spirv_cross(
spv_binary: &[u32],
entry_point: &str,
stage: naga::ShaderStage,
) -> Result<String, String> {
use std::io::Write;
use std::process::{Command, Stdio};

let stage_name = spirv_cross_stage_name(stage);
let bytes: &[u8] = bytemuck::cast_slice(spv_binary);

let mut child = Command::new("spirv-cross")
.args([
"-V",
"--version", "460",
"--entry", entry_point,
"--stage", stage_name,
"-",
])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect(
"Failed to execute spirv-cross. Install it via the Vulkan SDK \
or from https://github.com/KhronosGroup/SPIRV-Cross",
);

child.stdin.take().unwrap().write_all(bytes).unwrap();

let output = child.wait_with_output().unwrap();
let stderr = String::from_utf8_lossy(&output.stderr).replace("\r\n", "\n");
let stdout = String::from_utf8_lossy(&output.stdout).replace("\r\n", "\n");

if !output.status.success() {
let mut commented = String::new();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add an extra line like "spirv-cross error:", this also makes errors more searchable

for line in stderr.lines() {
commented.push_str(&format!("// {line}\n"));
}
Err(commented)
} else {
Ok(stdout)
}
}

/// Writes GLSL output generated by SPIRV-Cross for the given SPIR-V binary.
///
/// Behavior:
/// - For multiple entry points, each GLSL output is prefixed with a comment block
/// indicating the entry point name and shader stage (e.g., `// Entry point: "main" (frag) //`).
/// - For a single entry point, no header is added (cleaner output for single-shader files).
/// - On SPIRV-Cross failure, stderr is captured as commented lines in the output file,
/// and the function continues to write remaining entry points. This ensures test
/// snapshots are always generated even when translation fails.
fn write_spirv_cross_glsl(
input: &Input,
spv_binary: &[u32],
entry_points: &[(String, naga::ShaderStage)],
extension: &str,
) {
let multiple = entry_points.len() > 1;
let mut output = String::new();

for (i, (name, stage)) in entry_points.iter().enumerate() {
if multiple {
if i > 0 {
output.push('\n');
}
let stage_name = spirv_cross_stage_name(*stage);
let inner = format!(" Entry point: \"{name}\" ({stage_name}) ");
let width = inner.len() + 4; // "// " + inner + " //"
let bar: String = "/".repeat(width);
output.push_str(&bar);
output.push('\n');
output.push_str(&format!("//{inner}//"));
output.push('\n');
output.push_str(&bar);
output.push('\n');
}

match run_spirv_cross(spv_binary, name, *stage) {
Ok(glsl) => output.push_str(&glsl),
Err(err) => output.push_str(&err),
}
}

input.write_output_file("spv", extension, output, DIR_OUT);
}

fn write_output_spv(
input: &Input,
module: &naga::Module,
Expand All @@ -221,17 +324,29 @@ fn write_output_spv(
entry_point: ep.name.clone(),
shader_stage: ep.stage,
};
write_output_spv_inner(
let spv_binary = write_output_spv_inner(
input,
&module,
&info,
&options,
Some(&pipeline_options),
&format!("{}.spvasm", ep.name),
);
write_spirv_cross_glsl(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if in this case we should still combine them into one file, even if the spirv entry points will be written separately?

input,
&spv_binary,
&[(ep.name.clone(), ep.stage)],
&format!("{}.spvasm.glsl", ep.name),
);
}
} else {
write_output_spv_inner(input, &module, &info, &options, None, "spvasm");
let spv_binary = write_output_spv_inner(input, &module, &info, &options, None, "spvasm");
let entry_points: Vec<(String, naga::ShaderStage)> = module
.entry_points
.iter()
.map(|ep| (ep.name.clone(), ep.stage))
.collect();
write_spirv_cross_glsl(input, &spv_binary, &entry_points, "spvasm.glsl");
}
}

Expand All @@ -242,12 +357,12 @@ fn write_output_spv_inner(
options: &naga::back::spv::Options<'_>,
pipeline_options: Option<&naga::back::spv::PipelineOptions>,
extension: &str,
) {
) -> Vec<u32> {
use naga::back::spv;
use rspirv::binary::Disassemble;
println!("Generating SPIR-V for {:?}", input.file_name);
let spv = spv::write_vec(module, info, options, pipeline_options).unwrap();
let dis = rspirv::dr::load_words(spv)
let dis = rspirv::dr::load_words(spv.clone())
.expect("Produced invalid SPIR-V")
.disassemble();
// HACK escape CR/LF if source code is in side.
Expand All @@ -258,6 +373,7 @@ fn write_output_spv_inner(
dis
};
input.write_output_file("spv", extension, dis, DIR_OUT);
spv
}

fn write_output_msl(
Expand Down
18 changes: 18 additions & 0 deletions naga/tests/out/spv/spv-barrier.spvasm.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#version 450
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;

void _4()
{
barrier();
memoryBarrierBuffer();
memoryBarrierImage();
barrier();
memoryBarrier();
barrier();
}

void main()
{
_4();
}

35 changes: 35 additions & 0 deletions naga/tests/out/spv/spv-fetch_depth.spvasm.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#version 450
layout(local_size_x = 32, local_size_y = 1, local_size_z = 1) in;

struct _5
{
float _m0;
};

struct _8
{
uvec2 _m0;
};

layout(binding = 0, std430) buffer _12_11
{
_5 _m0;
} _11;

layout(binding = 1, std430) readonly buffer _15_14
{
_8 _m0;
} _14;

uniform sampler2D SPIRV_Cross_CombinedSPIRV_Cross_DummySampler;

void _20()
{
_11._m0._m0 = vec4(texelFetch(SPIRV_Cross_CombinedSPIRV_Cross_DummySampler, ivec2(_14._m0._m0), 0).x).x;
}

void main()
{
_20();
}

20 changes: 20 additions & 0 deletions naga/tests/out/spv/spv-per-vertex.spvasm.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#version 450
#extension GL_EXT_fragment_shader_barycentric : require

layout(location = 0) pervertexEXT in float _25[3];
layout(location = 0) out vec4 _28;
float _9[3] = float[](0.0, 0.0, 0.0);
vec4 _12 = vec4(0.0);

void _16()
{
_12 = vec4(_9[0], _9[1], _9[2], 1.0);
}

void main()
{
_9 = _25;
_16();
_28 = _12;
}

44 changes: 44 additions & 0 deletions naga/tests/out/spv/spv-subgroup-barrier.spvasm.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#version 450

#if defined(GL_KHR_shader_subgroup_basic)
#extension GL_KHR_shader_subgroup_basic : require
#elif defined(GL_NV_shader_thread_group)
#extension GL_NV_shader_thread_group : require
#elif defined(GL_ARB_shader_ballot) && defined(GL_ARB_shader_int64)
#extension GL_ARB_shader_int64 : enable
#extension GL_ARB_shader_ballot : require
#elif defined(GL_AMD_gcn_shader) && (defined(GL_AMD_gpu_shader_int64) || defined(GL_NV_gpu_shader5))
#extension GL_AMD_gpu_shader_int64 : enable
#extension GL_NV_gpu_shader5 : enable
#extension GL_AMD_gcn_shader : require
#else
#error No extensions available to emulate requested subgroup feature.
#endif

#if defined(GL_KHR_shader_subgroup_basic)
#extension GL_KHR_shader_subgroup_basic : require
#endif
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;

#ifndef GL_KHR_shader_subgroup_basic
void subgroupBarrier() { memoryBarrierShared(); }
#endif

#ifndef GL_KHR_shader_subgroup_basic
void subgroupMemoryBarrier() { groupMemoryBarrier(); }
void subgroupMemoryBarrierBuffer() { groupMemoryBarrier(); }
void subgroupMemoryBarrierShared() { memoryBarrierShared(); }
void subgroupMemoryBarrierImage() { groupMemoryBarrier(); }
#endif

void _4()
{
subgroupMemoryBarrier();
subgroupBarrier();
}

void main()
{
_4();
}

27 changes: 27 additions & 0 deletions naga/tests/out/spv/wgsl-6220-break-from-loop.spvasm.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#version 460
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

void _5()
{
int _10 = 0;
uvec2 _27 = uvec2(4294967295u);
for (;;)
{
if (all(equal(uvec2(0u), _27)))
{
break;
}
_27 -= uvec2(uint(_27.y == 0u), 1u);
if (!(_10 < 4))
{
break;
}
break;
}
}

void main()
{
_5();
}

41 changes: 41 additions & 0 deletions naga/tests/out/spv/wgsl-6438-conflicting-idents.spvasm.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//////////////////////////////
// Entry point: "vs" (vert) //
//////////////////////////////
#version 460

struct _6
{
vec4 _m0;
vec2 _m1;
};

layout(location = 0) in vec2 _8;
layout(location = 0) out vec2 _13;

void main()
{
_6 _19 = _6(vec4(0.0), vec2(0.0));
_19._m0 = vec4(_8, 0.0, 1.0);
gl_Position = _19._m0;
_13 = _19._m1;
}


//////////////////////////////
// Entry point: "fs" (frag) //
//////////////////////////////
#version 460

struct _6
{
vec4 _m0;
vec2 _m1;
};

layout(location = 0) out vec4 _32;

void main()
{
_32 = vec4(1.0, 0.0, 0.0, 1.0);
}

9 changes: 9 additions & 0 deletions naga/tests/out/spv/wgsl-6772-unpack-expr-accesses.spvasm.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#version 460
#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require
#extension GL_EXT_shader_8bit_storage : require
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

void main()
{
}

13 changes: 13 additions & 0 deletions naga/tests/out/spv/wgsl-7048-multiple-dynamic-1.spvasm.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#version 460
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

const vec3 _13[2] = vec3[](vec3(0.0), vec3(0.0));

void main()
{
vec4 _17 = vec4(0.0);
int _19 = 0;
int _21 = 0;
_17.x += (_13[_21].y * _13[_19].z);
}

Loading
Loading