- Introduction
- Conversion Architecture
- WGSL ↔ GLSL Conversion
- WGSL ↔ HLSL Conversion
- GLSL ↔ HLSL Conversion
- ISF Conversion
- AST-Based Conversion
- Syntax Mapping
- Type System Conversion
- Built-in Function Mapping
- Resource Binding Conversion
- Semantic Mapping
- Conversion Validation
- Error Handling
- Performance Considerations
- Extensibility
The Shader Conversion Framework is a comprehensive system designed to convert shaders between different shading languages while preserving functionality and optimizing for target platform capabilities. This framework enables seamless interoperability between WGSL, GLSL, HLSL, and ISF shaders.
The framework addresses several key challenges:
- Syntax differences between shading languages
- Semantic variations in variable declarations and usage
- Different resource binding models
- Platform-specific optimizations
- Error handling and validation
The conversion framework follows a modular architecture with the following components:
- Parser Layer: Tokenizes and parses source shaders
- AST Generator: Creates abstract syntax trees
- Transformation Engine: Applies language-specific transformations
- Code Generator: Produces target language output
- Validation System: Ensures correctness of converted shaders
- Optimization Passes: Improves performance of converted shaders
graph TD
A[Source Shader] --> B[Lexer]
B --> C[Parser]
C --> D[AST Generator]
D --> E[Semantic Analysis]
E --> F[Transformation Engine]
F --> G[Target AST]
G --> H[Code Generator]
H --> I[Target Shader]
I --> J[Validation]
J --> K[Optimized Shader]
- Lexical Analysis: Tokenize source shader
- Syntactic Analysis: Parse tokens into AST
- Semantic Analysis: Validate source semantics
- Transformation: Apply language mappings
- Code Generation: Produce target code
- Validation: Verify correctness
- Optimization: Apply platform-specific optimizations
// WGSL
@group(0) @binding(0) var<uniform> time: f32;
@group(0) @binding(1) var myTexture: texture_2d<f32>;
var<private> counter: i32 = 0;// GLSL Equivalent
layout(binding = 0) uniform float time;
layout(binding = 1) uniform sampler2D myTexture;
int counter = 0;// WGSL
fn calculateLighting(normal: vec3<f32>, lightDir: vec3<f32>) -> f32 {
return max(dot(normalize(normal), normalize(lightDir)), 0.0);
}// GLSL Equivalent
float calculateLighting(vec3 normal, vec3 lightDir) {
return max(dot(normalize(normal), normalize(lightDir)), 0.0);
}// WGSL
@builtin(position) position: vec4<f32>;
@builtin(vertex_index) vertexIndex: u32;// GLSL Equivalent
gl_Position; // Built-in output
gl_VertexID; // Built-in input// GLSL
layout(std140) uniform CameraBlock {
mat4 viewProjection;
vec3 cameraPosition;
} camera;// WGSL Equivalent
struct CameraBlock {
viewProjection: mat4x4<f32>,
cameraPosition: vec3<f32>,
}
@group(0) @binding(0) var<uniform> camera: CameraBlock;// GLSL
vec4 color = texture(myTexture, texCoord);// WGSL Equivalent
let color = textureSample(myTexture, mySampler, texCoord);// WGSL
@group(0) @binding(0) var<uniform> cameraBuffer: CameraBuffer;
@group(0) @binding(1) var diffuseTexture: texture_2d<f32>;
@group(0) @binding(2) var linearSampler: sampler;// HLSL Equivalent
cbuffer CameraBuffer : register(b0) {
// Buffer contents
}
Texture2D<float4> diffuseTexture : register(t0);
SamplerState linearSampler : register(s0);// WGSL
let position: vec3<f32> = vec3(1.0, 2.0, 3.0);
let matrix: mat4x4<f32> = mat4x4(1.0);// HLSL Equivalent
float3 position = float3(1.0, 2.0, 3.0);
float4x4 matrix = float4x4(1.0);// HLSL
cbuffer LightingConstants : register(b1) {
float3 lightDirection;
float lightIntensity;
}// WGSL Equivalent
struct LightingConstants {
lightDirection: vec3<f32>,
lightIntensity: f32,
}
@group(0) @binding(1) var<uniform> lightingConstants: LightingConstants;// HLSL
Texture2D<float4> diffuseTexture : register(t0);
SamplerState linearSampler : register(s0);// WGSL Equivalent
@group(0) @binding(0) var diffuseTexture: texture_2d<f32>;
@group(0) @binding(1) var linearSampler: sampler;// GLSL Vertex Shader
#version 330 core
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
out vec3 fragNormal;
void main() {
gl_Position = vec4(position, 1.0);
fragNormal = normal;
}// HLSL Vertex Shader
struct VSInput {
float3 position : POSITION;
float3 normal : NORMAL;
};
struct VSOutput {
float4 position : SV_POSITION;
float3 normal : NORMAL;
};
VSOutput VSMain(VSInput input) {
VSOutput output;
output.position = float4(input.position, 1.0);
output.normal = input.normal;
return output;
}// GLSL
uniform mat4 modelViewProjection;
uniform sampler2D diffuseTexture;// HLSL Equivalent
cbuffer TransformBuffer : register(b0) {
float4x4 modelViewProjection;
}
Texture2D<float4> diffuseTexture : register(t0);
SamplerState linearSampler : register(s0);// HLSL
StructuredBuffer<float4> particleBuffer : register(t0);
RWStructuredBuffer<float4> rwParticleBuffer : register(u0);// GLSL Equivalent
layout(binding = 0) buffer ParticleBuffer {
uint particleCount;
vec4 particles[];
};
layout(binding = 1, rgba32f) uniform restrict writeonly imageBuffer outputBuffer;// HLSL Compute Shader
[numthreads(256, 1, 1)]
void CSMain(uint3 dispatchThreadID : SV_DispatchThreadID) {
// Compute logic
}// GLSL Compute Shader
#version 430
layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in;
void main() {
uvec3 dispatchThreadID = gl_GlobalInvocationID;
// Compute logic
}// ISF JSON Metadata
{
"CATEGORIES": ["Stylize"],
"DESCRIPTION": "Simple color tint effect",
"INPUTS": [
{
"NAME": "inputImage",
"TYPE": "image"
},
{
"NAME": "tintColor",
"TYPE": "color",
"DEFAULT": [1.0, 0.0, 0.0, 1.0]
}
]
}// Converted GLSL
uniform sampler2D inputImage;
uniform vec4 tintColor;
varying vec2 isf_FragNormCoord;
void main() {
vec4 color = texture2D(inputImage, isf_FragNormCoord);
gl_FragColor = color * tintColor;
}// ISF GLSL Special Variables
vec2 isf_FragNormCoord;
float TIME;
vec2 RENDERSIZE;// WGSL Equivalent
@group(0) @binding(0) var<uniform> isfUniforms: ISFUniforms;
struct ISFUniforms {
fragNormCoord: vec2<f32>,
time: f32,
renderSize: vec2<f32>,
}The conversion framework uses ASTs to represent shader code:
// Simplified AST node structure
enum ShaderNode {
VariableDeclaration {
name: String,
type_spec: TypeSpec,
qualifiers: Vec<Qualifier>,
initializer: Option<Box<ShaderNode>>,
},
FunctionDefinition {
name: String,
parameters: Vec<Parameter>,
return_type: TypeSpec,
body: Box<ShaderNode>,
},
Expression {
kind: ExpressionKind,
operands: Vec<ShaderNode>,
},
Statement {
kind: StatementKind,
children: Vec<ShaderNode>,
},
}// Example transformation rule
fn transform_texture_sample(node: &ShaderNode) -> ShaderNode {
match node {
ShaderNode::Expression { kind: ExpressionKind::FunctionCall, operands } => {
if operands[0].as_identifier() == "texture" {
// Transform GLSL texture() to WGSL textureSample()
return ShaderNode::Expression {
kind: ExpressionKind::FunctionCall,
operands: vec![
ShaderNode::Identifier("textureSample".to_string()),
operands[1].clone(), // texture
operands[2].clone(), // sampler
operands[3].clone(), // coordinates
],
};
}
}
_ => node.clone(),
}
}| Feature | WGSL | GLSL | HLSL |
|---|---|---|---|
| Variable Declaration | var name: type |
type name |
type name |
| Function Definition | fn name() -> type |
type name() |
type name() |
| Vector Creation | vec3(1.0, 2.0, 3.0) |
vec3(1.0, 2.0, 3.0) |
float3(1.0, 2.0, 3.0) |
| Matrix Creation | mat4x4(1.0) |
mat4(1.0) |
float4x4(1.0) |
| Texture Sampling | textureSample(t, s, uv) |
texture(t, uv) |
t.Sample(s, uv) |
# WGSL to GLSL keyword mapping
keywords:
fn: ""
var: ""
let: ""
const: ""
vec2: vec2
vec3: vec3
vec4: vec4
mat2x2: mat2
mat3x3: mat3
mat4x4: mat4
texture_2d: sampler2D
texture_cube: samplerCube
sampler: ""| WGSL | GLSL | HLSL |
|---|---|---|
| f32 | float | float |
| f16 | N/A | half/min16float |
| i32 | int | int |
| u32 | uint | uint |
| bool | bool | bool |
| WGSL | GLSL | HLSL |
|---|---|---|
| vec2 | vec2 | float2 |
| vec3 | ivec3 | int3 |
| vec4 | uvec4 | uint4 |
| vec2 | bvec2 | bool2 |
| WGSL | GLSL | HLSL |
|---|---|---|
| mat2x2 | mat2 | float2x2 |
| mat3x3 | mat3 | float3x3 |
| mat4x4 | mat4 | float4x4 |
| WGSL | GLSL | HLSL |
|---|---|---|
| abs(x) | abs(x) | abs(x) |
| ceil(x) | ceil(x) | ceil(x) |
| floor(x) | floor(x) | floor(x) |
| round(x) | round(x) | round(x) |
| sin(x) | sin(x) | sin(x) |
| cos(x) | cos(x) | cos(x) |
| tan(x) | tan(x) | tan(x) |
| pow(x, y) | pow(x, y) | pow(x, y) |
| exp(x) | exp(x) | exp(x) |
| log(x) | log(x) | log(x) |
| WGSL | GLSL | HLSL |
|---|---|---|
| length(v) | length(v) | length(v) |
| distance(a, b) | distance(a, b) | distance(a, b) |
| dot(a, b) | dot(a, b) | dot(a, b) |
| cross(a, b) | cross(a, b) | cross(a, b) |
| normalize(v) | normalize(v) | normalize(v) |
| reflect(i, n) | reflect(i, n) | reflect(i, n) |
| refract(i, n, eta) | refract(i, n, eta) | refract(i, n, eta) |
| WGSL | GLSL | HLSL |
|---|---|---|
| textureSample(t, s, uv) | texture(t, uv) | t.Sample(s, uv) |
| textureSampleLevel(t, s, uv, l) | textureLod(t, uv, l) | t.SampleLevel(s, uv, l) |
| textureSampleGrad(t, s, uv, dx, dy) | textureGrad(t, uv, dx, dy) | t.SampleGrad(s, uv, dx, dy) |
graph TD
A[WGSL @group/@binding] --> B[GLSL layout(binding=N)]
A --> C[HLSL register(spaceN, register(M))]
D[ISF JSON Metadata] --> E[GLSL Uniform/Sampler]
D --> F[HLSL cbuffer/Texture]
// WGSL
@group(0) @binding(0) var<uniform> camera: CameraBuffer;
@group(0) @binding(1) var texture: texture_2d<f32>;
@group(0) @binding(2) var sampler: sampler;// GLSL Equivalent
layout(binding = 0) uniform CameraBuffer { /* ... */ } camera;
layout(binding = 1) uniform sampler2D texture;// HLSL Equivalent
cbuffer CameraBuffer : register(b0) { /* ... */ }
Texture2D<float4> texture : register(t0);
SamplerState sampler : register(s0);| WGSL | GLSL | HLSL |
|---|---|---|
| @location(N) position | layout(location = N) in vec3 position | float3 position : POSITIONN |
| @builtin(position) | gl_Position | SV_POSITION |
| @builtin(vertex_index) | gl_VertexID | SV_VertexID |
| WGSL | GLSL | HLSL |
|---|---|---|
| @location(N) color | layout(location = N) out vec4 color | float4 color : SV_TARGETN |
| @builtin(position) | gl_FragCoord | SV_POSITION |
| @builtin(front_facing) | gl_FrontFacing | SV_IsFrontFace |
The conversion framework validates semantic correctness:
// Example validation rules
fn validate_conversion(source_ast: &Ast, target_ast: &Ast) -> Result<(), ValidationError> {
// Check variable usage
validate_variable_scoping(source_ast, target_ast)?;
// Check function signatures
validate_function_signatures(source_ast, target_ast)?;
// Check resource bindings
validate_resource_bindings(source_ast, target_ast)?;
// Check built-in variable usage
validate_builtin_variables(source_ast, target_ast)?;
Ok(())
}// Type compatibility validation
fn check_type_compatibility(source_type: &Type, target_type: &Type) -> bool {
match (source_type, target_type) {
(Type::Vector { size: s1, .. }, Type::Vector { size: s2, .. }) => s1 == s2,
(Type::Matrix { rows: r1, cols: c1, .. }, Type::Matrix { rows: r2, cols: c2, .. }) => {
r1 == r2 && c1 == c2
}
_ => source_type == target_type,
}
}The framework handles various conversion errors:
#[derive(Debug)]
enum ConversionError {
SyntaxError { message: String, location: SourceLocation },
SemanticError { message: String, node: AstNode },
TypeError { expected: Type, actual: Type },
UnsupportedFeature { feature: String },
ResourceBindingError { message: String },
ValidationFailed { errors: Vec<ValidationError> },
}
impl Display for ConversionError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
ConversionError::SyntaxError { message, location } => {
write!(f, "Syntax error at {}: {}", location, message)
}
ConversionError::SemanticError { message, .. } => {
write!(f, "Semantic error: {}", message)
}
// ... other variants
}
}
}// Error recovery mechanisms
fn recover_from_error(error: &ConversionError, ast: &mut Ast) -> RecoveryAction {
match error {
ConversionError::UnsupportedFeature { feature } => {
// Comment out unsupported features
RecoveryAction::CommentOutFeature(feature.clone())
}
ConversionError::TypeError { .. } => {
// Insert type casts
RecoveryAction::InsertTypeCast
}
_ => RecoveryAction::Abort,
}
}The conversion framework applies optimization passes:
// Optimization pipeline
fn optimize_shader(ast: &mut Ast) -> Result<(), OptimizationError> {
// Dead code elimination
eliminate_dead_code(ast)?;
// Constant folding
fold_constants(ast)?;
// Common subexpression elimination
eliminate_common_subexpressions(ast)?;
// Loop optimization
optimize_loops(ast)?;
// Platform-specific optimizations
apply_platform_optimizations(ast)?;
Ok(())
}// Platform-specific optimization strategies
trait PlatformOptimizer {
fn optimize_texture_access(&self, ast: &mut Ast);
fn optimize_branching(&self, ast: &mut Ast);
fn optimize_memory_access(&self, ast: &mut Ast);
}
struct MobileOptimizer;
struct DesktopOptimizer;
struct WebGPUOptimizer;The framework supports extensibility through plugins:
// Plugin trait
trait ConversionPlugin {
fn name(&self) -> &str;
fn supported_conversions(&self) -> &[ConversionPair];
fn transform(&self, ast: &mut Ast) -> Result<(), PluginError>;
fn validate(&self, ast: &Ast) -> Result<(), ValidationError>;
}
// Example plugin registration
fn register_plugins(converter: &mut ShaderConverter) {
converter.register_plugin(Box::new(TextureCompressionPlugin));
converter.register_plugin(Box::new(HalfPrecisionPlugin));
converter.register_plugin(Box::new(MobileOptimizationPlugin));
}// Custom rule definition
struct CustomConversionRule {
source_pattern: Pattern,
target_template: Template,
conditions: Vec<Condition>,
priority: u32,
}
// Rule application
fn apply_custom_rules(ast: &mut Ast, rules: &[CustomConversionRule]) {
for rule in rules.iter().sorted_by_key(|r| r.priority) {
if rule.conditions.iter().all(|c| c.evaluate(ast)) {
rule.apply(ast);
}
}
}End of Shader Conversion Framework
Next steps: - Application Usage Guide - WGSL Shader Studio Architecture - Advanced Features Documentation