rlsl 0.1.1 → 1.0.0

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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -1
  3. data/README.md +2 -0
  4. data/lib/rlsl/base_translator/call_parser.rb +68 -0
  5. data/lib/rlsl/base_translator/code_rewriter.rb +107 -0
  6. data/lib/rlsl/base_translator/code_scanner.rb +102 -0
  7. data/lib/rlsl/base_translator.rb +159 -63
  8. data/lib/rlsl/code_generator/math_prelude.rb +79 -0
  9. data/lib/rlsl/code_generator/ruby_wrapper_generator.rb +97 -0
  10. data/lib/rlsl/code_generator/shader_function_generator.rb +19 -0
  11. data/lib/rlsl/code_generator/template_context.rb +41 -0
  12. data/lib/rlsl/code_generator/uniform_struct_generator.rb +29 -0
  13. data/lib/rlsl/code_generator.rb +26 -201
  14. data/lib/rlsl/compiled_shader.rb +4 -12
  15. data/lib/rlsl/function_context.rb +31 -14
  16. data/lib/rlsl/glsl/translator.rb +19 -38
  17. data/lib/rlsl/msl/shader.rb +8 -38
  18. data/lib/rlsl/msl/translator.rb +19 -36
  19. data/lib/rlsl/msl/uniform_buffer_packer.rb +68 -0
  20. data/lib/rlsl/prism/ast_visitor/control_flow_visiting.rb +96 -0
  21. data/lib/rlsl/prism/ast_visitor/definition_visiting.rb +79 -0
  22. data/lib/rlsl/prism/ast_visitor/expression_visiting.rb +168 -0
  23. data/lib/rlsl/prism/ast_visitor/scope_context.rb +44 -0
  24. data/lib/rlsl/prism/ast_visitor/visitor_registry.rb +21 -0
  25. data/lib/rlsl/prism/ast_visitor.rb +54 -298
  26. data/lib/rlsl/prism/builtins/function_registry.rb +114 -0
  27. data/lib/rlsl/prism/builtins/operator_rules.rb +99 -0
  28. data/lib/rlsl/prism/builtins/swizzle_rules.rb +38 -0
  29. data/lib/rlsl/prism/builtins.rb +31 -148
  30. data/lib/rlsl/prism/compilation_unit.rb +7 -0
  31. data/lib/rlsl/prism/emitters/base_emitter/control_flow_emission.rb +83 -0
  32. data/lib/rlsl/prism/emitters/base_emitter/definition_emission.rb +104 -0
  33. data/lib/rlsl/prism/emitters/base_emitter/expression_emission.rb +92 -0
  34. data/lib/rlsl/prism/emitters/base_emitter/statement_emission.rb +89 -0
  35. data/lib/rlsl/prism/emitters/base_emitter.rb +68 -423
  36. data/lib/rlsl/prism/emitters/c_emitter.rb +78 -121
  37. data/lib/rlsl/prism/emitters/glsl_emitter.rb +30 -65
  38. data/lib/rlsl/prism/emitters/msl_emitter.rb +35 -62
  39. data/lib/rlsl/prism/emitters/target_emitter.rb +77 -0
  40. data/lib/rlsl/prism/emitters/target_profile.rb +34 -0
  41. data/lib/rlsl/prism/emitters/wgsl_emitter.rb +65 -61
  42. data/lib/rlsl/prism/ir/control_flow.rb +83 -0
  43. data/lib/rlsl/prism/ir/definitions.rb +67 -0
  44. data/lib/rlsl/prism/ir/expressions.rb +197 -0
  45. data/lib/rlsl/prism/ir/node.rb +21 -0
  46. data/lib/rlsl/prism/ir/nodes.rb +4 -371
  47. data/lib/rlsl/prism/ir/traversal.rb +62 -0
  48. data/lib/rlsl/prism/source_extractor/block_locator.rb +52 -0
  49. data/lib/rlsl/prism/source_extractor.rb +14 -133
  50. data/lib/rlsl/prism/source_unit/parser.rb +78 -0
  51. data/lib/rlsl/prism/source_unit.rb +42 -0
  52. data/lib/rlsl/prism/target_capability_validator.rb +85 -0
  53. data/lib/rlsl/prism/transpiler.rb +63 -60
  54. data/lib/rlsl/prism/type_inference/call_type_resolver.rb +35 -0
  55. data/lib/rlsl/prism/type_inference/call_validator.rb +71 -0
  56. data/lib/rlsl/prism/type_inference/collection_type_resolver.rb +80 -0
  57. data/lib/rlsl/prism/type_inference/control_flow_inferer.rb +66 -0
  58. data/lib/rlsl/prism/type_inference/definition_inferer.rb +41 -0
  59. data/lib/rlsl/prism/type_inference/expression_inferer.rb +92 -0
  60. data/lib/rlsl/prism/type_inference/field_type_resolver.rb +17 -0
  61. data/lib/rlsl/prism/type_inference/inferer_registry.rb +38 -0
  62. data/lib/rlsl/prism/type_inference/scope_stack.rb +47 -0
  63. data/lib/rlsl/prism/type_inference/type_environment.rb +112 -0
  64. data/lib/rlsl/prism/type_inference/type_shapes.rb +33 -0
  65. data/lib/rlsl/prism/type_inference.rb +114 -248
  66. data/lib/rlsl/runtime_shader.rb +47 -0
  67. data/lib/rlsl/shader_builder/build_service.rb +77 -0
  68. data/lib/rlsl/shader_builder/native_extension_compiler.rb +71 -0
  69. data/lib/rlsl/shader_builder/shader_definition.rb +68 -0
  70. data/lib/rlsl/shader_builder/source_resolver.rb +91 -0
  71. data/lib/rlsl/shader_builder.rb +22 -113
  72. data/lib/rlsl/types/catalog.rb +47 -0
  73. data/lib/rlsl/types/target_resolver.rb +15 -0
  74. data/lib/rlsl/types/type_spec.rb +167 -0
  75. data/lib/rlsl/types/value_normalizer.rb +81 -0
  76. data/lib/rlsl/types.rb +11 -29
  77. data/lib/rlsl/uniform_context.rb +6 -12
  78. data/lib/rlsl/version.rb +1 -1
  79. data/lib/rlsl/wgsl/translator.rb +23 -39
  80. data/lib/rlsl.rb +14 -12
  81. metadata +55 -2
@@ -0,0 +1,91 @@
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
+ return source_snippet(@definition.helpers_block.call) unless ruby_helpers?
32
+
33
+ source_snippet(
34
+ helpers_transpiler.transpile_helpers(@definition.helpers_block, target, @definition.custom_functions),
35
+ format: :target
36
+ )
37
+ end
38
+
39
+ def fragment_source(target)
40
+ return source_snippet("") unless @definition.fragment_block
41
+ return source_snippet(@definition.fragment_block.call) unless ruby_fragment?
42
+
43
+ source_snippet(fragment_transpiler.transpile(@definition.fragment_block, target), format: :target)
44
+ end
45
+
46
+ def source_snippet(code, format: :legacy)
47
+ BaseTranslator::SourceSnippet.new(code: code.to_s, format: format)
48
+ end
49
+
50
+ def helpers_transpiler
51
+ @helpers_transpiler ||= build_transpiler
52
+ end
53
+
54
+ def fragment_transpiler
55
+ @fragment_transpiler ||= build_transpiler(globals: helper_globals)
56
+ end
57
+
58
+ def build_transpiler(globals: {})
59
+ @transpiler_class.new(@definition.uniforms, @definition.custom_functions, globals: globals)
60
+ end
61
+
62
+ def helper_globals
63
+ return {} unless ruby_helpers? && @definition.helpers_block
64
+
65
+ @helper_globals ||= begin
66
+ compilation = helpers_transpiler.compile_helpers(@definition.helpers_block, @definition.custom_functions)
67
+ extract_global_types(compilation.ir)
68
+ end
69
+ end
70
+
71
+ def extract_global_types(ir)
72
+ return {} unless ir.is_a?(Prism::IR::Block)
73
+
74
+ ir.statements.each_with_object({}) do |statement, globals|
75
+ next unless statement.is_a?(Prism::IR::GlobalDecl)
76
+ next unless statement.type
77
+
78
+ globals[statement.name] = statement.type
79
+ end
80
+ end
81
+
82
+ def ruby_helpers?
83
+ @definition.ruby_helpers?
84
+ end
85
+
86
+ def ruby_fragment?
87
+ @definition.ruby_fragment?
88
+ end
89
+ end
90
+ end
91
+ end
@@ -1,165 +1,74 @@
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
+ def initialize(name, definition = ShaderDefinition.new)
12
13
  @name = name.to_s
13
- @uniforms = {}
14
- @fragment_mode = :c
15
- @helpers_mode = :c
16
- @custom_functions = {}
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
+ @definition = @definition.with_uniforms(ctx.uniforms)
24
22
  else
25
- @uniforms
23
+ @definition.uniforms
26
24
  end
27
25
  end
28
26
 
29
27
  def helpers(mode = :ruby, &block)
30
- @helpers_block = block
31
- @helpers_mode = mode
28
+ @definition = @definition.with_helpers(mode: mode, block: block)
32
29
  end
33
30
 
34
31
  def functions(&block)
35
32
  ctx = FunctionContext.new
36
33
  ctx.instance_eval(&block)
37
- @custom_functions = ctx.functions
34
+ @definition = @definition.with_custom_functions(ctx.functions)
38
35
  end
39
36
 
40
37
  def fragment(&block)
41
- @fragment_block = block
42
- @fragment_mode = block.arity > 0 ? :ruby : :c
43
- end
44
-
45
- def ruby_mode?
46
- @fragment_mode == :ruby
38
+ @definition = @definition.with_fragment(
39
+ mode: block.arity > 0 ? :ruby : :c,
40
+ block: block
41
+ )
47
42
  end
48
43
 
49
44
  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)
45
+ build_service.compile_and_load
62
46
  end
63
47
 
64
48
  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)
49
+ build_service.build_metal_shader
77
50
  end
78
51
 
79
52
  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
53
+ build_service.build_wgsl_shader
90
54
  end
91
55
 
92
56
  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
57
+ build_service.build_glsl_shader(version: version)
103
58
  end
104
59
 
105
60
  def transpile_fragment(target)
106
- return "" unless @fragment_block
107
-
108
- transpiler = Prism::Transpiler.new(@uniforms, @custom_functions)
109
- transpiler.transpile(@fragment_block, target)
61
+ build_service.transpile_fragment(target)
110
62
  end
111
63
 
112
64
  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
65
+ build_service.transpile_helpers(target)
121
66
  end
122
67
 
123
68
  private
124
69
 
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
142
- end
143
-
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
70
+ def build_service
71
+ BuildService.new(@name, @definition)
163
72
  end
164
73
  end
165
74
  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: "bool",
120
+ msl_type: "bool",
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: nil,
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
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ module UniformTypes
5
+ module ValueNormalizer
6
+ def normalize_values(uniform_types, uniforms, shader_name: nil)
7
+ uniform_types.each_with_object({}) do |(name, type), normalized|
8
+ unless uniforms.key?(name)
9
+ raise ArgumentError, missing_uniform_message(name, shader_name)
10
+ end
11
+
12
+ normalized[name] = normalize_value(type, uniforms[name], name: name, shader_name: shader_name)
13
+ end
14
+ end
15
+
16
+ def normalize_value(type, value, name:, shader_name: nil)
17
+ return value if type.nil?
18
+
19
+ spec = fetch(type)
20
+ raise ArgumentError, unsupported_runtime_type_message(type, shader_name) unless spec.runtime_supported?
21
+
22
+ case spec.wrapper_kind
23
+ when :float
24
+ Float(value)
25
+ when :int
26
+ Integer(value)
27
+ when :bool
28
+ normalize_bool(value, name: name, shader_name: shader_name)
29
+ when :vector
30
+ normalize_vector(value, spec.vector_size, name: name, shader_name: shader_name)
31
+ else
32
+ raise ArgumentError, unsupported_runtime_type_message(type, shader_name)
33
+ end
34
+ rescue TypeError
35
+ raise ArgumentError, invalid_uniform_message(name, type, value, shader_name)
36
+ rescue ArgumentError => e
37
+ raise e if e.message.start_with?("Invalid value for uniform", "Unsupported runtime uniform type")
38
+
39
+ raise ArgumentError, invalid_uniform_message(name, type, value, shader_name)
40
+ end
41
+
42
+ def normalize_bool(value, name:, shader_name: nil)
43
+ return value if value == true || value == false
44
+ return false if value == 0
45
+ return true if value == 1
46
+
47
+ raise ArgumentError, invalid_uniform_message(name, :bool, value, shader_name)
48
+ end
49
+
50
+ def normalize_vector(value, vector_size, name:, shader_name: nil)
51
+ unless value.is_a?(Array) && value.length == vector_size
52
+ raise ArgumentError, invalid_vector_message(name, vector_size, value, shader_name)
53
+ end
54
+
55
+ value.map { |component| Float(component) }
56
+ end
57
+
58
+ private
59
+
60
+ def missing_uniform_message(name, shader_name)
61
+ "Missing uniform #{name.inspect}#{shader_suffix(shader_name)}"
62
+ end
63
+
64
+ def invalid_uniform_message(name, type, value, shader_name)
65
+ "Invalid value for uniform #{name.inspect}#{shader_suffix(shader_name)}: expected #{type}, got #{value.inspect}"
66
+ end
67
+
68
+ def invalid_vector_message(name, vector_size, value, shader_name)
69
+ "Invalid value for uniform #{name.inspect}#{shader_suffix(shader_name)}: expected vec#{vector_size}, got #{value.inspect}"
70
+ end
71
+
72
+ def unsupported_runtime_type_message(type, shader_name)
73
+ "Unsupported runtime uniform type #{type.inspect}#{shader_suffix(shader_name)}"
74
+ end
75
+
76
+ def shader_suffix(shader_name)
77
+ shader_name ? " in #{shader_name}" : ""
78
+ end
79
+ end
80
+ end
81
+ end
data/lib/rlsl/types.rb CHANGED
@@ -1,36 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module RLSL
4
- # C type definitions for the shader system
5
- C_TYPES = <<~C
6
- typedef struct { float x, y; } vec2;
7
- typedef struct { float x, y, z; } vec3;
8
- typedef struct { float x, y, z, w; } vec4;
9
- typedef struct { float m[4]; } mat2;
10
- typedef struct { float m[9]; } mat3;
11
- typedef struct { float m[16]; } mat4;
12
- typedef struct { void* data; int width; int height; } sampler2D;
13
-
14
- #define PI 3.14159265f
15
- #define TAU 6.28318530f
16
- C
3
+ require_relative "types/type_spec"
4
+ require_relative "types/catalog"
5
+ require_relative "types/target_resolver"
6
+ require_relative "types/value_normalizer"
17
7
 
18
- # Uniform type symbols
19
- UNIFORM_TYPES = %i[float vec2 vec3 vec4 int bool mat2 mat3 mat4 sampler2D].freeze
8
+ module RLSL
9
+ module UniformTypes
10
+ extend Catalog
11
+ extend TargetResolver
12
+ extend ValueNormalizer
13
+ end
20
14
 
21
- # Type mapping helper
22
15
  module TypeMapping
23
- C_UNIFORM_TYPES = {
24
- float: "float",
25
- int: "int",
26
- bool: "int",
27
- vec2: "vec2",
28
- vec3: "vec3",
29
- vec4: "vec4",
30
- mat2: "mat2",
31
- mat3: "mat3",
32
- mat4: "mat4",
33
- sampler2D: "sampler2D"
34
- }.freeze
16
+ C_UNIFORM_TYPES = UNIFORM_TYPE_SPECS.transform_values(&:c_type).freeze
35
17
  end
36
18
  end
@@ -9,20 +9,14 @@ module RLSL
9
9
  @uniforms = {}
10
10
  end
11
11
 
12
- def float(name)
13
- @uniforms[name] = :float
12
+ def define_uniform(name, type)
13
+ @uniforms[name] = type
14
14
  end
15
15
 
16
- def vec2(name)
17
- @uniforms[name] = :vec2
18
- end
19
-
20
- def vec3(name)
21
- @uniforms[name] = :vec3
22
- end
23
-
24
- def vec4(name)
25
- @uniforms[name] = :vec4
16
+ RLSL::UNIFORM_TYPES.each do |type|
17
+ define_method(type) do |name|
18
+ define_uniform(name, type)
19
+ end
26
20
  end
27
21
  end
28
22
  end
data/lib/rlsl/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RLSL
4
- VERSION = "0.1.1"
4
+ VERSION = "1.0.0"
5
5
  end