rlsl 0.1.0 → 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 +14 -1
  3. data/README.md +18 -1
  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 -16
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ class CodeGenerator
5
+ class RubyWrapperGenerator
6
+ VECTOR_CONSTRUCTORS = {
7
+ 2 => "vec2_new",
8
+ 3 => "vec3_new",
9
+ 4 => "vec4_new"
10
+ }.freeze
11
+
12
+ def initialize(context)
13
+ @context = context
14
+ end
15
+
16
+ def generate
17
+ <<~C
18
+ static void render_scanline(uint8_t *pixels, int width, int height, int y, vec2 resolution, Uniforms uniforms) {
19
+ int flipped_y = height - 1 - y;
20
+ for (int x = 0; x < width; x++) {
21
+ vec2 frag_coord = vec2_new((float)x, (float)flipped_y);
22
+ vec3 color = shader_#{@context.name}(frag_coord, resolution, uniforms);
23
+ int idx = (y * width + x) * 4;
24
+ // Output as BGRA (macOS native format)
25
+ pixels[idx] = (uint8_t)(clamp_f(color.z, 0.0f, 1.0f) * 255.0f);
26
+ pixels[idx+1] = (uint8_t)(clamp_f(color.y, 0.0f, 1.0f) * 255.0f);
27
+ pixels[idx+2] = (uint8_t)(clamp_f(color.x, 0.0f, 1.0f) * 255.0f);
28
+ pixels[idx+3] = 255;
29
+ }
30
+ }
31
+
32
+ static VALUE shader_#{@context.name}_render(VALUE self, VALUE rb_buffer, VALUE rb_width, VALUE rb_height#{@context.uniform_argument_list}) {
33
+ int width = NUM2INT(rb_width);
34
+ int height = NUM2INT(rb_height);
35
+ vec2 resolution = vec2_new((float)width, (float)height);
36
+
37
+ Uniforms uniforms;
38
+ #{uniform_assignments}
39
+
40
+ Check_Type(rb_buffer, T_STRING);
41
+ rb_str_modify(rb_buffer);
42
+ uint8_t *pixels = (uint8_t *)RSTRING_PTR(rb_buffer);
43
+
44
+ #ifdef __APPLE__
45
+ dispatch_apply(height, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t y) {
46
+ render_scanline(pixels, width, height, (int)y, resolution, uniforms);
47
+ });
48
+ #else
49
+ for (int y = 0; y < height; y++) {
50
+ render_scanline(pixels, width, height, y, resolution, uniforms);
51
+ }
52
+ #endif
53
+
54
+ return Qnil;
55
+ }
56
+ C
57
+ end
58
+
59
+ private
60
+
61
+ def uniform_assignments
62
+ @context.uniform_entries.map do |uniform_name, spec|
63
+ generate_uniform_assignment(uniform_name, spec)
64
+ end.join("\n")
65
+ end
66
+
67
+ def generate_uniform_assignment(uniform_name, spec)
68
+ case spec.wrapper_kind
69
+ when :float
70
+ " uniforms.#{uniform_name} = (float)NUM2DBL(rb_#{uniform_name});"
71
+ when :int
72
+ " uniforms.#{uniform_name} = NUM2INT(rb_#{uniform_name});"
73
+ when :bool
74
+ " uniforms.#{uniform_name} = RTEST(rb_#{uniform_name}) ? 1 : 0;"
75
+ when :vector
76
+ generate_vector_uniform_assignment(uniform_name, spec.vector_size)
77
+ else
78
+ raise ArgumentError, "Unsupported compiled uniform type: #{spec.c_type}"
79
+ end
80
+ end
81
+
82
+ def generate_vector_uniform_assignment(uniform_name, vector_size)
83
+ constructor = VECTOR_CONSTRUCTORS.fetch(vector_size)
84
+ components = (0...vector_size).map do |index|
85
+ " (float)NUM2DBL(rb_ary_entry(rb_#{uniform_name}, #{index}))"
86
+ end
87
+
88
+ <<~C.strip
89
+ Check_Type(rb_#{uniform_name}, T_ARRAY);
90
+ uniforms.#{uniform_name} = #{constructor}(
91
+ #{components.join(",\n")}
92
+ );
93
+ C
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ class CodeGenerator
5
+ class ShaderFunctionGenerator
6
+ def initialize(context)
7
+ @context = context
8
+ end
9
+
10
+ def generate
11
+ <<~C
12
+ static vec3 shader_#{@context.name}(vec2 frag_coord, vec2 resolution, Uniforms u) {
13
+ #{@context.fragment_code}
14
+ }
15
+ C
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ class CodeGenerator
5
+ class TemplateContext
6
+ attr_reader :name
7
+
8
+ def initialize(name:, uniforms:, helpers_block:, fragment_block:)
9
+ @name = name
10
+ @uniforms = uniforms
11
+ @helpers_block = helpers_block
12
+ @fragment_block = fragment_block
13
+ end
14
+
15
+ def render_arity
16
+ 3 + @uniforms.size
17
+ end
18
+
19
+ def helpers_code
20
+ return "" unless @helpers_block
21
+
22
+ @helpers_block.call
23
+ end
24
+
25
+ def fragment_code
26
+ @fragment_block.call
27
+ end
28
+
29
+ def uniform_entries
30
+ @uniform_entries ||= @uniforms.map do |uniform_name, type|
31
+ [uniform_name, UniformTypes.compiled_spec(type)]
32
+ end
33
+ end
34
+
35
+ def uniform_argument_list
36
+ arguments = @uniforms.map { |uniform_name, _| "VALUE rb_#{uniform_name}" }
37
+ arguments.empty? ? "" : ", #{arguments.join(', ')}"
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ class CodeGenerator
5
+ class UniformStructGenerator
6
+ def initialize(context)
7
+ @context = context
8
+ end
9
+
10
+ def generate
11
+ return "typedef struct {} Uniforms;\n" if @context.uniform_entries.empty?
12
+
13
+ <<~C
14
+ typedef struct {
15
+ #{field_lines}
16
+ } Uniforms;
17
+ C
18
+ end
19
+
20
+ private
21
+
22
+ def field_lines
23
+ @context.uniform_entries.map do |uniform_name, spec|
24
+ " #{spec.c_type} #{uniform_name};"
25
+ end.join("\n")
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,12 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "code_generator/template_context"
4
+ require_relative "code_generator/math_prelude"
5
+ require_relative "code_generator/uniform_struct_generator"
6
+ require_relative "code_generator/shader_function_generator"
7
+ require_relative "code_generator/ruby_wrapper_generator"
8
+
3
9
  module RLSL
4
10
  class CodeGenerator
5
11
  def initialize(name, uniforms, helpers_block, fragment_block)
6
- @name = name
7
- @uniforms = uniforms
8
- @helpers_block = helpers_block
9
- @fragment_block = fragment_block
12
+ @context = TemplateContext.new(
13
+ name: name,
14
+ uniforms: uniforms,
15
+ helpers_block: helpers_block,
16
+ fragment_block: fragment_block
17
+ )
10
18
  end
11
19
 
12
20
  def generate
@@ -18,212 +26,29 @@ module RLSL
18
26
  #include <dispatch/dispatch.h>
19
27
  #endif
20
28
 
21
- #{generate_types}
22
- #{generate_math_helpers}
23
- #{generate_uniform_struct}
24
- #{generate_custom_helpers}
25
- #{generate_shader_function}
26
- #{generate_ruby_wrapper}
29
+ #{RLSL::C_TYPES}
30
+ #{MathPrelude.code}
31
+ #{UniformStructGenerator.new(@context).generate}
32
+ #{custom_helpers}
33
+ #{ShaderFunctionGenerator.new(@context).generate}
34
+ #{RubyWrapperGenerator.new(@context).generate}
27
35
 
28
- void Init_#{@name}(void) {
29
- VALUE mRLSL = rb_define_module("RLSL");
30
- VALUE mShaders = rb_define_module_under(mRLSL, "CompiledShaders");
31
- rb_define_module_function(mShaders, "#{@name}_render", shader_#{@name}_render, #{3 + @uniforms.size});
32
- }
36
+ #{init_function}
33
37
  C
34
38
  end
35
39
 
36
40
  private
37
41
 
38
- def generate_types
39
- <<~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
-
44
- #define PI 3.14159265f
45
- #define TAU 6.28318530f
46
- C
47
- end
48
-
49
- def generate_math_helpers
50
- <<~C
51
- static inline vec2 vec2_new(float x, float y) { return (vec2){x, y}; }
52
- static inline vec3 vec3_new(float x, float y, float z) { return (vec3){x, y, z}; }
53
- static inline vec4 vec4_new(float x, float y, float z, float w) { return (vec4){x, y, z, w}; }
54
-
55
- static inline vec2 vec2_add(vec2 a, vec2 b) { return (vec2){a.x + b.x, a.y + b.y}; }
56
- static inline vec3 vec3_add(vec3 a, vec3 b) { return (vec3){a.x + b.x, a.y + b.y, a.z + b.z}; }
57
-
58
- static inline vec2 vec2_sub(vec2 a, vec2 b) { return (vec2){a.x - b.x, a.y - b.y}; }
59
- static inline vec3 vec3_sub(vec3 a, vec3 b) { return (vec3){a.x - b.x, a.y - b.y, a.z - b.z}; }
60
-
61
- static inline vec2 vec2_mul(vec2 a, float s) { return (vec2){a.x * s, a.y * s}; }
62
- static inline vec3 vec3_mul(vec3 a, float s) { return (vec3){a.x * s, a.y * s, a.z * s}; }
63
-
64
- static inline vec2 vec2_div(vec2 a, float s) { return (vec2){a.x / s, a.y / s}; }
65
- static inline vec3 vec3_div(vec3 a, float s) { return (vec3){a.x / s, a.y / s, a.z / s}; }
66
-
67
- static inline float vec2_dot(vec2 a, vec2 b) { return a.x * b.x + a.y * b.y; }
68
- static inline float vec3_dot(vec3 a, vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
69
-
70
- static inline float vec2_length(vec2 v) { return sqrtf(v.x * v.x + v.y * v.y); }
71
- static inline float vec3_length(vec3 v) { return sqrtf(v.x * v.x + v.y * v.y + v.z * v.z); }
72
-
73
- static inline vec2 vec2_normalize(vec2 v) { float l = vec2_length(v); return l > 0 ? vec2_div(v, l) : v; }
74
- static inline vec3 vec3_normalize(vec3 v) { float l = vec3_length(v); return l > 0 ? vec3_div(v, l) : v; }
75
-
76
- static inline float fract(float x) { return x - floorf(x); }
77
- static inline float mix_f(float a, float b, float t) { return a + (b - a) * t; }
78
- static inline vec3 mix_v3(vec3 a, vec3 b, float t) {
79
- return (vec3){a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t};
80
- }
81
-
82
- static inline float clamp_f(float x, float lo, float hi) {
83
- return x < lo ? lo : (x > hi ? hi : x);
84
- }
85
-
86
- static inline float smoothstep(float edge0, float edge1, float x) {
87
- float t = clamp_f((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
88
- return t * t * (3.0f - 2.0f * t);
89
- }
90
-
91
- static inline float hash21(vec2 p) {
92
- float dot = p.x * 12.9898f + p.y * 78.233f;
93
- return fract(sinf(dot) * 43758.5453f);
94
- }
95
-
96
- static inline vec2 hash22(vec2 p) {
97
- float n = sinf(vec2_dot(p, vec2_new(127.1f, 311.7f)));
98
- return vec2_new(fract(n * 43758.5453f), fract(n * 12345.6789f));
99
- }
100
-
101
- // reflect: I - 2 * dot(N, I) * N
102
- static inline vec3 reflect(vec3 I, vec3 N) {
103
- float d = vec3_dot(N, I);
104
- return vec3_new(I.x - 2.0f * d * N.x, I.y - 2.0f * d * N.y, I.z - 2.0f * d * N.z);
105
- }
106
-
107
- // refract: Snell's law
108
- static inline vec3 refract(vec3 I, vec3 N, float eta) {
109
- float d = vec3_dot(N, I);
110
- float k = 1.0f - eta * eta * (1.0f - d * d);
111
- if (k < 0.0f) {
112
- return vec3_new(0.0f, 0.0f, 0.0f); // Total internal reflection
113
- }
114
- float s = eta * d + sqrtf(k);
115
- return vec3_new(eta * I.x - s * N.x, eta * I.y - s * N.y, eta * I.z - s * N.z);
116
- }
117
- C
118
- end
119
-
120
- def generate_uniform_struct
121
- if @uniforms.empty?
122
- "typedef struct {} Uniforms;\n"
123
- else
124
- fields = @uniforms.map do |name, type|
125
- case type
126
- when :float then " float #{name};"
127
- when :vec2 then " vec2 #{name};"
128
- when :vec3 then " vec3 #{name};"
129
- when :vec4 then " vec4 #{name};"
130
- end
131
- end.join("\n")
132
-
133
- "typedef struct {\n#{fields}\n} Uniforms;\n"
134
- end
135
- end
136
-
137
- def generate_custom_helpers
138
- return "" unless @helpers_block
139
-
140
- @helpers_block.call
42
+ def custom_helpers
43
+ @context.helpers_code
141
44
  end
142
45
 
143
- def generate_shader_function
144
- shader_body = @fragment_block.call
145
-
46
+ def init_function
146
47
  <<~C
147
- static vec3 shader_#{@name}(vec2 frag_coord, vec2 resolution, Uniforms u) {
148
- #{shader_body}
149
- }
150
- C
151
- end
152
-
153
- def generate_ruby_wrapper
154
- uniform_args = @uniforms.map { |name, _| "VALUE rb_#{name}" }.join(", ")
155
- uniform_args = ", " + uniform_args unless uniform_args.empty?
156
-
157
- uniform_parsing = @uniforms.map do |name, type|
158
- case type
159
- when :float
160
- " uniforms.#{name} = (float)NUM2DBL(rb_#{name});"
161
- when :vec2
162
- <<~C.strip
163
- Check_Type(rb_#{name}, T_ARRAY);
164
- uniforms.#{name} = vec2_new(
165
- (float)NUM2DBL(rb_ary_entry(rb_#{name}, 0)),
166
- (float)NUM2DBL(rb_ary_entry(rb_#{name}, 1))
167
- );
168
- C
169
- when :vec3
170
- <<~C.strip
171
- Check_Type(rb_#{name}, T_ARRAY);
172
- uniforms.#{name} = vec3_new(
173
- (float)NUM2DBL(rb_ary_entry(rb_#{name}, 0)),
174
- (float)NUM2DBL(rb_ary_entry(rb_#{name}, 1)),
175
- (float)NUM2DBL(rb_ary_entry(rb_#{name}, 2))
176
- );
177
- C
178
- end
179
- end.join("\n")
180
-
181
- <<~C
182
- static VALUE shader_#{@name}_render(VALUE self, VALUE rb_buffer, VALUE rb_width, VALUE rb_height#{uniform_args}) {
183
- int width = NUM2INT(rb_width);
184
- int height = NUM2INT(rb_height);
185
- vec2 resolution = vec2_new((float)width, (float)height);
186
-
187
- Uniforms uniforms;
188
- #{uniform_parsing}
189
-
190
- Check_Type(rb_buffer, T_STRING);
191
- rb_str_modify(rb_buffer);
192
- uint8_t *pixels = (uint8_t *)RSTRING_PTR(rb_buffer);
193
-
194
- #ifdef __APPLE__
195
- dispatch_apply(height, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t y) {
196
- int flipped_y = height - 1 - (int)y;
197
- for (int x = 0; x < width; x++) {
198
- vec2 frag_coord = vec2_new((float)x, (float)flipped_y);
199
- vec3 color = shader_#{@name}(frag_coord, resolution, uniforms);
200
-
201
- int idx = ((int)y * width + x) * 4;
202
- // Output as BGRA (macOS native format)
203
- pixels[idx] = (uint8_t)(clamp_f(color.z, 0.0f, 1.0f) * 255.0f);
204
- pixels[idx+1] = (uint8_t)(clamp_f(color.y, 0.0f, 1.0f) * 255.0f);
205
- pixels[idx+2] = (uint8_t)(clamp_f(color.x, 0.0f, 1.0f) * 255.0f);
206
- pixels[idx+3] = 255;
207
- }
208
- });
209
- #else
210
- for (int y = 0; y < height; y++) {
211
- int flipped_y = height - 1 - y;
212
- for (int x = 0; x < width; x++) {
213
- vec2 frag_coord = vec2_new((float)x, (float)flipped_y);
214
- vec3 color = shader_#{@name}(frag_coord, resolution, uniforms);
215
-
216
- int idx = (y * width + x) * 4;
217
- // Output as BGRA (macOS native format)
218
- pixels[idx] = (uint8_t)(clamp_f(color.z, 0.0f, 1.0f) * 255.0f);
219
- pixels[idx+1] = (uint8_t)(clamp_f(color.y, 0.0f, 1.0f) * 255.0f);
220
- pixels[idx+2] = (uint8_t)(clamp_f(color.x, 0.0f, 1.0f) * 255.0f);
221
- pixels[idx+3] = 255;
222
- }
223
- }
224
- #endif
225
-
226
- return Qnil;
48
+ void Init_#{@context.name}(void) {
49
+ VALUE mRLSL = rb_define_module("RLSL");
50
+ VALUE mShaders = rb_define_module_under(mRLSL, "CompiledShaders");
51
+ rb_define_module_function(mShaders, "#{@context.name}_render", shader_#{@context.name}_render, #{@context.render_arity});
227
52
  }
228
53
  C
229
54
  end
@@ -1,23 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RLSL
4
- class CompiledShader
5
- def initialize(name, ext_name, uniform_names)
6
- @name = name
4
+ class CompiledShader < RuntimeShader
5
+ def initialize(name, ext_name, uniforms)
6
+ super(name, uniforms)
7
7
  @ext_name = ext_name
8
- @uniform_names = uniform_names
9
8
  @render_method = RLSL::CompiledShaders.method("#{name}_render")
10
9
  end
11
10
 
12
- def metal?
13
- false
14
- end
15
-
16
11
  def render(buffer, width, height, uniforms = {})
17
- args = [buffer, width, height]
18
- @uniform_names.each do |name|
19
- args << uniforms[name]
20
- end
12
+ args = [buffer, width, height] + ordered_uniform_values(uniforms)
21
13
  @render_method.call(*args)
22
14
  end
23
15
  end
@@ -8,28 +8,45 @@ module RLSL
8
8
  @functions = {}
9
9
  end
10
10
 
11
- def float(*names)
12
- names.each { |name| @functions[name.to_sym] = { returns: :float } }
11
+ # Full form: specify return type and parameter types
12
+ # @example
13
+ # define :path_point, returns: :vec3, params: { z: :float }
14
+ # define :noise_a, returns: :float, params: { f: :float, h: :float, k: :float, p: :vec3 }
15
+ def define(name, returns:, params: {})
16
+ @functions[name.to_sym] = {
17
+ returns: validate_type!(returns),
18
+ params: normalize_params(params)
19
+ }
13
20
  end
14
21
 
15
- def vec2(*names)
16
- names.each { |name| @functions[name.to_sym] = { returns: :vec2 } }
22
+ UniformTypes.function_shorthand_types.each do |type|
23
+ define_method(type) do |*names|
24
+ register_shorthand_functions(type, *names)
25
+ end
17
26
  end
18
27
 
19
- def vec3(*names)
20
- names.each { |name| @functions[name.to_sym] = { returns: :vec3 } }
28
+ private
29
+
30
+ def register_shorthand_functions(type, *names)
31
+ validated_type = validate_type!(type)
32
+ names.each do |name|
33
+ @functions[name.to_sym] = { returns: validated_type }
34
+ end
21
35
  end
22
36
 
23
- def vec4(*names)
24
- names.each { |name| @functions[name.to_sym] = { returns: :vec4 } }
37
+ def normalize_params(params)
38
+ params.each_with_object({}) do |(name, type), normalized|
39
+ normalized[name.to_sym] = validate_type!(type)
40
+ end
25
41
  end
26
42
 
27
- # Full form: specify return type and parameter types
28
- # @example
29
- # define :path_point, returns: :vec3, params: { z: :float }
30
- # define :noise_a, returns: :float, params: { f: :float, h: :float, k: :float, p: :vec3 }
31
- def define(name, returns:, params: {})
32
- @functions[name.to_sym] = { returns: returns, params: params }
43
+ def validate_type!(type)
44
+ if type.is_a?(Array)
45
+ return type.map { |element_type| validate_type!(element_type) }
46
+ end
47
+
48
+ UniformTypes.fetch(type)
49
+ type
33
50
  end
34
51
  end
35
52
  end
@@ -3,17 +3,15 @@
3
3
  module RLSL
4
4
  module GLSL
5
5
  class Translator < BaseTranslator
6
- TYPE_MAP = {
7
- "vec2" => "vec2",
8
- "vec3" => "vec3",
9
- "vec4" => "vec4"
10
- }.freeze
11
-
12
- FUNC_REPLACEMENTS = BaseTranslator.common_func_replacements(
13
- target_vec2: "vec2",
14
- target_vec3: "vec3",
15
- target_vec4: "vec4"
16
- ).freeze
6
+ PROFILE = BaseTranslator.build_profile(
7
+ uniform_target: :glsl,
8
+ identifier_replacements: {},
9
+ call_rewrites: BaseTranslator.common_call_rewrites(
10
+ target_vec2: "vec2",
11
+ target_vec3: "vec3",
12
+ target_vec4: "vec4"
13
+ )
14
+ )
17
15
 
18
16
  def initialize(uniforms, helpers_code, fragment_code, version: "450")
19
17
  super(uniforms, helpers_code, fragment_code)
@@ -22,15 +20,6 @@ module RLSL
22
20
 
23
21
  protected
24
22
 
25
- def translate_code(c_code)
26
- result = super(c_code)
27
- return result if result.empty?
28
-
29
- result.gsub!(/\bstatic\s+/, "")
30
- result.gsub!(/\binline\s+/, "")
31
- result
32
- end
33
-
34
23
  def generate_shader(helpers, fragment)
35
24
  <<~GLSL
36
25
  #version #{@version}
@@ -70,28 +59,20 @@ module RLSL
70
59
 
71
60
  private
72
61
 
62
+ def profile
63
+ PROFILE
64
+ end
65
+
73
66
  def generate_uniform_declarations
74
- declarations = ["layout(binding = 1) uniform ShaderUniforms {",
75
- " vec2 resolution;"]
76
- @uniforms.each do |name, type|
77
- glsl_type = uniform_type_to_target(type)
78
- declarations << " #{glsl_type} #{name};"
79
- end
67
+ declarations = ["layout(binding = 1) uniform ShaderUniforms {"]
68
+ declarations.concat(
69
+ uniform_lines(resolution_line: " #{target_vec2_type} resolution;") do |name, glsl_type|
70
+ " #{glsl_type} #{name};"
71
+ end
72
+ )
80
73
  declarations << "} u;"
81
74
  declarations.join("\n")
82
75
  end
83
-
84
- def target_vec2_type
85
- "vec2"
86
- end
87
-
88
- def target_vec3_type
89
- "vec3"
90
- end
91
-
92
- def target_vec4_type
93
- "vec4"
94
- end
95
76
  end
96
77
  end
97
78
  end
@@ -9,21 +9,24 @@ end
9
9
 
10
10
  module RLSL
11
11
  module MSL
12
- class Shader
12
+ class Shader < RuntimeShader
13
13
  attr_reader :name, :msl_source
14
14
 
15
15
  def initialize(name, uniforms, msl_source)
16
- @name = name
17
- @uniform_types = uniforms
18
- @uniform_names = uniforms.keys
16
+ super(name, uniforms)
19
17
  @msl_source = msl_source
20
18
  @compiled_handles = {}
19
+ @uniform_buffer_packer = UniformBufferPacker.new(@name, @uniform_types, @uniform_names)
21
20
  end
22
21
 
23
22
  def metal?
24
23
  true
25
24
  end
26
25
 
26
+ def render(handle, width, height, uniforms = {})
27
+ render_metal(handle, width, height, uniforms)
28
+ end
29
+
27
30
  def render_metal(handle, width, height, uniforms = {})
28
31
  unless METACO_AVAILABLE
29
32
  raise LoadError, "metaco gem is required for Metal rendering. Install it with: gem install metaco"
@@ -43,40 +46,7 @@ module RLSL
43
46
  private
44
47
 
45
48
  def pack_uniforms(uniforms, width, height)
46
- data = [width.to_f, height.to_f].pack("ff")
47
- current_offset = 8
48
-
49
- @uniform_names.each do |name|
50
- value = uniforms[name]
51
- type = @uniform_types[name]
52
-
53
- alignment = case type
54
- when :float then 4
55
- when :vec2 then 8
56
- when :vec3, :vec4 then 16
57
- end
58
-
59
- padding_needed = (alignment - (current_offset % alignment)) % alignment
60
- data += "\x00" * padding_needed
61
- current_offset += padding_needed
62
-
63
- case type
64
- when :float
65
- data += [value.to_f].pack("f")
66
- current_offset += 4
67
- when :vec2
68
- data += value.pack("ff")
69
- current_offset += 8
70
- when :vec3
71
- data += (value + [0.0]).pack("ffff")
72
- current_offset += 16
73
- when :vec4
74
- data += value.pack("ffff")
75
- current_offset += 16
76
- end
77
- end
78
-
79
- data.ljust(256, "\x00")
49
+ @uniform_buffer_packer.pack(width, height, uniforms)
80
50
  end
81
51
  end
82
52
  end