1use 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
15pub fn generate(program: &TypedProgram) -> KoreResult<String> {
17 let mut output = String::new();
18
19 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 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 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 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 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 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 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 output.push_str(&format!("{}// Early-out for bounds check\n", ctx.indent()));
126
127 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 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(¶m.ty);
142 output.push_str(&format!(" {} {} : TEXCOORD{};\n", usf_type, param.name, i));
143 }
144 output.push_str("};\n\n");
145
146 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 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 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(¶m.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 output.push_str(&format!("{}{} {} = {};\n", ctx.indent(), expr_type, name, expr_code));
225 ctx.vars.insert(name.clone(), name.clone());
226 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 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 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 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 },
293 }
294
295 Ok(output)
296}
297
298fn 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 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 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 "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 "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 "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 "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 "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 "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 "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 "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" | "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 "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 _ => {
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}