Skip to main content

kore/codegen/
usf.rs

1//! USF Code Generation - KORE to Unreal Shader Format
2//! Generates UE5-compatible .usf/.ush files for compute and pixel shaders
3//! 
4//! Key UE5 patterns supported:
5//! - #include "/Engine/Public/Platform.ush" headers
6//! - RWTexture2D UAV bindings for compute outputs
7//! - [numthreads] compute dispatch
8//! - SHADER_PARAMETER_STRUCT compatible parameter layout
9
10use crate::types::{TypedProgram, TypedItem, TypedShader};
11use crate::error::{KoreResult, KoreError};
12use crate::ast::{Type, ShaderStage, Expr, Stmt, Block, BinaryOp, Pattern};
13use std::collections::HashMap;
14
15/// Generate USF code from typed KORE program
16pub fn generate(program: &TypedProgram) -> KoreResult<String> {
17    let mut output = String::new();
18    
19    // UE5 header
20    output.push_str("// Generated by KORE Compiler - Unreal Shader Format\n");
21    output.push_str("// Compatible with UE5 SHADER_PARAMETER_STRUCT bindings\n\n");
22    output.push_str("#include \"/Engine/Public/Platform.ush\"\n\n");
23    
24    for item in &program.items {
25        if let TypedItem::Shader(shader) = item {
26            output.push_str(&emit_shader(shader)?);
27        }
28    }
29    
30    Ok(output)
31}
32
33struct USFContext {
34    vars: HashMap<String, String>,
35    indent_level: usize,
36    uniform_bindings: Vec<(String, String, u32)>,
37}
38
39impl USFContext {
40    fn new() -> Self {
41        Self {
42            vars: HashMap::new(),
43            indent_level: 0,
44            uniform_bindings: Vec::new(),
45        }
46    }
47    
48    fn indent(&self) -> String {
49        "    ".repeat(self.indent_level)
50    }
51    
52    fn push_indent(&mut self) {
53        self.indent_level += 1;
54    }
55    
56    fn pop_indent(&mut self) {
57        if self.indent_level > 0 {
58            self.indent_level -= 1;
59        }
60    }
61}
62
63fn emit_shader(shader: &TypedShader) -> KoreResult<String> {
64    let mut output = String::new();
65    let mut ctx = USFContext::new();
66    
67    // Collect uniforms 
68    for uniform in &shader.ast.uniforms {
69        let usf_type = map_type_to_usf(&uniform.ty);
70        ctx.uniform_bindings.push((uniform.name.clone(), usf_type, uniform.binding));
71    }
72    
73    // Categorize uniform types
74    let mut texture_inputs = Vec::new();
75    let mut uav_outputs = Vec::new();
76    let mut scalar_params = Vec::new();
77    
78    for (name, ty, binding) in &ctx.uniform_bindings {
79        if ty.starts_with("RWTexture") || ty.starts_with("RWBuffer") || ty.starts_with("RWStructuredBuffer") {
80            uav_outputs.push((name.clone(), ty.clone(), *binding));
81        } else if ty.contains("Texture") {
82            texture_inputs.push((name.clone(), ty.clone(), *binding));
83        } else {
84            scalar_params.push((name.clone(), ty.clone(), *binding));
85        }
86    }
87    
88    // Emit scalar parameters (C++ bindable via SHADER_PARAMETER)
89    if !scalar_params.is_empty() {
90        output.push_str("// Scalar Parameters - Bind via SHADER_PARAMETER_STRUCT\n");
91        for (name, ty, _) in &scalar_params {
92            output.push_str(&format!("{} {};\n", ty, name));
93        }
94        output.push_str("\n");
95    }
96    
97    // Emit texture inputs with samplers
98    if !texture_inputs.is_empty() {
99        output.push_str("// Texture Inputs\n");
100        for (name, ty, binding) in &texture_inputs {
101            output.push_str(&format!("{} {} : register(t{});\n", ty, name, binding));
102            output.push_str(&format!("SamplerState {}Sampler : register(s{});\n", name, binding));
103        }
104        output.push_str("\n");
105    }
106    
107    // Emit UAV outputs
108    if !uav_outputs.is_empty() {
109        output.push_str("// UAV Outputs\n");
110        for (name, ty, binding) in &uav_outputs {
111            output.push_str(&format!("{} {} : register(u{});\n", ty, name, binding));
112        }
113        output.push_str("\n");
114    }
115    
116    match shader.ast.stage {
117        ShaderStage::Compute => {
118            // Compute shader with UE5 conventions
119            output.push_str("[numthreads(8, 8, 1)]\n");
120            output.push_str(&format!("void {}CS(uint3 ThreadId : SV_DispatchThreadID)\n{{\n", 
121                capitalize_first(&shader.ast.name)));
122            ctx.push_indent();
123            
124            // UE5 convention: early-out for out-of-bounds threads
125            output.push_str(&format!("{}// Early-out for bounds check\n", ctx.indent()));
126            
127            // Add compute builtins to context
128            ctx.vars.insert("thread_id".to_string(), "ThreadId".to_string());
129            ctx.vars.insert("dispatch_thread_id".to_string(), "ThreadId".to_string());
130            
131            let body_code = emit_block(&mut ctx, &shader.ast.body)?;
132            output.push_str(&body_code);
133            
134            ctx.pop_indent();
135            output.push_str("}\n");
136        },
137        ShaderStage::Fragment => {
138            // Pixel shader for UE5
139            output.push_str("struct FPSInput\n{\n");
140            for (i, param) in shader.ast.inputs.iter().enumerate() {
141                let usf_type = map_type_to_usf(&param.ty);
142                output.push_str(&format!("    {} {} : TEXCOORD{};\n", usf_type, param.name, i));
143            }
144            output.push_str("};\n\n");
145            
146            // Output struct
147            let out_type = map_type_to_usf(&shader.ast.outputs);
148            output.push_str("struct FPSOutput\n{\n");
149            output.push_str(&format!("    {} Color : SV_Target0;\n", out_type));
150            output.push_str("};\n\n");
151            
152            // Main function
153            output.push_str(&format!("FPSOutput {}PS(FPSInput Input)\n{{\n", 
154                capitalize_first(&shader.ast.name)));
155            ctx.push_indent();
156            
157            for param in &shader.ast.inputs {
158                ctx.vars.insert(param.name.clone(), format!("Input.{}", param.name));
159            }
160            
161            let body_code = emit_block(&mut ctx, &shader.ast.body)?;
162            output.push_str(&body_code);
163            
164            ctx.pop_indent();
165            output.push_str("}\n");
166        },
167        ShaderStage::Vertex => {
168            // Vertex shader
169            output.push_str("struct FVSInput\n{\n");
170            for (i, param) in shader.ast.inputs.iter().enumerate() {
171                let usf_type = map_type_to_usf(&param.ty);
172                let semantic = match param.name.as_str() {
173                    "position" => "POSITION",
174                    "normal" => "NORMAL",
175                    "tangent" => "TANGENT",
176                    "color" => "COLOR",
177                    _ => "TEXCOORD",
178                };
179                let idx = if semantic == "TEXCOORD" { i.to_string() } else { String::new() };
180                output.push_str(&format!("    {} {} : {}{};\n", usf_type, param.name, semantic, idx));
181            }
182            output.push_str("};\n\n");
183            
184            output.push_str("struct FVSOutput\n{\n");
185            output.push_str("    float4 Position : SV_Position;\n");
186            output.push_str("};\n\n");
187            
188            output.push_str(&format!("FVSOutput {}VS(FVSInput Input)\n{{\n", 
189                capitalize_first(&shader.ast.name)));
190            ctx.push_indent();
191            
192            for param in &shader.ast.inputs {
193                ctx.vars.insert(param.name.clone(), format!("Input.{}", param.name));
194            }
195            
196            let body_code = emit_block(&mut ctx, &shader.ast.body)?;
197            output.push_str(&body_code);
198            
199            ctx.pop_indent();
200            output.push_str("}\n");
201        },
202    }
203    
204    Ok(output)
205}
206
207fn emit_block(ctx: &mut USFContext, block: &Block) -> KoreResult<String> {
208    let mut output = String::new();
209    for stmt in &block.stmts {
210        output.push_str(&emit_stmt(ctx, stmt)?);
211    }
212    Ok(output)
213}
214
215fn emit_stmt(ctx: &mut USFContext, stmt: &Stmt) -> KoreResult<String> {
216    let mut output = String::new();
217    
218    match stmt {
219        Stmt::Let { pattern, value, .. } => {
220            if let Some(value) = value {
221                if let Pattern::Binding { name, mutable, .. } = pattern {
222                    let (expr_code, expr_type) = emit_expr(ctx, value)?;
223                    // Both let and var use same declaration in HLSL/USF
224                    output.push_str(&format!("{}{} {} = {};\n", ctx.indent(), expr_type, name, expr_code));
225                    ctx.vars.insert(name.clone(), name.clone());
226                    // Mark mutable vars so we know they can be reassigned
227                    if *mutable {
228                        ctx.vars.insert(format!("__mutable_{}", name), "true".to_string());
229                    }
230                }
231            }
232        },
233        Stmt::Return(Some(expr), _) => {
234            let (expr_code, _) = emit_expr(ctx, expr)?;
235            output.push_str(&format!("{}FPSOutput _Result;\n", ctx.indent()));
236            output.push_str(&format!("{}_Result.Color = {};\n", ctx.indent(), expr_code));
237            output.push_str(&format!("{}return _Result;\n", ctx.indent()));
238        },
239        Stmt::Return(None, _) => {
240            output.push_str(&format!("{}return;\n", ctx.indent()));
241        },
242        Stmt::Expr(expr) => {
243            // Handle if expressions as statements
244            if let Expr::If { condition, then_branch, else_branch, .. } = expr {
245                output.push_str(&emit_if_statement(ctx, condition, then_branch, else_branch)?);
246            } else {
247                let (expr_code, _) = emit_expr(ctx, expr)?;
248                // Skip empty expressions
249                if !expr_code.is_empty() {
250                    output.push_str(&format!("{}{};\n", ctx.indent(), expr_code));
251                }
252            }
253        },
254        Stmt::While { condition, body, .. } => {
255            let (cond_code, _) = emit_expr(ctx, condition)?;
256            output.push_str(&format!("{}while ({})\n", ctx.indent(), cond_code));
257            output.push_str(&format!("{}{{\n", ctx.indent()));
258            ctx.push_indent();
259            output.push_str(&emit_block(ctx, body)?);
260            ctx.pop_indent();
261            output.push_str(&format!("{}}}\n", ctx.indent()));
262        },
263        Stmt::For { binding, iter: _, body, .. } => {
264            if let Pattern::Binding { name, .. } = binding {
265                output.push_str(&format!("{}for (int {} = 0; {} < 10; {}++)\n", 
266                    ctx.indent(), name, name, name));
267                output.push_str(&format!("{}{{\n", ctx.indent()));
268                ctx.push_indent();
269                ctx.vars.insert(name.clone(), name.clone());
270                output.push_str(&emit_block(ctx, body)?);
271                ctx.pop_indent();
272                output.push_str(&format!("{}}}\n", ctx.indent()));
273            }
274        },
275        Stmt::Loop { body, .. } => {
276            // Infinite loop - use while(true)
277            output.push_str(&format!("{}while (true)\n", ctx.indent()));
278            output.push_str(&format!("{}{{\n", ctx.indent()));
279            ctx.push_indent();
280            output.push_str(&emit_block(ctx, body)?);
281            ctx.pop_indent();
282            output.push_str(&format!("{}}}\n", ctx.indent()));
283        },
284        Stmt::Break(_, _) => {
285            output.push_str(&format!("{}break;\n", ctx.indent()));
286        },
287        Stmt::Continue(_) => {
288            output.push_str(&format!("{}continue;\n", ctx.indent()));
289        },
290        Stmt::Item(_) => {
291            // Nested items not supported in shader body
292        },
293    }
294    
295    Ok(output)
296}
297
298// Helper to emit if statements with proper else-if chains
299fn emit_if_statement(ctx: &mut USFContext, condition: &Expr, then_branch: &Block, else_branch: &Option<Box<crate::ast::ElseBranch>>) -> KoreResult<String> {
300    let mut output = String::new();
301    let (cond_code, _) = emit_expr(ctx, condition)?;
302    
303    output.push_str(&format!("{}if ({})\n", ctx.indent(), cond_code));
304    output.push_str(&format!("{}{{\n", ctx.indent()));
305    ctx.push_indent();
306    output.push_str(&emit_block(ctx, then_branch)?);
307    ctx.pop_indent();
308    output.push_str(&format!("{}}}", ctx.indent()));
309    
310    if let Some(else_br) = else_branch {
311        match else_br.as_ref() {
312            crate::ast::ElseBranch::Else(block) => {
313                output.push_str("\n");
314                output.push_str(&format!("{}else\n", ctx.indent()));
315                output.push_str(&format!("{}{{\n", ctx.indent()));
316                ctx.push_indent();
317                output.push_str(&emit_block(ctx, block)?);
318                ctx.pop_indent();
319                output.push_str(&format!("{}}}\n", ctx.indent()));
320            },
321            crate::ast::ElseBranch::ElseIf(cond, block, next_else) => {
322                output.push_str("\n");
323                output.push_str(&emit_if_statement(ctx, cond, block, next_else)?);
324            },
325        }
326    } else {
327        output.push_str("\n");
328    }
329    
330    Ok(output)
331}
332
333fn emit_expr(ctx: &mut USFContext, expr: &Expr) -> KoreResult<(String, String)> {
334    match expr {
335        Expr::Ident(name, _) => {
336            if let Some(mapped) = ctx.vars.get(name) {
337                Ok((mapped.clone(), "float4".to_string()))
338            } else {
339                Ok((name.clone(), "float4".to_string()))
340            }
341        },
342        Expr::Float(f, _) => {
343            Ok((format!("{:.6}", f), "float".to_string()))
344        },
345        Expr::Int(i, _) => {
346            Ok((format!("{}", i), "int".to_string()))
347        },
348        Expr::Bool(b, _) => {
349            Ok((format!("{}", b), "bool".to_string()))
350        },
351        Expr::String(s, _) => {
352            Ok((format!("\"{}\"", s), "string".to_string()))
353        },
354        Expr::Binary { left, op, right, .. } => {
355            let (left_code, left_ty) = emit_expr(ctx, left)?;
356            let (right_code, _) = emit_expr(ctx, right)?;
357            
358            let op_str = match op {
359                BinaryOp::Add => "+",
360                BinaryOp::Sub => "-",
361                BinaryOp::Mul => "*",
362                BinaryOp::Div => "/",
363                BinaryOp::Mod => "%",
364                BinaryOp::Eq => "==",
365                BinaryOp::Ne => "!=",
366                BinaryOp::Lt => "<",
367                BinaryOp::Le => "<=",
368                BinaryOp::Gt => ">",
369                BinaryOp::Ge => ">=",
370                BinaryOp::And => "&&",
371                BinaryOp::Or => "||",
372                BinaryOp::BitAnd => "&",
373                BinaryOp::BitOr => "|",
374                BinaryOp::BitXor => "^",
375                BinaryOp::Shl => "<<",
376                BinaryOp::Shr => ">>",
377                _ => return Err(KoreError::codegen("Unsupported binary op in USF", expr.span())),
378            };
379            
380            let result_ty = match op {
381                BinaryOp::Eq | BinaryOp::Ne | BinaryOp::Lt | BinaryOp::Le | 
382                BinaryOp::Gt | BinaryOp::Ge | BinaryOp::And | BinaryOp::Or => "bool".to_string(),
383                _ => left_ty,
384            };
385            
386            Ok((format!("({} {} {})", left_code, op_str, right_code), result_ty))
387        },
388        Expr::Unary { op, operand, .. } => {
389            let (operand_code, ty) = emit_expr(ctx, operand)?;
390            let op_str = match op {
391                crate::ast::UnaryOp::Neg => "-",
392                crate::ast::UnaryOp::Not => "!",
393                crate::ast::UnaryOp::BitNot => "~",
394                crate::ast::UnaryOp::Ref | crate::ast::UnaryOp::RefMut => {
395                    return Ok((operand_code, ty));
396                },
397                crate::ast::UnaryOp::Deref => {
398                    return Ok((operand_code, ty));
399                },
400            };
401            Ok((format!("({}{})", op_str, operand_code), ty))
402        },
403        Expr::Call { callee, args, .. } => {
404            if let Expr::Ident(name, _) = &**callee {
405                emit_function_call(ctx, name, args)
406            } else {
407                Err(KoreError::codegen("Complex callee not supported in USF", expr.span()))
408            }
409        },
410        Expr::Field { object, field, .. } => {
411            let (obj_code, _) = emit_expr(ctx, object)?;
412            Ok((format!("{}.{}", obj_code, field), infer_swizzle_type(field)))
413        },
414        Expr::Index { object, index, .. } => {
415            let (obj_code, obj_ty) = emit_expr(ctx, object)?;
416            let (idx_code, _) = emit_expr(ctx, index)?;
417            let elem_ty = if obj_ty.starts_with("float") { "float".to_string() } else { obj_ty };
418            Ok((format!("{}[{}]", obj_code, idx_code), elem_ty))
419        },
420        Expr::Assign { target, value, .. } => {
421            let (target_code, _) = emit_expr(ctx, target)?;
422            let (value_code, ty) = emit_expr(ctx, value)?;
423            Ok((format!("{} = {}", target_code, value_code), ty))
424        },
425        Expr::If { condition, then_branch, else_branch, .. } => {
426            let (cond_code, _) = emit_expr(ctx, condition)?;
427            
428            // Try to emit as ternary if simple enough
429            if then_branch.stmts.len() == 1 && else_branch.is_some() {
430                if let Stmt::Expr(then_expr) = &then_branch.stmts[0] {
431                    let (then_code, then_ty) = emit_expr(ctx, then_expr)?;
432                    
433                    if let Some(crate::ast::ElseBranch::Else(else_block)) = else_branch.as_ref().map(|b| b.as_ref()) {
434                        if else_block.stmts.len() == 1 {
435                            if let Stmt::Expr(else_expr) = &else_block.stmts[0] {
436                                let (else_code, _) = emit_expr(ctx, else_expr)?;
437                                return Ok((format!("({} ? {} : {})", cond_code, then_code, else_code), then_ty));
438                            }
439                        }
440                    }
441                }
442            }
443            
444            // For complex if expressions used as values, we need a temp variable
445            // For now, return empty - these should be handled as statements
446            Ok(("".to_string(), "void".to_string()))
447        },
448        Expr::Paren(inner, _) => {
449            let (inner_code, ty) = emit_expr(ctx, inner)?;
450            Ok((format!("({})", inner_code), ty))
451        },
452        _ => Err(KoreError::codegen("Unsupported expression in USF", expr.span())),
453    }
454}
455
456fn emit_function_call(ctx: &mut USFContext, name: &str, args: &[crate::ast::CallArg]) -> KoreResult<(String, String)> {
457    match name {
458        // Vector constructors
459        "vec2" | "Vec2" => {
460            let mut arg_codes = Vec::new();
461            for arg in args {
462                let (code, _) = emit_expr(ctx, &arg.value)?;
463                arg_codes.push(code);
464            }
465            Ok((format!("float2({})", arg_codes.join(", ")), "float2".to_string()))
466        },
467        "vec3" | "Vec3" => {
468            let mut arg_codes = Vec::new();
469            for arg in args {
470                let (code, _) = emit_expr(ctx, &arg.value)?;
471                arg_codes.push(code);
472            }
473            Ok((format!("float3({})", arg_codes.join(", ")), "float3".to_string()))
474        },
475        "vec4" | "Vec4" => {
476            let mut arg_codes = Vec::new();
477            for arg in args {
478                let (code, _) = emit_expr(ctx, &arg.value)?;
479                arg_codes.push(code);
480            }
481            Ok((format!("float4({})", arg_codes.join(", ")), "float4".to_string()))
482        },
483        
484        // Math intrinsics - direct pass-through
485        "sin" | "cos" | "tan" | "asin" | "acos" | "atan" | 
486        "abs" | "floor" | "ceil" | "round" | "trunc" | "sqrt" | "rsqrt" |
487        "exp" | "exp2" | "log" | "log2" | "log10" | "sign" | "saturate" => {
488            let (arg_code, ty) = emit_expr(ctx, &args[0].value)?;
489            Ok((format!("{}({})", name, arg_code), ty))
490        },
491        "fract" => {
492            let (arg_code, ty) = emit_expr(ctx, &args[0].value)?;
493            Ok((format!("frac({})", arg_code), ty))
494        },
495        
496        // Two-arg math
497        "pow" | "min" | "max" | "fmod" | "step" | "atan2" | "distance" => {
498            let (arg1, ty) = emit_expr(ctx, &args[0].value)?;
499            let (arg2, _) = emit_expr(ctx, &args[1].value)?;
500            let ret_ty = if name == "distance" { "float".to_string() } else { ty };
501            Ok((format!("{}({}, {})", name, arg1, arg2), ret_ty))
502        },
503        
504        // Three-arg math
505        "clamp" | "smoothstep" | "mad" => {
506            let (arg1, ty) = emit_expr(ctx, &args[0].value)?;
507            let (arg2, _) = emit_expr(ctx, &args[1].value)?;
508            let (arg3, _) = emit_expr(ctx, &args[2].value)?;
509            Ok((format!("{}({}, {}, {})", name, arg1, arg2, arg3), ty))
510        },
511        "mix" | "lerp" => {
512            let (arg1, ty) = emit_expr(ctx, &args[0].value)?;
513            let (arg2, _) = emit_expr(ctx, &args[1].value)?;
514            let (arg3, _) = emit_expr(ctx, &args[2].value)?;
515            Ok((format!("lerp({}, {}, {})", arg1, arg2, arg3), ty))
516        },
517        
518        // Vector ops
519        "length" => {
520            let (arg_code, _) = emit_expr(ctx, &args[0].value)?;
521            Ok((format!("length({})", arg_code), "float".to_string()))
522        },
523        "normalize" => {
524            let (arg_code, ty) = emit_expr(ctx, &args[0].value)?;
525            Ok((format!("normalize({})", arg_code), ty))
526        },
527        "dot" => {
528            let (arg1, _) = emit_expr(ctx, &args[0].value)?;
529            let (arg2, _) = emit_expr(ctx, &args[1].value)?;
530            Ok((format!("dot({}, {})", arg1, arg2), "float".to_string()))
531        },
532        "cross" => {
533            let (arg1, ty) = emit_expr(ctx, &args[0].value)?;
534            let (arg2, _) = emit_expr(ctx, &args[1].value)?;
535            Ok((format!("cross({}, {})", arg1, arg2), ty))
536        },
537        "reflect" | "refract" => {
538            let (arg1, ty) = emit_expr(ctx, &args[0].value)?;
539            let (arg2, _) = emit_expr(ctx, &args[1].value)?;
540            if name == "refract" && args.len() > 2 {
541                let (arg3, _) = emit_expr(ctx, &args[2].value)?;
542                Ok((format!("refract({}, {}, {})", arg1, arg2, arg3), ty))
543            } else {
544                Ok((format!("{}({}, {})", name, arg1, arg2), ty))
545            }
546        },
547        
548        // Texture sampling - UE5 style with separate sampler
549        "sample" => {
550            let (texture, _) = emit_expr(ctx, &args[0].value)?;
551            let (coords, _) = emit_expr(ctx, &args[1].value)?;
552            Ok((format!("{}.Sample({}Sampler, {})", texture, texture, coords), "float4".to_string()))
553        },
554        "sample_lod" | "SampleLevel" => {
555            let (texture, _) = emit_expr(ctx, &args[0].value)?;
556            let (coords, _) = emit_expr(ctx, &args[1].value)?;
557            let (lod, _) = emit_expr(ctx, &args[2].value)?;
558            Ok((format!("{}.SampleLevel({}Sampler, {}, {})", texture, texture, coords, lod), "float4".to_string()))
559        },
560        "load" | "Load" => {
561            let (texture, _) = emit_expr(ctx, &args[0].value)?;
562            let (location, _) = emit_expr(ctx, &args[1].value)?;
563            Ok((format!("{}.Load(int3({}, 0))", texture, location), "float4".to_string()))
564        },
565        
566        // UAV store operations (compute shaders)
567        "store" => {
568            let (uav, _) = emit_expr(ctx, &args[0].value)?;
569            let (coord, _) = emit_expr(ctx, &args[1].value)?;
570            let (value, _) = emit_expr(ctx, &args[2].value)?;
571            Ok((format!("{}[{}] = {}", uav, coord, value), "void".to_string()))
572        },
573        
574        // Atomics for compute
575        "atomic_add" => {
576            let (uav, _) = emit_expr(ctx, &args[0].value)?;
577            let (value, _) = emit_expr(ctx, &args[1].value)?;
578            Ok((format!("InterlockedAdd({}, {})", uav, value), "void".to_string()))
579        },
580        "atomic_min" | "atomic_max" => {
581            let hlsl_name = if name == "atomic_min" { "InterlockedMin" } else { "InterlockedMax" };
582            let (uav, _) = emit_expr(ctx, &args[0].value)?;
583            let (value, _) = emit_expr(ctx, &args[1].value)?;
584            Ok((format!("{}({}, {})", hlsl_name, uav, value), "void".to_string()))
585        },
586        
587        // Barrier for compute
588        "barrier" | "GroupMemoryBarrierWithGroupSync" => {
589            Ok(("GroupMemoryBarrierWithGroupSync()".to_string(), "void".to_string()))
590        },
591        "device_barrier" | "DeviceMemoryBarrier" => {
592            Ok(("DeviceMemoryBarrier()".to_string(), "void".to_string()))
593        },
594        
595        // UE5 luminance helper
596        "GetLuminance" | "luminance" => {
597            let (arg_code, _) = emit_expr(ctx, &args[0].value)?;
598            Ok((format!("dot({}, float3(0.299, 0.587, 0.114))", arg_code), "float".to_string()))
599        },
600        
601        // Fallback for unknown functions
602        _ => {
603            let mut arg_codes = Vec::new();
604            for arg in args {
605                let (code, _) = emit_expr(ctx, &arg.value)?;
606                arg_codes.push(code);
607            }
608            Ok((format!("{}({})", name, arg_codes.join(", ")), "float4".to_string()))
609        },
610    }
611}
612
613fn map_type_to_usf(ty: &Type) -> String {
614    match ty {
615        Type::Named { name, .. } => match name.as_str() {
616            "Float" | "f32" => "float".to_string(),
617            "Double" | "f64" => "double".to_string(),
618            "Int" | "i32" => "int".to_string(),
619            "UInt" | "u32" => "uint".to_string(),
620            "Bool" => "bool".to_string(),
621            "Vec2" => "float2".to_string(),
622            "Vec3" => "float3".to_string(),
623            "Vec4" => "float4".to_string(),
624            "IVec2" => "int2".to_string(),
625            "IVec3" => "int3".to_string(),
626            "IVec4" => "int4".to_string(),
627            "UVec2" => "uint2".to_string(),
628            "UVec3" => "uint3".to_string(),
629            "UVec4" => "uint4".to_string(),
630            "Mat2" => "float2x2".to_string(),
631            "Mat3" => "float3x3".to_string(),
632            "Mat4" => "float4x4".to_string(),
633            "Sampler2D" => "Texture2D<float4>".to_string(),
634            "Sampler3D" => "Texture3D<float4>".to_string(),
635            "SamplerCube" => "TextureCube<float4>".to_string(),
636            "Image2D" => "RWTexture2D<float4>".to_string(),
637            "Image3D" => "RWTexture3D<float4>".to_string(),
638            "RWTexture2D" => "RWTexture2D<float4>".to_string(),
639            "RWTexture2D_Float" => "RWTexture2D<float>".to_string(),
640            "RWTexture2D_Float2" => "RWTexture2D<float2>".to_string(),
641            "RWTexture2D_Float3" => "RWTexture2D<float3>".to_string(),
642            "RWTexture2D_Int" => "RWTexture2D<int>".to_string(),
643            "RWTexture2D_UInt" => "RWTexture2D<uint>".to_string(),
644            "StructuredBuffer" => "StructuredBuffer<float4>".to_string(),
645            "RWStructuredBuffer" => "RWStructuredBuffer<float4>".to_string(),
646            _ => name.clone(),
647        },
648        _ => "float4".to_string(),
649    }
650}
651
652fn infer_swizzle_type(swizzle: &str) -> String {
653    match swizzle.len() {
654        1 => "float".to_string(),
655        2 => "float2".to_string(),
656        3 => "float3".to_string(),
657        4 => "float4".to_string(),
658        _ => "float".to_string(),
659    }
660}
661
662fn capitalize_first(s: &str) -> String {
663    let mut chars = s.chars();
664    match chars.next() {
665        None => String::new(),
666        Some(c) => c.to_uppercase().collect::<String>() + chars.as_str(),
667    }
668}