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
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ class ShaderBuilder
5
+ class ShaderDefinition
6
+ attr_reader :uniforms, :custom_functions, :helpers_block, :helpers_mode, :fragment_block, :fragment_mode
7
+
8
+ def initialize(
9
+ uniforms: {},
10
+ custom_functions: {},
11
+ helpers_block: nil,
12
+ helpers_mode: :ruby,
13
+ fragment_block: nil,
14
+ fragment_mode: :c
15
+ )
16
+ @uniforms = normalize_hash(uniforms)
17
+ @custom_functions = normalize_hash(custom_functions)
18
+ @helpers_block = helpers_block
19
+ @helpers_mode = helpers_mode
20
+ @fragment_block = fragment_block
21
+ @fragment_mode = fragment_mode
22
+ end
23
+
24
+ def with_uniforms(uniforms)
25
+ copy(uniforms: uniforms)
26
+ end
27
+
28
+ def with_custom_functions(custom_functions)
29
+ copy(custom_functions: custom_functions)
30
+ end
31
+
32
+ def with_helpers(mode:, block:)
33
+ copy(helpers_mode: mode, helpers_block: block)
34
+ end
35
+
36
+ def with_fragment(mode:, block:)
37
+ copy(fragment_mode: mode, fragment_block: block)
38
+ end
39
+
40
+ def ruby_fragment?
41
+ %i[ruby ruby_source].include?(@fragment_mode)
42
+ end
43
+
44
+ def ruby_helpers?
45
+ %i[ruby ruby_source].include?(@helpers_mode)
46
+ end
47
+
48
+ private
49
+
50
+ def copy(**overrides)
51
+ self.class.new(
52
+ uniforms: overrides.fetch(:uniforms, @uniforms),
53
+ custom_functions: overrides.fetch(:custom_functions, @custom_functions),
54
+ helpers_block: overrides.fetch(:helpers_block, @helpers_block),
55
+ helpers_mode: overrides.fetch(:helpers_mode, @helpers_mode),
56
+ fragment_block: overrides.fetch(:fragment_block, @fragment_block),
57
+ fragment_mode: overrides.fetch(:fragment_mode, @fragment_mode)
58
+ )
59
+ end
60
+
61
+ def normalize_hash(hash)
62
+ hash.each_with_object({}) do |(name, value), normalized|
63
+ normalized[name.to_sym] = value
64
+ end.freeze
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ class ShaderBuilder
5
+ class SourceResolver
6
+ def initialize(definition, transpiler_class: Prism::Transpiler)
7
+ @definition = definition
8
+ @transpiler_class = transpiler_class
9
+ end
10
+
11
+ def sources_for(target)
12
+ [helpers_code(target), fragment_code(target)]
13
+ end
14
+
15
+ def translation_sources_for(target)
16
+ [helpers_source(target), fragment_source(target)]
17
+ end
18
+
19
+ def helpers_code(target)
20
+ helpers_source(target).code
21
+ end
22
+
23
+ def fragment_code(target)
24
+ fragment_source(target).code
25
+ end
26
+
27
+ private
28
+
29
+ def helpers_source(target)
30
+ return source_snippet("") unless @definition.helpers_block
31
+ if @definition.helpers_mode == :ruby_source
32
+ source = @definition.helpers_block.call
33
+ return source_snippet(
34
+ helpers_transpiler.transpile_helpers_source(source, target, @definition.custom_functions),
35
+ format: :target
36
+ )
37
+ end
38
+ return source_snippet(@definition.helpers_block.call) unless ruby_helpers?
39
+
40
+ source_snippet(
41
+ helpers_transpiler.transpile_helpers(@definition.helpers_block, target, @definition.custom_functions),
42
+ format: :target
43
+ )
44
+ end
45
+
46
+ def fragment_source(target)
47
+ return source_snippet("") unless @definition.fragment_block
48
+ if @definition.fragment_mode == :ruby_source
49
+ source = @definition.fragment_block.call
50
+ return source_snippet(fragment_transpiler.transpile_source(source, target), format: :target)
51
+ end
52
+ return source_snippet(@definition.fragment_block.call) unless ruby_fragment?
53
+
54
+ source_snippet(fragment_transpiler.transpile(@definition.fragment_block, target), format: :target)
55
+ end
56
+
57
+ def source_snippet(code, format: :legacy)
58
+ BaseTranslator::SourceSnippet.new(code: code.to_s, format: format)
59
+ end
60
+
61
+ def helpers_transpiler
62
+ @helpers_transpiler ||= build_transpiler
63
+ end
64
+
65
+ def fragment_transpiler
66
+ @fragment_transpiler ||= build_transpiler(globals: helper_globals)
67
+ end
68
+
69
+ def build_transpiler(globals: {})
70
+ @transpiler_class.new(@definition.uniforms, @definition.custom_functions, globals: globals)
71
+ end
72
+
73
+ def helper_globals
74
+ return {} unless ruby_helpers? && @definition.helpers_block
75
+
76
+ @helper_globals ||= begin
77
+ compilation = if @definition.helpers_mode == :ruby_source
78
+ helpers_transpiler.compile_helpers_source(
79
+ @definition.helpers_block.call,
80
+ @definition.custom_functions
81
+ )
82
+ else
83
+ helpers_transpiler.compile_helpers(@definition.helpers_block, @definition.custom_functions)
84
+ end
85
+ extract_global_types(compilation.ir)
86
+ end
87
+ end
88
+
89
+ def extract_global_types(ir)
90
+ return {} unless ir.is_a?(Prism::IR::Block)
91
+
92
+ ir.statements.each_with_object({}) do |statement, globals|
93
+ next unless statement.is_a?(Prism::IR::GlobalDecl)
94
+ next unless statement.type
95
+
96
+ globals[statement.name] = statement.type
97
+ end
98
+ end
99
+
100
+ def ruby_helpers?
101
+ @definition.ruby_helpers?
102
+ end
103
+
104
+ def ruby_fragment?
105
+ @definition.ruby_fragment?
106
+ end
107
+ end
108
+ end
109
+ end
@@ -1,165 +1,114 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "fileutils"
4
- require "digest"
5
- require "rbconfig"
3
+ require_relative "shader_builder/shader_definition"
4
+ require_relative "shader_builder/build_service"
5
+ require_relative "shader_builder/source_resolver"
6
+ require_relative "shader_builder/native_extension_compiler"
6
7
 
7
8
  module RLSL
8
9
  class ShaderBuilder
9
10
  attr_reader :name
10
11
 
11
- def initialize(name)
12
- @name = name.to_s
13
- @uniforms = {}
14
- @fragment_mode = :c
15
- @helpers_mode = :c
16
- @custom_functions = {}
12
+ def initialize(name, definition = ShaderDefinition.new)
13
+ @name = RLSL.validate_shader_name!(name)
14
+ @definition = definition
17
15
  end
18
16
 
19
17
  def uniforms(&block)
20
18
  if block_given?
21
19
  ctx = UniformContext.new
22
20
  ctx.instance_eval(&block)
23
- @uniforms = ctx.uniforms
21
+ update_definition(@definition.with_uniforms(ctx.uniforms))
24
22
  else
25
- @uniforms
23
+ @definition.uniforms
26
24
  end
27
25
  end
28
26
 
27
+ def uniform_types
28
+ @definition.uniforms
29
+ end
30
+
29
31
  def helpers(mode = :ruby, &block)
30
- @helpers_block = block
31
- @helpers_mode = mode
32
+ raise ArgumentError, "helpers requires a block" unless block
33
+
34
+ resolved_mode = mode.to_sym
35
+ raise ArgumentError, "helpers mode must be :c or :ruby" unless %i[c ruby].include?(resolved_mode)
36
+
37
+ if resolved_mode == :ruby
38
+ source_unit = Prism::SourceExtractor.new.extract_unit(block)
39
+ update_definition(@definition.with_helpers(mode: :ruby_source, block: -> { source_unit }))
40
+ else
41
+ update_definition(@definition.with_helpers(mode: :c, block: block))
42
+ end
43
+ end
44
+
45
+ def helpers_source(source)
46
+ update_definition(@definition.with_helpers(mode: :ruby_source, block: -> { source.to_s }))
32
47
  end
33
48
 
34
49
  def functions(&block)
35
50
  ctx = FunctionContext.new
36
51
  ctx.instance_eval(&block)
37
- @custom_functions = ctx.functions
52
+ update_definition(@definition.with_custom_functions(ctx.functions))
38
53
  end
39
54
 
40
- def fragment(&block)
41
- @fragment_block = block
42
- @fragment_mode = block.arity > 0 ? :ruby : :c
55
+ def fragment(mode = :auto, &block)
56
+ raise ArgumentError, "fragment requires a block" unless block
57
+
58
+ resolved_mode = if mode == :auto
59
+ block.parameters.empty? ? :c : :ruby
60
+ else
61
+ mode.to_sym
62
+ end
63
+ unless %i[c ruby].include?(resolved_mode)
64
+ raise ArgumentError, "fragment mode must be :c or :ruby"
65
+ end
66
+
67
+ if resolved_mode == :ruby
68
+ source_unit = Prism::SourceExtractor.new.extract_unit(block)
69
+ update_definition(@definition.with_fragment(mode: :ruby_source, block: -> { source_unit }))
70
+ else
71
+ update_definition(@definition.with_fragment(mode: :c, block: block))
72
+ end
43
73
  end
44
74
 
45
- def ruby_mode?
46
- @fragment_mode == :ruby
75
+ def fragment_source(source)
76
+ update_definition(@definition.with_fragment(mode: :ruby_source, block: -> { source.to_s }))
47
77
  end
48
78
 
49
79
  def compile_and_load
50
- c_code = generate_c_code
51
- code_hash = Digest::MD5.hexdigest(c_code)[0..7]
52
- ext_name = "#{@name}_#{code_hash}"
53
- ext_dir = File.join(RLSL.cache_dir, ext_name)
54
- ext_file = File.join(ext_dir, "#{@name}.#{RbConfig::CONFIG['DLEXT']}")
55
-
56
- unless File.exist?(ext_file)
57
- compile_extension(@name, ext_dir, c_code)
58
- end
59
-
60
- require ext_file
61
- CompiledShader.new(@name, ext_name, @uniforms.keys)
80
+ build_service.compile_and_load
62
81
  end
63
82
 
64
83
  def build_metal_shader
65
- if ruby_mode?
66
- fragment_code = transpile_fragment(:msl)
67
- helpers_code = @helpers_block ? @helpers_block.call : ""
68
- else
69
- helpers_code = @helpers_block ? @helpers_block.call : ""
70
- fragment_code = @fragment_block ? @fragment_block.call : ""
71
- end
72
-
73
- translator = MSL::Translator.new(@uniforms, helpers_code, fragment_code)
74
- msl_source = translator.translate
75
-
76
- MSL::Shader.new(@name, @uniforms, msl_source)
84
+ build_service.build_metal_shader
77
85
  end
78
86
 
79
87
  def build_wgsl_shader
80
- if ruby_mode?
81
- fragment_code = transpile_fragment(:wgsl)
82
- helpers_code = @helpers_block ? @helpers_block.call : ""
83
- else
84
- helpers_code = @helpers_block ? @helpers_block.call : ""
85
- fragment_code = @fragment_block ? @fragment_block.call : ""
86
- end
87
-
88
- translator = WGSL::Translator.new(@uniforms, helpers_code, fragment_code)
89
- translator.translate
88
+ build_service.build_wgsl_shader
90
89
  end
91
90
 
92
91
  def build_glsl_shader(version: "450")
93
- if ruby_mode?
94
- fragment_code = transpile_fragment(:glsl)
95
- helpers_code = @helpers_block ? @helpers_block.call : ""
96
- else
97
- helpers_code = @helpers_block ? @helpers_block.call : ""
98
- fragment_code = @fragment_block ? @fragment_block.call : ""
99
- end
100
-
101
- translator = GLSL::Translator.new(@uniforms, helpers_code, fragment_code, version: version)
102
- translator.translate
92
+ build_service.build_glsl_shader(version: version)
103
93
  end
104
94
 
105
95
  def transpile_fragment(target)
106
- return "" unless @fragment_block
107
-
108
- transpiler = Prism::Transpiler.new(@uniforms, @custom_functions)
109
- transpiler.transpile(@fragment_block, target)
96
+ build_service.transpile_fragment(target)
110
97
  end
111
98
 
112
99
  def transpile_helpers(target)
113
- return "" unless @helpers_block
114
-
115
- transpiler = Prism::Transpiler.new(@uniforms, @custom_functions)
116
- transpiler.transpile_helpers(@helpers_block, target, @custom_functions)
117
- end
118
-
119
- def helpers_ruby_mode?
120
- @helpers_mode == :ruby
100
+ build_service.transpile_helpers(target)
121
101
  end
122
102
 
123
103
  private
124
104
 
125
- def generate_c_code
126
- if helpers_ruby_mode?
127
- helpers_code = transpile_helpers(:c)
128
- helpers_block = -> { helpers_code }
129
- else
130
- helpers_block = @helpers_block
131
- end
132
-
133
- if ruby_mode?
134
- fragment_code = transpile_fragment(:c)
135
- fragment_block = -> { fragment_code }
136
- else
137
- fragment_block = @fragment_block
138
- end
139
-
140
- codegen = CodeGenerator.new(@name, @uniforms, helpers_block, fragment_block)
141
- codegen.generate
105
+ def build_service
106
+ @build_service ||= BuildService.new(@name, @definition)
142
107
  end
143
108
 
144
- def compile_extension(ext_name, ext_dir, c_code)
145
- FileUtils.mkdir_p(ext_dir)
146
-
147
- File.write(File.join(ext_dir, "#{ext_name}.c"), c_code)
148
-
149
- extconf = <<~RUBY
150
- require "mkmf"
151
- $CFLAGS << " -O3 -ffast-math"
152
- if RUBY_PLATFORM =~ /darwin/
153
- $CFLAGS << " -fblocks"
154
- end
155
- create_makefile("#{ext_name}")
156
- RUBY
157
- File.write(File.join(ext_dir, "extconf.rb"), extconf)
158
-
159
- Dir.chdir(ext_dir) do
160
- system("#{RbConfig.ruby} extconf.rb > /dev/null 2>&1") or raise "extconf failed for #{ext_name}"
161
- system("/usr/bin/make > /dev/null 2>&1") or raise "make failed for #{ext_name}"
162
- end
109
+ def update_definition(definition)
110
+ @definition = definition
111
+ @build_service = nil
163
112
  end
164
113
  end
165
114
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ IDENTIFIER_PATTERN = /\A[A-Za-z_][A-Za-z0-9_]*\z/
5
+ SHADER_NAME_PATTERN = IDENTIFIER_PATTERN
6
+
7
+ def self.validate_identifier!(name, context: "identifier")
8
+ normalized = name.to_s
9
+ return normalized if normalized.match?(IDENTIFIER_PATTERN)
10
+
11
+ raise ArgumentError,
12
+ "Invalid #{context} #{name.inspect}: use an ASCII identifier beginning with a letter or underscore"
13
+ end
14
+
15
+ def self.validate_shader_name!(name)
16
+ validate_identifier!(name, context: "shader name")
17
+ end
18
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ module UniformTypes
5
+ module Catalog
6
+ def supported?(type)
7
+ UNIFORM_TYPE_SPECS.key?(type.to_sym)
8
+ end
9
+
10
+ def fetch(type)
11
+ UNIFORM_TYPE_SPECS.fetch(type) do
12
+ raise ArgumentError, "Unsupported uniform type: #{type.inspect}"
13
+ end
14
+ end
15
+
16
+ def c_type(type)
17
+ fetch(type).c_type
18
+ end
19
+
20
+ def compiled_types
21
+ @compiled_types ||= UNIFORM_TYPE_SPECS.select { |_type, spec| spec.compiled? }.keys.freeze
22
+ end
23
+
24
+ def compiled_spec(type)
25
+ spec = fetch(type)
26
+ return spec if spec.compiled?
27
+
28
+ raise ArgumentError, "Unsupported compiled uniform type: #{type}"
29
+ end
30
+
31
+ def metal_spec(type)
32
+ spec = fetch(type)
33
+ return spec if spec.metal_packable?
34
+
35
+ raise ArgumentError, "Unsupported Metal uniform type: #{type}"
36
+ end
37
+
38
+ def runtime_types
39
+ @runtime_types ||= UNIFORM_TYPE_SPECS.select { |_type, spec| spec.runtime_supported? }.keys.freeze
40
+ end
41
+
42
+ def function_shorthand_types
43
+ @function_shorthand_types ||= UNIFORM_TYPE_SPECS.select { |_type, spec| spec.function_shorthand? }.keys.freeze
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ module UniformTypes
5
+ module TargetResolver
6
+ def target_type(type, target)
7
+ spec = fetch(type)
8
+ target_type = spec.public_send(:"#{target}_type")
9
+ return target_type if target_type
10
+
11
+ raise ArgumentError, "Unsupported #{target.to_s.upcase} uniform type: #{type}"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ UniformTypeSpec = Struct.new(
5
+ :c_type,
6
+ :glsl_type,
7
+ :wgsl_type,
8
+ :msl_type,
9
+ :wrapper_kind,
10
+ :vector_size,
11
+ :metal_alignment,
12
+ :metal_size,
13
+ :compiled_supported,
14
+ :runtime_supported,
15
+ :function_shorthand,
16
+ keyword_init: true
17
+ ) do
18
+ def compiled?
19
+ compiled_supported
20
+ end
21
+
22
+ def metal_packable?
23
+ !metal_alignment.nil? && !metal_size.nil?
24
+ end
25
+
26
+ def runtime_supported?
27
+ runtime_supported
28
+ end
29
+
30
+ def function_shorthand?
31
+ function_shorthand
32
+ end
33
+
34
+ def target_supported?(target)
35
+ !public_send(:"#{target}_type").nil?
36
+ end
37
+ end
38
+
39
+ C_TYPES = <<~C
40
+ typedef struct { float x, y; } vec2;
41
+ typedef struct { float x, y, z; } vec3;
42
+ typedef struct { float x, y, z, w; } vec4;
43
+ typedef struct { float m[4]; } mat2;
44
+ typedef struct { float m[9]; } mat3;
45
+ typedef struct { float m[16]; } mat4;
46
+ typedef struct { void* data; int width; int height; } sampler2D;
47
+
48
+ #define PI 3.14159265f
49
+ #define TAU 6.28318530f
50
+ C
51
+
52
+ UNIFORM_TYPE_SPECS = {
53
+ float: UniformTypeSpec.new(
54
+ c_type: "float",
55
+ glsl_type: "float",
56
+ wgsl_type: "f32",
57
+ msl_type: "float",
58
+ wrapper_kind: :float,
59
+ metal_alignment: 4,
60
+ metal_size: 4,
61
+ compiled_supported: true,
62
+ runtime_supported: true,
63
+ function_shorthand: true
64
+ ),
65
+ vec2: UniformTypeSpec.new(
66
+ c_type: "vec2",
67
+ glsl_type: "vec2",
68
+ wgsl_type: "vec2<f32>",
69
+ msl_type: "float2",
70
+ wrapper_kind: :vector,
71
+ vector_size: 2,
72
+ metal_alignment: 8,
73
+ metal_size: 8,
74
+ compiled_supported: true,
75
+ runtime_supported: true,
76
+ function_shorthand: true
77
+ ),
78
+ vec3: UniformTypeSpec.new(
79
+ c_type: "vec3",
80
+ glsl_type: "vec3",
81
+ wgsl_type: "vec3<f32>",
82
+ msl_type: "float3",
83
+ wrapper_kind: :vector,
84
+ vector_size: 3,
85
+ metal_alignment: 16,
86
+ metal_size: 16,
87
+ compiled_supported: true,
88
+ runtime_supported: true,
89
+ function_shorthand: true
90
+ ),
91
+ vec4: UniformTypeSpec.new(
92
+ c_type: "vec4",
93
+ glsl_type: "vec4",
94
+ wgsl_type: "vec4<f32>",
95
+ msl_type: "float4",
96
+ wrapper_kind: :vector,
97
+ vector_size: 4,
98
+ metal_alignment: 16,
99
+ metal_size: 16,
100
+ compiled_supported: true,
101
+ runtime_supported: true,
102
+ function_shorthand: true
103
+ ),
104
+ int: UniformTypeSpec.new(
105
+ c_type: "int",
106
+ glsl_type: "int",
107
+ wgsl_type: "i32",
108
+ msl_type: "int",
109
+ wrapper_kind: :int,
110
+ metal_alignment: 4,
111
+ metal_size: 4,
112
+ compiled_supported: true,
113
+ runtime_supported: true,
114
+ function_shorthand: true
115
+ ),
116
+ bool: UniformTypeSpec.new(
117
+ c_type: "int",
118
+ glsl_type: "bool",
119
+ wgsl_type: "i32",
120
+ msl_type: "int",
121
+ wrapper_kind: :bool,
122
+ metal_alignment: 4,
123
+ metal_size: 4,
124
+ compiled_supported: true,
125
+ runtime_supported: true,
126
+ function_shorthand: true
127
+ ),
128
+ mat2: UniformTypeSpec.new(
129
+ c_type: "mat2",
130
+ glsl_type: "mat2",
131
+ wgsl_type: "mat2x2<f32>",
132
+ msl_type: "float2x2",
133
+ compiled_supported: false,
134
+ runtime_supported: false,
135
+ function_shorthand: true
136
+ ),
137
+ mat3: UniformTypeSpec.new(
138
+ c_type: "mat3",
139
+ glsl_type: "mat3",
140
+ wgsl_type: "mat3x3<f32>",
141
+ msl_type: "float3x3",
142
+ compiled_supported: false,
143
+ runtime_supported: false,
144
+ function_shorthand: true
145
+ ),
146
+ mat4: UniformTypeSpec.new(
147
+ c_type: "mat4",
148
+ glsl_type: "mat4",
149
+ wgsl_type: "mat4x4<f32>",
150
+ msl_type: "float4x4",
151
+ compiled_supported: false,
152
+ runtime_supported: false,
153
+ function_shorthand: true
154
+ ),
155
+ sampler2D: UniformTypeSpec.new(
156
+ c_type: "sampler2D",
157
+ glsl_type: "sampler2D",
158
+ wgsl_type: "texture_2d<f32>",
159
+ msl_type: "texture2d<float>",
160
+ compiled_supported: false,
161
+ runtime_supported: false,
162
+ function_shorthand: true
163
+ )
164
+ }.transform_values(&:freeze).freeze
165
+
166
+ UNIFORM_TYPES = UNIFORM_TYPE_SPECS.keys.freeze
167
+ end