Skip to content
Merged
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
4 changes: 3 additions & 1 deletion glslc/src/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,9 @@ An input file of - represents standard input.
-fshader-stage=<stage>
Treat subsequent input files as having stage <stage>.
Valid stages are vertex, vert, fragment, frag, tesscontrol,
tesc, tesseval, tese, geometry, geom, compute, and comp.
tesc, tesseval, tese, geometry, geom, compute, comp, raygen,
rgen, anyhit, rahit, closest, rchit, miss, rmiss, intersect,
rint, callable, rcall, task, and mesh.
-g Generate source-level debug information.
-h Display available options.
--help Display available options.
Expand Down
6 changes: 6 additions & 0 deletions glslc/src/shader_stage.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,17 @@ shaderc_shader_kind MapStageNameToForcedKind(const string_piece& stage_name) {
{"geom", shaderc_glsl_geometry_shader},
{"compute", shaderc_glsl_compute_shader},
{"comp", shaderc_glsl_compute_shader},
{"raygen", shaderc_glsl_raygen_shader},
{"rgen", shaderc_glsl_raygen_shader },
{"anyhit", shaderc_glsl_anyhit_shader},
{"rahit", shaderc_glsl_anyhit_shader },
{"closest", shaderc_glsl_closesthit_shader},
{"rchit", shaderc_glsl_closesthit_shader },
{"miss", shaderc_glsl_miss_shader},
{"rmiss", shaderc_glsl_miss_shader },
{"intersect", shaderc_glsl_intersection_shader},
{"rint", shaderc_glsl_intersection_shader },
{"callable", shaderc_glsl_callable_shader},
{"rcall", shaderc_glsl_callable_shader },
{"task", shaderc_glsl_task_shader },
{"mesh", shaderc_glsl_mesh_shader },
Expand Down
311 changes: 309 additions & 2 deletions glslc/test/option_shader_stage.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,266 @@ def simple_compute_shader():
uvec3 temp = gl_WorkGroupID;
}"""

def simple_raygen_shader():
return """#version 460
#extension GL_EXT_ray_tracing : enable

// 1. Top-Level Acceleration Structure (TLAS) containing your scene geometry
layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS;

// 2. Storage image to write the output colors to
layout(binding = 1, set = 0, rgba8) uniform writeonly image2D imageOutput;

// 3. Payload layout to receive ray tracing result (matches hit/miss shaders)
layout(location = 0) rayPayloadEXT vec3 hitColor;

void main()
{
// Compute pixel coordinates based on launch ID
ivec2 pixel = ivec2(gl_LaunchIDEXT.xy);
vec2 uv = vec2(gl_LaunchIDEXT.xy) / vec2(gl_LaunchSizeEXT.xy);

// Simple orthographic/normalized ray setup
vec3 origin = vec3(uv * 2.0 - 1.0, -1.0); // Normalized Screen Space to World [-1, 1]
vec3 direction = vec3(0.0, 0.0, 1.0); // Ray pointing forward into screen

uint rayFlags = gl_RayFlagsOpaqueEXT;
uint cullMask = 0xFF; // Trace all geometries
float tMin = 0.001;
float tMax = 1000.0;

// Trace the ray into the acceleration structure
traceRayEXT(
topLevelAS, // TLAS handle
rayFlags, // Ray behavior flags
cullMask, // Visibility mask
0, 0, 0, // SBT offset, stride, miss index
origin, // Ray Origin (vec3)
tMin, // Minimum intersection distance
direction, // Ray Direction (vec3)
tMax, // Maximum intersection distance
0 // Payload location index
);

// Store the resulting color into the output image
imageStore(imageOutput, pixel, vec4(hitColor, 1.0));
}
"""

def simple_intersection_shader():
return """#version 460
#extension GL_EXT_ray_tracing : enable

// Intersection payload/attribute sent to Closest Hit shader
hitAttributeEXT vec3 normal;

void main()
{
// Built-in ray parameters supplied by Vulkan API
vec3 rayOrigin = gl_WorldRayOriginEXT;
vec3 rayDirection = gl_WorldRayDirectionEXT;
float tMin = gl_RayTminEXT;
float tMax = gl_RayTmaxEXT;

// Define a sphere centered at (0,0,0) with radius 1.0
float radius = 1.0;

// Ray-Sphere Intersection Math (Quadratic Equation: a*t^2 + b*t + c = 0)
float a = dot(rayDirection, rayDirection);
float b = 2.0 * dot(rayOrigin, rayDirection);
float c = dot(rayOrigin, rayOrigin) - (radius * radius);

float discriminant = b * b - 4.0 * a * c;

if (discriminant >= 0.0)
{
// Calculate nearest intersection point distance (t)
float t = (-b - sqrt(discriminant)) / (2.0 * a);

// Check if the intersection point lies within the valid ray range [tMin, tMax]
if (t >= tMin && t <= tMax)
{
// Compute hit normal to pass along to Closest Hit Shader
normal = normalize(rayOrigin + t * rayDirection);

// Report the intersection to Vulkan (0 is a user-defined hit type)
reportIntersectionEXT(t, 0);
}
}
}
"""

def simple_anyhit_shader():
return """#version 460
#extension GL_EXT_ray_tracing : enable
#extension GL_EXT_nonuniform_qualifier : enable

// Texture sampler layout (e.g., alpha mask)
layout(binding = 2, set = 0) uniform sampler2D alphaTextures[];

// Attributes received from Intersection Shader or Built-in Triangle barycentrics
hitAttributeEXT vec2 hitAttribs;

void main()
{
// Compute UVs using barycentric coordinates (for triangle geometry)
vec3 barycentrics = vec3(1.0 - hitAttribs.x - hitAttribs.y, hitAttribs.x, hitAttribs.y);

// Fetch UV coordinates or material ID for this instance/primitive
// (Assuming simple UV mapping from vertex attribute data)
vec2 uv = hitAttribs;

// Sample the alpha channel from texture
float alpha = texture(alphaTextures[gl_InstanceCustomIndexEXT], uv).a;

// Alpha cutoff threshold
if (alpha < 0.5)
{
// Reject this intersection candidate; traversal continues along the ray
ignoreIntersectionEXT;
}

// If not ignored, Vulkan accepts this candidate hit by default.
}
"""

def simple_miss_shader():
return """#version 460
#extension GL_EXT_ray_tracing : enable

// Payload output sent back to the Ray Generation Shader
// (Location index MUST match the payload location index passed to traceRayEXT)
layout(location = 0) rayPayloadInEXT vec3 hitColor;

void main()
{
// Get the normalized direction vector of the ray that missed
vec3 rayDir = normalize(gl_WorldRayDirectionEXT);

// Create a simple vertical sky gradient (blue to white) based on Y direction
float t = 0.5 * (rayDir.y + 1.0);
vec3 skyColor = mix(vec3(1.0, 1.0, 1.0), vec3(0.5, 0.7, 1.0), t);

// Write output color into payload
hitColor = skyColor;
}
"""

def simple_closest_shader():
return """#version 460
#extension GL_EXT_ray_tracing : enable

// Payload received from Raygen (location MUST match traceRayEXT call)
layout(location = 0) rayPayloadInEXT vec3 hitColor;

// Attributes passed from hardware ray-triangle intersection (Barycentric coordinates)
hitAttributeEXT vec2 hitAttribs;

void main()
{
// 1. Calculate barycentric coordinates for the hit point on the triangle
vec3 barycentrics = vec3(1.0 - hitAttribs.x - hitAttribs.y, hitAttribs.x, hitAttribs.y);

// 2. Derive surface normal (For demo purposes: using flat normal from triangle hit)
// In a production setup, you would fetch vertex normals using gl_PrimitiveID / SSBOs
vec3 objectNormal = vec3(0.0, 1.0, 0.0);
vec3 worldNormal = normalize(gl_ObjectToWorldEXT * vec4(objectNormal, 0.0));

// 3. Define a simple directional light source
vec3 lightDir = normalize(vec3(0.5, 1.0, 0.3));
vec3 baseColor = vec3(0.8, 0.2, 0.2); // Red material

// 4. Compute diffuse lighting (N dot L)
float diffuse = max(dot(worldNormal, lightDir), 0.1); // 0.1 ambient floor

// 5. Output shaded color to payload
hitColor = baseColor * diffuse;
}
"""

def simple_callable_shader():
return """#version 460
#extension GL_EXT_ray_tracing : enable

layout(location = 0) callableDataInEXT struct Params {
float foo;
} params;

void main()
{
params.foo *= 2;
}
"""

def simple_mesh_shader():
return """#version 460
#extension GL_EXT_mesh_shader : enable

layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

// Output configuration: primitive type, max vertex count, max primitive count
layout(triangles, max_vertices = 3, max_primitives = 1) out;

// Custom per-vertex outputs to the fragment shader
layout(location = 0) out vec3 outColor[];

void main()
{
// Set total vertices and primitives emitted by this meshlet thread group
SetMeshOutputsEXT(3, 1);

// 1. Define Vertex Positions (in Normalized Device Coordinates [-1, 1])
gl_MeshVerticesEXT[0].gl_Position = vec4(-0.5, -0.5, 0.0, 1.0);
gl_MeshVerticesEXT[1].gl_Position = vec4( 0.5, -0.5, 0.0, 1.0);
gl_MeshVerticesEXT[2].gl_Position = vec4( 0.0, 0.5, 0.0, 1.0);

// 2. Pass Per-Vertex Attributes
outColor[0] = vec3(1.0, 0.0, 0.0); // Red
outColor[1] = vec3(0.0, 1.0, 0.0); // Green
outColor[2] = vec3(0.0, 0.0, 1.0); // Blue

// 3. Define Primitive Index Topologies (1 triangle using 3 vertex indices)
gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0, 1, 2);
}
"""

def simple_task_shader():
return """#version 460
#extension GL_EXT_mesh_shader : enable

// Define workgroup dimensions for task shader evaluation
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

// Payload structure passed from Task Shader to Mesh Shader
struct TaskPayload {
uint meshletID;
};
taskPayloadSharedEXT TaskPayload payload;

void main()
{
// Local meshlet index to test
uint meshletIndex = gl_GlobalInvocationID.x;

// 1. Perform Culling Test (Frustum, Occlusion, or Backface)
bool isVisible = true; // Placeholder for actual culling logic

if (isVisible)
{
// 2. Populate payload data shared with spawned Mesh Shaders
payload.meshletID = meshletIndex;

// 3. Emit task count: Launch 1 Mesh Shader workgroup (X=1, Y=1, Z=1)
EmitMeshTasksEXT(1, 1, 1);
}
else
{
// Cull completely: Do not spawn any Mesh Shader workgroups
EmitMeshTasksEXT(0, 0, 0);
}
}
"""


@inside_glslc_testsuite('OptionShaderStage')
class TestShaderStageWithGlslExtension(expect.ValidObjectFile):
Expand Down Expand Up @@ -104,8 +364,8 @@ class TestShaderStageWithNoExtension(expect.ValidObjectFile):


@inside_glslc_testsuite('OptionShaderStage')
class TestAllShaderStages(expect.ValidObjectFile):
"""Tests all possible -fshader-stage values."""
class TestAllShaderStagesForSpv1p0(expect.ValidObjectFile):
"""Tests all possible -fshader-stage values for SPIR-V 1.0."""

shader1 = FileShader(simple_vertex_shader(), '.glsl')
shader2 = FileShader(simple_fragment_shader(), '.glsl')
Expand All @@ -123,6 +383,53 @@ class TestAllShaderStages(expect.ValidObjectFile):
'-fshader-stage=compute', shader6]


@inside_glslc_testsuite('OptionShaderStage')
class TestAllShaderStagesForSpv1p4(expect.ValidObjectFile1_4):
"""Tests all possible -fshader-stage values that only target at least SPIR-V 1.4"""

raygen = FileShader(simple_raygen_shader(), '.glsl')
intersection = FileShader(simple_intersection_shader(), '.glsl')
anyhit = FileShader(simple_anyhit_shader(), '.glsl')
miss = FileShader(simple_miss_shader(), '.glsl')
closest = FileShader(simple_closest_shader(), '.glsl')
miss = FileShader(simple_miss_shader(), '.glsl')
callable_ = FileShader(simple_callable_shader(), '.glsl')
mesh = FileShader(simple_mesh_shader(), '.glsl')
task = FileShader(simple_task_shader(), '.glsl')
glslc_args = [
'-c',
'--target-spv=spv1.4',
'-fshader-stage=raygen', raygen,
'-fshader-stage=intersect', intersection,
'-fshader-stage=anyhit', anyhit,
'-fshader-stage=closest', closest,
'-fshader-stage=miss', miss,
'-fshader-stage=callable', callable_,
'-fshader-stage=mesh', mesh,
'-fshader-stage=task', task]


@inside_glslc_testsuite('OptionShaderStage')
class TestAllShaderStagesForSpv1p4ShortNames(expect.ValidObjectFile1_4):
"""Tests all possible -fshader-stage values that only target at least SPIR-V 1.4"""

raygen = FileShader(simple_raygen_shader(), '.glsl')
intersection = FileShader(simple_intersection_shader(), '.glsl')
anyhit = FileShader(simple_anyhit_shader(), '.glsl')
miss = FileShader(simple_miss_shader(), '.glsl')
closest = FileShader(simple_closest_shader(), '.glsl')
callable_ = FileShader(simple_callable_shader(), '.glsl')
glslc_args = [
'-c',
'--target-spv=spv1.4',
'-fshader-stage=rgen', raygen,
'-fshader-stage=rint', intersection,
'-fshader-stage=rahit', anyhit,
'-fshader-stage=rchit', closest,
'-fshader-stage=rmiss', miss,
'-fshader-stage=rcall', callable_]


@inside_glslc_testsuite('OptionShaderStage')
class TestShaderStageOverwriteFileExtension(expect.ValidObjectFile):
"""Tests -fshader-stage has precedence over file extension."""
Expand Down
4 changes: 3 additions & 1 deletion glslc/test/parameter_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,9 @@ class HelpParameters(
-fshader-stage=<stage>
Treat subsequent input files as having stage <stage>.
Valid stages are vertex, vert, fragment, frag, tesscontrol,
tesc, tesseval, tese, geometry, geom, compute, and comp.
tesc, tesseval, tese, geometry, geom, compute, comp, raygen,
rgen, anyhit, rahit, closest, rchit, miss, rmiss, intersect,
rint, callable, rcall, task, and mesh.
-g Generate source-level debug information.
-h Display available options.
--help Display available options.
Expand Down
Loading