rlsl 0.1.1 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -0
  3. data/CHANGELOG.md +13 -2
  4. data/README.md +101 -26
  5. data/Rakefile +7 -0
  6. data/lib/rlsl/base_translator/call_parser.rb +79 -0
  7. data/lib/rlsl/base_translator/code_rewriter.rb +107 -0
  8. data/lib/rlsl/base_translator/code_scanner.rb +140 -0
  9. data/lib/rlsl/base_translator.rb +182 -64
  10. data/lib/rlsl/code_generator/math_prelude.rb +172 -0
  11. data/lib/rlsl/code_generator/ruby_wrapper_generator.rb +137 -0
  12. data/lib/rlsl/code_generator/shader_function_generator.rb +19 -0
  13. data/lib/rlsl/code_generator/template_context.rb +44 -0
  14. data/lib/rlsl/code_generator/uniform_struct_generator.rb +29 -0
  15. data/lib/rlsl/code_generator.rb +30 -202
  16. data/lib/rlsl/compiled_shader.rb +5 -13
  17. data/lib/rlsl/errors.rb +29 -0
  18. data/lib/rlsl/function_context.rb +31 -14
  19. data/lib/rlsl/glsl/translator.rb +35 -41
  20. data/lib/rlsl/msl/shader.rb +34 -46
  21. data/lib/rlsl/msl/translator.rb +38 -34
  22. data/lib/rlsl/msl/uniform_buffer_packer.rb +72 -0
  23. data/lib/rlsl/prism/ast_visitor/control_flow_visiting.rb +137 -0
  24. data/lib/rlsl/prism/ast_visitor/definition_visiting.rb +92 -0
  25. data/lib/rlsl/prism/ast_visitor/expression_visiting.rb +172 -0
  26. data/lib/rlsl/prism/ast_visitor/scope_context.rb +48 -0
  27. data/lib/rlsl/prism/ast_visitor/visitor_registry.rb +21 -0
  28. data/lib/rlsl/prism/ast_visitor.rb +100 -286
  29. data/lib/rlsl/prism/builtins/function_registry.rb +131 -0
  30. data/lib/rlsl/prism/builtins/operator_rules.rb +123 -0
  31. data/lib/rlsl/prism/builtins/swizzle_rules.rb +47 -0
  32. data/lib/rlsl/prism/builtins.rb +39 -148
  33. data/lib/rlsl/prism/compilation_unit.rb +7 -0
  34. data/lib/rlsl/prism/emitters/base_emitter/control_flow_emission.rb +126 -0
  35. data/lib/rlsl/prism/emitters/base_emitter/definition_emission.rb +134 -0
  36. data/lib/rlsl/prism/emitters/base_emitter/expression_emission.rb +108 -0
  37. data/lib/rlsl/prism/emitters/base_emitter/statement_emission.rb +95 -0
  38. data/lib/rlsl/prism/emitters/base_emitter.rb +120 -414
  39. data/lib/rlsl/prism/emitters/c_emitter.rb +165 -112
  40. data/lib/rlsl/prism/emitters/glsl_emitter.rb +63 -50
  41. data/lib/rlsl/prism/emitters/msl_emitter.rb +67 -52
  42. data/lib/rlsl/prism/emitters/target_emitter.rb +124 -0
  43. data/lib/rlsl/prism/emitters/target_profile.rb +34 -0
  44. data/lib/rlsl/prism/emitters/wgsl_emitter.rb +217 -58
  45. data/lib/rlsl/prism/errors.rb +9 -0
  46. data/lib/rlsl/prism/ir/control_flow.rb +85 -0
  47. data/lib/rlsl/prism/ir/definitions.rb +82 -0
  48. data/lib/rlsl/prism/ir/expressions.rb +201 -0
  49. data/lib/rlsl/prism/ir/node.rb +21 -0
  50. data/lib/rlsl/prism/ir/nodes.rb +4 -371
  51. data/lib/rlsl/prism/ir/traversal.rb +66 -0
  52. data/lib/rlsl/prism/mutation_analyzer.rb +30 -0
  53. data/lib/rlsl/prism/node_traversal.rb +41 -0
  54. data/lib/rlsl/prism/parameter_list.rb +46 -0
  55. data/lib/rlsl/prism/return_flow_validator.rb +73 -0
  56. data/lib/rlsl/prism/source_extractor/block_locator.rb +50 -0
  57. data/lib/rlsl/prism/source_extractor.rb +19 -137
  58. data/lib/rlsl/prism/source_unit/parser.rb +88 -0
  59. data/lib/rlsl/prism/source_unit.rb +42 -0
  60. data/lib/rlsl/prism/target_capability_validator.rb +110 -0
  61. data/lib/rlsl/prism/transpiler.rb +99 -59
  62. data/lib/rlsl/prism/type_inference/call_type_resolver.rb +59 -0
  63. data/lib/rlsl/prism/type_inference/call_validator.rb +75 -0
  64. data/lib/rlsl/prism/type_inference/collection_type_resolver.rb +106 -0
  65. data/lib/rlsl/prism/type_inference/control_flow_inferer.rb +147 -0
  66. data/lib/rlsl/prism/type_inference/definition_inferer.rb +56 -0
  67. data/lib/rlsl/prism/type_inference/expression_inferer.rb +96 -0
  68. data/lib/rlsl/prism/type_inference/field_type_resolver.rb +38 -0
  69. data/lib/rlsl/prism/type_inference/inferer_registry.rb +38 -0
  70. data/lib/rlsl/prism/type_inference/scope_stack.rb +47 -0
  71. data/lib/rlsl/prism/type_inference/type_environment.rb +112 -0
  72. data/lib/rlsl/prism/type_inference/type_shapes.rb +33 -0
  73. data/lib/rlsl/prism/type_inference.rb +120 -249
  74. data/lib/rlsl/runtime_shader.rb +47 -0
  75. data/lib/rlsl/shader_builder/build_service.rb +104 -0
  76. data/lib/rlsl/shader_builder/native_extension_compiler.rb +97 -0
  77. data/lib/rlsl/shader_builder/shader_definition.rb +68 -0
  78. data/lib/rlsl/shader_builder/source_resolver.rb +109 -0
  79. data/lib/rlsl/shader_builder.rb +60 -111
  80. data/lib/rlsl/shader_name.rb +18 -0
  81. data/lib/rlsl/types/catalog.rb +47 -0
  82. data/lib/rlsl/types/target_resolver.rb +15 -0
  83. data/lib/rlsl/types/type_spec.rb +167 -0
  84. data/lib/rlsl/types/value_normalizer.rb +86 -0
  85. data/lib/rlsl/types.rb +9 -31
  86. data/lib/rlsl/uniform_context.rb +22 -11
  87. data/lib/rlsl/version.rb +1 -1
  88. data/lib/rlsl/wgsl/translator.rb +46 -39
  89. data/lib/rlsl/wgsl/uniform_layout.rb +25 -0
  90. data/lib/rlsl.rb +38 -15
  91. metadata +76 -11
@@ -3,150 +3,203 @@
3
3
  module RLSL
4
4
  module Prism
5
5
  module Emitters
6
- class CEmitter < BaseEmitter
7
- TYPE_MAP = {
8
- float: "float",
9
- int: "int",
10
- bool: "int",
11
- vec2: "vec2",
12
- vec3: "vec3",
13
- vec4: "vec4",
14
- mat2: "mat2",
15
- mat3: "mat3",
16
- mat4: "mat4",
17
- sampler2D: "sampler2D"
18
- }.freeze
19
-
20
- VECTOR_CONSTRUCTORS = {
21
- vec2: "vec2_new",
22
- vec3: "vec3_new",
23
- vec4: "vec4_new"
24
- }.freeze
25
-
26
- MATRIX_CONSTRUCTORS = {
27
- mat2: "mat2_new",
28
- mat3: "mat3_new",
29
- mat4: "mat4_new"
30
- }.freeze
31
-
32
- TEXTURE_FUNCTIONS = {
33
- texture2D: "texture_sample",
34
- texture: "texture_sample",
35
- textureLod: "texture_sample_lod"
36
- }.freeze
37
-
38
- VECTOR_OPS = {
39
- "+" => "add",
40
- "-" => "sub",
41
- "*" => "mul",
42
- "/" => "div"
43
- }.freeze
44
-
45
- MATH_FUNCTIONS = {
46
- sin: "sinf",
47
- cos: "cosf",
48
- tan: "tanf",
49
- asin: "asinf",
50
- acos: "acosf",
51
- atan: "atanf",
52
- atan2: "atan2f",
53
- sqrt: "sqrtf",
54
- pow: "powf",
55
- exp: "expf",
56
- log: "logf",
57
- abs: "fabsf",
58
- floor: "floorf",
59
- ceil: "ceilf",
60
- min: "fminf",
61
- max: "fmaxf",
62
- fract: "fract",
63
- mod: "fmodf",
64
- clamp: "clamp_f",
65
- mix: "mix_f",
66
- smoothstep: "smoothstep",
67
- length: "vec_length",
68
- normalize: "vec_normalize",
69
- dot: "vec_dot"
70
- }.freeze
6
+ class CEmitter < TargetEmitter
7
+ PROFILE = TargetProfile.new(
8
+ type_map: {
9
+ float: "float",
10
+ int: "int",
11
+ bool: "int",
12
+ vec2: "vec2",
13
+ vec3: "vec3",
14
+ vec4: "vec4",
15
+ mat2: "mat2",
16
+ mat3: "mat3",
17
+ mat4: "mat4",
18
+ sampler2D: "sampler2D"
19
+ },
20
+ vector_constructors: {
21
+ vec2: "vec2_new",
22
+ vec3: "vec3_new",
23
+ vec4: "vec4_new"
24
+ },
25
+ matrix_constructors: {
26
+ mat2: "mat2_new",
27
+ mat3: "mat3_new",
28
+ mat4: "mat4_new"
29
+ },
30
+ texture_functions: {
31
+ texture2D: "texture_sample",
32
+ texture: "texture_sample",
33
+ textureLod: "texture_sample_lod"
34
+ },
35
+ math_functions: {
36
+ sin: "sinf",
37
+ cos: "cosf",
38
+ tan: "tanf",
39
+ asin: "asinf",
40
+ acos: "acosf",
41
+ atan: "atanf",
42
+ atan2: "atan2f",
43
+ sqrt: "sqrtf",
44
+ pow: "powf",
45
+ exp: "expf",
46
+ log: "logf",
47
+ abs: "fabsf",
48
+ floor: "floorf",
49
+ ceil: "ceilf",
50
+ sign: "sign_f",
51
+ step: "step_f",
52
+ min: "fminf",
53
+ max: "fmaxf",
54
+ fract: "fract",
55
+ mod: "rlsl_mod",
56
+ clamp: "clamp_f",
57
+ mix: "mix_f",
58
+ smoothstep: "smoothstep",
59
+ length: "vec_length",
60
+ normalize: "vec_normalize",
61
+ dot: "vec_dot"
62
+ },
63
+ vector_ops: {
64
+ "+" => "add",
65
+ "-" => "sub",
66
+ "*" => "mul",
67
+ "/" => "div"
68
+ },
69
+ call_resolver: :emit_profile_func_call,
70
+ binary_op_resolver: :emit_profile_binary_op
71
+ ).freeze
71
72
 
72
73
  protected
73
74
 
74
- def type_name(type)
75
- TYPE_MAP[type&.to_sym] || "float"
76
- end
75
+ def format_number(value, type: nil)
76
+ formatted = super(value, type: type)
77
+ return formatted if type == :int || value.is_a?(Integer)
77
78
 
78
- def format_number(value)
79
- formatted = super(value)
80
79
  "#{formatted}f"
81
80
  end
82
81
 
83
- def emit_func_call(node)
82
+ def emit_profile_func_call(node)
84
83
  name = node.name.to_sym
85
-
86
- if VECTOR_CONSTRUCTORS.key?(name)
87
- args = node.args.map { |arg| emit(arg) }.join(", ")
88
- return "#{VECTOR_CONSTRUCTORS[name]}(#{args})"
84
+ return emit_c_vector_constructor(node) if vector_type?(name)
85
+ if name == :atan && node.args.length == 2
86
+ return emit_named_call("atan2f", node.args, expected_types: node.expected_arg_types)
89
87
  end
90
-
91
- if MATRIX_CONSTRUCTORS.key?(name)
92
- args = node.args.map { |arg| emit(arg) }.join(", ")
93
- return "#{MATRIX_CONSTRUCTORS[name]}(#{args})"
88
+ if %i[distance cross].include?(name) && vector_type?(node.args.first&.type)
89
+ return emit_named_call("#{node.args.first.type}_#{name}", node.args,
90
+ expected_types: node.expected_arg_types)
94
91
  end
95
-
96
- if TEXTURE_FUNCTIONS.key?(name)
97
- args = node.args.map { |arg| emit(arg) }.join(", ")
98
- return "#{TEXTURE_FUNCTIONS[name]}(#{args})"
92
+ if vector_math_call?(name, node)
93
+ return emit_named_call("#{node.args.first.type}_#{name}", node.args,
94
+ expected_types: node.expected_arg_types)
99
95
  end
100
-
101
- if %i[length normalize dot].include?(name) && node.args.first&.type
102
- vec_type = node.args.first.type
103
- if %i[vec2 vec3 vec4].include?(vec_type)
104
- func_name = "#{vec_type}_#{name}"
105
- args = node.args.map { |arg| emit(arg) }.join(", ")
106
- return "#{func_name}(#{args})"
107
- end
96
+ if vector_mix_call?(name, node)
97
+ return emit_named_call("mix_#{vector_suffix(node.args.first.type)}", node.args,
98
+ expected_types: node.expected_arg_types)
108
99
  end
100
+ end
109
101
 
110
- if MATH_FUNCTIONS.key?(name)
111
- func_name = MATH_FUNCTIONS[name]
102
+ def emit_profile_binary_op(node)
103
+ return emit_float_modulo(node) if node.operator == "%" && node.type == :float
104
+ return unless profile.vector_ops.key?(node.operator)
112
105
 
113
- if name == :mix && node.args.first&.type
114
- first_type = node.args.first.type
115
- if %i[vec2 vec3 vec4].include?(first_type)
116
- func_name = "mix_v3"
117
- end
118
- end
106
+ emit_vector_binary_op(node)
107
+ end
119
108
 
120
- args = node.args.map { |arg| emit(arg) }.join(", ")
121
- return "#{func_name}(#{args})"
109
+ def emit_unary_op(node)
110
+ if node.operator == "-" && vector_type?(node.operand.type)
111
+ return emit_named_call("#{node.operand.type}_mul_scalar", [node.operand, IR::Literal.new(-1.0, :float)])
122
112
  end
123
113
 
124
114
  super
125
115
  end
126
116
 
127
- def emit_binary_op(node)
128
- left_type = node.left.type
129
- op = node.operator
117
+ def emit_bool_literal(node)
118
+ node.value ? "1" : "0"
119
+ end
120
+
121
+ def emit_field_access(node)
122
+ return super if node.receiver.type == :uniforms
130
123
 
131
- if vector_type?(left_type) && VECTOR_OPS.key?(op)
132
- vec_func = "#{left_type}_#{VECTOR_OPS[op]}"
133
- left = emit(node.left)
134
- right = emit(node.right)
135
- return "#{vec_func}(#{left}, #{right})"
124
+ field = node.field.to_s
125
+ if Builtins.single_component_field?(field)
126
+ component = Builtins::SWIZZLE_COMPONENTS.fetch(field)
127
+ return "#{emit(node.receiver)}.#{%w[x y z w].fetch(component)}"
136
128
  end
129
+ return super unless Builtins.swizzle?(field)
137
130
 
138
- super
131
+ indices = field.each_char.map { |component| Builtins::SWIZZLE_COMPONENTS.fetch(component) }
132
+ "#{node.receiver.type}_swizzle#{indices.length}(#{emit(node.receiver)}, #{indices.join(', ')})"
139
133
  end
140
134
 
141
- def emit_bool_literal(node)
142
- node.value ? "1" : "0"
135
+ def emit_float_operand(node)
136
+ return emit(node) unless node.type == :int
137
+
138
+ "(float)(#{emit(node)})"
143
139
  end
144
140
 
145
141
  private
146
142
 
143
+ def vector_math_call?(name, node)
144
+ %i[length normalize dot].include?(name) && vector_type?(node.args.first&.type)
145
+ end
146
+
147
+ def vector_mix_call?(name, node)
148
+ name == :mix && vector_type?(node.args.first&.type)
149
+ end
150
+
147
151
  def vector_type?(type)
148
152
  %i[vec2 vec3 vec4].include?(type)
149
153
  end
154
+
155
+ def vector_suffix(type)
156
+ { vec2: "v2", vec3: "v3", vec4: "v4" }.fetch(type)
157
+ end
158
+
159
+ def emit_float_modulo(node)
160
+ "rlsl_mod(#{emit_float_operand(node.left)}, #{emit_float_operand(node.right)})"
161
+ end
162
+
163
+ def emit_vector_binary_op(node)
164
+ left_vector = vector_type?(node.left.type)
165
+ right_vector = vector_type?(node.right.type)
166
+ return unless left_vector || right_vector
167
+
168
+ vector_type = left_vector ? node.left.type : node.right.type
169
+ operation = profile.vector_ops.fetch(node.operator)
170
+
171
+ if left_vector && right_vector
172
+ suffix = %w[* /].include?(node.operator) ? "_components" : ""
173
+ return emit_named_call("#{vector_type}_#{operation}#{suffix}", [node.left, node.right])
174
+ end
175
+
176
+ if left_vector
177
+ return emit_named_call("#{vector_type}_#{operation}_scalar", [node.left, node.right])
178
+ end
179
+
180
+ emit_named_call("#{vector_type}_scalar_#{operation}", [node.left, node.right])
181
+ end
182
+
183
+ def emit_c_vector_constructor(node)
184
+ size = { vec2: 2, vec3: 3, vec4: 4 }.fetch(node.name.to_sym)
185
+ component_count = node.args.sum { |argument| vector_size(argument.type) || 1 }
186
+ component_count = size if node.args.length == 1 && !vector_type?(node.args.first.type)
187
+
188
+ unless component_count == size
189
+ raise TargetCapabilityError,
190
+ "#{node.name} constructor produces #{component_count} components on C, expected #{size}"
191
+ end
192
+
193
+ return "#{node.name}_splat(#{emit(node.args.first)})" if node.args.length == 1 && !vector_type?(node.args.first.type)
194
+ return emit_named_call("#{node.name}_new", node.args) unless node.args.any? { |argument| vector_type?(argument.type) }
195
+
196
+ signature = node.args.map { |argument| vector_type?(argument.type) ? argument.type : :float }.join("_")
197
+ emit_named_call("#{node.name}_from_#{signature}", node.args)
198
+ end
199
+
200
+ def vector_size(type)
201
+ { vec2: 2, vec3: 3, vec4: 4 }[type]
202
+ end
150
203
  end
151
204
  end
152
205
  end
@@ -3,70 +3,83 @@
3
3
  module RLSL
4
4
  module Prism
5
5
  module Emitters
6
- class GLSLEmitter < BaseEmitter
7
- TYPE_MAP = {
8
- float: "float",
9
- int: "int",
10
- bool: "bool",
11
- vec2: "vec2",
12
- vec3: "vec3",
13
- vec4: "vec4",
14
- mat2: "mat2",
15
- mat3: "mat3",
16
- mat4: "mat4",
17
- sampler2D: "sampler2D"
18
- }.freeze
6
+ class GLSLEmitter < TargetEmitter
7
+ PROFILE = TargetProfile.new(
8
+ type_map: {
9
+ float: "float",
10
+ int: "int",
11
+ bool: "bool",
12
+ vec2: "vec2",
13
+ vec3: "vec3",
14
+ vec4: "vec4",
15
+ mat2: "mat2",
16
+ mat3: "mat3",
17
+ mat4: "mat4",
18
+ sampler2D: "sampler2D"
19
+ },
20
+ vector_constructors: {
21
+ vec2: "vec2",
22
+ vec3: "vec3",
23
+ vec4: "vec4"
24
+ },
25
+ matrix_constructors: {
26
+ mat2: "mat2",
27
+ mat3: "mat3",
28
+ mat4: "mat4"
29
+ },
30
+ texture_functions: {
31
+ texture2D: "texture2D",
32
+ texture: "texture",
33
+ textureLod: "textureLod"
34
+ }
35
+ ).freeze
19
36
 
20
- VECTOR_CONSTRUCTORS = {
21
- vec2: "vec2",
22
- vec3: "vec3",
23
- vec4: "vec4"
24
- }.freeze
37
+ protected
25
38
 
26
- MATRIX_CONSTRUCTORS = {
27
- mat2: "mat2",
28
- mat3: "mat3",
29
- mat4: "mat4"
30
- }.freeze
39
+ def function_qualifier
40
+ ""
41
+ end
31
42
 
32
- TEXTURE_FUNCTIONS = {
33
- texture2D: "texture2D",
34
- texture: "texture",
35
- textureLod: "textureLod"
36
- }.freeze
43
+ def emit_binary_op(node)
44
+ if node.operator == "%" && (node.left.type != :int || node.right.type != :int)
45
+ return "mod(#{emit_float_operand(node.left)}, #{emit_float_operand(node.right)})"
46
+ end
37
47
 
38
- protected
48
+ super
49
+ end
39
50
 
40
- def type_name(type)
41
- TYPE_MAP[type&.to_sym] || "float"
51
+ def emit_result_struct(func_name, types)
52
+ fields = types.each_with_index.map { |type, index| "#{type_name(type)} v#{index};" }.join(" ")
53
+ "struct #{func_name}_result { #{fields} };\n"
42
54
  end
43
55
 
44
- def emit_func_call(node)
45
- name = node.name.to_sym
56
+ def emit_field_access(node)
57
+ return node.field.to_s if node.receiver.type == :uniforms && node.type == :sampler2D
46
58
 
47
- if VECTOR_CONSTRUCTORS.key?(name)
48
- args = node.args.map { |arg| emit(arg) }.join(", ")
49
- return "#{VECTOR_CONSTRUCTORS[name]}(#{args})"
50
- end
59
+ super
60
+ end
51
61
 
52
- if MATRIX_CONSTRUCTORS.key?(name)
53
- args = node.args.map { |arg| emit(arg) }.join(", ")
54
- return "#{MATRIX_CONSTRUCTORS[name]}(#{args})"
55
- end
62
+ def emit_texture_call(name, node)
63
+ return unless profile.texture_functions.key?(name) && node.args.length >= 2
56
64
 
57
- if TEXTURE_FUNCTIONS.key?(name)
58
- args = node.args.map { |arg| emit(arg) }.join(", ")
59
- return "#{TEXTURE_FUNCTIONS[name]}(#{args})"
65
+ args = [node.args[0], node.args[1], node.args[2] || IR::Literal.new(0.0, :float)]
66
+ emit_named_call("textureLod", args, expected_types: node.expected_arg_types)
67
+ end
68
+
69
+ def emit_func_call(node)
70
+ if node.name.to_sym == :atan2
71
+ return emit_named_call("atan", node.args, expected_types: node.expected_arg_types)
60
72
  end
61
73
 
62
- args = node.args.map { |arg| emit(arg) }.join(", ")
63
- "#{name}(#{args})"
74
+ super
64
75
  end
65
76
 
66
- def emit_binary_op(node)
67
- left = emit_with_precedence(node.left, node.operator)
68
- right = emit_with_precedence(node.right, node.operator)
69
- "#{left} #{node.operator} #{right}"
77
+ def emit_tuple_value(node)
78
+ expected_types = Array(current_return_type)
79
+ elements = node.elements.each_with_index.map do |element, index|
80
+ emit_typed_argument(element, expected_types[index])
81
+ end.join(", ")
82
+ "#{current_return_struct_name}(#{elements})"
70
83
  end
71
84
  end
72
85
  end
@@ -3,72 +3,87 @@
3
3
  module RLSL
4
4
  module Prism
5
5
  module Emitters
6
- class MSLEmitter < BaseEmitter
7
- TYPE_MAP = {
8
- float: "float",
9
- int: "int",
10
- bool: "bool",
11
- vec2: "float2",
12
- vec3: "float3",
13
- vec4: "float4",
14
- mat2: "float2x2",
15
- mat3: "float3x3",
16
- mat4: "float4x4",
17
- sampler2D: "texture2d<float>"
18
- }.freeze
6
+ class MSLEmitter < TargetEmitter
7
+ PROFILE = TargetProfile.new(
8
+ type_map: {
9
+ float: "float",
10
+ int: "int",
11
+ bool: "bool",
12
+ vec2: "float2",
13
+ vec3: "float3",
14
+ vec4: "float4",
15
+ mat2: "float2x2",
16
+ mat3: "float3x3",
17
+ mat4: "float4x4",
18
+ sampler2D: "texture2d<float>"
19
+ },
20
+ vector_constructors: {
21
+ vec2: "float2",
22
+ vec3: "float3",
23
+ vec4: "float4"
24
+ },
25
+ matrix_constructors: {
26
+ mat2: "float2x2",
27
+ mat3: "float3x3",
28
+ mat4: "float4x4"
29
+ },
30
+ texture_functions: {
31
+ texture2D: "sample",
32
+ texture: "sample",
33
+ textureLod: "sample"
34
+ }
35
+ ).freeze
19
36
 
20
- VECTOR_CONSTRUCTORS = {
21
- vec2: "float2",
22
- vec3: "float3",
23
- vec4: "float4"
24
- }.freeze
37
+ protected
25
38
 
26
- MATRIX_CONSTRUCTORS = {
27
- mat2: "float2x2",
28
- mat3: "float3x3",
29
- mat4: "float4x4"
30
- }.freeze
39
+ def emit_texture_call(name, node)
40
+ return unless profile.texture_functions.key?(name) && node.args.length >= 2
31
41
 
32
- TEXTURE_FUNCTIONS = {
33
- texture2D: "sample",
34
- texture: "sample",
35
- textureLod: "sample"
36
- }.freeze
42
+ texture = emit(node.args[0])
43
+ uv = emit(node.args[1])
44
+ if name == :textureLod
45
+ lod = emit_typed_argument(node.args[2], :float)
46
+ return "#{texture}.sample(rlsl_texture_sampler, #{uv}, level(#{lod}))"
47
+ end
37
48
 
38
- protected
49
+ "#{texture}.sample(rlsl_texture_sampler, #{uv})"
50
+ end
51
+
52
+ def emit_binary_op(node)
53
+ if node.operator == "%" && node.type == :float
54
+ return "rlsl_mod(#{emit_float_operand(node.left)}, #{emit_float_operand(node.right)})"
55
+ end
39
56
 
40
- def type_name(type)
41
- TYPE_MAP[type&.to_sym] || "float"
57
+ super
42
58
  end
43
59
 
44
60
  def emit_func_call(node)
45
- name = node.name.to_sym
46
-
47
- if VECTOR_CONSTRUCTORS.key?(name)
48
- args = node.args.map { |arg| emit(arg) }.join(", ")
49
- return "#{VECTOR_CONSTRUCTORS[name]}(#{args})"
61
+ if node.name.to_sym == :mod
62
+ args = node.args.map { |argument| emit_float_operand(argument) }
63
+ return "rlsl_mod(#{args.join(', ')})"
50
64
  end
51
-
52
- if MATRIX_CONSTRUCTORS.key?(name)
53
- args = node.args.map { |arg| emit(arg) }.join(", ")
54
- return "#{MATRIX_CONSTRUCTORS[name]}(#{args})"
65
+ if node.name.to_sym == :atan && node.args.length == 2
66
+ return emit_named_call("atan2", node.args, expected_types: node.expected_arg_types)
55
67
  end
56
68
 
57
- # MSL texture sampling: texture.sample(sampler, uv)
58
- if TEXTURE_FUNCTIONS.key?(name) && node.args.length >= 2
59
- texture = emit(node.args[0])
60
- uv = emit(node.args[1])
61
- return "#{texture}.sample(textureSampler, #{uv})"
62
- end
69
+ super
70
+ end
63
71
 
64
- args = node.args.map { |arg| emit(arg) }.join(", ")
65
- "#{name}(#{args})"
72
+ def emit_field_access(node)
73
+ return node.field.to_s if node.receiver.type == :uniforms && node.type == :sampler2D
74
+
75
+ code = super
76
+ return "(#{code} != 0)" if node.receiver.type == :uniforms && node.type == :bool
77
+
78
+ code
66
79
  end
67
80
 
68
- def emit_binary_op(node)
69
- left = emit_with_precedence(node.left, node.operator)
70
- right = emit_with_precedence(node.right, node.operator)
71
- "#{left} #{node.operator} #{right}"
81
+ def emit_tuple_value(node)
82
+ expected_types = Array(current_return_type)
83
+ elements = node.elements.each_with_index.map do |element, index|
84
+ emit_typed_argument(element, expected_types[index])
85
+ end.join(", ")
86
+ "#{current_return_struct_name}{#{elements}}"
72
87
  end
73
88
  end
74
89
  end